import { test, expect } from "@playwright/test"; import { readJson, resetData } from "./helpers"; test.beforeEach(async () => { await resetData("empty"); }); test("shows resolved paths and current settings", async ({ page }) => { await page.goto("/settings"); await page.locator("summary").filter({ hasText: "System paths" }).click(); await expect(page.getByText("transcriptsDir")).toBeVisible(); await expect(page.getByText("ytdlpBin")).toBeVisible(); await expect(page.getByLabel(/admin title/i)).not.toHaveValue(""); }); test("saves settings via the form", async ({ page }) => { await page.goto("/settings"); await page.getByLabel(/admin title/i).fill("Cypress Admin"); await page.getByRole("button", { name: /save settings/i }).click(); await expect( page.getByRole("status").filter({ hasText: "Saved" }), ).toBeVisible(); const saved = await readJson<{ adminTitle: string; siteTitle?: string; groups?: unknown; }>("test-settings.json"); expect(saved.adminTitle).toBe("Cypress Admin"); // Operational-only: branding/groups must NOT round-trip into settings.json. expect(saved.siteTitle).toBeUndefined(); expect(saved.groups).toBeUndefined(); }); test("saves global default social links", async ({ page }) => { await page.goto("/settings"); await page.getByRole("button", { name: /add link/i }).click(); await page.getByPlaceholder(/label \(e\.g\. github\)/i).fill("GitHub"); // Exact placeholder: /https:\/\// also matches the archive-overflow field's // "https://archives.example.com". The social-link URL input's placeholder is // the bare "https://…" (an ellipsis character, not three dots). await page.getByPlaceholder("https://…").fill("https://github.com/example"); await page .getByPlaceholder(/'); await page.getByRole("checkbox", { name: "Keep in header on small screens" }).check(); await page.getByRole("button", { name: /save settings/i }).click(); await expect( page.getByRole("status").filter({ hasText: "Saved" }), ).toBeVisible(); const saved = await readJson<{ socialLinks?: { label: string; url: string; svg: string; featured?: boolean }[]; }>("test-settings.json"); expect(saved.socialLinks).toHaveLength(1); expect(saved.socialLinks?.[0].label).toBe("GitHub"); // "Keep in header on small screens" is stored as featured: true. expect(saved.socialLinks?.[0].featured).toBe(true); // SVG was normalized on save (fill="currentColor" injected). expect(saved.socialLinks?.[0].svg).toContain("currentColor"); }); test("rejects out-of-range maxTranscriptPageBytes", async ({ page }) => { await page.goto("/settings"); await page.getByLabel(/max transcript page bytes/i).fill("42"); await page.getByRole("button", { name: /save settings/i }).click(); await expect(page.locator("form").getByRole("alert")).toContainText( "between", ); }); test("saves build pipeline settings (concurrency, image), and has no mode", async ({ page, }) => { await page.goto("/settings"); // The Basic | Docker select was a label nothing read (dropped in release 11, // follow-up O6c). await expect(page.getByLabel("Build mode")).toHaveCount(0); await page.getByLabel(/max parallel builds/i).fill("4"); await page.getByLabel(/docker image tag/i).fill("my-build-image"); await page.getByRole("button", { name: /save settings/i }).click(); await expect( page.getByRole("status").filter({ hasText: "Saved" }), ).toBeVisible(); const saved = await readJson<{ buildPipeline?: Record; }>("test-settings.json"); expect(saved.buildPipeline?.maxParallelBuilds).toBe(4); expect(saved.buildPipeline?.dockerImage).toBe("my-build-image"); expect(saved.buildPipeline && "mode" in saved.buildPipeline).toBe(false); });