Publish curated ITWorx_MCP_Hub source from 6c7434368ce2a594005983c5a6601a31a639e7f1

This commit is contained in:
Public Source Publisher committed 2026-10-01 18:03:00 +00:00
1 parent 77946d62c4
commit 6d40ab975c
86 files changed
+6784 -1478

No files matched your search

+4 -2
View File
@@ -16,8 +16,10 @@ test.describe("authentication", () => {
await expect(page).toHaveURL(/\/$/);
await expect(page.getByText("Authenticated", { exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "Governed MCP, with the current signal first." })).toBeVisible();
// Real backend data, not a placeholder: Ludarium is the seeded catalog project.
await expect(page.getByText("Ludarium", { exact: true })).toBeVisible();
// Assert a real bounded dashboard row without depending on catalog ordering.
const snapshot = await (await page.request.get("/api/dashboard")).json();
expect(snapshot.projects.length).toBeGreaterThan(0);
await expect(page.locator(`[href="/projects/${snapshot.projects[0].slug}"]`).first()).toContainText(snapshot.projects[0].name);
});
test("an authenticated user can sign out and stale management data disappears", async ({ page }) => {
+2 -1
View File
@@ -18,7 +18,8 @@ test.describe("mobile navigation drawer", () => {
await trigger.click();
await expect(page.locator(".sidebar.sidebar-open")).toHaveCount(1);
await expect(trigger).toHaveAttribute("aria-expanded", "true");
await expect(page.locator(".nav-trigger")).toHaveAttribute("aria-expanded", "true");
await expect(page.getByRole("dialog", { name: "Application navigation" })).toBeVisible();
});
test("closes via its own close button", async ({ page }) => {
+194
View File
@@ -0,0 +1,194 @@
import AxeBuilder from "@axe-core/playwright";
import { expect, loginAs, test } from "./fixtures";
const darkReview = process.env.WP400_DARK_REVIEW === "1";
test.use({ colorScheme: darkReview ? "dark" : "light" });
const viewports = [
{ width: 320, height: 568 }, { width: 360, height: 800 }, { width: 390, height: 844 },
{ width: 430, height: 932 }, { width: 768, height: 1024 }, { width: 1280, height: 800 },
{ width: 1440, height: 900 }, { width: 844, height: 390 },
];
for (const viewport of viewports) {
test(`operator layout and accessible surfaces at ${viewport.width}x${viewport.height}`, async ({ page }, info) => {
test.setTimeout(180_000);
await page.setViewportSize(viewport);
await loginAs(page, "hub-admin");
for (const path of ["/", "/projects", "/servers", "/tools", "/connectors", "/clients", "/policies", "/audit", "/operations", "/settings", "/projects/ludarium", "/connectors/knowledge-default"]) {
await page.goto(path);
await expect(page.locator(".topbar")).toBeVisible();
if (viewport.width <= 840) {
const target = await page.locator(".nav-trigger").boundingBox();
expect(target?.width).toBeGreaterThanOrEqual(44);
expect(target?.height).toBeGreaterThanOrEqual(44);
}
await expect(page.locator(".loading-state")).toHaveCount(0, { timeout: 15_000 });
const dimensions = await page.evaluate(() => ({ document: document.documentElement.scrollWidth, viewport: innerWidth }));
expect(dimensions.document, path).toBeLessThanOrEqual(dimensions.viewport + 1);
if (path === "/" && viewport.width <= 430) {
await expect(page.getByLabel("Project status records")).toBeVisible();
await expect(page.getByLabel("Project status table")).toBeHidden();
const grid = await page.locator(".primary-metrics").evaluate((element) => getComputedStyle(element).gridTemplateColumns);
expect(grid.split(" ")).toHaveLength(1);
}
if (path === "/tools" && viewport.width <= 430) await expect(page.getByRole("group", { name: "Table density" })).toBeHidden();
if (viewport.width === 390 || viewport.width === 1440) {
const result = await new AxeBuilder({ page }).analyze();
expect(result.violations.filter((violation) => ["serious", "critical"].includes(violation.impact ?? "")), path).toEqual([]);
}
if (process.env.SOL_VISUAL_REVIEW && ["/", "/tools", "/clients", "/operations", "/projects/ludarium", "/connectors/knowledge-default"].includes(path)) {
const themeSuffix = darkReview ? "-dark" : "";
await page.screenshot({ path: info.outputPath(`${path.replaceAll("/", "_") || "dashboard"}-${viewport.width}${themeSuffix}.png`), fullPage: true });
await page.screenshot({ path: info.outputPath(`${path.replaceAll("/", "_") || "dashboard"}-${viewport.width}${themeSuffix}-viewport.png`) });
}
}
});
}
test("mobile client actions require an explicit disclosure and confirmation", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await loginAs(page, "hub-admin");
await page.goto("/clients");
const name = `mobile-safety-${Date.now()}`;
await page.getByLabel("Name", { exact: true }).fill(name);
await page.getByRole("button", { name: "Create pending client" }).click();
const card = page.locator(".mobile-record").filter({ hasText: name }).first();
await expect(card.getByRole("button", { name: "Config", exact: true })).toBeVisible();
await expect(card.getByRole("button", { name: "Revoke", exact: true })).toBeHidden();
await card.locator("summary").click();
await card.getByRole("button", { name: "Revoke", exact: true }).click();
await expect(page.getByRole("dialog", { name: /Revoke/ })).toBeVisible();
await page.getByRole("button", { name: "Cancel", exact: true }).click();
});
test("desktop density persists while mobile cards have no inert density control", async ({ page }) => {
await loginAs(page, "hub-viewer");
await page.setViewportSize({ width: 1280, height: 800 });
await page.goto("/tools");
await page.getByRole("button", { name: "Compact", exact: true }).click();
await page.reload();
await expect(page.getByRole("button", { name: "Compact", exact: true })).toHaveAttribute("aria-pressed", "true");
await page.setViewportSize({ width: 430, height: 932 });
await expect(page.getByRole("group", { name: "Table density" })).toBeHidden();
});
test("phone trend keeps outcome details and operations errors never imply no alerts", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await loginAs(page, "hub-viewer");
await page.route("**/api/operations/summary", async (route) => {
const response = await route.fetch();
const summary = await response.json();
summary.trend = Array.from({ length: 24 }, (_, hour) => ({ hour: new Date(Date.UTC(2026, 9, 1, hour)).toISOString(), success: hour, denied: 1, error: 0, rate_limited: 0 }));
await route.fulfill({ json: summary });
});
await page.goto("/operations");
const mobile = page.getByRole("group", { name: "Four-hour call buckets" });
await expect(mobile).toBeVisible();
await expect(mobile.getByRole("button")).toHaveCount(6);
await mobile.getByRole("button").first().click();
await expect(page.locator(".mobile-trend .trend-detail")).toContainText("success");
await page.unroute("**/api/operations/summary");
await page.route("**/api/operations/summary", (route) => route.fulfill({ status: 503, body: "unavailable" }));
await page.reload();
await expect(page.getByText("Alerts are unknown while the summary is unavailable.")).toBeVisible();
await expect(page.getByText("No alerts in the latest summary")).toHaveCount(0);
});
test("status refresh backs off, reports disconnection and does not invoke probes", async ({ page }) => {
await loginAs(page, "hub-viewer");
await page.clock.install();
await page.goto("/");
await expect(page.getByText("Current · auto refresh")).toBeVisible();
let reads = 0;
let writes = 0;
page.on("request", (request) => { if (request.method() === "POST" && request.url().includes("/api/")) writes += 1; });
await page.route("**/api/dashboard", (route) => { reads += 1; return route.fulfill({ status: 503, body: "unavailable" }); });
await page.clock.fastForward(31_000);
await expect(page.getByText("Disconnected · retry available")).toBeVisible();
expect(reads).toBe(1);
await page.clock.fastForward(30_000);
expect(reads).toBe(1);
await page.clock.fastForward(31_000);
await expect.poll(() => reads).toBe(2);
expect(writes).toBe(0);
});
test("a hidden dashboard pauses scheduled refresh and never sends an automatic write", async ({ page }) => {
await loginAs(page, "hub-viewer");
await page.clock.install();
await page.addInitScript(() => {
Object.defineProperty(document, "hidden", {
configurable: true,
get: () => sessionStorage.getItem("e2e-document-hidden") !== "false",
});
});
let dashboardReads = 0;
let apiWrites = 0;
page.on("request", (request) => {
const url = new URL(request.url());
if (url.pathname === "/api/dashboard" && request.method() === "GET") dashboardReads += 1;
if (url.pathname.startsWith("/api/") && request.method() === "POST") apiWrites += 1;
});
await page.goto("/");
await expect(page.getByText("Auto refresh paused")).toBeVisible();
expect(dashboardReads).toBe(1); // The one initial snapshot; no timer-driven read.
await page.clock.fastForward(120_000);
expect(dashboardReads).toBe(1);
expect(apiWrites).toBe(0);
});
test("the dashboard action inbox starts with four signals and expands the rest on demand", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await loginAs(page, "hub-viewer");
await page.route("**/api/dashboard", async (route) => {
const response = await route.fetch();
const snapshot = await response.json();
snapshot.generatedAt = new Date().toISOString();
snapshot.staleAfterSeconds = 3600;
snapshot.health = { hub: "healthy", gateway: "healthy", detail: "Fixture is current." };
snapshot.connectors = Array.from({ length: 6 }, (_, index) => ({
id: `inbox-fixture-${index + 1}`,
name: `Inbox fixture ${index + 1}`,
project: "ludarium",
state: "degraded",
detail: "A controlled connector needs review.",
registrationState: "registered",
readiness: { state: "unknown", detail: "No live probe was requested.", observedAt: null, source: "not-probed" },
}));
snapshot.drift = { count: 0, projects: [], known: true, detail: "No drift." };
snapshot.backup = { state: "healthy", lastSuccessfulAt: null, detail: "Fixture backup state." };
snapshot.denials = { count: 0, window: "24h" };
await route.fulfill({ json: snapshot });
});
await page.goto("/");
const inbox = page.getByRole("region", { name: "6 actionable signals" });
await expect(inbox.locator(".exception-item")).toHaveCount(4);
await expect(inbox.getByRole("link", { name: /Inbox fixture 1/ })).toBeVisible();
await expect(inbox.getByRole("link", { name: /Inbox fixture 4/ })).toBeVisible();
await expect(inbox.getByRole("link", { name: /Inbox fixture 5/ })).toHaveCount(0);
await inbox.getByRole("button", { name: "Show 2 more signals" }).click();
await expect(inbox.locator(".exception-item")).toHaveCount(6);
await expect(inbox.getByRole("link", { name: /Inbox fixture 6/ })).toBeVisible();
await inbox.getByRole("button", { name: "Show fewer signals" }).click();
await expect(inbox.locator(".exception-item")).toHaveCount(4);
});
test("project and connector detail links resolve to registered records", async ({ page }) => {
await loginAs(page, "hub-admin");
await page.setViewportSize({ width: 1280, height: 800 });
await page.goto("/connectors/knowledge-default");
await expect(page.getByRole("heading", { name: "Knowledge Default", exact: true })).toBeVisible();
await page.goto("/projects/ludarium?tab=MCP%20Servers");
await expect(page.getByLabel("MCP servers", { exact: true }).getByRole("heading", { name: "ludarium-prod-readonly", exact: true })).toBeVisible();
await page.goto("/projects/ludarium?tab=Knowledge%20Sources");
await expect(page.getByLabel("Knowledge sources", { exact: true }).getByRole("heading", { name: "discovery://repository/ludarium", exact: true })).toBeVisible();
});
+118
View File
@@ -0,0 +1,118 @@
import { expect, loginAs, test } from "./fixtures";
test.describe("operator review regressions", () => {
test.beforeEach(async ({ page }) => { await loginAs(page, "hub-admin"); });
test("filter changes retain the URL project and environment at the API boundary", async ({ page }) => {
await page.goto("/connectors?project=ludarium&environment=prod");
await expect(page.getByLabel("Filter records")).toBeVisible();
const response = page.waitForResponse((value) => {
const url = new URL(value.url());
return url.pathname === "/api/connectors" && url.searchParams.get("q") === "knowledge-default";
});
await page.getByLabel("Filter records").fill("knowledge-default");
const actual = new URL((await response).url());
expect(actual.searchParams.get("project")).toBe("ludarium");
expect(actual.searchParams.get("environment")).toBe("prod");
await expect.poll(() => new URL(page.url()).searchParams.get("q")).toBe("knowledge-default");
expect(new URL(page.url()).searchParams.get("project")).toBe("ludarium");
expect(new URL(page.url()).searchParams.get("environment")).toBe("prod");
await expect(page.getByRole("link", { name: "Knowledge Default", exact: true })).toBeVisible();
});
test("catalog scope stays locked until an explicit apply finishes", async ({ page }) => {
let release!: () => void;
const pending = new Promise<void>((resolve) => { release = resolve; });
await page.route("**/api/catalog/preview", (route) => route.fulfill({ json: { valid: true, changes: ["Review fixture change"], errors: [] } }));
await page.route("**/api/catalog/apply", async (route) => { await pending; await route.fulfill({ json: { accepted: true, message: "Reviewed catalog apply completed" } }); });
await page.goto("/tools?project=ludarium");
const workflow = page.locator(".workflow-panel").filter({ has: page.getByRole("heading", { name: "Catalog validation and preview" }) });
await workflow.getByRole("button", { name: "Validate and preview" }).click();
await workflow.getByRole("button", { name: "Apply approved catalog" }).click();
await page.getByRole("button", { name: "Confirm apply" }).click();
await expect(workflow.getByRole("combobox", { name: "Project", exact: true })).toBeDisabled();
await expect(workflow.getByRole("button", { name: "Applying…" })).toBeDisabled();
release();
await expect(workflow.getByText("Reviewed catalog apply completed")).toBeVisible();
await expect(workflow.getByRole("combobox", { name: "Project", exact: true })).toBeEnabled();
});
test("a pending client created after SPA navigation retains a visible resume path", async ({ page }) => {
let release!: () => void;
const pending = new Promise<void>((resolve) => { release = resolve; });
let createdId = "";
await page.route("**/api/client-management", async (route) => {
if (route.request().method() !== "POST") { await route.continue(); return; }
const response = await route.fetch();
createdId = ((await response.json()) as { client: { id: string } }).client.id;
await pending;
await route.fulfill({ response });
});
await page.goto("/clients");
const name = `creation-navigation-${Date.now()}`;
await page.getByLabel("Name", { exact: true }).fill(name);
await page.getByRole("button", { name: "Create pending client" }).click();
await expect.poll(() => createdId).not.toBe("");
await page.getByRole("navigation", { name: "Primary navigation" }).getByRole("link", { name: "Operations", exact: true }).click();
await expect(page.getByRole("heading", { name: "Operations", level: 1 })).toBeVisible();
release();
const notice = page.locator(".client-created-notice");
await expect(notice).toContainText(name);
expect(new URL(page.url()).pathname).toBe("/operations");
await expect(notice.getByRole("link", { name: "Resume onboarding" })).toHaveAttribute("href", `/clients?onboard=${createdId}`);
await notice.getByRole("link", { name: "Resume onboarding" }).click();
await expect(page.getByText(/Resumable Hub identity/)).toBeVisible();
expect(new URL(page.url()).searchParams.get("onboard")).toBe(createdId);
});
test("operation-check results link to registered connector identities", async ({ page }) => {
await page.route("**/api/operations/check", (route) => route.fulfill({ json: {
generatedAt: new Date().toISOString(), healthy: true,
connectors: { "itworx-knowledge": { reachable: true, status: "healthy", detail: "Bounded contract fixture", connectorRefs: [{ id: "knowledge-default", name: "Knowledge Default" }], connectorRefsTruncated: false } },
} }));
await page.goto("/operations");
await page.getByRole("button", { name: "Run live checks" }).click();
const registration = page.locator(".connector-checks").getByRole("link", { name: "Knowledge Default", exact: true });
await expect(registration).toHaveAttribute("href", "/connectors/knowledge-default");
await registration.click();
await expect(page.getByRole("heading", { name: "Knowledge Default", exact: true })).toBeVisible();
});
test("Quick Jump sends an ordinary client to its filtered records instead of an unavailable onboarding workflow", async ({ page }) => {
await page.route("**/api/clients?*", (route) => route.fulfill({ json: {
kind: "clients", generatedAt: new Date().toISOString(),
rows: [{ id: "ordinary-fixture-client", name: "Ordinary fixture client", status: "healthy", summary: "Existing client", project: "ludarium", environment: "prod" }],
page: { total: 1, nextCursor: null },
} }));
await page.getByRole("button", { name: "Open quick jump" }).click();
const dialog = page.getByRole("dialog", { name: "Quick jump" });
await dialog.getByRole("combobox").fill("ordinary-fixture-client");
await dialog.getByRole("group", { name: "Clients", exact: true }).getByRole("option", { name: /Ordinary fixture client/ }).click();
await expect(page).toHaveURL(/\/clients\?q=ordinary-fixture-client$/);
await expect(page.locator(".desktop-data-table").getByText("Existing client", { exact: true })).toBeVisible();
const desktopRecord = page.locator(".desktop-data-table").getByRole("link", { name: "Ordinary fixture client", exact: true });
await expect(desktopRecord).toHaveAttribute("href", "/clients?q=ordinary-fixture-client");
await desktopRecord.click();
await expect(page).toHaveURL(/\/clients\?q=ordinary-fixture-client$/);
await page.setViewportSize({ width: 390, height: 844 });
const mobileRecord = page.locator(".mobile-record-list").getByRole("link", { name: "Ordinary fixture client", exact: true });
await expect(mobileRecord).toHaveAttribute("href", "/clients?q=ordinary-fixture-client");
await mobileRecord.click();
await expect(page).toHaveURL(/\/clients\?q=ordinary-fixture-client$/);
await expect(page.getByText(/Resumable Hub identity/)).toHaveCount(0);
});
test("Quick Jump retains the selected audit trace as its destination filter", async ({ page }) => {
await page.route("**/api/audit?*", (route) => route.fulfill({ json: {
kind: "audit", generatedAt: new Date().toISOString(),
rows: [{ id: "audit-fixture", name: "tool.invocation", status: "healthy", summary: "Fixture event", traceId: "review-fixture-trace", project: "ludarium" }],
page: { total: 1, nextCursor: null },
} }));
await page.getByRole("button", { name: "Open quick jump" }).click();
const dialog = page.getByRole("dialog", { name: "Quick jump" });
await dialog.getByRole("combobox").fill("review-fixture-trace");
await dialog.getByRole("group", { name: "Audit", exact: true }).getByRole("option", { name: /tool.invocation/ }).click();
await expect(page).toHaveURL(/\/audit\?q=review-fixture-trace$/);
await expect(page.getByLabel("Filter records")).toHaveValue("review-fixture-trace");
});
});
+144
View File
@@ -0,0 +1,144 @@
import { expect, loginAs, test } from "./fixtures";
test.use({ viewport: { width: 390, height: 844 } });
test.describe("mobile operator shell", () => {
test.beforeEach(async ({ page }) => {
await loginAs(page, "hub-viewer");
await page.goto("/");
});
test("navigation isolates the page, traps focus, locks scroll, and restores focus on Escape", async ({ page }, testInfo) => {
const trigger = page.getByRole("button", { name: "Open navigation" });
const main = page.locator("main");
await trigger.focus();
await trigger.press("Enter");
const drawer = page.getByRole("dialog", { name: "Application navigation" });
const close = drawer.getByRole("button", { name: "Close navigation" });
await expect(drawer).toBeVisible();
await expect(drawer).toHaveAttribute("aria-modal", "true");
await expect(close).toBeFocused();
if (process.env.SOL_VISUAL_REVIEW === "1") await page.screenshot({ path: testInfo.outputPath("mobile-drawer.png"), animations: "disabled" });
await expect.poll(() => main.evaluate((element) => (element as HTMLElement).inert)).toBe(true);
await expect.poll(() => page.evaluate(() => document.body.style.overflow)).toBe("hidden");
await drawer.getByRole("link", { name: "ITWorx MCP Hub" }).focus();
await page.keyboard.press("Shift+Tab");
await expect(drawer.getByRole("link", { name: "Platform" })).toBeFocused();
await page.keyboard.press("Tab");
await expect(drawer.getByRole("link", { name: "ITWorx MCP Hub" })).toBeFocused();
await page.keyboard.press("Escape");
await expect(drawer).toHaveCount(0);
await expect(trigger).toBeFocused();
await expect.poll(() => main.evaluate((element) => (element as HTMLElement).inert)).toBe(false);
await expect.poll(() => page.evaluate(() => document.body.style.overflow)).toBe("");
});
test("short phones can scroll the full drawer while the page stays locked", async ({ page }) => {
await page.setViewportSize({ width: 320, height: 520 });
await page.getByRole("button", { name: "Open navigation" }).click();
const drawer = page.getByRole("dialog", { name: "Application navigation" });
await expect(drawer).toBeVisible();
const metrics = await drawer.evaluate((element) => ({
height: element.getBoundingClientRect().height,
clientHeight: element.clientHeight,
scrollHeight: element.scrollHeight,
overflowY: getComputedStyle(element).overflowY,
}));
expect(metrics.height).toBeLessThanOrEqual(521);
expect(metrics.overflowY).toBe("auto");
expect(metrics.scrollHeight).toBeGreaterThan(metrics.clientHeight);
await drawer.evaluate((element) => { element.scrollTop = element.scrollHeight; });
await expect(drawer.getByRole("link", { name: "Platform" })).toBeInViewport();
});
test("Quick Jump searches grouped entities with keyboard control and never requests audit for a viewer", async ({ page }, testInfo) => {
const auditRequests: string[] = [];
page.on("request", (request) => {
if (new URL(request.url()).pathname === "/api/audit") auditRequests.push(request.url());
});
await page.getByRole("button", { name: "Open quick jump" }).click();
const dialog = page.getByRole("dialog", { name: "Quick jump" });
const input = dialog.getByRole("combobox", { name: "Find a workspace or resource" });
await expect(input).toBeFocused();
await expect(dialog).toHaveAttribute("aria-modal", "true");
await expect.poll(() => page.locator("main").evaluate((element) => Boolean(element.closest("[inert]")))).toBe(true);
await input.fill("Ludarium");
const project = dialog.getByRole("group", { name: "Projects", exact: true }).getByRole("option", { name: /Ludarium/i });
await expect(project).toBeVisible();
expect((await project.boundingBox())!.height).toBeLessThanOrEqual(120);
if (process.env.SOL_VISUAL_REVIEW === "1") await page.screenshot({ path: testInfo.outputPath("mobile-quick-jump.png"), animations: "disabled" });
await expect(dialog.getByRole("group", { name: "Projects" })).toBeVisible();
await input.press("ArrowDown");
await expect(input).toHaveAttribute("aria-activedescendant", /quick-jump-option-/);
await input.press("Enter");
await expect(page).toHaveURL(/\/projects\/ludarium$/);
expect(auditRequests).toEqual([]);
});
test("Quick Jump keeps recent navigation in memory and shows search failures", async ({ page }) => {
await page.getByRole("button", { name: "Open quick jump" }).click();
let dialog = page.getByRole("dialog", { name: "Quick jump" });
await dialog.getByRole("option", { name: "Catalog" }).click();
await expect(page).toHaveURL(/\/tools$/);
await page.getByRole("button", { name: "Open quick jump" }).click();
dialog = page.getByRole("dialog", { name: "Quick jump" });
await expect(dialog.getByRole("group", { name: "Recent destinations" }).getByRole("option", { name: "Catalog" })).toBeVisible();
await page.route("**/api/projects?*", (route) => route.fulfill({ status: 503, body: "unavailable" }));
const input = dialog.getByRole("combobox", { name: "Find a workspace or resource" });
await input.fill("Ludarium");
await expect(dialog.getByRole("alert")).toHaveText("One resource type could not be searched. Results may be incomplete.");
});
test("sign-out remains explicit and reports its busy state on a narrow screen", async ({ page }) => {
await page.route("**/auth/logout", async (route) => {
await new Promise((resolve) => setTimeout(resolve, 300));
await route.fulfill({ status: 503, body: "unavailable" });
});
const signOut = page.getByRole("button", { name: "Sign out" });
await expect(signOut).toBeVisible();
await expect(signOut).toHaveText("Sign out");
await signOut.click();
const busyButton = page.getByRole("button", { name: "Signing out…" });
await expect(busyButton).toBeDisabled();
await expect(page.getByRole("alert")).toContainText("control API is unavailable");
await expect(page.getByRole("button", { name: "Sign out" })).toBeEnabled();
});
});
test.describe("mobile workflow dialog", () => {
test.use({ viewport: { width: 390, height: 844 } });
test("large tool workflow fills the dynamic viewport and scrolls inside the dialog", async ({ page }, testInfo) => {
await loginAs(page, "hub-operator");
await page.goto("/tools");
const trigger = page.locator(".mobile-record-list").getByRole("button", { name: "Readiness" }).first();
await trigger.click();
const dialog = page.getByRole("dialog", { name: /Tool console/ });
await expect(dialog).toBeVisible();
await expect.poll(() => page.evaluate(() => document.body.style.overflow)).toBe("hidden");
await expect.poll(() => page.locator("main").evaluate((element) => Boolean(element.closest("[inert]")))).toBe(true);
const layout = await dialog.evaluate((element) => ({
height: element.getBoundingClientRect().height,
overflow: getComputedStyle(element).overflowY,
bodyOverflow: getComputedStyle(element.querySelector(".modal-body")!).overflowY,
}));
expect(layout.height).toBeGreaterThan(800);
expect(layout.overflow).toBe("hidden");
expect(layout.bodyOverflow).toBe("auto");
if (process.env.SOL_VISUAL_REVIEW === "1") {
await expect(dialog.getByRole("button", { name: "Run readiness check" })).toBeEnabled();
await page.screenshot({ path: testInfo.outputPath("mobile-tool-workflow.png"), animations: "disabled" });
}
await page.keyboard.press("Escape");
await expect(dialog).toHaveCount(0);
await expect(trigger).toBeFocused();
});
});
@@ -0,0 +1,329 @@
import { expect, loginAs, test } from "./fixtures";
function projectDetailFixture(slug: string) {
return {
slug,
name: `Project ${slug.toUpperCase()}`,
description: `Delayed route fixture for ${slug}.`,
environments: [{ name: "prod", classification: "production" }],
capabilityPacks: [],
connectorBindings: [],
mcpServers: [],
knowledgeSources: [],
virtualServerCount: 0,
clientCount: 0,
activeClientCount: 0,
};
}
function connectorDetailFixture(id: string) {
return {
id,
name: `Connector ${id.toUpperCase()}`,
connectorType: "fixture",
description: `Delayed route fixture for ${id}.`,
registration: { state: "registered" as const, detail: "Fixture registration." },
readiness: { reachable: null, status: "unknown", detail: "No probe was requested.", observedAt: null, source: "unavailable" as const },
bindings: [],
relatedTools: [],
};
}
test.describe("operator workflow details", () => {
test.beforeEach(async ({ page }) => {
await loginAs(page, "hub-admin");
});
test("client onboarding resumes from its durable identity and never treats a Hub check as MCP proof", async ({ page }) => {
await page.goto("/clients");
const clientName = `onboarding-resume-${Date.now()}`;
await page.getByLabel("Name").fill(clientName);
await page.getByLabel("Client profile").selectOption("native-2026");
const createResponsePromise = page.waitForResponse((response) => {
const url = new URL(response.url());
return response.request().method() === "POST" && url.pathname === "/api/client-management";
});
await page.getByRole("button", { name: "Create pending client" }).click();
const created = await (await createResponsePromise).json() as {
client: { id: string };
bootstrap: { reference: string };
config: { kind: string; protocolVersion: string; transport: string; credentialReference: string };
provisioning: { oauthUsername: string; issuer: string; tokenEndpoint: string; oauthClientId: string };
};
await expect(page.getByText(`Client created: ${clientName}`)).toBeVisible();
await expect.poll(() => page.url()).toMatch(/\/clients\?onboard=[0-9a-f-]+$/i);
const savedUrl = page.url();
expect(new URL(savedUrl).searchParams.get("onboard")).toBe(created.client.id);
await page.reload();
await expect(page.getByText(/Resumable Hub identity/)).toBeVisible();
const handoff = page.locator(".onboarding-config-auth");
const configPanel = page.locator(".onboarding-config");
const configValue = (label: string) => configPanel.locator("dl > div").filter({ hasText: label }).locator("dd");
await expect(configValue("Client profile")).toHaveText(created.config.kind);
await expect(configValue("Protocol version")).toHaveText(created.config.protocolVersion);
await expect(configValue("Transport")).toHaveText(created.config.transport);
await expect(configPanel.getByText("Credential environment variable", { exact: true })).toHaveCount(0);
await expect(configPanel.getByText("Bridge command", { exact: true })).toHaveCount(0);
await expect(handoff).toContainText(`External account: ${created.provisioning.oauthUsername}`);
await expect(handoff).toContainText(`Issuer: ${created.provisioning.issuer}`);
await expect(handoff).toContainText(`Token endpoint: ${created.provisioning.tokenEndpoint}`);
await expect(handoff).toContainText(`OAuth client ID: ${created.provisioning.oauthClientId}`);
const resumedText = await page.locator("body").innerText();
expect(resumedText).not.toContain(created.bootstrap.reference);
expect(resumedText).not.toContain(created.config.credentialReference);
await expect(page.getByText("OAuth scopes", { exact: true })).toBeVisible();
await expect(page.getByText(/External verification still required/)).toBeVisible();
await expect(page.getByRole("button", { name: "Record Hub checks" })).toBeEnabled();
await page.getByRole("button", { name: "Record Hub checks" }).click();
await expect(page.locator(".onboarding-check-recorded")).toContainText("Hub checks were recorded");
await expect(page.getByText(/does not mark external OAuth grants or MCP discovery as complete/)).toBeVisible();
await expect(page.locator(".onboarding-identity-bar small")).toContainText("· pending");
expect(page.url()).toBe(savedUrl);
});
test("a delayed verify response cannot overwrite the onboarding identity after the route changes", async ({ page }) => {
const onboardingStatus = (clientId: string, subjectDetail: string, verified = false) => ({
clientId,
project: "ludarium",
environment: "prod",
clientStatus: "pending",
provisioning: {
status: "pending-authentik-service-account",
subject: null,
issuer: "https://identity.example.invalid",
oauthUsername: `svc-mcp-codex-ludarium-${clientId}`,
tokenEndpoint: "https://identity.example.invalid/application/o/token/",
oauthClientId: "hub-fixture-client",
steps: ["Complete external account setup with the administrator."],
},
checks: {
externalSubject: { state: "pending", detail: subjectDetail },
externalScope: { state: "unavailable", detail: "External OAuth grants are not visible to the Hub." },
hubBinding: { state: "pending", detail: "The client remains disabled." },
catalog: { state: "unknown", detail: "Catalog check has not passed." },
connector: { state: "unknown", detail: "Connector state is unknown." , observedAt: null },
mcpProof: { state: "unavailable", detail: "MCP discovery and invocation must be confirmed externally." },
},
verification: { checkedAt: verified ? "2026-10-01T10:00:00.000Z" : null, checkedByManagement: verified, verifiedByManagement: verified, hubChecksPassed: verified, detail: verified ? "Client A checks passed." : "No verification record." },
});
let releaseVerify!: () => void;
const verifyGate = new Promise<void>((resolve) => { releaseVerify = resolve; });
await page.route("**/api/client-management/a/onboarding-status", (route) => route.fulfill({ json: onboardingStatus("a", "Client A is awaiting its external subject.") }));
await page.route("**/api/client-management/a/config", (route) => route.fulfill({ json: { clientId: "a", endpoint: "https://hub.example.invalid/mcp", scopes: ["project:ludarium:read"] } }));
await page.route("**/api/client-management/b/onboarding-status", (route) => route.fulfill({ json: onboardingStatus("b", "Client B is awaiting its external subject.") }));
await page.route("**/api/client-management/b/config", (route) => route.fulfill({ json: { clientId: "b", endpoint: "https://hub.example.invalid/mcp", scopes: ["project:ludarium:read"] } }));
await page.route("**/api/client-management/a/verify", async (route) => {
await verifyGate;
await route.fulfill({ json: onboardingStatus("a", "Client A is verified.", true) });
});
await page.goto("/clients?onboard=a");
await expect(page.getByText("Client A is awaiting its external subject.")).toBeVisible();
const verifyRequest = page.waitForRequest((request) => request.url().endsWith("/api/client-management/a/verify"));
await page.getByRole("button", { name: "Record Hub checks" }).click();
await verifyRequest;
await page.evaluate(() => {
window.history.pushState({}, "", "/clients?onboard=b");
window.dispatchEvent(new PopStateEvent("popstate"));
});
await expect(page.getByText("Client B is awaiting its external subject.")).toBeVisible();
releaseVerify();
await expect(page.getByText("Client B is awaiting its external subject.")).toBeVisible();
await expect(page.getByText("Client A is verified.")).toHaveCount(0);
await expect(page.getByText(/Hub checks passed/)).toHaveCount(0);
await expect(page).toHaveURL(/\/clients\?onboard=b$/);
});
test("a delayed catalog preview for the previous project cannot be shown or applied", async ({ page }) => {
await page.route((url) => url.pathname === "/api/projects" && url.searchParams.has("limit"), (route) => route.fulfill({ json: {
kind: "projects",
generatedAt: "2026-10-01T10:00:00.000Z",
rows: [
{ id: "ludarium", name: "Ludarium", status: "healthy", summary: "Fixture project A" },
{ id: "forgeflow", name: "ForgeFlow", status: "healthy", summary: "Fixture project B" },
],
page: { total: 2 },
} }));
let releasePreview!: () => void;
const previewGate = new Promise<void>((resolve) => { releasePreview = resolve; });
const previewProjects: string[] = [];
await page.route("**/api/catalog/preview", async (route) => {
const body = route.request().postDataJSON() as { project: string };
previewProjects.push(body.project);
if (body.project === "ludarium") {
await previewGate;
await route.fulfill({ json: { valid: true, changes: ["Project A private preview"], errors: [] } });
return;
}
await route.fulfill({ json: { valid: true, changes: ["Project B preview"], errors: [] } });
});
await page.goto("/tools");
const workflow = page.locator(".workflow-panel").filter({ has: page.getByRole("heading", { name: "Catalog validation and preview" }) });
const project = workflow.getByLabel("Project");
await expect(project.locator("option")).toHaveCount(2);
await project.selectOption("ludarium");
const previewRequest = page.waitForRequest((request) => new URL(request.url()).pathname === "/api/catalog/preview");
await workflow.getByRole("button", { name: "Validate and preview" }).click();
await previewRequest;
try {
await project.selectOption("forgeflow");
await expect(workflow.locator(".diff-box")).toHaveCount(0);
releasePreview();
await expect(workflow.getByText("Project A private preview")).toHaveCount(0);
await expect(workflow.getByRole("button", { name: "Apply approved catalog" })).toHaveCount(0);
expect(previewProjects).toEqual(["ludarium"]);
} finally {
releasePreview();
}
});
test("a delayed project detail response is ignored after navigating to another project", async ({ page }) => {
let releaseProjectA!: () => void;
const projectGate = new Promise<void>((resolve) => { releaseProjectA = resolve; });
await page.route("**/api/projects/a", async (route) => {
await projectGate;
await route.fulfill({ json: projectDetailFixture("a") });
});
await page.route("**/api/projects/b", (route) => route.fulfill({ json: projectDetailFixture("b") }));
const projectARequest = page.waitForRequest((request) => new URL(request.url()).pathname === "/api/projects/a");
await page.goto("/projects/a");
await projectARequest;
await page.evaluate(() => {
window.history.pushState({}, "", "/projects/b");
window.dispatchEvent(new PopStateEvent("popstate"));
});
try {
await expect(page.getByRole("heading", { name: "Project B", exact: true })).toBeVisible();
releaseProjectA();
await expect(page.getByRole("heading", { name: "Project A", exact: true })).toHaveCount(0);
await expect(page).toHaveURL(/\/projects\/b$/);
} finally {
releaseProjectA();
}
});
test("a delayed connector detail response is ignored after navigating to another connector", async ({ page }) => {
let releaseConnectorA!: () => void;
const connectorGate = new Promise<void>((resolve) => { releaseConnectorA = resolve; });
await page.route("**/api/connectors/a", async (route) => {
await connectorGate;
await route.fulfill({ json: connectorDetailFixture("a") });
});
await page.route("**/api/connectors/b", (route) => route.fulfill({ json: connectorDetailFixture("b") }));
const connectorARequest = page.waitForRequest((request) => new URL(request.url()).pathname === "/api/connectors/a");
await page.goto("/connectors/a");
await connectorARequest;
await page.evaluate(() => {
window.history.pushState({}, "", "/connectors/b");
window.dispatchEvent(new PopStateEvent("popstate"));
});
try {
await expect(page.getByRole("heading", { name: "Connector B", exact: true })).toBeVisible();
releaseConnectorA();
await expect(page.getByRole("heading", { name: "Connector A", exact: true })).toHaveCount(0);
await expect(page).toHaveURL(/\/connectors\/b$/);
} finally {
releaseConnectorA();
}
});
test("typed tool arguments reach the readiness API and recent runs retain metadata only", async ({ page }) => {
const capturedRequests: Array<Record<string, unknown>> = [];
await page.route(/\/api\/tools(?:\?.*)?$/, (route) => route.fulfill({ json: {
kind: "tools",
generatedAt: "2026-10-01T10:00:00.000Z",
rows: [{ id: "typed-tool", name: "Typed Tool", status: "healthy", summary: "Typed schema fixture", project: "ludarium", environment: "prod", risk: "R1", canonicalName: "test.typed" }],
page: { total: 1 },
} }));
await page.route("**/api/tool-definitions/test.typed", (route) => route.fulfill({ json: {
canonicalName: "test.typed",
description: "Typed schema fixture",
readOnly: true,
riskClass: "R1",
inputSchema: { type: "object", required: ["count", "enabled", "mode", "tags", "filters"], properties: {
count: { type: "integer", title: "Count", minimum: 1, maximum: 10 },
enabled: { type: "boolean", title: "Enabled" },
mode: { type: "string", title: "Mode", enum: ["safe", "full"] },
tags: { type: "array", title: "Tags" },
filters: { type: "object", title: "Filters" },
} },
limits: { timeoutMs: 1000, maxOutputBytes: 2048, maxItems: 10 },
} }));
await page.route("**/api/tools/test", async (route) => {
capturedRequests.push(route.request().postDataJSON() as Record<string, unknown>);
await route.fulfill({ json: {
ready: true,
tool: "test.typed",
project: "ludarium",
environment: "prod",
note: "Readiness only; no MCP tool invocation was performed.",
checks: {
catalog: { valid: true, readOnly: true, connectorType: "fixture" },
input: { valid: true, errors: [] },
binding: { valid: true, detail: "Fixture binding" },
policy: { allowed: true, decision: "allow", reasonCode: "FIXTURE_ALLOWED", traceId: "ready-trace" },
connector: { reachable: true, status: "healthy", detail: "Fixture route" },
},
} });
});
await page.route("**/api/tools/invoke", (route) => route.fulfill({ json: {
ok: false,
traceId: "trace-typed-001",
tool: "test.typed",
content: [{ type: "text", text: "controlled fixture result" }],
error: { code: "controlled", message: "The fixture returned a controlled error." },
} }));
await page.goto("/tools");
await page.getByRole("button", { name: "Readiness" }).click();
const dialog = page.getByRole("dialog");
await expect(dialog.getByRole("button", { name: "Run readiness check" })).toBeDisabled();
await dialog.getByLabel("Project").selectOption("ludarium");
await dialog.getByLabel("Environment").selectOption("prod");
await dialog.getByLabel("Count").fill("4");
await dialog.getByLabel("Enabled").selectOption("true");
await dialog.getByLabel("Mode").selectOption({ label: "full" });
await dialog.getByLabel("Tags").fill('["never-store-tags"]');
await dialog.getByLabel("Filters").fill('{"author":"never-store-filter"}');
await dialog.getByRole("button", { name: "Run readiness check" }).click();
await expect(dialog.getByText("Ready", { exact: true })).toBeVisible();
expect(capturedRequests[0]).toMatchObject({
project: "ludarium",
environment: "prod",
tool: "test.typed",
arguments: { count: 4, enabled: true, mode: "full", tags: ["never-store-tags"], filters: { author: "never-store-filter" } },
});
await dialog.getByLabel("Count").fill("5");
await expect(dialog.getByText("Ready", { exact: true })).toHaveCount(0);
await expect(dialog.getByRole("button", { name: "Run tool" })).toBeDisabled();
await dialog.getByRole("button", { name: "Run readiness check" }).click();
await expect(dialog.getByText("Ready", { exact: true })).toBeVisible();
await dialog.getByRole("button", { name: "Run tool" }).click();
await expect(dialog.getByRole("heading", { name: "Controlled error" })).toBeVisible();
const storedHistory = await page.evaluate(() => sessionStorage.getItem("itworx-tool-history-v1"));
expect(storedHistory).toContain("trace-typed-001");
expect(storedHistory).not.toContain("never-store-tags");
expect(storedHistory).not.toContain("never-store-filter");
expect(storedHistory).not.toContain("controlled fixture result");
await dialog.getByRole("button", { name: "Close dialog" }).click();
const recentRuns = page.getByRole("region", { name: "Recent Tool Runs" });
await expect(recentRuns.getByText("Controlled error")).toBeVisible();
await expect(recentRuns.getByText("trace-typed-001")).toBeVisible();
await recentRuns.getByRole("button", { name: "Open tool context" }).click();
await expect(page).toHaveURL(/\/tools\?reopen=test\.typed&project=ludarium&environment=prod/);
await expect(page.getByRole("dialog", { name: /Tool console/ })).toBeVisible();
await expect(page.getByRole("dialog").getByLabel("Project")).toHaveValue("ludarium");
await expect(page.getByRole("dialog").getByLabel("Environment")).toHaveValue("prod");
});
});
+5 -5
View File
@@ -33,12 +33,12 @@ test.describe("product-quality interaction and composition", () => {
const dialog = page.getByRole("dialog", { name: "Quick jump" });
await expect(dialog).toBeVisible();
const search = dialog.getByLabel("Find a workspace");
const search = dialog.getByRole("combobox", { name: "Find a workspace or resource" });
await expect(search).toBeFocused();
await expect(dialog.getByRole("button", { name: /Audit/ })).toHaveCount(0);
await expect(dialog.getByRole("option", { name: /Audit/ })).toHaveCount(0);
await search.fill("catalog");
await dialog.getByRole("button", { name: /Catalog/ }).click();
await dialog.getByRole("group", { name: "Build", exact: true }).getByRole("option", { name: /Catalog/ }).click();
await expect(page).toHaveURL(/\/tools$/);
await expect(page.getByRole("heading", { name: "Catalog", level: 2 })).toBeVisible();
});
@@ -48,7 +48,7 @@ test.describe("product-quality interaction and composition", () => {
await page.goto("/tools");
const filter = page.getByLabel("Filter records");
await filter.fill(" Fleet Ops ");
await expect(page).toHaveURL(/\/tools\?q=Fleet%20Ops$/);
await expect.poll(() => new URL(page.url()).searchParams.get("q")).toBe("Fleet Ops");
await expect(page.getByText("Updating results…")).toHaveCount(0);
await filter.fill(" ");
@@ -79,7 +79,7 @@ test.describe("product-quality interaction and composition", () => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto("/tools");
await expect(page.getByRole("heading", { name: "Catalog", level: 2 })).toBeVisible();
await expect(page.getByRole("heading", { name: "Catalog", level: 2 })).toBeVisible({ timeout: 15_000 });
await expect(page.getByLabel("Filter records")).toBeVisible();
await expectNoPageOverflow(page);
if (captureReview) await page.screenshot({ path: testInfo.outputPath("catalog-desktop.png"), fullPage: false });
+8 -6
View File
@@ -29,13 +29,13 @@ test.describe("management workflows, driven end to end from the browser", () =>
}) => {
await page.goto("/projects/ludarium?tab=Environments");
await expect(page.getByLabel("Project detail sections").getByRole("link", { name: "Environments" })).toHaveAttribute("aria-current", "page");
await expect(page.getByRole("cell", { name: "prod", exact: true })).toBeVisible();
await expect(page.getByLabel("Project environments", { exact: true }).getByRole("heading", { name: "prod", exact: true })).toBeVisible();
// ludarium never registered a staging environment (docs/known-state, and
// tests/test_wp230_ui_completion.py's equivalent backend assertion).
await expect(page.getByRole("cell", { name: "staging" })).toHaveCount(0);
await expect(page.getByLabel("Project environments", { exact: true }).getByRole("heading", { name: "staging", exact: true })).toHaveCount(0);
await page.getByLabel("Project detail sections").getByRole("link", { name: "Connectors" }).click();
await expect(page.getByRole("cell", { name: "knowledge", exact: true })).toBeVisible();
await expect(page.getByLabel("Connector bindings", { exact: true }).getByRole("heading", { name: "knowledge", exact: true })).toBeVisible();
});
test("an unregistered project is a real 404, not a fabricated default", async ({ page }) => {
@@ -63,12 +63,14 @@ test.describe("management workflows, driven end to end from the browser", () =>
await page.goto("/tools");
await page.getByRole("button", { name: "Validate and preview" }).click();
const outcome = page.getByText(/Valid preview|Validation blocked/);
await expect(outcome).toBeVisible();
await expect(outcome).toBeVisible({ timeout: 15_000 });
const applyButton = page.getByRole("button", { name: "Apply approved catalog" });
if (await outcome.filter({ hasText: "Valid preview" }).count()) {
await expect(applyButton).toBeEnabled();
await applyButton.click();
await expect(page.getByRole("dialog")).toContainText("All registered project routes may be updated");
await page.getByRole("button", { name: "Confirm apply" }).click();
await expect(page.getByText(/Catalog reconciliation completed and verified/)).toBeVisible({ timeout: 15_000 });
} else {
await expect(applyButton).toBeDisabled();
}
@@ -167,10 +169,10 @@ test.describe("management workflows, driven end to end from the browser", () =>
test("tool readiness validates the full registered path without invoking the tool", async ({ page }) => {
await page.goto("/tools");
await page.getByRole("button", { name: "Validate and preview" }).click();
await expect(page.getByText("Valid preview")).toBeVisible();
await expect(page.getByText("Valid preview")).toBeVisible({ timeout: 15_000 });
await page.getByRole("button", { name: "Apply approved catalog" }).click();
await page.getByRole("button", { name: "Confirm apply" }).click();
await expect(page.getByText(/Catalog reconciliation completed and verified/)).toBeVisible();
await expect(page.getByText(/Catalog reconciliation completed and verified/)).toBeVisible({ timeout: 15_000 });
await page.getByLabel("Filter records").fill("Fleet Ops Operations Summary");
const row = page.getByRole("row", { name: /Fleet Ops Operations Summary/ });