Initial curated EventLens source from 0d80d3b38dfb817285481665b9e71e4b8a0656a0

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:30:24 +02:00
commit 663ea8c829
85 files changed
+12395

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
import { expect, test } from "@playwright/test";
test("workspace navigation and primary controls expose accessible state", async ({ page }) => {
await page.goto("/#analysis");
for (const workspace of ["Analysis", "Timeline", "Hunting", "Reports", "Settings"]) {
await expect(page.getByRole("button", { name: workspace, exact: false }).first()).toBeEnabled();
}
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await expect(page.getByRole("button", { name: "Timeline", exact: false }).first()).toHaveAttribute("aria-current", "page");
await expect(page.getByRole("textbox", { name: "Search normalized evidence" })).toBeVisible();
await expect(page.getByLabel("Filter by category")).toBeVisible();
await expect(page.getByLabel("Filter by level")).toBeVisible();
await expect(page.getByLabel("Filter by provider")).toBeVisible();
await expect(page.getByRole("button", { name: "Previous evidence" })).toHaveAttribute("aria-label", "Previous evidence");
await expect(page.getByRole("listitem").first()).toHaveAttribute("aria-setsize", /[0-9]+/);
await page.getByRole("textbox", { name: "Search normalized evidence" }).focus();
await page.keyboard.press("Tab");
await expect(page.locator(":focus")).toBeVisible();
await page.getByRole("button", { name: "Hunting", exact: false }).click();
await expect(page.getByRole("textbox", { name: "Search hunting evidence" })).toBeVisible();
await page.getByRole("button", { name: "Reports", exact: false }).click();
await expect(page.getByRole("button", { name: "Export JSON", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Settings", exact: false }).click();
await expect(page.getByRole("group", { name: "Timestamp display" })).toBeVisible();
await page.setViewportSize({ width: 700, height: 900 });
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await expect(page.getByLabel("Evidence time window")).toBeVisible();
await expect(page.getByLabel("Hide informational noise")).toBeVisible();
await expect(page.getByLabel("Focus incident")).toBeVisible();
});
+64
View File
@@ -0,0 +1,64 @@
import { expect, test } from "@playwright/test";
const validUpload = { name: "sample.evtx", mimeType: "application/octet-stream", buffer: Buffer.from("synthetic") };
test("malformed successful analysis keeps the previous investigation", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await expect(page.getByRole("region", { name: "Current investigation context" })).toContainText("bundled-demo");
await page.route("**/api/analyze", (route) => route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ source: "bad.evtx", events: [], incidents: [{ id: "broken" }] }),
}));
await page.locator('input[type="file"]').setInputFiles(validUpload);
await expect(page.getByRole("alert")).toContainText("Analysis returned an invalid response.");
await expect(page.getByRole("region", { name: "Current investigation context" })).toContainText("bundled-demo");
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await expect(page.getByRole("heading", { name: "Inspector", exact: true })).toBeVisible();
});
test("structured parser errors have safe, useful wording", async ({ page }) => {
await page.route("**/api/analyze", (route) => route.fulfill({
status: 422,
contentType: "application/json",
body: JSON.stringify({ detail: { code: "no_readable_events", message: "No readable Windows events were found in this file." } }),
}));
await page.goto("/#analysis");
await page.locator('input[type="file"]').setInputFiles(validUpload);
await expect(page.getByRole("alert")).toContainText("No readable Windows events were found in this file.");
await expect(page.getByRole("alert")).not.toContainText("HTTP 422");
});
test("rejecting an invalid file cancels an older pending analysis", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await expect(page.getByRole("region", { name: "Current investigation context" })).toContainText("bundled-demo");
let routeStarted!: () => void;
let releaseRoute!: () => void;
const started = new Promise<void>((resolve) => { routeStarted = resolve; });
const released = new Promise<void>((resolve) => { releaseRoute = resolve; });
await page.route("**/api/analyze", async (route) => {
routeStarted();
await released;
try {
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ source: "late.evtx", events: [], incidents: [] }) });
} catch {
// The route can already be cancelled by the browser request abort.
}
});
const fileInput = page.locator('input[type="file"]');
await fileInput.setInputFiles(validUpload);
await started;
await fileInput.setInputFiles({ name: "invalid.txt", mimeType: "text/plain", buffer: Buffer.from("invalid") });
await expect(page.getByRole("alert")).toContainText("Choose a Windows .evtx file.");
releaseRoute();
await page.waitForTimeout(300);
await expect(page.getByRole("region", { name: "Current investigation context" })).toContainText("bundled-demo");
await expect(page.getByRole("alert")).toContainText("Choose a Windows .evtx file.");
});
+28
View File
@@ -0,0 +1,28 @@
import { expect, test } from "@playwright/test";
const assets = [
["favicon.svg", /image\/svg\+xml/i],
["favicon.ico", /image\/(x-icon|vnd\.microsoft\.icon)/i],
["apple-touch-icon.png", /image\/png/i],
["eventlens-icon-192.png", /image\/png/i],
["eventlens-icon-512.png", /image\/png/i],
["site.webmanifest", /(manifest|json)/i],
] as const;
test("canonical EventLens branding and icon package are locally served", async ({ page, request }) => {
await page.goto("/#analysis");
await expect(page).toHaveTitle("EventLens — Windows Incident Reconstruction");
await expect(page.locator('meta[name="application-name"]')).toHaveAttribute("content", "EventLens");
await expect(page.locator('link[rel="manifest"]')).toHaveAttribute("href", "/site.webmanifest");
await expect(page.locator(".event-logo svg")).toBeVisible();
for (const [asset, contentType] of assets) {
const response = await request.get(`/${asset}`);
expect(response.status(), `${asset} should be served`).toBe(200);
expect(response.headers()["content-type"] ?? "", `${asset} MIME type`).toMatch(contentType);
}
for (const workspace of ["Analysis", "Timeline", "Hunting", "Reports", "Settings"]) {
await expect(page.getByRole("button", { name: workspace, exact: false }).first().locator("svg")).toHaveCount(1);
}
});
+40
View File
@@ -0,0 +1,40 @@
import { expect, test } from "@playwright/test";
test("demo investigation workflow crosses Analysis, Timeline and Reports", async ({ page }) => {
await page.goto("/#analysis");
await expect(page.getByRole("heading", { name: "No event log loaded", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await expect(page.getByRole("region", { name: "Current investigation context" }).getByText("bundled-demo", { exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "Incident windows", exact: true })).toBeVisible();
await expect(page.getByText("Analysis status", { exact: true })).toBeVisible();
await expect(page.getByText("Deterministic demo fixture", { exact: true })).toBeVisible();
await expect(page.getByText("Current source", { exact: true })).toHaveCount(0);
await expect(page.getByText("How to continue", { exact: true })).toBeVisible();
await page.getByText("Service failure recorded", { exact: true }).click();
await expect(page).toHaveURL(/#timeline$/);
await expect(page.getByRole("heading", { name: "Event timeline", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "Example Sync Service terminated unexpectedly", exact: true })).toBeVisible();
await expect(page.getByText("Focus active", { exact: true })).toBeVisible();
const search = page.getByRole("textbox", { name: "Search normalized evidence" });
await search.fill("DNS name resolution");
await expect(page.locator(".event-row")).toHaveCount(0);
await search.fill("");
await page.getByRole("button", { name: "Stop focus", exact: true }).click();
await page.getByLabel("Filter by category").selectOption("network");
await expect(page.locator(".event-row")).toHaveCount(1);
await page.getByLabel("Filter by category").selectOption("");
await page.getByRole("button", { name: /Next evidence/ }).click();
await expect(page.getByText(/of [0-9]+/).first()).toBeVisible();
await page.getByRole("button", { name: "Reports", exact: false }).click();
await expect(page.getByRole("heading", { name: "Export report", exact: true })).toBeVisible();
await expect(page.getByText("Redaction preview", { exact: true })).toBeVisible();
const jsonDownload = page.waitForEvent("download");
await page.getByRole("button", { name: "Export JSON", exact: true }).click();
expect((await jsonDownload).suggestedFilename()).toMatch(/eventlens\.json$/);
const markdownDownload = page.waitForEvent("download");
await page.getByRole("button", { name: "Export Markdown", exact: true }).click();
expect((await markdownDownload).suggestedFilename()).toMatch(/eventlens\.md$/);
});
+25
View File
@@ -0,0 +1,25 @@
import { expect, test } from "@playwright/test";
const partialAnalysis = {
source: "partial.evtx",
events: [{ id: "1", timestamp: "2026-09-09T10:00:00Z", timestamp_source: "event", timestamp_valid: true, provider: "Synthetic", event_id: 1000, level: "error", channel: "Application", computer: "TEST-PC", category: "crash", summary: "Synthetic crash", fields: {} }],
incidents: [{ id: "incident-1", rule_id: "windows.generic.attention", start: "2026-09-09T10:00:00Z", end: "2026-09-09T10:00:00Z", title: "Synthetic crash", category: "crash", severity: "warning", reasons: ["Parser retained only a partial result."], evidence_event_ids: ["1"], original_count: 1, compressed_count: 1 }],
parse: { parser: "evtx-rs", status: "partial", parsed_events: 1, records_seen: 4, skipped_records: 2, issue_count: 1, truncated: true, truncation_reason: "event_limit_reached", duration_ms: 8, issues: [{ code: "event_limit_reached", record_id: 4, message: "later records were not analyzed", skipped_record: true }] },
};
test("partial result is unmistakable and invalid extension is rejected", async ({ page }) => {
await page.route("**/api/analyze", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(partialAnalysis) }));
await page.goto("/#analysis");
await expect(page.getByRole("heading", { name: "Analysis", exact: true })).toBeVisible();
await page.locator('input[type="file"]').setInputFiles({ name: "partial.evtx", mimeType: "application/octet-stream", buffer: Buffer.from("synthetic") });
await expect(page.getByRole("alert")).toContainText("Partial analysis");
await expect(page.getByRole("alert")).toContainText("incident reconstruction may be incomplete");
await expect(page.getByText("event_limit_reached", { exact: true })).toBeVisible();
await expect(page.getByText("Analysis status", { exact: true })).toBeVisible();
await expect(page.getByText("2 skipped · 1 parser issue", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await expect(page.getByRole("alert")).toContainText("incident reconstruction may be incomplete");
await page.locator('input[type="file"]').setInputFiles({ name: "not-an-evtx.txt", mimeType: "text/plain", buffer: Buffer.from("nope") });
await expect(page.getByRole("alert").filter({ hasText: "Choose a Windows .evtx file." })).toBeVisible();
});
+29
View File
@@ -0,0 +1,29 @@
import { expect, test } from "@playwright/test";
test("capture deterministic v1.0.6 portfolio views", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await expect(page.getByRole("heading", { name: "Incident windows", exact: true })).toBeVisible();
await page.screenshot({ path: "../docs/assets/eventlens-demo-overview.png", fullPage: true });
await page.getByText("Service failure recorded", { exact: true }).click();
await expect(page.getByRole("heading", { name: "Event timeline", exact: true })).toBeVisible();
await page.screenshot({ path: "../docs/assets/eventlens-timeline-evidence.png", fullPage: true });
await page.getByRole("button", { name: "Stop focus", exact: true }).click();
await page.getByRole("textbox", { name: "Search normalized evidence" }).fill("DNS name resolution");
await expect(page.locator(".event-row")).toHaveCount(1);
await page.screenshot({ path: "../docs/assets/eventlens-filter-search.png", fullPage: true });
await page.getByRole("button", { name: "Hunting", exact: false }).click();
await expect(page.getByRole("heading", { name: "Hunting", exact: true })).toBeVisible();
await page.screenshot({ path: "../docs/assets/eventlens-hunting.png", fullPage: true });
await page.getByRole("button", { name: "Reports", exact: false }).click();
await expect(page.getByRole("heading", { name: "Export report", exact: true })).toBeVisible();
await page.screenshot({ path: "../docs/assets/eventlens-redaction-export.png", fullPage: true });
await page.getByRole("button", { name: "Settings", exact: true }).click();
await expect(page.getByRole("heading", { name: "Local data & privacy", exact: true })).toBeVisible();
await page.screenshot({ path: "../docs/assets/eventlens-settings.png", fullPage: true });
});
+19
View File
@@ -0,0 +1,19 @@
import { existsSync } from "node:fs";
import { expect, test } from "@playwright/test";
const fixture = process.env.EVENTLENS_EVTX_FIXTURE;
test("pinned real EVTX fixture survives the browser upload path", async ({ page }) => {
test.skip(!fixture || !existsSync(fixture), "CI supplies the hash-verified public EVTX fixture");
await page.goto("/#analysis");
await page.locator('input[type="file"]').setInputFiles(fixture!);
await expect(page.locator(".metric-card").first()).toContainText("7", { timeout: 30_000 });
await expect(page.getByText("events parsed", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await page.getByLabel("Hide informational noise").uncheck();
await expect(page.locator(".event-row").first()).toBeVisible();
await expect(page.getByRole("button", { name: /4625/ })).toBeVisible();
await page.getByRole("button", { name: "Reports", exact: false }).click();
await expect(page.getByRole("heading", { name: "Export report", exact: true })).toBeVisible();
await page.getByRole("button", { name: /Export JSON/ }).click();
});
+17
View File
@@ -0,0 +1,17 @@
import { expect, test } from "@playwright/test";
test("core workspace actions remain usable across supported viewport widths", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
for (const viewport of [{ width: 360, height: 800 }, { width: 390, height: 844 }, { width: 768, height: 900 }, { width: 1024, height: 768 }, { width: 1366, height: 768 }, { width: 1440, height: 900 }, { width: 1920, height: 1080 }]) {
await page.setViewportSize(viewport);
await expect(page.getByRole("button", { name: "Open .evtx", exact: true }).first()).toBeVisible();
for (const workspace of ["Analysis", "Timeline", "Hunting", "Reports", "Settings"]) {
const button = page.getByRole("button", { name: workspace, exact: false }).first();
await button.click();
await expect(page.getByRole("heading", { name: workspace, exact: true })).toBeVisible();
const overflow = await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1);
expect(overflow, `horizontal overflow in ${workspace} at ${viewport.width}px`).toBeTruthy();
}
}
});
@@ -0,0 +1,32 @@
import { expect, test } from "@playwright/test";
test("settings keeps session state and reports a persistence failure", async ({ page }) => {
await page.addInitScript(() => {
const originalSetItem = Storage.prototype.setItem;
Storage.prototype.setItem = function setItem(key: string, value: string) {
if (key === "eventlens.preferences.v1") {
throw new DOMException("Storage is unavailable", "QuotaExceededError");
}
return originalSetItem.call(this, key, value);
};
});
await page.goto("/#settings");
const compact = page.getByRole("radio", { name: /Compact/i });
await compact.press("Space");
await expect(compact).toBeChecked();
await expect(page.locator(".settings-save-feedback")).toHaveText("Preferences changed for this session only");
await expect(page.getByText("Saved automatically in this browser", { exact: true })).toBeHidden();
await expect(page.getByText("Preferences are available for this session only because browser storage is unavailable.")).toBeVisible();
await expect(page.getByText("Not saved to browser storage")).toBeVisible();
await expect(page.getByText("Interface preferences for this session")).toBeVisible();
await page.waitForTimeout(2_600);
await expect(page.getByRole("status")).toHaveText("Preferences changed for this session only");
await expect(page.getByText("Saved automatically in this browser", { exact: true })).toBeHidden();
await page.reload();
await expect(page.getByRole("radio", { name: /Comfortable/i })).toBeChecked();
});
+59
View File
@@ -0,0 +1,59 @@
import { expect, test } from "@playwright/test";
test("Settings communicates browser-local saving and resets accessible preference controls", async ({ page }) => {
await page.goto("/#settings");
await expect(page.getByText("Saved automatically in this browser", { exact: true })).toBeVisible();
await expect(page.getByRole("group", { name: "Timestamp display" })).toBeVisible();
await expect(page.getByRole("group", { name: "Interface density" })).toBeVisible();
await expect(page.getByRole("switch", { name: "Export redaction by default" })).toBeChecked();
await page.locator("label.preference-option").filter({ hasText: "Local browser time" }).click();
await expect(page.getByRole("status").filter({ hasText: "Preferences saved" })).toBeVisible();
await page.getByRole("switch", { name: "Export redaction by default" }).press("Space");
await page.getByLabel("Default noise mode").selectOption("all");
await page.getByLabel("Default workspace").selectOption("hunting");
await page.getByRole("radio", { name: /Compact/ }).press("Space");
await page.reload();
await expect(page.getByRole("radio", { name: /Local browser time/ })).toBeChecked();
await expect(page.getByRole("switch", { name: "Export redaction by default" })).not.toBeChecked();
await expect(page.getByLabel("Default noise mode")).toHaveValue("all");
await expect(page.getByLabel("Default workspace")).toHaveValue("hunting");
await expect(page.getByRole("radio", { name: /Compact/ })).toBeChecked();
const storedKeys = await page.evaluate(() => Object.keys(localStorage));
expect(storedKeys).toEqual(["eventlens.preferences.v1"]);
await page.getByText("Technical details", { exact: true }).press("Enter");
await expect(page.getByText("eventlens.preferences.v1", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Reset all preferences" }).press("Enter");
await expect(page.getByRole("status").filter({ hasText: "Preferences reset to defaults" })).toBeVisible();
await expect(page.getByRole("radio", { name: /^UTC/ })).toBeChecked();
await expect(page.getByRole("switch", { name: "Export redaction by default" })).toBeChecked();
await expect(page.getByLabel("Default noise mode")).toHaveValue("attention");
await expect(page.getByLabel("Default workspace")).toHaveValue("analysis");
});
test("a browser-local default workspace applies only without a deep-link override", async ({ page }) => {
await page.goto("/#settings");
await page.getByLabel("Default workspace").selectOption("hunting");
await page.goto("/");
await expect(page).toHaveURL(/#hunting$/);
await expect(page.getByRole("heading", { name: "Hunting", exact: true })).toBeVisible();
await page.goto("/#timeline");
await expect(page).toHaveURL(/#timeline$/);
await expect(page.getByRole("heading", { name: "Timeline", exact: true })).toBeVisible();
});
test("the active Settings tab remains visible in the compact workspace navigation", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/#settings");
const settingsTab = page.getByRole("button", { name: "Settings", exact: true });
await expect(settingsTab).toHaveAttribute("aria-current", "page");
const box = await settingsTab.boundingBox();
expect(box).not.toBeNull();
expect(box!.x).toBeGreaterThanOrEqual(0);
expect(box!.x + box!.width).toBeLessThanOrEqual(390);
});
+22
View File
@@ -0,0 +1,22 @@
import { expect, test } from "@playwright/test";
function largeAnalysis(count: number) {
return {
source: "synthetic-large.evtx",
events: Array.from({ length: count }, (_, index) => ({ id: String(index), timestamp: "2026-09-09T10:00:00Z", timestamp_source: "event", timestamp_valid: true, provider: "Synthetic Provider", event_id: index, level: "warning", channel: "Application", computer: "TEST-PC", category: "other", summary: `Synthetic event ${index}`, fields: { message: `Synthetic event ${index}` } })),
incidents: [],
};
}
test("large timelines virtualize DOM rows and keep search usable", async ({ page }) => {
const analysis = largeAnalysis(10_000);
await page.route("**/api/demo", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(analysis) }));
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await expect(page.getByText("10,000", { exact: false }).first()).toBeVisible();
expect(await page.locator(".event-row").count()).toBeLessThan(80);
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await page.getByRole("textbox", { name: "Search normalized evidence" }).fill("Synthetic event 9999");
await expect(page.locator(".event-row")).toHaveCount(1);
await expect(page.locator(".event-row").first()).toContainText("9999");
});
+77
View File
@@ -0,0 +1,77 @@
import { expect, test } from "@playwright/test";
test("all workspaces are actionable and preserve the loaded analysis", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
for (const workspace of ["Analysis", "Timeline", "Hunting", "Reports", "Settings"]) {
const button = page.getByRole("button", { name: workspace, exact: false }).first();
await expect(button).toBeEnabled();
await button.click();
await expect(button).toHaveAttribute("aria-current", "page");
}
await expect(page.getByRole("region", { name: "Local preferences context" })).toBeVisible();
await expect(page.getByRole("region", { name: "Current investigation context" })).toHaveCount(0);
await page.getByRole("button", { name: "Timeline", exact: false }).click();
await expect(page.getByRole("heading", { name: "Event timeline", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "Inspector", exact: true })).toBeVisible();
await page.goBack();
await expect(page.getByRole("heading", { name: "Settings", exact: true })).toBeVisible();
await page.goForward();
await expect(page.getByRole("heading", { name: "Event timeline", exact: true })).toBeVisible();
});
test("context and global actions keep an ordered, workspace-appropriate layout", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
const geometry = await page.evaluate(() => {
const rect = (selector: string) => document.querySelector(selector)?.getBoundingClientRect();
const topbar = rect(".event-topbar");
const context = rect(".investigation-context");
const actions = rect(".global-actions");
return { topbar, context, actions };
});
expect(geometry.topbar?.bottom).toBeLessThanOrEqual((geometry.context?.top ?? 0) + 1);
expect(geometry.context?.bottom).toBeLessThanOrEqual((geometry.actions?.top ?? 0) + 1);
await page.getByRole("button", { name: "Settings", exact: true }).click();
await expect(page.getByRole("region", { name: "Local preferences context" })).toBeVisible();
await expect(page.getByRole("region", { name: "Current investigation context" })).toHaveCount(0);
await expect(page.getByRole("region", { name: "Analysis actions" })).toBeVisible();
await expect(page.getByText("Source", { exact: true })).toHaveCount(0);
});
test("Hunting combines facets and opens a result in the inspector", async ({ page }) => {
await page.goto("/#analysis");
await page.getByRole("button", { name: "Load demo", exact: true }).first().click();
await page.getByRole("button", { name: "Hunting", exact: false }).click();
await page.getByRole("textbox", { name: "Search hunting evidence" }).fill("DNS name resolution");
await expect(page.getByRole("status")).toHaveText("1 of 6 matching events");
await expect(page.getByRole("button", { name: /DNS name resolution timed out/ })).toBeVisible();
await page.getByRole("button", { name: /DNS name resolution timed out/ }).click();
await expect(page.getByRole("heading", { name: "Inspector", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "DNS name resolution timed out", exact: true })).toBeVisible();
await page.getByRole("button", { name: "Open in Timeline", exact: false }).click();
await expect(page).toHaveURL(/#timeline$/);
await expect(page.getByRole("heading", { name: "Event timeline", exact: true })).toBeVisible();
});
test("Settings persist preferences without persisting event data", async ({ page }) => {
await page.goto("/#settings");
await page.getByRole("radio", { name: /Local browser time/ }).press("Space");
await page.getByLabel("Default noise mode").selectOption("all");
await page.getByLabel("Default workspace").selectOption("hunting");
await page.getByRole("radio", { name: /Compact/ }).press("Space");
await page.reload();
await expect(page.getByLabel("Local browser time", { exact: false })).toBeChecked();
await expect(page.getByLabel("Default noise mode")).toHaveValue("all");
await expect(page.getByLabel("Default workspace")).toHaveValue("hunting");
await expect(page.getByLabel("Compact", { exact: false })).toBeChecked();
const stored = await page.evaluate(() => JSON.stringify(localStorage));
expect(stored).toContain("eventlens.preferences.v1");
expect(stored).not.toContain("bundled-demo");
expect(stored).not.toContain("Example Sync Service");
});