diff --git a/tests/Feature/AdminPolishTest.php b/tests/Feature/AdminPolishTest.php new file mode 100644 index 0000000..a226ec4 --- /dev/null +++ b/tests/Feature/AdminPolishTest.php @@ -0,0 +1,139 @@ +withoutMiddleware([ + VerifyCsrfToken::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, + ]); + } + + // ─── Pagination partial: First + Last buttons ───── + + public function test_pagination_partial_renders_first_and_last_controls(): void + { + $paginator = new LengthAwarePaginator( + items: ['a', 'b', 'c'], + total: 100, + perPage: 10, + currentPage: 5, + options: ['path' => '/videos'] + ); + + $html = view('partials.pagination', [ + 'paginator' => $paginator, + 'elements' => $paginator->linkCollection()->toArray(), + ])->render(); + + $this->assertStringContainsString('aria-label="First page"', $html); + $this->assertStringContainsString('aria-label="Last page"', $html); + $this->assertStringContainsString('aria-label="Previous page"', $html); + $this->assertStringContainsString('aria-label="Next page"', $html); + + // The anchors should link to page 1 and page 10 respectively. + $this->assertStringContainsString('href="/videos?page=1"', $html); + $this->assertStringContainsString('href="/videos?page=10"', $html); + } + + public function test_pagination_disables_first_on_page_one(): void + { + // Two-page paginator — if there's only one page the partial + // short-circuits via @if($paginator->hasPages()) and renders + // nothing, which isn't what we're testing. + $paginator = new LengthAwarePaginator( + items: ['a'], + total: 20, + perPage: 10, + currentPage: 1, + options: ['path' => '/videos'] + ); + + $html = view('partials.pagination', [ + 'paginator' => $paginator, + 'elements' => $paginator->linkCollection()->toArray(), + ])->render(); + + // On page 1 the First button is a disabled span, not an anchor. + $this->assertMatchesRegularExpression( + '/]+class="pag-item disabled"[^>]+title="First page"/', + $html + ); + } + + // ─── Admin videos list: HTML-stripped description ─ + + public function test_admin_video_description_renders_without_html_tags(): void + { + $owner = User::factory()->create([ + 'username' => 'u'.substr(bin2hex(random_bytes(4)), 0, 8), + 'role' => 'super_admin', + ]); + $video = Video::create([ + 'user_id' => $owner->id, + 'title' => 'The Full Video Title That Will Be Ellipsized', + 'description' => '

Hello world link

', + 'filename' => 'users/x/videos/y/video.mp4', + 'path' => 'users/x/videos/y/video.mp4', + 'type' => 'generic', + 'visibility' => 'public', + 'status' => 'ready', + 'share_token' => bin2hex(random_bytes(8)), + ]); + $this->actingAs($owner); + + $html = $this->get('/admin/videos')->getContent(); + + // Admin list must render plain text, not literal

/ . + $this->assertStringNotContainsString('<p>Hello', $html, + 'Description must be stripped of HTML tags before display.'); + $this->assertStringContainsString('Hello world link', $html, + 'Stripped text should still be visible.'); + // Title tooltip in both the title column and description preview. + $this->assertStringContainsString( + 'title="The Full Video Title That Will Be Ellipsized"', + $html, + 'Admin title column must carry a title="" attribute for the full name.' + ); + } + + // ─── Dashboard: Ready label ────────────────────── + + public function test_dashboard_ready_stat_card_uses_the_new_label(): void + { + $owner = User::factory()->create([ + 'username' => 'ad'.substr(bin2hex(random_bytes(4)), 0, 6), + 'role' => 'super_admin', + ]); + $this->actingAs($owner); + + $html = $this->get('/admin/dashboard')->getContent(); + + $this->assertStringContainsString('>Ready<', $html); + $this->assertStringNotContainsString('>Live Videos<', $html, + 'The old "Live Videos" stat-card label should be gone.'); + } +}