import { test, expect } from "@playwright/test"; import { readJson, resetData, writeSettings } from "./helpers"; // /review is the page for the findings a PERSON decides about: duplicate // clusters and media-integrity findings. Both are flag-only and both carry a // reviewed state, which is what makes them neither an operation nor bytes. // // duplicate-shorts.spec.ts drives the duplicates half end to end. This file is // the page itself, and the empty states — the media scan has never had an e2e // at all, and "never run" is the one thing that section must not render as // "all clear". test("/review renders both sections and their empty states", async ({ page, }) => { await resetData("empty"); await page.goto("/review"); await expect( page.getByRole("heading", { level: 1, name: "Review" }), ).toBeVisible(); await expect(page.getByLabel("duplicate-shorts empty")).toContainText( "Run detection to scan.", ); await expect(page.getByLabel("media scan empty")).toContainText( "Run a scan to check the media on disk.", ); }); test("Review is in the Corpus group of the sidebar", async ({ page }) => { await resetData("empty"); await page.goto("/"); await expect( page.locator("aside").getByRole("link", { name: /^Review$/ }), ).toHaveAttribute("href", "/review"); }); // AN AUTO-PAUSE IS A REVIEW FINDING. The storage watch forces a channel to // `paused` when the drive its media is on stops answering, and restores it when // the drive comes back. Both halves were right; what was missing was anywhere // that said it had happened — the record surfaced only as a chip beside that // one channel's tier on /channels, so the way an operator found out a platter // had gone away was by noticing a channel had quietly stopped doing anything. test("an auto-paused channel is listed with its reason", async ({ page }) => { await resetData("one-youtube-channel-with-data"); await writeSettings({ adminTitle: "Test Admin", channelPriority: { focus: { kind: "none" }, channels: { "test-youtube": { tier: "paused", autoPaused: { reason: "storage", since: "2026-09-19T04:05:06.000Z", previousTier: "low", }, }, }, }, }); await page.goto("/review"); const section = page.getByLabel("auto-paused channels"); await expect(section).toBeVisible(); await expect( page.getByRole("heading", { name: "Auto-paused channels (1)" }), ).toBeVisible(); const row = page.getByLabel("auto-paused test-youtube"); await expect(row).toBeVisible(); await expect(row).toContainText("2026-09-19"); // ONE WORDING, shared with the rack and the channel page (autoPauseReasonOf). await expect( page.getByLabel("auto-paused reason for test-youtube"), ).toContainText("drive that is not there"); // It offers to put the channel back where it WAS, not at "normal" — the // previous tier is the whole content of the record, and resuming a `low` // channel as `normal` would silently demote it. const resume = page.getByLabel("resume test-youtube"); await expect(resume).toHaveText("Resume as low"); // And the fix for the actual problem is one link away. await expect(section.getByRole("link", { name: "Storage" })).toHaveAttribute( "href", "/storage", ); // --- the operator's word wins ------------------------------------------ // The writer is the ONE writer (saveChannelPriorityAction), and setting a // tier by hand is what clears the machine's record — so this is not a second // path that has to remember to clean up after itself. await resume.click(); await expect .poll( async () => { const s = await readJson<{ channelPriority?: { channels: Record; }; }>("test-settings.json"); return s.channelPriority?.channels["test-youtube"]; }, { timeout: 20_000 }, ) .toMatchObject({ tier: "low" }); const after = await readJson<{ channelPriority?: { channels: Record; }; }>("test-settings.json"); expect( after.channelPriority?.channels["test-youtube"]?.autoPaused, ).toBe(undefined); // The section goes away entirely rather than rendering a permanent all-clear // about a condition that is normally absent. await page.goto("/review"); await expect(page.getByLabel("auto-paused channels")).toHaveCount(0); });