mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
Refresh Patrol browser qualification
This commit is contained in:
@@ -587,7 +587,7 @@ test.describe("Patrol runtime-state browser contract", () => {
|
||||
await expect(page.getByText("Patrol paused").first()).toBeVisible();
|
||||
await expect(page.getByText(PATROL_BLOCK_REASON).first()).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Run Patrol" }),
|
||||
page.getByRole("button", { name: "Check now" }),
|
||||
).toBeDisabled();
|
||||
await expect(page.getByText(/Patrol quickstart/i)).toHaveCount(0);
|
||||
await expect(page.getByText("Health A · 100/100")).toHaveCount(0);
|
||||
@@ -631,7 +631,7 @@ test.describe("Patrol runtime-state browser contract", () => {
|
||||
|
||||
await page.goto("/patrol", { waitUntil: "domcontentloaded" });
|
||||
|
||||
const runButton = page.getByRole("button", { name: "Run Patrol" });
|
||||
const runButton = page.getByRole("button", { name: "Check now" });
|
||||
await expect(runButton).toBeEnabled();
|
||||
await runButton.click();
|
||||
|
||||
@@ -801,6 +801,7 @@ test.describe("Patrol runtime-state browser contract", () => {
|
||||
|
||||
await page.goto("/patrol", { waitUntil: "domcontentloaded" });
|
||||
|
||||
await page.getByText("How Patrol operates", { exact: true }).click();
|
||||
const modeGroup = page.getByRole("group", { name: "Patrol mode" });
|
||||
await expect(modeGroup).toBeVisible();
|
||||
await expect(
|
||||
|
||||
@@ -605,9 +605,8 @@ test.describe("Patrol Assistant operator briefing", () => {
|
||||
|
||||
await page.goto("/patrol", { waitUntil: "domcontentloaded" });
|
||||
await page
|
||||
.getByText("Patrol checks, investigations, and run history", {
|
||||
exact: true,
|
||||
})
|
||||
.locator("summary")
|
||||
.filter({ hasText: "Operational records and run history" })
|
||||
.click();
|
||||
|
||||
await page.getByText("High CPU usage").click();
|
||||
@@ -745,9 +744,8 @@ test.describe("Patrol Assistant operator briefing", () => {
|
||||
includePendingApproval = false;
|
||||
await page.reload({ waitUntil: "domcontentloaded" });
|
||||
await page
|
||||
.getByText("Patrol checks, investigations, and run history", {
|
||||
exact: true,
|
||||
})
|
||||
.locator("summary")
|
||||
.filter({ hasText: "Operational records and run history" })
|
||||
.click();
|
||||
const queuedFindingTitle = page.getByText("High CPU usage").first();
|
||||
await expect(queuedFindingTitle).toBeVisible();
|
||||
@@ -779,9 +777,8 @@ test.describe("Patrol Assistant operator briefing", () => {
|
||||
includeInvestigationProposedFix = true;
|
||||
await page.reload({ waitUntil: "domcontentloaded" });
|
||||
await page
|
||||
.getByText("Patrol checks, investigations, and run history", {
|
||||
exact: true,
|
||||
})
|
||||
.locator("summary")
|
||||
.filter({ hasText: "Operational records and run history" })
|
||||
.click();
|
||||
const expiredFindingTitle = page.getByText("High CPU usage").first();
|
||||
await expect(expiredFindingTitle).toBeVisible();
|
||||
|
||||
@@ -659,7 +659,7 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
desktopNav.getByRole("tab", {
|
||||
name: "Patrol: 1 active attention item",
|
||||
name: /Patrol: 1 (?:action awaits approval|active attention item)/,
|
||||
}),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
@@ -714,9 +714,9 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
||||
page.getByRole("heading", { level: 1, name: "Patrol" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { level: 2, name: "Needs attention" }),
|
||||
page.getByRole("heading", { level: 2, name: "Needs you" }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText("Nothing needs your attention")).toBeVisible();
|
||||
await expect(page.getByText("Nothing needs you right now")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("list", { name: "Patrol protection posture" }),
|
||||
).toHaveCount(0);
|
||||
@@ -760,7 +760,7 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
||||
await page.getByRole("tab", { name: /Patrol/ }).click();
|
||||
await expect(page).toHaveURL(/\/patrol$/);
|
||||
await expect(
|
||||
page.getByRole("heading", { level: 2, name: "Needs attention" }),
|
||||
page.getByRole("heading", { level: 2, name: "Needs you" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("list", { name: "Patrol attention items" }),
|
||||
@@ -883,9 +883,8 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
||||
page.getByRole("heading", { level: 1, name: "Patrol" }),
|
||||
).toBeVisible();
|
||||
await page
|
||||
.getByText("Patrol checks, investigations, and run history", {
|
||||
exact: true,
|
||||
})
|
||||
.locator("summary")
|
||||
.filter({ hasText: "Operational records and run history" })
|
||||
.click();
|
||||
await expect(
|
||||
page.getByText("Operating system updates need review").first(),
|
||||
@@ -931,9 +930,8 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
||||
);
|
||||
await page.reload({ waitUntil: "domcontentloaded" });
|
||||
await page
|
||||
.getByText("Patrol checks, investigations, and run history", {
|
||||
exact: true,
|
||||
})
|
||||
.locator("summary")
|
||||
.filter({ hasText: "Operational records and run history" })
|
||||
.click();
|
||||
const cleanupTitle = page
|
||||
.getByText("Downloaded package data is using needed space")
|
||||
@@ -990,9 +988,8 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
||||
);
|
||||
await page.reload({ waitUntil: "domcontentloaded" });
|
||||
await page
|
||||
.getByText("Patrol checks, investigations, and run history", {
|
||||
exact: true,
|
||||
})
|
||||
.locator("summary")
|
||||
.filter({ hasText: "Operational records and run history" })
|
||||
.click();
|
||||
await page.getByRole("button", { name: "Resolved", exact: true }).click();
|
||||
const resolvedTitle = page
|
||||
|
||||
@@ -361,6 +361,8 @@ const routeActionFixtures = async (
|
||||
new URL(route.request().url()).pathname.split("/").pop() || "",
|
||||
);
|
||||
const audit = all.find((candidate) => candidate.id === id);
|
||||
const hasDeliveryRecord =
|
||||
audit && audit.state !== "planned" && audit.state !== "pending_approval";
|
||||
await route.fulfill({
|
||||
status: audit ? 200 : 404,
|
||||
contentType: "application/json",
|
||||
@@ -369,20 +371,37 @@ const routeActionFixtures = async (
|
||||
? {
|
||||
audit,
|
||||
events: [],
|
||||
attempt: {
|
||||
id: `${id}-attempt`,
|
||||
actionId: id,
|
||||
state: "receipt_recorded",
|
||||
createdAt: audit.createdAt,
|
||||
updatedAt: audit.updatedAt,
|
||||
dispatchCount: 1,
|
||||
},
|
||||
receipt: {
|
||||
attemptId: `${id}-attempt`,
|
||||
actionId: id,
|
||||
transportRequestId: `${id}-transport`,
|
||||
receivedAt: "2026-07-12T10:05:00Z",
|
||||
readiness: {
|
||||
ready: audit.state === "planned" || audit.state === "pending_approval",
|
||||
code:
|
||||
audit.state === "planned" || audit.state === "pending_approval"
|
||||
? "ready"
|
||||
: "action_not_actionable",
|
||||
message:
|
||||
audit.state === "planned" || audit.state === "pending_approval"
|
||||
? "Action is ready for approval and dispatch."
|
||||
: "This action is no longer open for approval or dispatch.",
|
||||
refreshable: false,
|
||||
checkedAt: audit.updatedAt,
|
||||
},
|
||||
...(hasDeliveryRecord
|
||||
? {
|
||||
attempt: {
|
||||
id: `${id}-attempt`,
|
||||
actionId: id,
|
||||
state: "receipt_recorded",
|
||||
createdAt: audit.createdAt,
|
||||
updatedAt: audit.updatedAt,
|
||||
dispatchCount: 1,
|
||||
},
|
||||
receipt: {
|
||||
attemptId: `${id}-attempt`,
|
||||
actionId: id,
|
||||
transportRequestId: `${id}-transport`,
|
||||
receivedAt: "2026-07-12T10:05:00Z",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
: { error: "not found" },
|
||||
),
|
||||
@@ -390,7 +409,7 @@ const routeActionFixtures = async (
|
||||
});
|
||||
};
|
||||
|
||||
test("Actions inbox exposes the canonical decision packet and durable calm history", async ({
|
||||
test("Activity history exposes the canonical decision packet and durable calm history", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await page.route("**/api/actions?*", async (route) => {
|
||||
@@ -409,14 +428,31 @@ test("Actions inbox exposes the canonical decision packet and durable calm histo
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ audit: action, events: [] }),
|
||||
body: JSON.stringify({
|
||||
audit: action,
|
||||
events: [],
|
||||
readiness: {
|
||||
ready: true,
|
||||
code: "ready",
|
||||
message: "Action is ready for approval and dispatch.",
|
||||
refreshable: false,
|
||||
checkedAt: action.updatedAt,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
await page.goto("/actions?action=action-1", {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
await expect(page.getByRole("heading", { name: "Actions" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Activity history" }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole("dialog", { name: "Restart" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("dialog", { name: "Restart" }).getByRole("button", {
|
||||
name: "Approve",
|
||||
}),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: "Close action review" }).click();
|
||||
await expect(page).toHaveURL(/\/actions$/);
|
||||
await expect(
|
||||
|
||||
@@ -36,6 +36,7 @@ test("Autopilot records a versioned server acknowledgement before effective full
|
||||
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ success: true, settings: { autonomy_level: active ? "full" : "monitor", requested_autonomy_level: active ? "full" : "monitor", effective_autonomy_level: active ? "full" : "monitor", full_mode_unlocked: active, autopilot_acknowledgement: status, investigation_budget: 15, investigation_timeout_sec: 300 }, autonomy_level: active ? "full" : "monitor", requested_autonomy_level: active ? "full" : "monitor", effective_autonomy_level: active ? "full" : "monitor", full_mode_unlocked: active, autopilot_acknowledgement: status, investigation_budget: 15, investigation_timeout_sec: 300 }) });
|
||||
});
|
||||
await page.goto("/patrol", { waitUntil: "domcontentloaded" });
|
||||
await page.getByText("How Patrol operates", { exact: true }).click();
|
||||
await page.getByRole("button", { name: "Autopilot" }).click();
|
||||
await expect(page.getByRole("dialog", { name: "Activate Autopilot" })).toBeVisible();
|
||||
const activate = page.getByRole("button", { name: "Record acknowledgement and activate" });
|
||||
|
||||
@@ -16,11 +16,11 @@ const test = base.extend<{}, WorkerFixtures>({
|
||||
}, { scope: "worker" }],
|
||||
});
|
||||
|
||||
test("Actions remains named, keyboard reachable, and free of horizontal overflow at phone width", async ({ page }, testInfo) => {
|
||||
test("Activity history remains named, keyboard reachable, and free of horizontal overflow at phone width", async ({ page }, testInfo) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.route("**/api/actions?*", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ view: "pending", actions: [], count: 0 }) }));
|
||||
await page.goto("/actions", { waitUntil: "domcontentloaded" });
|
||||
await expect(page.getByRole("heading", { name: "Actions", exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Activity history", exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("tablist", { name: "Action views" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Refresh actions" })).toBeVisible();
|
||||
const overflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth);
|
||||
|
||||
@@ -68,7 +68,7 @@ test.describe("Operational trust protection posture", () => {
|
||||
const detailRow = coverageTable.locator("[data-inline-detail-for]").first();
|
||||
await expect(detailRow).toBeVisible();
|
||||
await expect(detailRow).toContainText(
|
||||
/Protected:|Attention:|Unprotected:|Unknown:/,
|
||||
/Protected:|Needs attention:|Unprotected:|Unknown:/,
|
||||
);
|
||||
await expect(detailRow).toContainText(
|
||||
/Restore evidence|No restore evidence has been discovered/,
|
||||
|
||||
@@ -2,21 +2,6 @@ import { expect, test, type Page, type Route } from "@playwright/test";
|
||||
|
||||
type AttentionMode = "active" | "calm" | "failed";
|
||||
|
||||
async function selectAttentionState(page: Page, label: string, value: string) {
|
||||
const queue = page.getByRole("region", { name: "Needs attention" });
|
||||
const segmentedChoice = queue.getByRole("button", {
|
||||
name: label,
|
||||
exact: true,
|
||||
});
|
||||
if (await segmentedChoice.isVisible()) {
|
||||
await segmentedChoice.click();
|
||||
return;
|
||||
}
|
||||
await queue
|
||||
.getByRole("combobox", { name: "Attention state" })
|
||||
.selectOption(value);
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const pageErrors: string[] = [];
|
||||
const consoleErrors: string[] = [];
|
||||
@@ -689,7 +674,7 @@ test("starts from the normal monitor shell and reaches the canonical attention q
|
||||
|
||||
const patrolNavigation = testInfo.project.name.startsWith("mobile-")
|
||||
? page
|
||||
.getByRole("tablist", { name: "Mobile navigation" })
|
||||
.getByRole("navigation", { name: "Mobile navigation" })
|
||||
.getByRole("button", { name: /Patrol/ })
|
||||
: page
|
||||
.getByRole("tab", { name: /Patrol/ })
|
||||
@@ -698,7 +683,7 @@ test("starts from the normal monitor shell and reaches the canonical attention q
|
||||
|
||||
await expect(page).toHaveURL(/\/patrol/);
|
||||
await expect(
|
||||
page.getByRole("region", { name: "Needs attention" }),
|
||||
page.getByRole("region", { name: "Needs you" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Open CPU pressure on pve-main" }),
|
||||
@@ -718,8 +703,8 @@ test("makes active operational work primary and preserves the evidence boundary"
|
||||
}),
|
||||
),
|
||||
).toBeVisible();
|
||||
const queue = page.getByRole("region", { name: "Needs attention" });
|
||||
await expect(queue.getByLabel("2 active attention items")).toBeVisible();
|
||||
const queue = page.getByRole("region", { name: "Needs you" });
|
||||
await expect(queue.getByLabel("2 decisions need you")).toBeVisible();
|
||||
await expect(
|
||||
queue.getByText(
|
||||
"CPU has remained above the configured threshold for two collection cycles.",
|
||||
@@ -733,28 +718,6 @@ test("makes active operational work primary and preserves the evidence boundary"
|
||||
page.getByRole("button", { name: "Explain with Assistant" }),
|
||||
).toHaveCount(0);
|
||||
|
||||
await selectAttentionState(page, "Acknowledged 1", "acknowledged");
|
||||
await expect(
|
||||
queue.getByText("Memory pressure acknowledged on pve-lab"),
|
||||
).toBeVisible();
|
||||
await selectAttentionState(page, "Suppressed 1", "suppressed");
|
||||
await expect(
|
||||
queue.getByText("Maintenance alert suppressed on pve-maintenance"),
|
||||
).toBeVisible();
|
||||
await selectAttentionState(page, "Stale or unknown 1", "stale_unknown");
|
||||
await expect(
|
||||
queue.getByText("Connection state unknown for pve-edge"),
|
||||
).toBeVisible();
|
||||
await expect(queue.getByText("Evidence incomplete")).toBeVisible();
|
||||
await expect(queue.getByText(/timing unavailable/)).toHaveCount(0);
|
||||
await expect(queue.getByText("Protection Unknown")).toHaveCount(0);
|
||||
await expect(queue.getByText("Unknown / Partial")).toHaveCount(0);
|
||||
await selectAttentionState(page, "Recent resolved 1", "resolved");
|
||||
await expect(
|
||||
queue.getByText("Storage pressure resolved on pve-recovered"),
|
||||
).toBeVisible();
|
||||
await selectAttentionState(page, "Active 2", "active");
|
||||
|
||||
const itemButton = queue.getByRole("button", {
|
||||
name: "Open CPU pressure on pve-main",
|
||||
});
|
||||
@@ -873,10 +836,10 @@ test("shows calm only with current coverage and never converts failure into heal
|
||||
const fixture = await mockAttention(page, "calm");
|
||||
await page.goto("/patrol", { waitUntil: "domcontentloaded" });
|
||||
|
||||
await expect(page.getByText("Nothing needs your attention")).toBeVisible();
|
||||
await expect(page.getByText("Nothing needs you right now")).toBeVisible();
|
||||
await expect(
|
||||
page.getByText(
|
||||
"The current operational lifecycle evaluation has no active items.",
|
||||
"The current operational evaluation has no active items.",
|
||||
{
|
||||
exact: false,
|
||||
},
|
||||
@@ -1263,6 +1226,16 @@ async function mockGovernedAttentionAction(
|
||||
body: JSON.stringify({
|
||||
audit,
|
||||
events: [],
|
||||
readiness: {
|
||||
ready: actionState !== "completed",
|
||||
code: actionState === "completed" ? "action_not_actionable" : "ready",
|
||||
message:
|
||||
actionState === "completed"
|
||||
? "This action is already complete."
|
||||
: "The target is feasible and ready for approval.",
|
||||
refreshable: actionState !== "completed",
|
||||
checkedAt: evaluatedAt,
|
||||
},
|
||||
...(actionState === "completed"
|
||||
? {
|
||||
attempt: {
|
||||
|
||||
@@ -64,7 +64,7 @@ async function routeResources(page: Page, resources: RouteResource[]) {
|
||||
// Keep the fixture authoritative: an empty mocked socket makes the
|
||||
// websocket-first resource hook fall back to the routed REST snapshot
|
||||
// without accepting a live backend frame.
|
||||
await page.routeWebSocket("**/ws", () => {});
|
||||
await page.routeWebSocket("**/ws*", () => {});
|
||||
await page.route("**/api/resources**", async (route) => {
|
||||
const requestUrl = new URL(route.request().url());
|
||||
if (requestUrl.pathname !== "/api/resources") {
|
||||
@@ -587,10 +587,12 @@ test.describe("Operational trust availability resource facet", () => {
|
||||
|
||||
await page.goto("/patrol", { waitUntil: "domcontentloaded" });
|
||||
|
||||
const queue = page.getByRole("region", { name: "Needs attention" });
|
||||
const queue = page.getByRole("region", { name: "Needs you" });
|
||||
await expect(queue).toBeVisible({ timeout: 30_000 });
|
||||
await expect(
|
||||
page.getByRole("tab", { name: "Patrol: 1 active attention item" }),
|
||||
page.getByRole("tab", {
|
||||
name: /Patrol: 1 (?:action awaits approval|active attention item)/,
|
||||
}),
|
||||
).toBeVisible();
|
||||
await expect(queue.getByText(item.plainLanguageSummary)).toBeVisible();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user