Add Portfolio and Bookshop QR types with tailored landing pages.
Deploy Ladill QR Plus / deploy (push) Successful in 58s

Portfolio showcases freelancer work projects; Bookshop lists author titles with multi-platform purchase links. Includes create/edit forms, public assets, and tests.
This commit is contained in:
isaacclad
2026-07-16 20:56:52 +00:00
parent 95d73d1367
commit d83a314bee
13 changed files with 989 additions and 1 deletions
+135
View File
@@ -0,0 +1,135 @@
<?php
namespace Tests\Feature;
use App\Http\Middleware\EnsurePlatformSession;
use App\Models\QrCode;
use App\Models\User;
use App\Services\Qr\QrPayloadValidator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;
class PortfolioBookshopQrTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->withoutMiddleware([
EnsurePlatformSession::class,
\App\Http\Middleware\RedirectLegacyQrToLadillLink::class,
]);
if (method_exists($this, 'withoutVite')) {
$this->withoutVite();
}
}
private function user(): User
{
return User::create([
'public_id' => (string) Str::uuid(),
'name' => 'Creator',
'email' => 'creator+'.uniqid().'@example.com',
]);
}
public function test_portfolio_payload_validates_and_landing_renders(): void
{
$validator = app(QrPayloadValidator::class);
$result = $validator->validateForCreate(QrCode::TYPE_PORTFOLIO, [
'name' => 'Ama Mensah',
'role' => 'Brand designer',
'bio' => 'I design identity systems.',
'email' => 'ama@example.com',
'projects' => [
[
'title' => 'Cafe rebrand',
'description' => 'Logo and packaging',
'url' => 'https://example.com/cafe',
'tags' => 'branding, print',
],
],
]);
$this->assertSame('Ama Mensah', $result['content']['name']);
$this->assertCount(1, $result['content']['projects']);
$this->assertSame(['branding', 'print'], $result['content']['projects'][0]['tags']);
$user = $this->user();
$code = QrCode::create([
'user_id' => $user->id,
'short_code' => 'portf001',
'type' => QrCode::TYPE_PORTFOLIO,
'label' => 'Ama portfolio',
'is_active' => true,
'payload' => ['content' => $result['content'], 'style' => []],
]);
$this->get(route('qr.public.resolve', $code->short_code))
->assertOk()
->assertSee('Ama Mensah')
->assertSee('Cafe rebrand')
->assertSee('Selected work');
}
public function test_bookshop_payload_validates_and_landing_renders(): void
{
$validator = app(QrPayloadValidator::class);
$result = $validator->validateForCreate(QrCode::TYPE_BOOKSHOP, [
'author_name' => 'Kojo Asante',
'tagline' => 'Stories from Accra',
'books' => [
[
'title' => 'Harmattan Dust',
'description' => 'A novel of return.',
'buy_links' => [
['label' => 'Amazon', 'url' => 'https://amazon.com/dp/example'],
['label' => 'Bookshop.org', 'url' => 'https://bookshop.org/example'],
],
],
],
]);
$this->assertSame('Kojo Asante', $result['content']['author_name']);
$this->assertCount(2, $result['content']['books'][0]['buy_links']);
$user = $this->user();
$code = QrCode::create([
'user_id' => $user->id,
'short_code' => 'books001',
'type' => QrCode::TYPE_BOOKSHOP,
'label' => 'Kojo books',
'is_active' => true,
'payload' => ['content' => $result['content'], 'style' => []],
]);
$this->get(route('qr.public.resolve', $code->short_code))
->assertOk()
->assertSee('Kojo Asante')
->assertSee('Harmattan Dust')
->assertSee('Amazon')
->assertSee('Bookshop.org');
}
public function test_portfolio_requires_project(): void
{
$this->expectException(\RuntimeException::class);
app(QrPayloadValidator::class)->validateForCreate(QrCode::TYPE_PORTFOLIO, [
'name' => 'No projects',
'projects' => [],
]);
}
public function test_bookshop_requires_buy_link(): void
{
$this->expectException(\RuntimeException::class);
app(QrPayloadValidator::class)->validateForCreate(QrCode::TYPE_BOOKSHOP, [
'author_name' => 'Author',
'books' => [
['title' => 'Untitled', 'buy_links' => []],
],
]);
}
}