import { test, expect } from "./helpers/instrumentedTest"; test.describe("Webseed Stream Playback", () => { const VIDEO_URL = "https://ia800205.us.archive.org/2/items/PopeyeTheSailor-AncientFistory/PopeyeTheSailor-AncientFistory_512kb.mp4"; const INFO_HASH = "c59cf2122f84f28f72755cf5b829f2cf9f539e99"; const MAGNET_URI = `magnet:?xt=urn:btih:${INFO_HASH}`; test.beforeEach(async ({ page }) => { // networkidle can be flaky if background polling/relays are active. // We rely on explicit selector waits instead. await page.goto("/", { waitUntil: "domcontentloaded" }); try { // Wait for either the video list (success) or the error message (failure) await Promise.race([ page.waitForSelector('#videoList', { timeout: 30000 }), page.waitForSelector('.text-critical-strong', { timeout: 30000 }).then(async (el) => { const text = await el?.innerText(); throw new Error(`View load failed with message: ${text}`); }) ]); } catch (error) { // If timeout occurs, capture page content for debugging const body = await page.innerHTML('body'); console.error('Test failed to load video list. Body content:', body.substring(0, 500)); throw error; } }); test("plays directly from HTML link (CDN mode)", async ({ page }) => { // Record every src the player attaches. Asserting the FINAL src is wrong: // this test already tolerates "No playable source found" in its status // check (headless CI often cannot decode the archive.org H.264 sample), and // when the attempt fails resetVideoElement() clears src back to "". The // behaviour under test is that CDN mode ATTACHES the hosted URL, which is // observable regardless of whether the remote file then decodes. await page.evaluate(() => { (window as any).__attachedSrcs = []; new MutationObserver((mutations) => { for (const mutation of mutations) { const target = mutation.target as HTMLElement; if (mutation.attributeName === "src" && target.tagName === "VIDEO") { const value = target.getAttribute("src"); if (value) { (window as any).__attachedSrcs.push(value); } } } }).observe(document.body, { subtree: true, attributes: true, attributeFilter: ["src"], }); }); // 1. Trigger playback with just the URL await page.evaluate(async (url) => { const { getApplication } = await import("/js/applicationContext.js"); const app = getApplication(); if (!app) throw new Error("Application not found"); await app.playVideoWithoutEvent({ url, title: "Test CDN Playback", }); }, VIDEO_URL); // 2. Verify player modal opens const modal = page.locator("#playerModal"); await expect(modal).not.toHaveClass(/hidden/); // 3. Verify status indicates hosted playback attempt // In headless environments with limited codecs, actual playback might stall at "Checking..." // or fail immediately with "No playable source found" if the browser lacks H.264 support (common in CI) const status = page.locator("#modalStatus"); await expect(status).toHaveText(/Checking hosted URL|Streaming from hosted URL|No playable source found/i); // Verify source toggle state (CDN active) const urlToggle = page.locator('[data-source-toggle="url"]'); await expect(urlToggle).toHaveAttribute("aria-pressed", "true"); // Check the player attached the hosted URL (see the observer above). await expect .poll(async () => await page.evaluate(() => (window as any).__attachedSrcs)) .toContain(VIDEO_URL); }); test("falls back to WebTorrent using HTML link as webseed when CDN fails", async ({ page }) => { test.setTimeout(90000); // Increase timeout for fallback logic // 1. Intercept and fail the direct request to the MP4 await page.route(VIDEO_URL, (route) => route.abort()); // 2. Trigger playback with URL and Magnet await page.evaluate(async ({ url, magnet }) => { const { getApplication } = await import("/js/applicationContext.js"); const app = getApplication(); if (!app) throw new Error("Application not found"); // We don't await this because if it hangs on probe/fallback, we want to assert on UI changes app.playVideoWithoutEvent({ url, magnet, title: "Test WebSeed Fallback", }); }, { url: VIDEO_URL, magnet: MAGNET_URI }); // 3. Verify player modal opens const modal = page.locator("#playerModal"); await expect(modal).not.toHaveClass(/hidden/); // 4. Verify source toggle state changes to P2P/Torrent const torrentToggle = page.locator('[data-source-toggle="torrent"]'); // Wait for fallback to occur (probe timeout + watchdog stall detection) await expect(torrentToggle).toHaveAttribute("aria-pressed", "true", { timeout: 45000 }); // 5. Verify status text indicates WebTorrent usage const status = page.locator("#modalStatus"); await expect(status).toBeVisible(); }); });