Files
SkinbaseNova/tests/Feature/BrowseApiTest.php
2026-03-28 19:15:39 +01:00

203 lines
7.3 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Artwork;
use App\Models\Category;
use App\Models\ContentType;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
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('/explore?limit=12&grid=v2');
$response->assertOk();
$html = $response->getContent();
$this->assertNotFalse($html);
$this->assertStringContainsString('name="robots" content="index,follow"', $html);
$this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/explore\?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);
$secondPage = $this->get('/explore?limit=12&page=2&grid=v2');
$secondPage->assertOk();
$secondHtml = $secondPage->getContent();
$this->assertNotFalse($secondHtml);
$this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/explore\?[^"]*page=2/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);
$this->assertStringContainsString('page=2', $secondCanonicalUrl);
$pageOne = $this->get('/explore?limit=12&page=1&grid=v2');
$pageOne->assertOk();
$pageOneHtml = $pageOne->getContent();
$this->assertNotFalse($pageOneHtml);
$this->assertMatchesRegularExpression('/<link rel="canonical" href="[^"]*\/explore\?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']);
$contentType = ContentType::create([
'name' => 'Wallpapers',
'slug' => 'wallpapers',
'description' => 'Wallpapers content type',
]);
$category = Category::create([
'content_type_id' => $contentType->id,
'name' => 'Abstract',
'slug' => 'abstract',
'description' => 'Abstract wallpapers',
'is_active' => true,
'sort_order' => 1,
]);
$artwork = Artwork::factory()
->for($user)
->create([
'slug' => 'neon-city',
'published_at' => now()->subDay(),
]);
$artwork->categories()->attach($category->id);
$response = $this->getJson('/api/v1/browse');
$response->assertOk()
->assertJsonPath('data.0.slug', 'neon-city')
->assertJsonPath('data.0.category.slug', 'abstract')
->assertJsonPath('data.0.author.name', 'Author One');
}
public function test_web_browse_shows_artworks(): void
{
$user = User::factory()->create(['name' => 'Author Two']);
$contentType = ContentType::create([
'name' => 'Photography',
'slug' => 'photography',
'description' => 'Photos',
]);
$category = Category::create([
'content_type_id' => $contentType->id,
'name' => 'Nature',
'slug' => 'nature',
'description' => 'Nature photos',
'is_active' => true,
'sort_order' => 1,
]);
$artwork = Artwork::factory()
->for($user)
->create([
'title' => 'Forest Light',
'slug' => 'forest-light',
'published_at' => now()->subDay(),
]);
$artwork->categories()->attach($category->id);
$response = $this->get('/explore');
$response->assertOk();
$response->assertSee('Forest Light');
$response->assertSee('Author Two');
$html = $response->getContent();
$this->assertNotFalse($html);
$this->assertStringContainsString('data-react-masonry-gallery', $html);
$this->assertStringContainsString('data-gallery-type="browse"', $html);
$this->assertStringContainsString('Forest Light', $html);
}
}