gallery fix

This commit is contained in:
2026-02-21 21:39:23 +01:00
parent e4e0bdf8f1
commit 48e2055b6a
20 changed files with 1064 additions and 481 deletions

View File

@@ -7,13 +7,134 @@ use App\Models\Category;
use App\Models\ContentType;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;
class BrowseApiTest extends TestCase
{
use RefreshDatabase;
public function test_web_browse_renders_canonical_and_rel_prev_next_for_paginated_pages(): void
{
$user = User::factory()->create(['name' => 'Seo Author']);
$contentType = ContentType::create([
'name' => 'Skins',
'slug' => 'skins',
'description' => 'Skins content type',
]);
$category = Category::create([
'content_type_id' => $contentType->id,
'name' => 'Classic',
'slug' => 'classic',
'description' => 'Classic skins',
'is_active' => true,
'sort_order' => 1,
]);
for ($i = 1; $i <= 25; $i++) {
$artwork = Artwork::factory()
->for($user)
->create([
'title' => 'Seo Item ' . $i,
'slug' => 'seo-item-' . $i,
'published_at' => now()->subMinutes($i),
]);
$artwork->categories()->attach($category->id);
}
$response = $this->get('/browse?limit=12&grid=v2');
$response->assertOk();
$html = $response->getContent();
$this->assertNotFalse($html);
$this->assertStringContainsString('<meta name="robots" content="index,follow" />', $html);
$this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/browse\?limit=12"\s*\/>/i', $html);
preg_match('/<link rel="canonical" href="([^"]+)"\s*\/>/i', $html, $canonicalMatches);
$this->assertArrayHasKey(1, $canonicalMatches);
$canonicalUrl = html_entity_decode((string) $canonicalMatches[1], ENT_QUOTES);
$this->assertStringNotContainsString('grid=v2', $canonicalUrl);
$this->assertMatchesRegularExpression('/<link rel="next" href="([^"]+)"\s*\/>/i', $html);
preg_match('/<link rel="next" href="([^"]+)"\s*\/>/i', $html, $nextMatches);
$this->assertArrayHasKey(1, $nextMatches);
$nextUrl = html_entity_decode((string) $nextMatches[1], ENT_QUOTES);
$this->assertStringContainsString('cursor=', $nextUrl);
$this->assertStringNotContainsString('grid=v2', $nextUrl);
$secondPage = $this->get($nextUrl);
$secondPage->assertOk();
$secondHtml = $secondPage->getContent();
$this->assertNotFalse($secondHtml);
$this->assertMatchesRegularExpression('/<link rel="prev" href="([^"]+)"\s*\/>/i', $secondHtml);
preg_match('/<link rel="prev" href="([^"]+)"\s*\/>/i', $secondHtml, $prevMatches);
$this->assertArrayHasKey(1, $prevMatches);
$prevUrl = html_entity_decode((string) $prevMatches[1], ENT_QUOTES);
$this->assertStringNotContainsString('grid=v2', $prevUrl);
$this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/browse\?[^\"]*cursor=/i', $secondHtml);
preg_match('/<link rel="canonical" href="([^"]+)"\s*\/>/i', $secondHtml, $secondCanonicalMatches);
$this->assertArrayHasKey(1, $secondCanonicalMatches);
$secondCanonicalUrl = html_entity_decode((string) $secondCanonicalMatches[1], ENT_QUOTES);
$this->assertStringNotContainsString('grid=v2', $secondCanonicalUrl);
$pageOne = $this->get('/browse?limit=12&page=1&grid=v2');
$pageOne->assertOk();
$pageOneHtml = $pageOne->getContent();
$this->assertNotFalse($pageOneHtml);
$this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/browse\?limit=12"\s*\/>/i', $pageOneHtml);
preg_match('/<link rel="canonical" href="([^"]+)"\s*\/>/i', $pageOneHtml, $pageOneCanonicalMatches);
$this->assertArrayHasKey(1, $pageOneCanonicalMatches);
$pageOneCanonicalUrl = html_entity_decode((string) $pageOneCanonicalMatches[1], ENT_QUOTES);
$this->assertStringNotContainsString('page=1', $pageOneCanonicalUrl);
}
public function test_api_browse_supports_limit_and_cursor_pagination(): void
{
$user = User::factory()->create(['name' => 'Cursor Author']);
$contentType = ContentType::create([
'name' => 'Skins',
'slug' => 'skins',
'description' => 'Skins content type',
]);
$category = Category::create([
'content_type_id' => $contentType->id,
'name' => 'Winamp',
'slug' => 'winamp',
'description' => 'Winamp skins',
'is_active' => true,
'sort_order' => 1,
]);
for ($i = 1; $i <= 6; $i++) {
$artwork = Artwork::factory()
->for($user)
->create([
'title' => 'Cursor Item ' . $i,
'slug' => 'cursor-item-' . $i,
'published_at' => now()->subMinutes($i),
]);
$artwork->categories()->attach($category->id);
}
$first = $this->getJson('/api/v1/browse?limit=2');
$first->assertOk();
$first->assertJsonCount(2, 'data');
$nextCursor = (string) data_get($first->json(), 'links.next', '');
$this->assertNotEmpty($nextCursor);
$this->assertStringContainsString('cursor=', $nextCursor);
$second = $this->getJson($nextCursor);
$second->assertOk();
$second->assertJsonCount(2, 'data');
$firstFirstSlug = data_get($first->json(), 'data.0.slug');
$secondFirstSlug = data_get($second->json(), 'data.0.slug');
$this->assertNotSame($firstFirstSlug, $secondFirstSlug);
}
public function test_api_browse_returns_public_artworks(): void
{
$user = User::factory()->create(['name' => 'Author One']);
@@ -82,5 +203,14 @@ class BrowseApiTest extends TestCase
$response->assertOk();
$response->assertSee('Forest Light');
$response->assertSee('Author Two');
$html = $response->getContent();
$this->assertNotFalse($html);
$this->assertStringContainsString('itemprop="thumbnailUrl"', $html);
// First card (index 0) is eager-loaded with fetchpriority=high — no blur-preview
$this->assertStringContainsString('loading="eager"', $html);
$this->assertStringContainsString('decoding="sync"', $html);
$this->assertStringContainsString('fetchpriority="high"', $html);
$this->assertMatchesRegularExpression('/<img[^>]*loading="eager"[^>]*width="\d+"[^>]*height="\d+"/i', $html);
}
}

View File

@@ -47,11 +47,19 @@ class DashboardFavoritesTest extends TestCase
DB::table($favTable)->insert($insert);
$this->actingAs($user)
$response = $this->actingAs($user)
->get(route('dashboard.favorites'))
->assertOk()
->assertSee('Fav Artwork');
$html = $response->getContent();
$this->assertNotFalse($html);
$this->assertStringContainsString('itemprop="thumbnailUrl"', $html);
$this->assertStringContainsString('data-blur-preview', $html);
$this->assertStringContainsString('loading="lazy"', $html);
$this->assertStringContainsString('decoding="async"', $html);
$this->assertMatchesRegularExpression('/<img[^>]*data-blur-preview[^>]*width="\d+"[^>]*height="\d+"/i', $html);
$this->actingAs($user)
->delete(route('dashboard.favorites.destroy', ['artwork' => $art->id]))
->assertRedirect(route('dashboard.favorites'));