frigate/web/e2e/specs/navigation.spec.ts
2026-04-06 11:15:22 -05:00

199 lines
6.0 KiB
TypeScript

/**
* Navigation tests -- CRITICAL tier.
*
* Tests sidebar (desktop) and bottombar (mobile) navigation,
* conditional nav items, settings menus, and route transitions.
*/
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 }) => {
await frigateApp.goto("/");
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
if (!frigateApp.isMobile) {
// Desktop: logo in sidebar
const logo = base.sidebar.locator('a[href="/"]').first();
await expect(logo).toBeVisible();
}
});
test("Live nav item is active on root path", async ({ frigateApp }) => {
await frigateApp.goto("/");
const liveLink = frigateApp.page.locator('a[href="/"]').first();
await expect(liveLink).toBeVisible();
});
test("navigate to Review page", async ({ frigateApp }) => {
await frigateApp.goto("/");
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
await base.navigateTo("/review");
await expect(frigateApp.page).toHaveURL(/\/review/);
});
test("navigate to Explore page", async ({ frigateApp }) => {
await frigateApp.goto("/");
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
await base.navigateTo("/explore");
await expect(frigateApp.page).toHaveURL(/\/explore/);
});
test("navigate to Export page", async ({ frigateApp }) => {
await frigateApp.goto("/");
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
await base.navigateTo("/export");
await expect(frigateApp.page).toHaveURL(/\/export/);
});
test("all primary nav links are present", async ({ frigateApp }) => {
await frigateApp.goto("/");
// Live, Review, Explore, Export are always present
await expect(frigateApp.page.locator('a[href="/"]').first()).toBeVisible();
await expect(
frigateApp.page.locator('a[href="/review"]').first(),
).toBeVisible();
await expect(
frigateApp.page.locator('a[href="/explore"]').first(),
).toBeVisible();
await expect(
frigateApp.page.locator('a[href="/export"]').first(),
).toBeVisible();
});
test("desktop sidebar is visible on desktop, hidden on mobile", 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 pages without crash", async ({ frigateApp }) => {
await frigateApp.goto("/");
const base = new BasePage(frigateApp.page, !frigateApp.isMobile);
const pageRoot = frigateApp.page.locator("#pageRoot");
// Navigate through all main pages in sequence
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 });
// Navigate back to review (not root, to avoid same-route re-render issues)
await base.navigateTo("/review");
await expect(pageRoot).toBeVisible({ timeout: 10_000 });
});
test("unknown route redirects to home", async ({ frigateApp }) => {
// Navigate to an unknown route - React Router's catch-all should redirect
await frigateApp.page.goto("/nonexistent-route");
// Wait for React to render and redirect
await frigateApp.page.waitForTimeout(2000);
// Should either be at root or show the page root (app didn't crash)
const url = frigateApp.page.url();
const hasPageRoot = await frigateApp.page
.locator("#pageRoot")
.isVisible()
.catch(() => false);
expect(url.endsWith("/") || hasPageRoot).toBeTruthy();
});
test("Faces nav hidden when face_recognition disabled", async ({
frigateApp,
}) => {
// Default config has face_recognition.enabled = false
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;
}
// Override config with genai.model = "none" to hide chat
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 and admin on desktop", async ({
frigateApp,
page,
}) => {
if (frigateApp.isMobile) {
test.skip();
return;
}
// Re-install with face_recognition enabled
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 and admin 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();
});
});