import fs from "node:fs"; import path from "node:path"; import { expect, test, type Page } from "@playwright/test"; import { CHANNEL_SLUG, VIDEO_TRANSCRIPT_ONLY } from "./fixtures/data"; import { expectModalOpen, installRoutes } from "./helpers"; // site.json `transcriptDownloads` gates the transcript modal's two per-video // export controls (Download menu, Copy MD); the yt-dlp clip command is not one. The suite serves ONE site (SITE_ID=testsite, SITES_DIR = // e2e/fixtures/sites) and currentSite() re-reads site.json on every dev render, // so the off case flips the key in the fixture for the duration of one test and // puts the file back. A run killed mid-test leaves the key behind; // playwright.config.ts strips it at load, before any spec runs. const SITE_FILE = path.resolve( process.cwd(), "e2e", "fixtures", "sites", "testsite", "site.json", ); function writeSiteFile(value: Record) { fs.writeFileSync(SITE_FILE, `${JSON.stringify(value, null, 2)}\n`); } let pristine = ""; test.describe.configure({ mode: "serial" }); test.beforeAll(() => { pristine = fs.readFileSync(SITE_FILE, "utf8"); }); test.afterEach(() => { fs.writeFileSync(SITE_FILE, pristine); }); // The two controls, by the accessible names they carry once the transcript // has loaded and no clip is marked. function exportControls(page: Page) { return [ page.getByRole("button", { name: "Download this transcript as a file" }), page.getByRole("button", { name: "Copy this transcript as Markdown (for AI)", }), ]; } // The yt-dlp clip command copies a line and serves no file, so it is not a // download: it shows whatever the switch says (unmarked state). function ytDlpButton(page: Page) { return page.getByRole("button", { name: "Mark both clip start and end first" }); } async function openModal(page: Page) { await installRoutes(page); await page.goto(`/?v=${CHANNEL_SLUG}/${VIDEO_TRANSCRIPT_ONLY}`); await expectModalOpen(page); // Loaded: the cue list is up, so the file controls are past "Nothing to …". await expect(page.locator("li", { hasText: "beta line" })).toBeVisible(); } test("default (key absent): the export controls and yt-dlp are present", async ({ page, }) => { await openModal(page); for (const control of exportControls(page)) { await expect(control).toBeVisible(); } await expect(ytDlpButton(page)).toBeVisible(); await expect( page.getByRole("button", { name: "Copy share link at current time" }), ).toBeVisible(); }); test("transcriptDownloads: false hides both; yt-dlp and Share stay", async ({ page, }) => { writeSiteFile({ ...JSON.parse(pristine), transcriptDownloads: false }); await openModal(page); for (const control of exportControls(page)) { await expect(control).toHaveCount(0); } await expect(ytDlpButton(page)).toBeVisible(); // Nothing else in the bar went with them. await expect( page.getByRole("button", { name: "Copy share link at current time" }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Mark clip end at current time" }), ).toBeVisible(); });