get('/explore')->assertOk(); }); it('GET /explore contains "Explore" heading', function () { $this->get('/explore')->assertOk()->assertSee('Explore', false); }); it('GET /explore/wallpapers returns 200', function () { $this->get('/explore/wallpapers')->assertOk(); }); it('GET /explore/skins returns 200', function () { $this->get('/explore/skins')->assertOk(); }); it('GET /explore/photography returns 200', function () { $this->get('/explore/photography')->assertOk(); }); it('GET /explore/artworks returns 200', function () { $this->get('/explore/artworks')->assertOk(); }); it('GET /explore/other returns 200', function () { $this->get('/explore/other')->assertOk(); }); it('GET /explore/wallpapers/trending returns 200', function () { $this->get('/explore/wallpapers/trending')->assertOk(); }); it('GET /explore/wallpapers/latest returns 200', function () { $this->get('/explore/wallpapers/latest')->assertOk(); }); it('GET /explore/wallpapers/best returns 200', function () { $this->get('/explore/wallpapers/best')->assertOk(); }); it('GET /explore/wallpapers/new-hot returns 200', function () { $this->get('/explore/wallpapers/new-hot')->assertOk(); }); it('/explore pages include canonical link tag', function () { $html = $this->get('/explore')->assertOk()->getContent(); expect($html)->toContain('rel="canonical"'); }); it('/explore pages set robots index,follow', function () { $html = $this->get('/explore')->assertOk()->getContent(); expect($html)->toContain('index,follow'); }); it('/explore pages include breadcrumb JSON-LD', function () { $html = $this->get('/explore/wallpapers')->assertOk()->getContent(); expect($html)->toContain('BreadcrumbList'); }); // ── 301 redirects from legacy routes ───────────────────────────────────────── it('GET /browse redirects to /explore with 301', function () { $this->get('/browse')->assertRedirect('/explore')->assertStatus(301); }); it('GET /discover redirects to /discover/trending with 301', function () { $this->get('/discover')->assertRedirect('/discover/trending')->assertStatus(301); }); it('GET /sections redirects to /categories with 301', function () { $this->get('/sections')->assertRedirect('/categories')->assertStatus(301); }); it('GET /browse-categories redirects to /categories with 301', function () { $this->get('/browse-categories')->assertRedirect('/categories')->assertStatus(301); }); it('legacy mixed-case category route redirects to the canonical category URL with 301', function () { $contentType = ContentType::query()->create([ 'name' => 'Skins', 'slug' => 'skins', ]); $category = Category::query()->create([ 'content_type_id' => $contentType->id, 'name' => 'BrowserBob', 'slug' => 'browserbob', ]); $this->get('/Skins/BrowserBob/' . $category->id . '?ref=legacy') ->assertRedirect('/skins/browserbob?ref=legacy') ->assertStatus(301); }); it('legacy nested category route redirects to the canonical nested category URL with 301', function () { $contentType = ContentType::query()->create([ 'name' => 'Skins', 'slug' => 'skins', ]); $parent = Category::query()->create([ 'content_type_id' => $contentType->id, 'name' => 'BrowserBob', 'slug' => 'browserbob', ]); $child = Category::query()->create([ 'content_type_id' => $contentType->id, 'parent_id' => $parent->id, 'name' => 'sdsdsdsd', 'slug' => 'sdsdsdsd', ]); $this->get('/Skins/BrowserBob/sdsdsdsd/' . $child->id) ->assertRedirect('/skins/browserbob/sdsdsdsd') ->assertStatus(301); }); it('legacy category route falls back to /categories and preserves query string when no category matches', function () { $this->get('/Skins/does-not-exist/999999?source=old-site') ->assertRedirect('/categories?source=old-site') ->assertStatus(301); }); it('legacy /category route redirects to the canonical category URL with 301', function () { $contentType = ContentType::query()->create([ 'name' => 'Skins', 'slug' => 'skins', ]); $category = Category::query()->create([ 'content_type_id' => $contentType->id, 'name' => 'BrowserBob', 'slug' => 'browserbob', ]); $this->get('/category/skins/browserbob/' . $category->id . '?ref=legacy-category') ->assertRedirect('/skins/browserbob?ref=legacy-category') ->assertStatus(301); }); it('legacy /category route falls back to /categories and preserves query string when no category matches', function () { $this->get('/category/skins/does-not-exist/999999?source=legacy-category') ->assertRedirect('/categories?source=legacy-category') ->assertStatus(301); }); it('GET /today-in-history redirects to /discover/on-this-day with 301', function () { $this->get('/today-in-history')->assertRedirect('/discover/on-this-day')->assertStatus(301); }); it('GET /members redirects to /creators/top with 301', function () { $this->get('/members')->assertRedirect('/creators/top')->assertStatus(301); }); it('GET /explore/members redirects to /creators/top with 301', function () { $this->get('/explore/members')->assertRedirect('/creators/top')->assertStatus(301); }); it('GET /latest-artworks redirects to /discover/fresh with 301', function () { $this->get('/latest-artworks')->assertRedirect('/discover/fresh')->assertStatus(301); }); it('GET /today-downloads redirects to /downloads/today with 301', function () { $this->get('/today-downloads')->assertRedirect('/downloads/today')->assertStatus(301); }); it('GET /monthly-commentators redirects to /comments/monthly with 301', function () { $this->get('/monthly-commentators')->assertRedirect('/comments/monthly')->assertStatus(301); }); it('GET /top-favourites redirects to /discover/top-rated with 301', function () { $this->get('/top-favourites')->assertRedirect('/discover/top-rated')->assertStatus(301); }); it('GET /downloads/today returns 200', function () { $this->get('/downloads/today')->assertOk(); }); // ── /blog routes ───────────────────────────────────────────────────────────── it('GET /blog returns 200', function () { $this->get('/blog')->assertOk(); }); it('/blog page contains Blog heading', function () { $html = $this->get('/blog')->assertOk()->getContent(); expect($html)->toContain('Blog'); }); it('/blog page includes canonical link', function () { $html = $this->get('/blog')->assertOk()->getContent(); expect($html)->toContain('rel="canonical"'); }); it('/blog/:slug returns 404 for non-existent post', function () { $this->get('/blog/this-post-does-not-exist-xyz')->assertNotFound(); }); it('published blog post is accessible at /blog/:slug', function () { $post = \App\Models\BlogPost::factory()->create([ 'slug' => 'hello-world', 'title' => 'Hello World Post', 'body' => '

