From 7fcf0bb827b40041ea94564c0f3fd4e781be12b9 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:51:12 -0500 Subject: [PATCH] formatting --- web/e2e/helpers/monaco.ts | 5 +- web/e2e/specs/auth.spec.ts | 13 +-- web/e2e/specs/chat.spec.ts | 34 +++----- web/e2e/specs/classification.spec.ts | 54 +++++-------- web/e2e/specs/config-editor.spec.ts | 117 +++++++++++++-------------- web/e2e/specs/explore.spec.ts | 14 ++-- web/e2e/specs/face-library.spec.ts | 17 ++-- web/e2e/specs/live.spec.ts | 26 +++--- web/e2e/specs/logs.spec.ts | 83 +++++++++---------- web/e2e/specs/navigation.spec.ts | 7 +- web/e2e/specs/replay.spec.ts | 19 ++--- web/e2e/specs/review.spec.ts | 12 +-- web/e2e/specs/system.spec.ts | 23 +++--- 13 files changed, 180 insertions(+), 244 deletions(-) diff --git a/web/e2e/helpers/monaco.ts b/web/e2e/helpers/monaco.ts index f9079db922..6e9bcd871a 100644 --- a/web/e2e/helpers/monaco.ts +++ b/web/e2e/helpers/monaco.ts @@ -14,10 +14,7 @@ import { expect, type Page } from "@playwright/test"; * lines. For short configs (our mocks) that's the full content. */ export async function getMonacoVisibleText(page: Page): Promise { - return page - .locator(".monaco-editor .view-lines") - .first() - .innerText(); + return page.locator(".monaco-editor .view-lines").first().innerText(); } /** diff --git a/web/e2e/specs/auth.spec.ts b/web/e2e/specs/auth.spec.ts index 5110935c67..f0326a5d02 100644 --- a/web/e2e/specs/auth.spec.ts +++ b/web/e2e/specs/auth.spec.ts @@ -9,14 +9,6 @@ import { test, expect } from "../fixtures/frigate-test"; import { viewerProfile } from "../fixtures/mock-data/profile"; -const ADMIN_ROUTES = [ - { path: "/system", marker: () => ({ label: "Select general" }) }, - { - path: "/logs", - marker: () => ({ label: "Select frigate" }), - }, -] as const; - test.describe("Auth — admin access @high", () => { test("admin /system renders general tab", async ({ frigateApp }) => { await frigateApp.goto("/system"); @@ -92,10 +84,7 @@ test.describe("Auth — viewer restrictions @high", () => { }); test.describe("Auth — viewer nav restrictions (desktop) @high", () => { - test.skip( - ({ frigateApp }) => frigateApp.isMobile, - "Sidebar only on desktop", - ); + test.skip(({ frigateApp }) => frigateApp.isMobile, "Sidebar only on desktop"); test("viewer sidebar hides admin routes", async ({ frigateApp }) => { await frigateApp.installDefaults({ profile: viewerProfile() }); diff --git a/web/e2e/specs/chat.spec.ts b/web/e2e/specs/chat.spec.ts index 189e4c6000..fcd10782c1 100644 --- a/web/e2e/specs/chat.spec.ts +++ b/web/e2e/specs/chat.spec.ts @@ -7,13 +7,6 @@ import { test, expect, type FrigateApp } from "../fixtures/frigate-test"; -/** - * Join NDJSON chunks into a single body (for non-streaming tests). - */ -function ndjsonBody(chunks: Array>): string { - return chunks.map((c) => JSON.stringify(c)).join("\n") + "\n"; -} - /** * Install a window.fetch override on the page so that POSTs to * chat/completion resolve with a real ReadableStream that emits the @@ -153,9 +146,9 @@ test.describe("Chat — streaming @medium", () => { await input.fill("greet me"); await input.press("Enter"); - await expect( - frigateApp.page.getByText(/Hello, world!/i), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText(/Hello, world!/i)).toBeVisible({ + timeout: 10_000, + }); }); test("tool_calls chunks render a ToolCallsGroup", async ({ frigateApp }) => { @@ -180,9 +173,9 @@ test.describe("Chat — streaming @medium", () => { // ToolCallsGroup normalizes "search_objects" → "Search Objects" via // normalizeName(). Match the rendered display label instead. - await expect( - frigateApp.page.getByText(/search objects/i), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText(/search objects/i)).toBeVisible({ + timeout: 10_000, + }); await expect( frigateApp.page.getByText(/searching for people/i), ).toBeVisible({ timeout: 5_000 }); @@ -210,9 +203,9 @@ test.describe("Chat — stop @medium", () => { await input.press("Enter"); // Wait for the first chunk to render - await expect( - frigateApp.page.getByText(/First chunk\./), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText(/First chunk\./)).toBeVisible({ + timeout: 10_000, + }); // The Stop button is a destructive rounded button shown while isLoading. // It contains only an FaStop SVG icon (no visible text). Find it by the @@ -228,9 +221,7 @@ test.describe("Chat — stop @medium", () => { }); // Third chunk should never appear. - await expect( - frigateApp.page.getByText(/Third chunk\./), - ).toHaveCount(0); + await expect(frigateApp.page.getByText(/Third chunk\./)).toHaveCount(0); }); }); @@ -308,10 +299,7 @@ test.describe("Chat — attachment chip @medium", () => { }); test.describe("Chat — mobile @medium @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("chat input is focusable at mobile viewport", async ({ frigateApp }) => { await frigateApp.goto("/chat"); diff --git a/web/e2e/specs/classification.spec.ts b/web/e2e/specs/classification.spec.ts index d06bb4d75b..83a33a815d 100644 --- a/web/e2e/specs/classification.spec.ts +++ b/web/e2e/specs/classification.spec.ts @@ -49,9 +49,9 @@ test.describe("Classification — model list @medium", () => { await installDatasetRoute(frigateApp, "state_classifier"); await frigateApp.goto("/classification"); await expect(frigateApp.page.locator("#pageRoot")).toBeVisible(); - await expect( - frigateApp.page.getByText("object_classifier"), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText("object_classifier")).toBeVisible({ + timeout: 10_000, + }); }); test("empty custom map renders without crash", async ({ frigateApp }) => { @@ -74,12 +74,10 @@ test.describe("Classification — model list @medium", () => { await installDatasetRoute(frigateApp, "state_classifier"); await frigateApp.goto("/classification"); // Objects is default — object_classifier visible, state_classifier hidden. - await expect( - frigateApp.page.getByText("object_classifier"), - ).toBeVisible({ timeout: 10_000 }); - await expect( - frigateApp.page.getByText("state_classifier"), - ).toHaveCount(0); + await expect(frigateApp.page.getByText("object_classifier")).toBeVisible({ + timeout: 10_000, + }); + await expect(frigateApp.page.getByText("state_classifier")).toHaveCount(0); // Click the "states" toggle. Radix ToggleGroup type="single" uses role="radio". const statesToggle = frigateApp.page @@ -88,12 +86,10 @@ test.describe("Classification — model list @medium", () => { await expect(statesToggle).toBeVisible({ timeout: 5_000 }); await statesToggle.click(); - await expect( - frigateApp.page.getByText("state_classifier"), - ).toBeVisible({ timeout: 5_000 }); - await expect( - frigateApp.page.getByText("object_classifier"), - ).toHaveCount(0); + await expect(frigateApp.page.getByText("state_classifier")).toBeVisible({ + timeout: 5_000, + }); + await expect(frigateApp.page.getByText("object_classifier")).toHaveCount(0); }); }); @@ -109,9 +105,7 @@ test.describe("Classification — model detail navigation @medium", () => { await installTrainRoute(frigateApp, "object_classifier"); await frigateApp.goto("/classification"); - const objectCard = frigateApp.page - .getByText("object_classifier") - .first(); + const objectCard = frigateApp.page.getByText("object_classifier").first(); await expect(objectCard).toBeVisible({ timeout: 10_000 }); await objectCard.click(); @@ -123,9 +117,7 @@ test.describe("Classification — model detail navigation @medium", () => { ).toBeVisible({ timeout: 5_000 }); // The model grid is no longer shown; state_classifier card is gone. - await expect( - frigateApp.page.getByText("state_classifier"), - ).toHaveCount(0); + await expect(frigateApp.page.getByText("state_classifier")).toHaveCount(0); }); }); @@ -167,18 +159,16 @@ test.describe("Classification — delete model (desktop) @medium", () => { await route.fulfill({ json: { success: true, require_restart: false } }); }); await frigateApp.goto("/classification"); - await expect( - frigateApp.page.getByText("object_classifier"), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText("object_classifier")).toBeVisible({ + timeout: 10_000, + }); // The card-level actions menu (FiMoreVertical three-dot icon) is a // DropdownMenuTrigger with asChild on a BlurredIconButton div. // Radix forwards aria-haspopup="menu" to the child element. // Scope the selector to the model card grid to avoid hitting the // settings sidebar trigger. - const cardGrid = frigateApp.page.locator( - ".grid.auto-rows-max", - ); + const cardGrid = frigateApp.page.locator(".grid.auto-rows-max"); await expect(cardGrid).toBeVisible({ timeout: 5_000 }); const trigger = cardGrid.locator('[aria-haspopup="menu"]').first(); await expect(trigger).toBeVisible({ timeout: 5_000 }); @@ -192,7 +182,10 @@ test.describe("Classification — delete model (desktop) @medium", () => { // Confirm the AlertDialog. const alert = frigateApp.page.getByRole("alertdialog"); await expect(alert).toBeVisible({ timeout: 5_000 }); - await alert.getByRole("button", { name: /delete|confirm/i }).first().click(); + await alert + .getByRole("button", { name: /delete|confirm/i }) + .first() + .click(); await expect.poll(() => deleteCalled, { timeout: 5_000 }).toBe(true); await expect.poll(() => configSetCalled, { timeout: 5_000 }).toBe(true); @@ -219,10 +212,7 @@ test.describe("Classification — admin only @medium", () => { }); test.describe("Classification — mobile @medium @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("page renders at mobile viewport", async ({ frigateApp }) => { await frigateApp.installDefaults({ diff --git a/web/e2e/specs/config-editor.spec.ts b/web/e2e/specs/config-editor.spec.ts index 2697fc53ce..2b51a93636 100644 --- a/web/e2e/specs/config-editor.spec.ts +++ b/web/e2e/specs/config-editor.spec.ts @@ -7,27 +7,25 @@ */ import { test, expect } from "../fixtures/frigate-test"; -import { - installWsFrameCapture, - waitForWsFrame, -} from "../helpers/ws-frames"; -import { - grantClipboardPermissions, - readClipboard, -} from "../helpers/clipboard"; +import { installWsFrameCapture, waitForWsFrame } from "../helpers/ws-frames"; +import { grantClipboardPermissions, readClipboard } from "../helpers/clipboard"; import { getMonacoVisibleText, replaceMonacoValue, waitForErrorMarker, } from "../helpers/monaco"; -const SAMPLE_CONFIG = "mqtt:\n host: mqtt\ncameras:\n front_door:\n enabled: true\n"; +const SAMPLE_CONFIG = + "mqtt:\n host: mqtt\ncameras:\n front_door:\n enabled: true\n"; async function installSaveRoute( app: { page: import("@playwright/test").Page }, status: number, body: Record, -): Promise<{ capturedUrl: () => string | null; capturedBody: () => string | null }> { +): Promise<{ + capturedUrl: () => string | null; + capturedBody: () => string | null; +}> { let lastUrl: string | null = null; let lastBody: string | null = null; await app.page.route("**/api/config/save**", async (route) => { @@ -42,14 +40,12 @@ async function installSaveRoute( } test.describe("Config Editor — Monaco @medium", () => { - test("editor loads with mocked configRaw content", async ({ - frigateApp, - }) => { + test("editor loads with mocked configRaw content", async ({ frigateApp }) => { await frigateApp.installDefaults({ configRaw: SAMPLE_CONFIG }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); // Assert via DOM-rendered visible text (Monaco virtualizes — works // for short configs which covers our mocked content). await expect @@ -72,17 +68,17 @@ test.describe("Config Editor — Save @medium", () => { message: "Config saved", }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); await frigateApp.page.getByLabel("Save Only").click(); - await expect.poll(() => capture.capturedUrl(), { timeout: 5_000 }).toMatch( - /config\/save\?save_option=saveonly/, - ); + await expect + .poll(() => capture.capturedUrl(), { timeout: 5_000 }) + .toMatch(/config\/save\?save_option=saveonly/); // Body is the raw YAML as text/plain - await expect.poll(() => capture.capturedBody(), { timeout: 5_000 }).toContain( - "front_door", - ); + await expect + .poll(() => capture.capturedBody(), { timeout: 5_000 }) + .toContain("front_door"); }); test("Save error shows the server message in the error area", async ({ @@ -93,13 +89,13 @@ test.describe("Config Editor — Save @medium", () => { message: "Invalid field `cameras.front_door`", }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); await frigateApp.page.getByLabel("Save Only").click(); - await expect( - frigateApp.page.getByText(/Invalid field/i), - ).toBeVisible({ timeout: 5_000 }); + await expect(frigateApp.page.getByText(/Invalid field/i)).toBeVisible({ + timeout: 5_000, + }); }); }); @@ -117,9 +113,9 @@ test.describe("Config Editor — Save and Restart @medium", () => { await installWsFrameCapture(frigateApp.page); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); await frigateApp.page.getByLabel("Save & Restart").click(); const dialog = frigateApp.page.getByRole("alertdialog"); @@ -140,9 +136,9 @@ test.describe("Config Editor — Save and Restart @medium", () => { await installSaveRoute(frigateApp, 200, { message: "Saved" }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); await frigateApp.page.getByLabel("Save & Restart").click(); const dialog = frigateApp.page.getByRole("alertdialog"); @@ -168,14 +164,14 @@ test.describe("Config Editor — Copy @medium", () => { await grantClipboardPermissions(context); await frigateApp.installDefaults({ configRaw: SAMPLE_CONFIG }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); await frigateApp.page.getByLabel("Copy Config").click(); - await expect.poll(() => readClipboard(frigateApp.page), { timeout: 5_000 }).toContain( - "front_door", - ); + await expect + .poll(() => readClipboard(frigateApp.page), { timeout: 5_000 }) + .toContain("front_door"); }); }); @@ -190,9 +186,9 @@ test.describe("Config Editor — schema markers @medium", () => { }) => { await frigateApp.installDefaults({ configRaw: SAMPLE_CONFIG }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); // Replace editor contents with clearly invalid YAML via keyboard. await replaceMonacoValue( @@ -219,17 +215,17 @@ test.describe("Config Editor — Cmd+S keyboard shortcut @medium", () => { message: "Saved", }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); // Focus the editor so Monaco's keybinding receives the shortcut. await frigateApp.page.locator(".monaco-editor").first().click(); await frigateApp.page.keyboard.press("ControlOrMeta+s"); - await expect.poll(() => capture.capturedUrl(), { timeout: 5_000 }).toMatch( - /config\/save\?save_option=saveonly/, - ); + await expect + .poll(() => capture.capturedUrl(), { timeout: 5_000 }) + .toMatch(/config\/save\?save_option=saveonly/); }); }); @@ -257,9 +253,9 @@ test.describe("Config Editor — Safe Mode auto-validation @medium", () => { }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); await expect.poll(() => autoSaveCalled, { timeout: 10_000 }).toBe(true); await expect( frigateApp.page.getByText(/safe-mode validation failure/i), @@ -268,16 +264,13 @@ test.describe("Config Editor — Safe Mode auto-validation @medium", () => { }); test.describe("Config Editor — mobile @medium @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("editor renders at narrow viewport", async ({ frigateApp }) => { await frigateApp.installDefaults({ configRaw: SAMPLE_CONFIG }); await frigateApp.goto("/config"); - await expect( - frigateApp.page.locator(".monaco-editor").first(), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.locator(".monaco-editor").first()).toBeVisible( + { timeout: 15_000 }, + ); }); }); diff --git a/web/e2e/specs/explore.spec.ts b/web/e2e/specs/explore.spec.ts index cb839b0baa..9d239a3f91 100644 --- a/web/e2e/specs/explore.spec.ts +++ b/web/e2e/specs/explore.spec.ts @@ -78,10 +78,7 @@ test.describe("Explore — search @high", () => { // --------------------------------------------------------------------------- test.describe("Explore — filters (desktop) @high", () => { - test.skip( - ({ frigateApp }) => frigateApp.isMobile, - "Desktop popovers", - ); + test.skip(({ frigateApp }) => frigateApp.isMobile, "Desktop popovers"); test("Cameras popover lists configured cameras", async ({ frigateApp }) => { // Navigate with a labels filter param so the filter bar renders. @@ -136,7 +133,9 @@ test.describe("Explore — filters (desktop) @high", () => { ); await expect(overlay.first()).toBeVisible({ timeout: 3_000 }); // "Sub Labels" section heading always renders inside the dialog. - await expect(frigateApp.page.getByText(/sub.?label/i).first()).toBeVisible(); + await expect( + frigateApp.page.getByText(/sub.?label/i).first(), + ).toBeVisible(); await frigateApp.page.keyboard.press("Escape"); }); @@ -251,10 +250,7 @@ test.describe("Explore — similarity search (desktop) @high", () => { // --------------------------------------------------------------------------- test.describe("Explore — mobile @high @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("search input is focusable at mobile viewport", async ({ frigateApp, diff --git a/web/e2e/specs/face-library.spec.ts b/web/e2e/specs/face-library.spec.ts index c4efddc50a..ca21642bd2 100644 --- a/web/e2e/specs/face-library.spec.ts +++ b/web/e2e/specs/face-library.spec.ts @@ -190,9 +190,9 @@ test.describe("Face Library — delete flow (desktop) @high", () => { .first() .click(); - await expect.poll(() => deleteUrl, { timeout: 5_000 }).toMatch( - /\/faces\/alice\/delete/, - ); + await expect + .poll(() => deleteUrl, { timeout: 5_000 }) + .toMatch(/\/faces\/alice\/delete/); expect(deleteBody).toMatchObject({ ids: expect.any(Array) }); }); }); @@ -245,9 +245,9 @@ test.describe("Face Library — rename flow (desktop) @high", () => { .first() .click(); - await expect.poll(() => renameUrl, { timeout: 5_000 }).toMatch( - /\/faces\/alice\/rename/, - ); + await expect + .poll(() => renameUrl, { timeout: 5_000 }) + .toMatch(/\/faces\/alice\/rename/); expect(renameBody).toEqual({ new_name: "alice_renamed" }); }); }); @@ -361,10 +361,7 @@ test.describe("FaceSelectionDialog @high", () => { }); test.describe("Face Library — mobile @high @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("mobile library selector dropdown closes cleanly on Escape", async ({ frigateApp, diff --git a/web/e2e/specs/live.spec.ts b/web/e2e/specs/live.spec.ts index e896884c12..af28f00307 100644 --- a/web/e2e/specs/live.spec.ts +++ b/web/e2e/specs/live.spec.ts @@ -9,10 +9,7 @@ import { test, expect } from "../fixtures/frigate-test"; import { LivePage } from "../pages/live.page"; -import { - installWsFrameCapture, - waitForWsFrame, -} from "../helpers/ws-frames"; +import { installWsFrameCapture, waitForWsFrame } from "../helpers/ws-frames"; import { expectBodyInteractive, waitForBodyInteractive, @@ -86,7 +83,9 @@ test.describe("Live Single Camera — desktop controls @critical", () => { await frigateApp.goto("/#front_door"); const live = new LivePage(frigateApp.page, true); // Wait for feature toggles to render (WS camera_activity must arrive first). - await expect(live.activeFeatureToggles.first()).toBeVisible({ timeout: 5_000 }); + await expect(live.activeFeatureToggles.first()).toBeVisible({ + timeout: 5_000, + }); const activeBefore = await live.activeFeatureToggles.count(); expect(activeBefore).toBeGreaterThan(0); @@ -96,9 +95,11 @@ test.describe("Live Single Camera — desktop controls @critical", () => { // front_door/ prefix + /set suffix (any feature). await waitForWsFrame( frigateApp.page, - (frame) => - frame.includes("front_door/") && frame.includes("/set"), - { message: "feature toggle should dispatch a //set frame" }, + (frame) => frame.includes("front_door/") && frame.includes("/set"), + { + message: + "feature toggle should dispatch a //set frame", + }, ); }); @@ -206,10 +207,7 @@ test.describe("Live PTZ preset dropdown @critical", () => { .first(); await expect(menu).toBeVisible({ timeout: 3_000 }); - await menu - .getByRole("menuitem", { name: PRESET_NAMES[0] }) - .first() - .click(); + await menu.getByRole("menuitem", { name: PRESET_NAMES[0] }).first().click(); await expect(menu).not.toBeVisible({ timeout: 3_000 }); await waitForWsFrame( @@ -251,7 +249,9 @@ test.describe("Live mobile layout @critical @mobile", () => { await frigateApp.goto("/"); await expect(frigateApp.page.locator("aside")).toHaveCount(0); const live = new LivePage(frigateApp.page, false); - await expect(live.cameraCard("front_door")).toBeVisible({ timeout: 10_000 }); + await expect(live.cameraCard("front_door")).toBeVisible({ + timeout: 10_000, + }); }); test("mobile camera tap opens single view", async ({ frigateApp }) => { diff --git a/web/e2e/specs/logs.spec.ts b/web/e2e/specs/logs.spec.ts index 5f0640c7f2..0b74acfa11 100644 --- a/web/e2e/specs/logs.spec.ts +++ b/web/e2e/specs/logs.spec.ts @@ -7,10 +7,7 @@ */ import { test, expect } from "../fixtures/frigate-test"; -import { - grantClipboardPermissions, - readClipboard, -} from "../helpers/clipboard"; +import { grantClipboardPermissions, readClipboard } from "../helpers/clipboard"; function logsJsonBody(lines: string[]) { return { lines, totalLines: lines.length }; @@ -29,17 +26,16 @@ test.describe("Logs — service tabs @medium", () => { }), ); // Silence the streaming fetch so it doesn't hang the test. - await frigateApp.page.route( - /\/api\/logs\/frigate\?stream=true/, - (route) => route.fulfill({ status: 200, body: "" }), + await frigateApp.page.route(/\/api\/logs\/frigate\?stream=true/, (route) => + route.fulfill({ status: 200, body: "" }), ); await frigateApp.goto("/logs"); await expect(frigateApp.page.getByLabel("Select frigate")).toBeVisible({ timeout: 5_000, }); - await expect( - frigateApp.page.getByText(/Frigate started/), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText(/Frigate started/)).toBeVisible({ + timeout: 10_000, + }); }); test("switching to go2rtc fires a GET to /logs/go2rtc", async ({ @@ -85,14 +81,13 @@ test.describe("Logs — actions @medium", () => { ]), }), ); - await frigateApp.page.route( - /\/api\/logs\/frigate\?stream=true/, - (route) => route.fulfill({ status: 200, body: "" }), + await frigateApp.page.route(/\/api\/logs\/frigate\?stream=true/, (route) => + route.fulfill({ status: 200, body: "" }), ); await frigateApp.goto("/logs"); - await expect( - frigateApp.page.getByText(/Frigate started/), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText(/Frigate started/)).toBeVisible({ + timeout: 10_000, + }); const copyBtn = frigateApp.page.getByLabel("Copy to Clipboard"); await expect(copyBtn).toBeVisible({ timeout: 5_000 }); @@ -112,9 +107,8 @@ test.describe("Logs — actions @medium", () => { } return route.fulfill({ json: logsJsonBody(["frigate line"]) }); }); - await frigateApp.page.route( - /\/api\/logs\/frigate\?stream=true/, - (route) => route.fulfill({ status: 200, body: "" }), + await frigateApp.page.route(/\/api\/logs\/frigate\?stream=true/, (route) => + route.fulfill({ status: 200, body: "" }), ); await frigateApp.goto("/logs"); @@ -132,9 +126,8 @@ test.describe("Logs — websocket tab @medium", () => { await frigateApp.page.route(/\/api\/logs\/frigate(\?|$)/, (route) => route.fulfill({ json: logsJsonBody(["frigate line"]) }), ); - await frigateApp.page.route( - /\/api\/logs\/frigate\?stream=true/, - (route) => route.fulfill({ status: 200, body: "" }), + await frigateApp.page.route(/\/api\/logs\/frigate\?stream=true/, (route) => + route.fulfill({ status: 200, body: "" }), ); await frigateApp.goto("/logs"); const wsTab = frigateApp.page.getByLabel("Select websocket"); @@ -152,9 +145,7 @@ test.describe("Logs — streaming @medium", () => { return route.fallback(); } return route.fulfill({ - json: logsJsonBody([ - "[2026-04-06 10:00:00] INFO: initial batch line", - ]), + json: logsJsonBody(["[2026-04-06 10:00:00] INFO: initial batch line"]), }); }); @@ -179,11 +170,15 @@ test.describe("Logs — streaming @medium", () => { async start(controller) { await new Promise((r) => setTimeout(r, 30)); controller.enqueue( - encoder.encode("[2026-04-06 10:00:02] INFO: streamed line one\n"), + encoder.encode( + "[2026-04-06 10:00:02] INFO: streamed line one\n", + ), ); await new Promise((r) => setTimeout(r, 30)); controller.enqueue( - encoder.encode("[2026-04-06 10:00:03] INFO: streamed line two\n"), + encoder.encode( + "[2026-04-06 10:00:03] INFO: streamed line two\n", + ), ); controller.close(); }, @@ -197,35 +192,31 @@ test.describe("Logs — streaming @medium", () => { await frigateApp.goto("/logs"); // The initial batch line is parsed by LogLineData and its content is // rendered in a .log-content cell — assert against that element. - await expect( - frigateApp.page.getByText("initial batch line"), - ).toBeVisible({ timeout: 10_000 }); - await expect( - frigateApp.page.getByText(/streamed line one/), - ).toBeVisible({ timeout: 10_000 }); - await expect( - frigateApp.page.getByText(/streamed line two/), - ).toBeVisible({ timeout: 10_000 }); + await expect(frigateApp.page.getByText("initial batch line")).toBeVisible({ + timeout: 10_000, + }); + await expect(frigateApp.page.getByText(/streamed line one/)).toBeVisible({ + timeout: 10_000, + }); + await expect(frigateApp.page.getByText(/streamed line two/)).toBeVisible({ + timeout: 10_000, + }); }); }); test.describe("Logs — mobile @medium @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("service tabs render at mobile viewport", async ({ frigateApp }) => { await frigateApp.page.route(/\/api\/logs\/frigate(\?|$)/, (route) => route.fulfill({ json: logsJsonBody(["frigate line"]) }), ); - await frigateApp.page.route( - /\/api\/logs\/frigate\?stream=true/, - (route) => route.fulfill({ status: 200, body: "" }), + await frigateApp.page.route(/\/api\/logs\/frigate\?stream=true/, (route) => + route.fulfill({ status: 200, body: "" }), ); await frigateApp.goto("/logs"); - await expect( - frigateApp.page.getByLabel("Select frigate"), - ).toBeVisible({ timeout: 5_000 }); + await expect(frigateApp.page.getByLabel("Select frigate")).toBeVisible({ + timeout: 5_000, + }); }); }); diff --git a/web/e2e/specs/navigation.spec.ts b/web/e2e/specs/navigation.spec.ts index 40b159bbcf..592247186c 100644 --- a/web/e2e/specs/navigation.spec.ts +++ b/web/e2e/specs/navigation.spec.ts @@ -75,7 +75,12 @@ test.describe("Navigation — conditional items @critical", () => { test.skip(frigateApp.isMobile, "Desktop sidebar"); await frigateApp.installDefaults({ config: { - genai: { enabled: false, provider: "ollama", model: "none", base_url: "" }, + genai: { + enabled: false, + provider: "ollama", + model: "none", + base_url: "", + }, }, }); await frigateApp.goto("/"); diff --git a/web/e2e/specs/replay.spec.ts b/web/e2e/specs/replay.spec.ts index ed7d49975d..eb19ed57d4 100644 --- a/web/e2e/specs/replay.spec.ts +++ b/web/e2e/specs/replay.spec.ts @@ -68,9 +68,7 @@ test.describe("Replay — active session @medium", () => { frigateApp.page.getByRole("heading", { level: 3, name: /Debug Replay/i }), ).toBeVisible({ timeout: 10_000 }); // Three tabs (Debug / Objects / Messages) in TabsList - await expect( - frigateApp.page.locator('[role="tab"]'), - ).toHaveCount(3); + await expect(frigateApp.page.locator('[role="tab"]')).toHaveCount(3); }); test("debug toggles render with bbox ON by default", async ({ @@ -126,7 +124,9 @@ test.describe("Replay — active session @medium", () => { // On mobile the Configuration button text span is hidden (md:inline). // It is the first button inside the right-side action group div // (the flex container that holds Config + Stop, sibling of the Back button). - const actionGroup = frigateApp.page.locator(".flex.items-center.gap-2 button"); + const actionGroup = frigateApp.page.locator( + ".flex.items-center.gap-2 button", + ); await actionGroup.first().click(); const dialog = frigateApp.page.getByRole("dialog"); @@ -172,9 +172,9 @@ test.describe("Replay — active session @medium", () => { }); // The object row renders the label. - await expect( - frigateApp.page.getByText(/person/i).first(), - ).toBeVisible({ timeout: 5_000 }); + await expect(frigateApp.page.getByText(/person/i).first()).toBeVisible({ + timeout: 5_000, + }); }); test("Messages tab renders WsMessageFeed container", async ({ @@ -287,10 +287,7 @@ test.describe("Replay — stop button (mobile) @medium @mobile", () => { }); test.describe("Replay — mobile @medium @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("no-session state renders at mobile viewport", async ({ frigateApp, diff --git a/web/e2e/specs/review.spec.ts b/web/e2e/specs/review.spec.ts index 595b57906d..8d52e083a8 100644 --- a/web/e2e/specs/review.spec.ts +++ b/web/e2e/specs/review.spec.ts @@ -163,9 +163,7 @@ test.describe("Review — filters (desktop) @critical", () => { // Radix popper wrapper), so scope by the grid role instead. const calendarGrid = frigateApp.page.locator('[role="grid"]').first(); await expect(calendarGrid).toBeVisible({ timeout: 3_000 }); - const dayButton = calendarGrid - .locator('[role="gridcell"] button') - .first(); + const dayButton = calendarGrid.locator('[role="gridcell"] button').first(); await expect(dayButton).toBeVisible({ timeout: 3_000 }); await frigateApp.page.keyboard.press("Escape"); }); @@ -183,7 +181,8 @@ test.describe("Review — filters (desktop) @critical", () => { await expect(showReviewedSwitch).toBeVisible({ timeout: 5_000 }); // Record initial checked state and click to toggle - const initialChecked = await showReviewedSwitch.getAttribute("aria-checked"); + const initialChecked = + await showReviewedSwitch.getAttribute("aria-checked"); await showReviewedSwitch.click(); const flippedChecked = initialChecked === "true" ? "false" : "true"; await expect(showReviewedSwitch).toHaveAttribute( @@ -211,10 +210,7 @@ test.describe("Review — timeline (desktop) @critical", () => { }); test.describe("Review — mobile @critical @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("severity tabs render on mobile", async ({ frigateApp }) => { await frigateApp.goto("/review"); diff --git a/web/e2e/specs/system.spec.ts b/web/e2e/specs/system.spec.ts index 3c5c440fd4..87e4db51ea 100644 --- a/web/e2e/specs/system.spec.ts +++ b/web/e2e/specs/system.spec.ts @@ -104,11 +104,9 @@ test.describe("System — tabs @medium", () => { } else { // Mobile: tab activation (data-state "on") already asserted above. // Additionally confirm general tab is no longer the active tab. - await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute( - "data-state", - "off", - { timeout: 5_000 }, - ); + await expect( + frigateApp.page.getByLabel("Select general"), + ).toHaveAttribute("data-state", "off", { timeout: 5_000 }); } }); @@ -129,7 +127,9 @@ test.describe("System — tabs @medium", () => { // mock has front_door, backyard, garage. for (const cam of ["front_door", "backyard", "garage"]) { await expect( - frigateApp.page.getByText(new RegExp(cam.replace("_", ".?"), "i")).first(), + frigateApp.page + .getByText(new RegExp(cam.replace("_", ".?"), "i")) + .first(), ).toBeVisible({ timeout: 10_000 }); } }); @@ -211,16 +211,13 @@ test.describe("System — RestartDialog @medium", () => { }); test.describe("System — mobile @medium @mobile", () => { - test.skip( - ({ frigateApp }) => !frigateApp.isMobile, - "Mobile-only", - ); + test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only"); test("tabs render at mobile viewport", async ({ frigateApp }) => { await frigateApp.goto("/system#general"); - await expect( - frigateApp.page.getByLabel("Select general"), - ).toBeVisible({ timeout: 15_000 }); + await expect(frigateApp.page.getByLabel("Select general")).toBeVisible({ + timeout: 15_000, + }); }); test("switching tabs works at mobile viewport", async ({ frigateApp }) => {