mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-22 15:07:41 +08:00
962d36323b
* add mock data * add helpers * page objects * updated specs * remove PENDING_REWARITE * formatting
238 lines
7.8 KiB
TypeScript
238 lines
7.8 KiB
TypeScript
/**
|
|
* System page tests -- MEDIUM tier (promoted to cover migrated
|
|
* RestartDialog test from radix-overlay-regressions.spec.ts).
|
|
*
|
|
* Tab switching, version + last-refreshed display, and the
|
|
* RestartDialog cancel flow.
|
|
*/
|
|
|
|
import { test, expect } from "../fixtures/frigate-test";
|
|
import {
|
|
expectBodyInteractive,
|
|
waitForBodyInteractive,
|
|
} from "../helpers/overlay-interaction";
|
|
|
|
test.describe("System — tabs @medium", () => {
|
|
test("general tab is active by default via #general hash", async ({
|
|
frigateApp,
|
|
}) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await expect(frigateApp.page.getByLabel("Select storage")).toBeVisible();
|
|
await expect(frigateApp.page.getByLabel("Select cameras")).toBeVisible();
|
|
});
|
|
|
|
test("Storage tab activates and deactivates General", async ({
|
|
frigateApp,
|
|
}) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await frigateApp.page.getByLabel("Select storage").click();
|
|
await expect(frigateApp.page.getByLabel("Select storage")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 5_000 },
|
|
);
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"off",
|
|
);
|
|
});
|
|
|
|
test("Cameras tab activates", async ({ frigateApp }) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await frigateApp.page.getByLabel("Select cameras").click();
|
|
await expect(frigateApp.page.getByLabel("Select cameras")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 5_000 },
|
|
);
|
|
});
|
|
|
|
test("general tab shows version and last-refreshed", async ({
|
|
frigateApp,
|
|
}) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await expect(frigateApp.page.getByText("0.15.0-test")).toBeVisible();
|
|
await expect(frigateApp.page.getByText(/Last refreshed/)).toBeVisible();
|
|
});
|
|
|
|
test("storage tab renders content after switching", async ({
|
|
frigateApp,
|
|
}) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await frigateApp.page.getByLabel("Select storage").click();
|
|
await expect(frigateApp.page.getByLabel("Select storage")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 5_000 },
|
|
);
|
|
// On desktop, tab buttons render text labels so the word "storage"
|
|
// always appears in #pageRoot after switching. On mobile, tabs are
|
|
// icon-only, so we verify the general-tab content disappears instead
|
|
// (the storage tab's metrics section is hidden but general is gone).
|
|
if (!frigateApp.isMobile) {
|
|
await expect
|
|
.poll(
|
|
async () => (await frigateApp.page.textContent("#pageRoot")) ?? "",
|
|
{ timeout: 10_000 },
|
|
)
|
|
.toMatch(/storage|mount|disk|used|free/i);
|
|
} 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 });
|
|
}
|
|
});
|
|
|
|
test("cameras tab renders each configured camera", async ({ frigateApp }) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await frigateApp.page.getByLabel("Select cameras").click();
|
|
await expect(frigateApp.page.getByLabel("Select cameras")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 5_000 },
|
|
);
|
|
// Cameras tab lists every camera from config/stats. The default
|
|
// 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(),
|
|
).toBeVisible({ timeout: 10_000 });
|
|
}
|
|
});
|
|
|
|
test("enrichments tab renders when semantic search is enabled", async ({
|
|
frigateApp,
|
|
}) => {
|
|
// Override config to guarantee the enrichments tab is present.
|
|
// System.tsx shows the tab when semantic_search.enabled === true.
|
|
await frigateApp.installDefaults({
|
|
config: { semantic_search: { enabled: true } },
|
|
});
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
const enrichTab = frigateApp.page.getByLabel(/select enrichments/i).first();
|
|
await expect(enrichTab).toBeVisible({ timeout: 5_000 });
|
|
await enrichTab.click();
|
|
await expect(enrichTab).toHaveAttribute("data-state", "on", {
|
|
timeout: 5_000,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe("System — RestartDialog @medium", () => {
|
|
test.skip(
|
|
({ frigateApp }) => frigateApp.isMobile,
|
|
"Sidebar menu is desktop-only",
|
|
);
|
|
|
|
test("cancelling restart leaves body interactive", async ({ frigateApp }) => {
|
|
// Migrated from radix-overlay-regressions.spec.ts.
|
|
await frigateApp.goto("/");
|
|
|
|
const sidebarTriggers = frigateApp.page
|
|
.locator('[role="complementary"] [aria-haspopup="menu"]')
|
|
.or(frigateApp.page.locator('aside [aria-haspopup="menu"]'));
|
|
const triggerCount = await sidebarTriggers.count();
|
|
expect(triggerCount).toBeGreaterThan(0);
|
|
|
|
let opened = false;
|
|
for (let i = 0; i < triggerCount; i++) {
|
|
const trigger = sidebarTriggers.nth(i);
|
|
await trigger.click().catch(() => {});
|
|
const restartItem = frigateApp.page
|
|
.getByRole("menuitem", { name: /restart/i })
|
|
.first();
|
|
const visible = await expect(restartItem)
|
|
.toBeVisible({ timeout: 300 })
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
if (visible) {
|
|
await restartItem.click();
|
|
opened = true;
|
|
break;
|
|
}
|
|
await frigateApp.page.keyboard.press("Escape").catch(() => {});
|
|
}
|
|
expect(opened).toBe(true);
|
|
|
|
const cancel = frigateApp.page.getByRole("button", { name: /cancel/i });
|
|
await expect(cancel).toBeVisible({ timeout: 3_000 });
|
|
await cancel.click();
|
|
|
|
await waitForBodyInteractive(frigateApp.page);
|
|
await expectBodyInteractive(frigateApp.page);
|
|
|
|
const postCancelTrigger = sidebarTriggers.first();
|
|
await postCancelTrigger.click();
|
|
await expect(
|
|
frigateApp.page
|
|
.locator('[role="menu"], [data-radix-menu-content]')
|
|
.first(),
|
|
).toBeVisible({ timeout: 3_000 });
|
|
});
|
|
});
|
|
|
|
test.describe("System — mobile @medium @mobile", () => {
|
|
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,
|
|
});
|
|
});
|
|
|
|
test("switching tabs works at mobile viewport", async ({ frigateApp }) => {
|
|
await frigateApp.goto("/system#general");
|
|
await expect(frigateApp.page.getByLabel("Select general")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 15_000 },
|
|
);
|
|
await frigateApp.page.getByLabel("Select storage").click();
|
|
await expect(frigateApp.page.getByLabel("Select storage")).toHaveAttribute(
|
|
"data-state",
|
|
"on",
|
|
{ timeout: 5_000 },
|
|
);
|
|
});
|
|
});
|