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);
});