Test content.

', 'is_published' => true, 'published_at' => now()->subDay(), ]); $html = $this->get('/blog/hello-world')->assertOk()->getContent(); expect($html)->toContain('Hello World Post'); }); it('unpublished blog post returns 404', function () { \App\Models\BlogPost::factory()->create([ 'slug' => 'draft-post', 'title' => 'Draft Post', 'body' => '

Draft.

', 'is_published' => false, ]); $this->get('/blog/draft-post')->assertNotFound(); }); it('/blog post includes Article JSON-LD', function () { \App\Models\BlogPost::factory()->create([ 'slug' => 'schema-post', 'title' => 'Schema Post', 'body' => '

Content.

', 'is_published' => true, 'published_at' => now()->subHour(), ]); $html = $this->get('/blog/schema-post')->assertOk()->getContent(); expect($html)->toContain('"Article"'); }); // ── /pages routes ───────────────────────────────────────────────────────────── it('GET /pages/:slug returns 404 for non-existent page', function () { $this->get('/pages/does-not-exist-xyz')->assertNotFound(); }); it('published page is accessible at /pages/:slug', function () { \App\Models\Page::factory()->create([ 'slug' => 'test-page', 'title' => 'Test Page Title', 'body' => '

Page content.

', 'is_published' => true, 'published_at' => now()->subDay(), ]); $html = $this->get('/pages/test-page')->assertOk()->getContent(); expect($html)->toContain('Test Page Title'); }); it('/about returns 200 when about page exists', function () { \App\Models\Page::factory()->create([ 'slug' => 'about', 'title' => 'About Skinbase', 'body' => '

About us.

', 'is_published' => true, 'published_at' => now()->subDay(), ]); $this->get('/about')->assertOk(); }); it('/legal/terms returns 200 when legal-terms page exists', function () { \App\Models\Page::factory()->create([ 'slug' => 'legal-terms', 'title' => 'Terms of Service', 'body' => '

Terms.

', 'is_published' => true, 'published_at' => now()->subDay(), ]); $this->get('/legal/terms')->assertOk(); }); // ── tags index layout ───────────────────────────────────────────────────────── it('/tags page renders with ContentLayout breadcrumbs', function () { $html = $this->get('/tags')->assertOk()->getContent(); // ContentLayout should inject breadcrumb nav expect($html)->toContain('Tags'); }); it('/tags page includes canonical and robots', function () { $html = $this->get('/tags')->assertOk()->getContent(); expect($html) ->toContain('rel="canonical"') ->toContain('index,follow'); });