import { expect, test, type Page } from "@playwright/test"; import { expectModalOpen, installRoutes } from "./helpers"; // Kick VODs have no iframe embed — they play from a persisted HLS manifest via // react-player/file (+ hls.js), and fall back to an expiry notice + source link // when no playable manifest exists. These tests mock a Kick channel's transcript // routes plus a minimal m3u8 so the playback path is exercised offline. const KICK_CHANNEL = "kick-channel"; const PLAYABLE = "kick-playable-uuid"; const EXPIRED = "kick-expired-uuid"; const MASTER_URL = "https://mock-kick-stream.test/master.m3u8"; const MASTER_M3U8 = [ "#EXTM3U", "#EXT-X-STREAM-INF:BANDWIDTH=630000,RESOLUTION=640x360", "360p30/playlist.m3u8", "", ].join("\n"); const MEDIA_M3U8 = [ "#EXTM3U", "#EXT-X-VERSION:3", "#EXT-X-TARGETDURATION:4", "#EXT-X-PLAYLIST-TYPE:VOD", "#EXTINF:4.0,", "seg0.ts", "#EXT-X-ENDLIST", "", ].join("\n"); function kickDetail( id: string, title: string, extra: Record, ) { return { slug: `${KICK_CHANNEL}/${id}`, id, channelSlug: KICK_CHANNEL, title, // Old upload so the expiry helper flags it as likely expired (Kick ~30d). uploadDate: "20200101", date: "2020-01-01", duration: 300, channel: "Kick Channel", description: "", tags: [] as string[], isLivestream: true, ageRestricted: false, platform: "kick" as const, webpageUrl: `https://kick.com/video/${id}`, cues: [ { start: 5, end: 9, text: "kick alpha line" }, { start: 50, end: 54, text: "kick beta line" }, ], ...extra, }; } async function fulfillJson( route: import("@playwright/test").Route, body: unknown, ) { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(body), }); } async function installKickRoutes(page: Page) { // Reuse the shared summaries/subs routes so the shell renders, then override // the transcript routes for the kick channel (later routes win in Playwright). await installRoutes(page); await page.route(/\/transcripts\/kick-channel\/manifest\.json$/, (route) => fulfillJson(route, { version: 1, channelSlug: KICK_CHANNEL, pageCount: 1, maxPageBytes: 8388608, generatedAt: new Date().toISOString(), slugToPage: { [PLAYABLE]: 0, [EXPIRED]: 0 }, }), ); await page.route(/\/transcripts\/kick-channel\/page-\d+\.json$/, (route) => fulfillJson(route, [ kickDetail(PLAYABLE, "Playable Kick VOD", { hlsUrl: MASTER_URL }), // No hlsUrl → the expiry/link fallback. kickDetail(EXPIRED, "Expired Kick VOD", {}), ]), ); // Minimal HLS so hls.js parses the manifest without a fatal error. The segment // 404s (non-fatal), which is fine — we only assert the