diff --git a/resources/views/user/liked.blade.php b/resources/views/user/liked.blade.php index 6a50a95..316f2cb 100644 --- a/resources/views/user/liked.blade.php +++ b/resources/views/user/liked.blade.php @@ -146,7 +146,8 @@ @endforeach -
{{ $videos->onEachSide(1)->links() }}
+ {{-- Cursor paginator has no "N pages" concept — just prev/next. --}} +
{{ $videos->links() }}
@endif @endsection diff --git a/tests/Feature/PostAuditPolishTest.php b/tests/Feature/PostAuditPolishTest.php new file mode 100644 index 0000000..5d11648 --- /dev/null +++ b/tests/Feature/PostAuditPolishTest.php @@ -0,0 +1,165 @@ +withoutMiddleware([ + VerifyCsrfToken::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, + ]); + } + + // ─── Match page: channel row ───────────────────── + + public function test_match_watch_page_renders_the_channel_row(): void + { + $owner = $this->makeUser('match-owner'); + $video = Video::create([ + 'user_id' => $owner->id, + 'title' => 'MatchTitle', + 'description' => '', + 'filename' => 'users/x/sports/y/video.mp4', + 'path' => 'users/x/sports/y/video.mp4', + 'type' => 'match', + 'visibility' => 'public', + 'status' => 'ready', + 'share_token' => bin2hex(random_bytes(8)), + ]); + // Minimal sports_match so the match blade renders without null checks exploding. + SportsMatch::create([ + 'video_id' => $video->id, + 'user_id' => $owner->id, + 'title' => 'MatchTitle', + 'participants' => [], + 'sport' => 'karate', + ]); + + $html = $this->get(route('videos.show', $video))->getContent(); + + $this->assertStringContainsString('channel-row', $html, + 'Match watch page must render the shared channel row component.'); + } + + // ─── Comment reply: nested handle button present ── + + public function test_reply_to_reply_button_carries_data_handle(): void + { + $owner = $this->makeUser('replyowner'); + $this->actingAs($owner); + $video = Video::create([ + 'user_id' => $owner->id, + 'title' => 'Vid', + 'description'=> '', + '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)), + ]); + $parent = Comment::create([ + 'user_id' => $owner->id, + 'video_id' => $video->id, + 'body' => 'top', + ]); + $replier = $this->makeUser('replier'); + Comment::create([ + 'user_id' => $replier->id, + 'video_id' => $video->id, + 'parent_id' => $parent->id, + 'body' => 'nested', + ]); + + $html = view('components.video-comments', ['video' => $video])->render(); + + // The nested reply trigger must carry the reply author's handle so the + // JS can prefill "@handle " when the parent's reply form opens. + $this->assertMatchesRegularExpression( + '/_comment-reply-nested-trigger[^"]*"\s+data-parent-id="' . $parent->id . '"\s+data-handle="' . preg_quote($replier->username, '/') . '"/', + $html + ); + } + + // ─── Liked feed: cursor pagination ──────────────── + + public function test_liked_controller_returns_a_cursor_paginator(): void + { + // The controller introspection is enough to confirm the pagination + // mode — the full HTML path depends on view rendering that costs + // 400 KB per test and isn't the behaviour we're asserting on. + $user = $this->makeUser('liker'); + $this->actingAs($user); + + $route = app('router')->getRoutes()->getByName('liked'); + [$class, $method] = explode('@', $route->getActionName()); + $controller = app($class); + + $response = $controller->$method(); + $videos = $response->getData()['videos'] ?? null; + + $this->assertInstanceOf( + \Illuminate\Pagination\CursorPaginator::class, + $videos, + 'liked() must return a CursorPaginator instance.' + ); + } + + // ─── Player hotkey code is present ──────────────── + + public function test_player_component_handles_shift_dot_hotkey(): void + { + // The player component accepts a $video context — pass a bare + // record so the view renders. + $u = $this->makeUser('p'); + $v = Video::create([ + 'user_id' => $u->id, + 'title' => 't', + 'description'=> '', + '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)), + ]); + $html = view('components.video-player', ['video' => $v])->render(); + + $this->assertStringContainsString("case '>':", $html); + $this->assertStringContainsString("case '<':", $html); + $this->assertStringContainsString('video.playbackRate', $html); + } + + // ─── Helpers ───────────────────────────────────── + + private function makeUser(string $prefix): User + { + return User::factory()->create([ + 'username' => $prefix.'-'.substr(bin2hex(random_bytes(4)), 0, 6), + ]); + } +}