mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-09 16:47:37 +03:00
228 lines
6.9 KiB
TypeScript
228 lines
6.9 KiB
TypeScript
|
|
/**
|
||
|
|
* Navigation tests -- CRITICAL tier.
|
||
|
|
*
|
||
|
|
* Tests sidebar (desktop) and bottombar (mobile) navigation,
|
||
|
|
* conditional nav items, settings menus, and their actual behaviors.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { test, expect } from "../fixtures/frigate-test";
|
||
|
|
import { BasePage } from "../pages/base.page";
|
||
|
|
|
||
|
|
test.describe("Navigation @critical", () => {
|
||
|
|
test("app loads and renders page root", async ({ frigateApp }) => {
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
await expect(frigateApp.page.locator("#pageRoot")).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("logo is visible and links to home", async ({ frigateApp }) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
const base = new BasePage(frigateApp.page, true);
|
||
|
|
const logo = base.sidebar.locator('a[href="/"]').first();
|
||
|
|
await expect(logo).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("all primary nav links are present and navigate", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
const routes = ["/review", "/explore", "/export"];
|
||
|
|
for (const route of routes) {
|
||
|
|
await expect(
|
||
|
|
frigateApp.page.locator(`a[href="${route}"]`).first(),
|
||
|
|
).toBeVisible();
|
||
|
|
}
|
||
|
|
// Verify clicking each one actually navigates
|
||
|
|
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
|
||
|
|
for (const route of routes) {
|
||
|
|
await base.navigateTo(route);
|
||
|
|
await expect(frigateApp.page).toHaveURL(new RegExp(route));
|
||
|
|
await expect(frigateApp.page.locator("#pageRoot")).toBeVisible();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("desktop sidebar is visible, mobile bottombar is visible", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
|
||
|
|
if (!frigateApp.isMobile) {
|
||
|
|
await expect(base.sidebar).toBeVisible();
|
||
|
|
} else {
|
||
|
|
await expect(base.sidebar).not.toBeVisible();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("navigate between all main pages without crash", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
|
||
|
|
const pageRoot = frigateApp.page.locator("#pageRoot");
|
||
|
|
|
||
|
|
await base.navigateTo("/review");
|
||
|
|
await expect(pageRoot).toBeVisible({ timeout: 10_000 });
|
||
|
|
await base.navigateTo("/explore");
|
||
|
|
await expect(pageRoot).toBeVisible({ timeout: 10_000 });
|
||
|
|
await base.navigateTo("/export");
|
||
|
|
await expect(pageRoot).toBeVisible({ timeout: 10_000 });
|
||
|
|
await base.navigateTo("/review");
|
||
|
|
await expect(pageRoot).toBeVisible({ timeout: 10_000 });
|
||
|
|
});
|
||
|
|
|
||
|
|
test("unknown route redirects to home", async ({ frigateApp }) => {
|
||
|
|
await frigateApp.page.goto("/nonexistent-route");
|
||
|
|
await frigateApp.page.waitForTimeout(2000);
|
||
|
|
const url = frigateApp.page.url();
|
||
|
|
const hasPageRoot = await frigateApp.page
|
||
|
|
.locator("#pageRoot")
|
||
|
|
.isVisible()
|
||
|
|
.catch(() => false);
|
||
|
|
expect(url.endsWith("/") || hasPageRoot).toBeTruthy();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe("Navigation - Conditional Items @critical", () => {
|
||
|
|
test("Faces nav hidden when face_recognition disabled", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
await expect(frigateApp.page.locator('a[href="/faces"]')).not.toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Chat nav hidden when genai model is none", async ({ frigateApp }) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.installDefaults({
|
||
|
|
config: {
|
||
|
|
genai: {
|
||
|
|
enabled: false,
|
||
|
|
provider: "ollama",
|
||
|
|
model: "none",
|
||
|
|
base_url: "",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
await expect(frigateApp.page.locator('a[href="/chat"]')).not.toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Faces nav visible when face_recognition enabled on desktop", async ({
|
||
|
|
frigateApp,
|
||
|
|
page,
|
||
|
|
}) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.installDefaults({
|
||
|
|
config: { face_recognition: { enabled: true } },
|
||
|
|
});
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
await expect(page.locator('a[href="/faces"]')).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Chat nav visible when genai model set on desktop", async ({
|
||
|
|
frigateApp,
|
||
|
|
page,
|
||
|
|
}) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.installDefaults({
|
||
|
|
config: { genai: { enabled: true, model: "llava" } },
|
||
|
|
});
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
await expect(page.locator('a[href="/chat"]')).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Classification nav visible for admin on desktop", async ({
|
||
|
|
frigateApp,
|
||
|
|
page,
|
||
|
|
}) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
await expect(page.locator('a[href="/classification"]')).toBeVisible();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe("Navigation - Settings Menu @critical", () => {
|
||
|
|
test("settings gear opens menu with navigation items (desktop)", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
// Settings gear is in the sidebar bottom section, a div with cursor-pointer
|
||
|
|
const sidebarBottom = frigateApp.page.locator("aside .mb-8");
|
||
|
|
const gearIcon = sidebarBottom
|
||
|
|
.locator("div[class*='cursor-pointer']")
|
||
|
|
.first();
|
||
|
|
await expect(gearIcon).toBeVisible({ timeout: 5_000 });
|
||
|
|
await gearIcon.click();
|
||
|
|
// Menu should open - look for the "Settings" menu item by aria-label
|
||
|
|
await expect(frigateApp.page.getByLabel("Settings")).toBeVisible({
|
||
|
|
timeout: 3_000,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test("settings menu items navigate to correct routes (desktop)", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const targets = [
|
||
|
|
{ label: "Settings", url: "/settings" },
|
||
|
|
{ label: "System metrics", url: "/system" },
|
||
|
|
{ label: "System logs", url: "/logs" },
|
||
|
|
{ label: "Configuration Editor", url: "/config" },
|
||
|
|
];
|
||
|
|
for (const target of targets) {
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
const gearIcon = frigateApp.page
|
||
|
|
.locator("aside .mb-8 div[class*='cursor-pointer']")
|
||
|
|
.first();
|
||
|
|
await gearIcon.click();
|
||
|
|
await frigateApp.page.waitForTimeout(300);
|
||
|
|
const menuItem = frigateApp.page.getByLabel(target.label);
|
||
|
|
if (await menuItem.isVisible().catch(() => false)) {
|
||
|
|
await menuItem.click();
|
||
|
|
await expect(frigateApp.page).toHaveURL(
|
||
|
|
new RegExp(target.url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("account button in sidebar is clickable (desktop)", async ({
|
||
|
|
frigateApp,
|
||
|
|
}) => {
|
||
|
|
if (frigateApp.isMobile) {
|
||
|
|
test.skip();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await frigateApp.goto("/");
|
||
|
|
const sidebarBottom = frigateApp.page.locator("aside .mb-8");
|
||
|
|
const items = sidebarBottom.locator("div[class*='cursor-pointer']");
|
||
|
|
const count = await items.count();
|
||
|
|
if (count >= 2) {
|
||
|
|
await items.nth(1).click();
|
||
|
|
await frigateApp.page.waitForTimeout(500);
|
||
|
|
}
|
||
|
|
await expect(frigateApp.page.locator("body")).toBeVisible();
|
||
|
|
});
|
||
|
|
});
|