import { test, expect } from "@playwright/test"; import { rmSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { WARM_TIMEOUT, warm } from "./warm"; // --------------------------------------------------------------------------- // A citation's evidence: the transcript around it, the window that plays it, // or -- with nothing on disk -- the fetch_clip line; and the one-per-screen // walk. // // priv/polemic-alpha c1 → sitechan/sv1, a clip window on disk // c2 → sitechan/sv2, cues only // --------------------------------------------------------------------------- const HERE = path.dirname(fileURLToPath(import.meta.url)); const NOTES = path.join(HERE, "..", ".e2e-song", "sites", "priv", "reports", "polemic-alpha", "notes.json"); test.beforeAll(async ({ playwright }) => { test.setTimeout(WARM_TIMEOUT); await warm(playwright, ["/sites/priv/polemic-alpha", "/sites/priv/polemic-alpha/evidence", "/api/sites/evidence?site=priv&report=polemic-alpha&cite=c1", "/api/notes?article=priv/polemic-alpha"]); }); test.beforeEach(() => rmSync(NOTES, { force: true })); test.afterAll(() => rmSync(NOTES, { force: true })); test("a citation opens its evidence: the cited cue marked, the window playing from the span", async ({ page }) => { await page.goto("/sites/priv/polemic-alpha"); await page.locator("button[data-cite='c1']").first().click(); const ev = page.locator("[data-evidence='c1']"); await expect(ev).toContainText("The vote was rigged and everybody knew."); await expect(ev.locator("[data-play]")).toHaveAttribute("data-play", "window"); await expect(ev.locator("[data-cited='true']")).toHaveCount(1); await expect(ev.locator("[data-cited='true']")).toContainText("The vote was rigged"); await expect(ev.locator("[data-cues] li")).toHaveCount(6); const video = ev.locator("video"); await expect(video).toHaveAttribute("src", /corpus=.*sv1.*clips/); await expect.poll(() => video.evaluate((v: HTMLVideoElement) => v.currentTime)).toBeGreaterThanOrEqual(0); }); test("with nothing on disk it says so and offers the fetch_clip line, never yt-dlp", async ({ page }) => { await page.goto("/sites/priv/polemic-alpha"); await page.locator("button[data-cite='c2']").first().click(); const ev = page.locator("[data-evidence='c2']"); await expect(ev.locator("[data-play]")).toHaveAttribute("data-play", "none"); await expect(ev).toContainText('fetch_clip {"channel":"sitechan","video":"sv2","start":8,"end":12'); await expect(ev).not.toContainText("yt-dlp"); }); test("the walk: one citation per screen, j/k, n notes it, the URL follows", async ({ page }) => { await page.setViewportSize({ width: 1366, height: 768 }); await page.goto("/sites/priv/polemic-alpha/evidence"); await expect(page.locator("[data-walk='c1']")).toBeVisible(); await expect(page.locator("[data-walk-position]")).toHaveText("1 / 2"); // The keys are live once the walk has hydrated, and `goto` resolves before // that: a press that lands first is lost. j at the last citation stays // there, so pressing again until it moves is safe. await expect(async () => { await page.keyboard.press("j"); await expect(page.locator("[data-walk='c2']")).toBeVisible({ timeout: 500 }); }).toPass({ timeout: 10_000 }); await expect(page).toHaveURL(/\?c=c2$/); await page.keyboard.press("n"); await page.getByLabel("note text").fill("Needs a clip."); await page.keyboard.press("Control+Enter"); await expect(page.locator("[data-walk='c2'] [data-note-card]")).toHaveCount(1); await page.keyboard.press("k"); await expect(page.locator("[data-walk='c1'] [data-note-card]")).toHaveCount(0); // It fits the laptop screen: no horizontal scroll. expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true); await page.goto("/sites/priv/polemic-alpha/evidence?c=c2"); await expect(page.locator("[data-walk='c2'] [data-note-card]")).toContainText("Needs a clip."); });