From f407829524decbda751b07a26e98e7a7ce353e84 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 14:25:43 +0000 Subject: [PATCH] test(web): media settings E2E with the media-settings screenshot The Media settings section (added in the previous web commit) lets the deployment owner point media at their own servers: ComfyUI URL and optional key, with the image model and custom image/video workflows folded under Advanced, and a transcription URL with optional model and key. Saved keys are never shown again; the key field says one is saved and offers to remove it. Members who do not own the deployment do not see the section. The spec fills both servers, captures `media-settings`, saves and checks the exact update the server receives, then removes the saved key and the transcription server; a second spec checks a member has no Media section. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u --- apps/web/e2e/media-settings.spec.ts | 122 ++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 apps/web/e2e/media-settings.spec.ts diff --git a/apps/web/e2e/media-settings.spec.ts b/apps/web/e2e/media-settings.spec.ts new file mode 100644 index 0000000..4f50ad7 --- /dev/null +++ b/apps/web/e2e/media-settings.spec.ts @@ -0,0 +1,122 @@ +import type { Page } from "@playwright/test"; +import { expect, test } from "@playwright/test"; +import { captureScreenshot, completeOnboarding, openUserSettings, signup } from "./helpers"; + +type Draft = { + comfyui: { + baseUrl: string; + apiKey?: string; + imageModel?: string; + imageWorkflow?: string; + videoWorkflow?: string; + } | null; + transcription: { baseUrl: string; model?: string; apiKey?: string } | null; +}; + +/** The server keeps keys; the settings screen only learns whether one is saved. */ +function view(draft: Draft, previous: { comfyui: boolean; transcription: boolean }) { + const hasKey = (key: string | undefined, had: boolean) => (key === undefined ? had : key !== ""); + const text = (value: string | undefined) => (value ? value : undefined); + return { + comfyui: draft.comfyui + ? { + baseUrl: draft.comfyui.baseUrl, + hasApiKey: hasKey(draft.comfyui.apiKey, previous.comfyui), + imageModel: text(draft.comfyui.imageModel), + imageWorkflow: text(draft.comfyui.imageWorkflow), + videoWorkflow: text(draft.comfyui.videoWorkflow), + } + : null, + transcription: draft.transcription + ? { + baseUrl: draft.transcription.baseUrl, + model: text(draft.transcription.model), + hasApiKey: hasKey(draft.transcription.apiKey, previous.transcription), + } + : null, + }; +} + +async function asDeploymentOwner(page: Page, isDeploymentOwner: boolean) { + await page.route("**/rpc/bootstrap", async (route) => { + const response = await route.fetch(); + const body = await response.json(); + await route.fulfill({ + response, + json: { json: { ...body.json, me: { ...body.json.me, isDeploymentOwner } } }, + }); + }); +} + +test("the deployment owner points media at their own servers", async ({ page }, testInfo) => { + await asDeploymentOwner(page, true); + let current: ReturnType = { comfyui: null, transcription: null }; + const saved: Draft[] = []; + await page.route("**/rpc/media/settings/get", (route) => + route.fulfill({ json: { json: current } }), + ); + await page.route("**/rpc/media/settings/update", (route) => { + const draft = (route.request().postDataJSON() as { json: Draft }).json; + saved.push(draft); + current = view(draft, { + comfyui: Boolean(current.comfyui?.hasApiKey), + transcription: Boolean(current.transcription?.hasApiKey), + }); + return route.fulfill({ json: { json: current } }); + }); + + await signup(page, `media-settings-${Date.now()}@rakazo.test`, "password12", "Media Owner"); + await completeOnboarding(page); + const settings = await openUserSettings(page); + await settings.getByTestId("settings-nav-media").click(); + const panel = settings.getByTestId("media-settings"); + await expect(panel).toBeVisible(); + + const comfy = panel.locator("section").filter({ hasText: "ComfyUI" }); + const transcription = panel.locator("section").filter({ hasText: "Transcription" }); + await comfy.getByLabel("Server URL", { exact: true }).fill("http://127.0.0.1:8188"); + await comfy.getByLabel("API key", { exact: true }).fill("fake-comfy-key"); + await transcription.getByLabel("Server URL", { exact: true }).fill("http://127.0.0.1:8080/v1"); + await transcription.getByLabel("Model", { exact: true }).fill("whisper-1"); + // The ComfyUI model and workflows stay folded away until asked for. + await expect(comfy.getByLabel("Image model", { exact: true })).toBeHidden(); + await comfy.scrollIntoViewIfNeeded(); + await captureScreenshot(page, testInfo, "media-settings"); + await comfy.getByText("Advanced", { exact: true }).click(); + await comfy.getByLabel("Image model", { exact: true }).fill("sd_xl_base_1.0.safetensors"); + + await panel.getByRole("button", { name: "Save", exact: true }).click(); + await expect(panel.getByText("Saved", { exact: true })).toBeVisible(); + expect(saved).toEqual([ + { + comfyui: { + baseUrl: "http://127.0.0.1:8188", + apiKey: "fake-comfy-key", + imageModel: "sd_xl_base_1.0.safetensors", + imageWorkflow: "", + videoWorkflow: "", + }, + transcription: { baseUrl: "http://127.0.0.1:8080/v1", model: "whisper-1" }, + }, + ]); + // The key never comes back; the field shows that one is saved and offers to remove it. + const comfyKey = comfy.getByLabel("API key", { exact: true }); + await expect(comfyKey).toHaveValue(""); + await expect(comfyKey).toHaveAttribute("placeholder", "Saved"); + + await comfy.getByRole("button", { name: "Remove", exact: true }).click(); + await transcription.getByLabel("Server URL", { exact: true }).fill(""); + await panel.getByRole("button", { name: "Save", exact: true }).click(); + await expect.poll(() => saved.length).toBe(2); + expect(saved[1]).toMatchObject({ comfyui: { apiKey: "" }, transcription: null }); + await expect(comfyKey).toHaveAttribute("placeholder", "Optional"); +}); + +test("members who do not own the deployment have no media settings", async ({ page }) => { + await asDeploymentOwner(page, false); + await signup(page, `media-member-${Date.now()}@rakazo.test`, "password12", "Media Member"); + await completeOnboarding(page); + const settings = await openUserSettings(page); + await expect(settings.getByTestId("settings-nav-voice")).toBeVisible(); + await expect(settings.getByTestId("settings-nav-media")).toHaveCount(0); +});