Files
pleno-vue/tests/e2e/invoice-transfer-monitor.spec.ts
T

307 lines
9.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { mockApi, seedAuthenticatedState } from "./support/network.js";
const QUEUE_LIST_PATH = "/collected-invoices/economic/queue";
const QUEUE_MONITOR_PATH = "/collected-invoices/economic/queue/monitor";
const QUEUE_DISMISS_PATH = "/collected-invoices/economic/queue/dismiss";
const QUEUE_DISMISS_TERMINAL_PATH = "/collected-invoices/economic/queue/dismiss-terminal";
function json(body: unknown, status = 200) {
return {
status,
contentType: "application/json",
body: JSON.stringify(body),
};
}
function matchesApiPath(urlString: string, expectedPath: string) {
const url = new URL(urlString);
return url.pathname === expectedPath || url.pathname === `/api${expectedPath}`;
}
function isWebKitProject(projectName: string) {
return /webkit/i.test(projectName);
}
async function suppressVueDevtoolsOverlay(page) {
await page.addInitScript(() => {
const style = document.createElement("style");
style.textContent =
"#__vue-devtools-container__, .vue-devtools__anchor-btn, .vue-devtools__panel-content { display: none !important; visibility: hidden !important; pointer-events: none !important; }";
document.documentElement.appendChild(style);
localStorage.setItem("lastVersionCheck", String(Date.now()));
});
}
async function bootstrapAuthenticatedSuperuser(page) {
const token = "superuser-transfer-monitor-e2e-token";
await suppressVueDevtoolsOverlay(page);
await seedAuthenticatedState(page, token);
await mockApi(page, {
authenticated: true,
permissions: ["superuser", "user"],
loginToken: token,
});
}
function monitorPayload() {
return {
data: {
jobs: [
{
id: 9301,
status: "PROCESSING",
progress_percent: 45,
progress_message: "Exporting invoice lines",
details_summary: {
message: "Exporting invoice lines",
target: {
collected_invoice_id: 7001,
},
customer: {
customer_number: 42424242,
name: "Acme Fleet",
},
},
},
{
id: 9302,
status: "FAILED",
progress_percent: 100,
error_message: "Missing e-conomic product",
details_summary: {
message: "Missing e-conomic product",
target: {
collected_invoice_id: 7002,
},
customer: {
customer_number: 52525252,
name: "Nordic Transport",
},
},
},
{
id: 9303,
status: "COMPLETED",
progress_percent: 100,
progress_message: "Completed",
details_summary: {
message: "Completed",
target: {
collected_invoice_id: 7003,
},
customer: {
customer_number: 62626262,
name: "Finished Carrier",
},
},
},
{
id: 9304,
status: "QUEUED",
progress_percent: 0,
progress_message: "Queued",
details_summary: {
message: "Queued",
target: {
collected_invoice_id: 7004,
},
customer: {
customer_number: 72727272,
name: "Queued Carrier",
},
},
},
],
counts: {
queued: 1,
in_progress: 1,
failed: 1,
completed: 1,
total: 4,
},
progress_percent: 61,
limit: 50,
},
meta: [],
includes: [],
};
}
function completedMonitorPayload() {
return {
data: {
jobs: [
{
id: 9401,
status: "COMPLETED",
progress_percent: 100,
progress_message: "Completed",
details_summary: {
message: "Completed",
target: {
collected_invoice_id: 7101,
},
customer: {
customer_number: 81818181,
name: "Green Carrier",
},
},
},
],
counts: {
queued: 0,
in_progress: 0,
failed: 0,
completed: 1,
total: 1,
},
progress_percent: 100,
limit: 50,
},
meta: [],
includes: [],
};
}
test.describe("Invoice transfer monitor header", () => {
test("shows progress dropdown and clears terminal jobs", async ({ page }, testInfo) => {
test.skip(
isWebKitProject(testInfo.project.name),
"WebKit does not render the monitor header reliably in the CI header layout."
);
let dismissedJobId: number | null = null;
let clearedTerminal = false;
await bootstrapAuthenticatedSuperuser(page);
await page.route("**/collected-invoices/economic/queue**", async (route) => {
const request = route.request();
const url = request.url();
if (request.method() === "GET" && matchesApiPath(url, QUEUE_MONITOR_PATH)) {
await route.fulfill(json(monitorPayload()));
return;
}
if (request.method() === "POST" && matchesApiPath(url, QUEUE_DISMISS_PATH)) {
const payload = request.postDataJSON() as { job_id?: number };
dismissedJobId = payload.job_id ?? null;
await route.fulfill(json({ data: { message: "cleared" } }));
return;
}
if (request.method() === "POST" && matchesApiPath(url, QUEUE_DISMISS_TERMINAL_PATH)) {
clearedTerminal = true;
await route.fulfill(json({ data: { dismissed_count: 2 } }));
return;
}
if (request.method() === "GET" && matchesApiPath(url, QUEUE_LIST_PATH)) {
await route.fulfill(
json({
data: {
items: [],
count: 0,
total: 0,
limit: 50,
offset: 0,
has_more: false,
},
})
);
return;
}
await route.fallback();
});
await page.goto("/superuser/invoices?activeTab=queue");
const monitor = page.getByTestId("invoice-transfer-monitor");
await expect(monitor).toBeVisible({ timeout: 15_000 });
const monitorButton = page.getByTestId("invoice-transfer-monitor-button");
await expect(monitorButton).toContainText("Overførsler");
await expect(monitorButton).toContainText("I kø 1");
await expect(monitorButton).toContainText("Fejlet 1");
await expect(monitorButton).toHaveClass(/has-failed-transfers/);
await expect(monitorButton.locator(".queue-monitor-progress > span")).toHaveCSS(
"background-color",
"rgb(239, 68, 68)"
);
const monitorBox = await monitor.boundingBox();
const userProfiles = page.getByRole("link", { name: /Jeppe|E2E|profile|profil/i });
if ((await userProfiles.count()) > 0) {
const userBox = await userProfiles.last().boundingBox();
expect(monitorBox?.x ?? 0).toBeLessThan(userBox?.x ?? Number.POSITIVE_INFINITY);
}
await monitorButton.click();
const monitorMenu = page.getByTestId("invoice-transfer-monitor-menu");
await expect(monitorMenu).toBeVisible();
await expect(monitorMenu).toHaveClass(/has-failed-transfers/);
await expect(monitorMenu.locator(".dropdown-content")).toHaveCSS("border-left-width", "2px");
await expect(page.getByTestId("invoice-transfer-monitor-job")).toHaveCount(4);
await expect(page.getByText("Faktura #7002")).toBeVisible();
await expect(page.getByText("Nordic Transport - 52525252")).toBeVisible();
await expect(page.getByText("Missing e-conomic product")).toBeVisible();
await page.getByTestId("invoice-transfer-monitor-clear-one").first().click();
await expect.poll(() => dismissedJobId).toBe(9302);
await page.getByTestId("invoice-transfer-monitor-clear-all").click();
await expect.poll(() => clearedTerminal).toBe(true);
});
test("shows a full green progress bar when every visible transfer completed successfully", async ({
page,
}, testInfo) => {
test.skip(
isWebKitProject(testInfo.project.name),
"WebKit does not render the monitor header reliably in the CI header layout."
);
await bootstrapAuthenticatedSuperuser(page);
await page.route("**/collected-invoices/economic/queue**", async (route) => {
const request = route.request();
const url = request.url();
if (request.method() === "GET" && matchesApiPath(url, QUEUE_MONITOR_PATH)) {
await route.fulfill(json(completedMonitorPayload()));
return;
}
if (request.method() === "GET" && matchesApiPath(url, QUEUE_LIST_PATH)) {
await route.fulfill(
json({
data: {
items: [],
count: 0,
total: 0,
limit: 50,
offset: 0,
has_more: false,
},
})
);
return;
}
await route.fallback();
});
await page.goto("/superuser/invoices?activeTab=queue");
const monitorButton = page.getByTestId("invoice-transfer-monitor-button");
await expect(monitorButton).toBeVisible({ timeout: 15_000 });
await expect(monitorButton).toContainText("Fuldført 1");
const progressFill = monitorButton.locator(".queue-monitor-progress > span");
await expect(monitorButton.locator(".queue-monitor-progress")).toHaveClass(/is-successful-complete/);
await expect(progressFill).toHaveCSS("background-color", "rgb(34, 197, 94)");
await expect(progressFill).toHaveCSS("width", /[1-9]\d{2}(\.\d+)?px/);
});
});