105 lines
3.9 KiB
JavaScript
105 lines
3.9 KiB
JavaScript
// Serve the fresh dist/ locally and run the persona-login dogfood against
|
|
// it to prove the source-side fix works (independent of the broken live
|
|
// proxy). Backend calls are stubbed at the route level so we measure UI
|
|
// behavior, not backend availability.
|
|
|
|
import { chromium } from "playwright";
|
|
import http from "node:http";
|
|
import { readFileSync, statSync } from "node:fs";
|
|
import { extname } from "node:path";
|
|
|
|
const ROOT = "/private/tmp/canvas-work/canvas-real/dist";
|
|
const MIME = {
|
|
".html": "text/html",
|
|
".js": "text/javascript",
|
|
".css": "text/css",
|
|
".svg": "image/svg+xml",
|
|
".png": "image/png",
|
|
".json": "application/json",
|
|
};
|
|
|
|
const server = http.createServer((req, res) => {
|
|
let path = req.url.split("?")[0];
|
|
if (path === "/") path = "/index.html";
|
|
try {
|
|
const full = ROOT + path;
|
|
statSync(full);
|
|
res.setHeader("content-type", MIME[extname(full)] || "application/octet-stream");
|
|
res.end(readFileSync(full));
|
|
} catch {
|
|
res.statusCode = 404;
|
|
res.end("not found");
|
|
}
|
|
});
|
|
|
|
await new Promise((r) => server.listen(0, r));
|
|
const port = server.address().port;
|
|
const URL = `http://127.0.0.1:${port}/`;
|
|
console.log("serving dist on", URL);
|
|
|
|
const b = await chromium.launch({ headless: true });
|
|
const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
|
|
const p = await ctx.newPage();
|
|
|
|
await ctx.route("**/api/v1/auth/microsoft/login", (route) => route.fulfill({ status: 503, body: "" }));
|
|
await ctx.route("**/api/v1/auth/dev-login-config", (route) =>
|
|
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ enabled: true }) }),
|
|
);
|
|
await ctx.route("**/api/v1/auth/dev-login", (route) =>
|
|
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ access_token: "T", refresh_token: "R" }) }),
|
|
);
|
|
await ctx.route("**/api/v1/auth/me", (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ user_id: "u1", tenant_id: "t1", email: "ceo-head@flow-master.ai", display_name: "Mariana Cole" }),
|
|
}),
|
|
);
|
|
await ctx.route("**/api/ea2/work-items", (route) =>
|
|
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ items: [] }) }),
|
|
);
|
|
await ctx.route("**/api/ea2/flow/processes**", (route) =>
|
|
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ items: [] }) }),
|
|
);
|
|
await ctx.route("**/api/runtime/transactions**", (route) =>
|
|
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ items: [] }) }),
|
|
);
|
|
|
|
const results = [];
|
|
function rec(n, ok, d = "") {
|
|
results.push({ n, ok, d });
|
|
console.log(`${ok ? "PASS" : "FAIL"} ${n}${d ? " — " + d : ""}`);
|
|
}
|
|
|
|
await p.goto(URL, { waitUntil: "domcontentloaded" });
|
|
await p.waitForTimeout(1500);
|
|
|
|
const onLogin = (await p.locator(".login-page").count()) === 1;
|
|
rec("login_renders", onLogin);
|
|
|
|
const personas = await p.locator(".persona-chip").allTextContents();
|
|
rec("login_shows_three_personas", personas.length === 3, personas.join(" | "));
|
|
|
|
const ssoCount = await p.locator(".sso-btn").count();
|
|
rec("login_has_sso_button", ssoCount === 1);
|
|
|
|
console.log("→ clicking CEO persona chip…");
|
|
await p.locator(".persona-chip").filter({ hasText: /Mariana/ }).first().click();
|
|
await p.waitForTimeout(2500);
|
|
|
|
const onLanding = (await p.locator(".login-page").count()) === 0;
|
|
rec("persona_click_signs_in_and_leaves_login", onLanding);
|
|
|
|
const errVisible = await p.locator(".login-error").count();
|
|
const errText = errVisible ? await p.locator(".login-error").innerText() : "";
|
|
rec("no_error_dump_on_login", !errText.includes("502") && !errText.includes("<html>"), errText.slice(0, 80));
|
|
|
|
await p.screenshot({ path: "/tmp/canvas-evidence/04-persona-login-success.png" });
|
|
|
|
await b.close();
|
|
server.close();
|
|
|
|
const fails = results.filter((r) => !r.ok);
|
|
console.log(`\n${results.length} checks · ${results.length - fails.length} pass · ${fails.length} fail`);
|
|
process.exit(fails.length ? 1 : 0);
|