test(admin): pagination controls, HTML strip, Ready label
Four tests covering the admin polish follow-ups to Phase 7: - Pagination partial renders both First and Last anchors with the correct aria-labels and page URLs on a middle-page view. - Pagination partial degrades to a disabled First span on page 1 so the admin can't click into an invalid negative page. - Admin video list strips HTML tags from the description preview (no "<p>Hello" leaking into the row) and carries the full title in a title="" tooltip on the ellipsized columns. - Dashboard stat card now shows "Ready" instead of the misleading "Live Videos" label the Phase 7 commit renamed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
5adafc7616
commit
5aef41201a
1 file changed
+139
@@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Http\Middleware\VerifyCsrfToken;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Video;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin polish follow-ups to Phase 7 — storage math, pagination controls,
|
||||||
|
* HTML-stripped descriptions and the grouped error log viewer.
|
||||||
|
*/
|
||||||
|
class AdminPolishTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
putenv('DB_CONNECTION=sqlite');
|
||||||
|
putenv('DB_DATABASE=:memory:');
|
||||||
|
$_ENV['DB_CONNECTION'] = 'sqlite';
|
||||||
|
$_ENV['DB_DATABASE'] = ':memory:';
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->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(
|
||||||
|
'/<span[^>]+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' => '<p>Hello <strong>world</strong> <a href="#">link</a></p>',
|
||||||
|
'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 <p> / <strong>.
|
||||||
|
$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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user