From 2db72d956e695973b2277c5d7c6e4d9824dc71bb Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 26 Aug 2026 12:01:57 -0500 Subject: [PATCH] add setup wizard account step e2e coverage --- web/e2e/specs/setup/wizard-account.spec.ts | 156 +++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 web/e2e/specs/setup/wizard-account.spec.ts diff --git a/web/e2e/specs/setup/wizard-account.spec.ts b/web/e2e/specs/setup/wizard-account.spec.ts new file mode 100644 index 0000000000..81203c9b6f --- /dev/null +++ b/web/e2e/specs/setup/wizard-account.spec.ts @@ -0,0 +1,156 @@ +/** + * Setup wizard account step -- HIGH tier. + * + * Covers the step's placement and gating, the password and user payloads it + * sends, the copy it shows when nobody is signed in (the internal port), and + * that skipping it writes nothing. + */ + +import { test, expect } from "../../fixtures/frigate-test"; +import type { Page } from "@playwright/test"; +import { installFirstRun } from "../../helpers/setup-wizard"; + +type Sent = { + method: string; + url: string; + body: Record | null; +}; + +async function captureUserCalls(page: Page): Promise { + const sent: Sent[] = []; + + await page.route("**/api/users**", (route) => { + const request = route.request(); + + if (request.method() === "GET") { + return route.fulfill({ json: [{ username: "admin", role: "admin" }] }); + } + + sent.push({ + method: request.method(), + url: request.url(), + body: request.postDataJSON(), + }); + return route.fulfill({ json: { message: "ok" } }); + }); + + return sent; +} + +async function gotoAccountStep(page: Page) { + await page.getByRole("button", { name: "Get Started" }).click(); + await expect( + page.getByRole("heading", { name: "Secure your account" }), + ).toBeVisible(); +} + +test.describe("setup wizard account @high @mobile", () => { + test("sets the admin password without an old password", async ({ + frigateApp, + page, + }) => { + await installFirstRun(frigateApp, page); + const sent = await captureUserCalls(page); + + await frigateApp.gotoAndWait("/", "text=Welcome to Frigate"); + await gotoAccountStep(page); + + await page.getByRole("button", { name: "Change password" }).click(); + + const dialog = page.getByRole("dialog"); + await expect(dialog).toBeVisible(); + // the dialog is in set-password mode, so it asks for no current password + await expect( + dialog.getByPlaceholder("Enter your current password"), + ).toBeHidden(); + await dialog + .getByPlaceholder("Enter new password", { exact: true }) + .fill("a-long-enough-password"); + await dialog + .getByPlaceholder("Re-enter new password") + .fill("a-long-enough-password"); + await dialog.getByRole("button", { name: "Save" }).click(); + + await expect(page.getByText("Password set")).toBeVisible(); + + const passwordCall = sent.find((call) => call.method === "PUT"); + expect(passwordCall?.url).toContain("/users/admin/password"); + // admins are exempt from the current-password check, so it must not be sent + expect(passwordCall?.body).toEqual({ password: "a-long-enough-password" }); + }); + + test("creates a user with a role", async ({ frigateApp, page }) => { + await installFirstRun(frigateApp, page); + const sent = await captureUserCalls(page); + + await frigateApp.gotoAndWait("/", "text=Welcome to Frigate"); + await gotoAccountStep(page); + + await page.getByRole("button", { name: "Add user" }).click(); + + await page.getByPlaceholder("Enter username").fill("family"); + await page + .getByPlaceholder("Enter password") + .fill("a-long-enough-password"); + await page + .getByPlaceholder("Confirm Password") + .fill("a-long-enough-password"); + await page.getByRole("button", { name: "Save" }).click(); + + const createCall = sent.find((call) => call.method === "POST"); + expect(createCall?.body).toEqual({ + username: "family", + password: "a-long-enough-password", + role: "viewer", + }); + }); + + test("shows anonymous copy when nobody is signed in", async ({ + frigateApp, + page, + }) => { + await installFirstRun(frigateApp, page, { + profile: { username: "anonymous", role: "admin", allowed_cameras: null }, + }); + await captureUserCalls(page); + + await frigateApp.gotoAndWait("/", "text=Welcome to Frigate"); + await gotoAccountStep(page); + + await expect( + page.getByText("This port doesn't require a login"), + ).toBeVisible(); + await expect(page.getByText("You're signed in as")).toBeHidden(); + }); + + test("is absent when native auth is disabled", async ({ + frigateApp, + page, + }) => { + await installFirstRun(frigateApp, page, { + config: { auth: { enabled: false } } as never, + }); + await captureUserCalls(page); + + await frigateApp.gotoAndWait("/", "text=Welcome to Frigate"); + await page.getByRole("button", { name: "Get Started" }).click(); + + // straight from welcome to the camera step, with no gap in the indicator + await expect(page.getByText("Add Your First Camera")).toBeVisible(); + await expect( + page.getByRole("heading", { name: "Secure your account" }), + ).toBeHidden(); + }); + + test("skipping sends nothing", async ({ frigateApp, page }) => { + await installFirstRun(frigateApp, page); + const sent = await captureUserCalls(page); + + await frigateApp.gotoAndWait("/", "text=Welcome to Frigate"); + await gotoAccountStep(page); + await page.getByRole("button", { name: "Skip" }).click(); + + await expect(page.getByText("Add Your First Camera")).toBeVisible(); + expect(sent).toHaveLength(0); + }); +});