Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Blake Blackshear
2026-10-02 06:39:23 -05:00
57 changed files with 1242 additions and 333 deletions
+12
View File
@@ -216,6 +216,18 @@ test.describe("Explore — content @high", () => {
// Similarity search URL param
// ---------------------------------------------------------------------------
test.describe("Explore: back button @high", () => {
test("direct visits do not show a back button", async ({ frigateApp }) => {
await frigateApp.goto("/explore?labels=person");
await expect(frigateApp.page.getByLabel("Labels").first()).toBeVisible({
timeout: 10_000,
});
await expect(
frigateApp.page.getByRole("button", { name: "Go back" }),
).toHaveCount(0);
});
});
test.describe("Explore — similarity search (desktop) @high", () => {
test.skip(
({ frigateApp }) => frigateApp.isMobile,
+70 -31
View File
@@ -30,40 +30,51 @@ function groupedFacesMock() {
});
}
async function installGroupedFaces(app: FrigateApp) {
const GROUPED_EVENT = {
id: GROUPED_EVENT_ID,
label: "person",
sub_label: null,
camera: "front_door",
start_time: 1775487131.3863528,
end_time: 1775487161.3863528,
false_positive: false,
zones: ["front_yard"],
thumbnail: null,
has_clip: true,
has_snapshot: true,
retain_indefinitely: false,
plus_id: null,
model_hash: "abc123",
detector_type: "cpu",
model_type: "ssd",
data: {
top_score: 0.92,
score: 0.92,
region: [0.1, 0.1, 0.5, 0.8],
box: [0.2, 0.15, 0.45, 0.75],
area: 0.18,
ratio: 0.6,
type: "object",
path_data: [],
},
};
async function installGroupedFaces(
app: FrigateApp,
opts: { withEventIds?: boolean } = {},
) {
await app.api.install({
events: [
{
id: GROUPED_EVENT_ID,
label: "person",
sub_label: null,
camera: "front_door",
start_time: 1775487131.3863528,
end_time: 1775487161.3863528,
false_positive: false,
zones: ["front_yard"],
thumbnail: null,
has_clip: true,
has_snapshot: true,
retain_indefinitely: false,
plus_id: null,
model_hash: "abc123",
detector_type: "cpu",
model_type: "ssd",
data: {
top_score: 0.92,
score: 0.92,
region: [0.1, 0.1, 0.5, 0.8],
box: [0.2, 0.15, 0.45, 0.75],
area: 0.18,
ratio: 0.6,
type: "object",
path_data: [],
},
},
],
events: [GROUPED_EVENT],
faces: groupedFacesMock(),
});
// api-mocker does not cover /api/event_ids, which the card needs to link to
// Explore. Registered after install so it takes precedence.
if (opts.withEventIds) {
await app.page.route("**/api/event_ids**", (route) =>
route.fulfill({ json: [GROUPED_EVENT] }),
);
}
}
async function openGroupedFaceDialog(app: FrigateApp): Promise<Locator> {
@@ -512,6 +523,34 @@ test.describe("FaceSelectionDialog @high", () => {
});
});
test.describe("Face Library: return from Explore @high", () => {
test("Explore back button returns to an outlined collection", async ({
frigateApp,
}) => {
await installGroupedFaces(frigateApp, { withEventIds: true });
await frigateApp.goto("/faces");
// Mobile opens the collection as a MobilePage, which has no dialog role
const card = frigateApp.page
.locator('img[src*="clips/faces/train/"]')
.first()
.locator("xpath=..");
await card.click();
await frigateApp.page.getByLabel("View in Explore").click();
await expect(frigateApp.page).toHaveURL(
new RegExp(`/explore\\?event_id=${GROUPED_EVENT_ID}`),
);
const back = frigateApp.page.getByRole("button", { name: "Go back" });
await expect(back).toBeVisible({ timeout: 5_000 });
await back.click();
await expect(frigateApp.page).toHaveURL(/\/faces/);
await expect(card).toHaveClass(/outline-selected/, { timeout: 5_000 });
await expect(card).not.toHaveClass(/outline-selected/, { timeout: 5_000 });
});
});
test.describe("Face Library — mobile @high @mobile", () => {
test.skip(({ frigateApp }) => !frigateApp.isMobile, "Mobile-only");