import { writeFile } from "node:fs/promises"; import { test, expect } from "@playwright/test"; import { readJson, resetData, resolvePath, generateReport } from "./helpers"; import { baseUrl } from "./baseUrl"; // Verifies the global debounced snapshot scheduler: a direct (non-managed) // action that changes report-relevant data regenerates the channel report on // its own, without visiting the channel page or clicking "Refresh report". const SLUG = "test-transcribe"; const SNAPSHOT_REL = `test-transcripts/channels/${SLUG}/snapshot.json`; function dataRel(videoId: string, file: string): string { return `test-transcripts/channels/${SLUG}/data/${videoId}/${file}`; } async function seedTranscript(videoId: string): Promise { await writeFile( resolvePath(dataRel(videoId, "transcript.json")), '{"transcription":[]}\n', ); } type Snap = { generatedAt: string; buckets: { transcribedWithAudio?: string[] }; }; test("a direct action auto-refreshes the channel report via the debounce", async ({ page, }) => { await resetData("one-transcribe-channel-with-audio"); await seedTranscript("vidA"); await seedTranscript("vidB"); await fetch(`${baseUrl}/api/test/invalidate-cache`).catch(() => {}); // Visiting the channel page generates the initial snapshot from disk: both // transcribed-with-audio videos are cleanable. await generateReport(page, SLUG); await page.goto(`/channels/${SLUG}`); const before = await readJson(SNAPSHOT_REL); expect(before.buckets.transcribedWithAudio).toContain("vidA"); expect(before.buckets.transcribedWithAudio).toContain("vidB"); // Mark vidA "do not clean" from its video page. This is a direct action that // does NOT regenerate the channel snapshot itself — only the debounced // scheduler will. Stay off the channel page afterward so a page-load regen // can't mask the scheduler. await page.goto(`/channels/${SLUG}/videos/vidA`); await page .getByRole("button", { name: "mark video vidA do not clean" }) .click(); await expect(page.getByLabel("media archived")).toBeVisible(); // Within the Fast debounce window (~1s) the report regenerates on its own: // vidA drops out of transcribedWithAudio while vidB stays, and generatedAt // advances — all without ever revisiting the channel page. await expect .poll( async () => { const snap = await readJson(SNAPSHOT_REL).catch(() => null); if (!snap) return null; return { regenerated: snap.generatedAt !== before.generatedAt, hasA: snap.buckets.transcribedWithAudio?.includes("vidA") ?? false, hasB: snap.buckets.transcribedWithAudio?.includes("vidB") ?? false, }; }, { timeout: 15_000, intervals: [200, 300, 500] }, ) .toEqual({ regenerated: true, hasA: false, hasB: true }); });