Files
pleno-vue/tests/e2e/support/network.js
T
Jeppe Bundgaard f372e59a07 Enhance Workfeed diagnostics and localization:
- Updated `listShifts` diagnostic modal to include new filters (date range, employee, release state) with improved UI/UX.
- Simplified employee and department lookup diagnostics by removing unnecessary search inputs.
- Standardized error handling and validation messages for shift diagnostics.
- Extended localization files across all supported languages with new diagnostic strings.
- Adjusted Workfeed module logic to reflect updated query parameters (`startFrom`, `startTo`, `employeeID`, `released`).
- Updated and added unit and e2e tests to validate new diagnostic flows and parameter constraints.
2026-03-24 11:52:15 +01:00

1078 lines
34 KiB
JavaScript

const API_HOST = /https?:\/\/(?:api\.truckwash\.io(?::\d+)?\/.*|localhost(?::\d+)?\/api\/.*|127\.0\.0\.1(?::\d+)?\/api\/.*)/i;
const TINY_PNG = Buffer.from(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAukB9pY9ZxQAAAAASUVORK5CYII=",
"base64"
);
function json(body, status = 200) {
return {
status,
contentType: "application/json",
body: JSON.stringify(body)
};
}
function binary(body, contentType = "image/png", status = 200) {
return {
status,
contentType,
body,
};
}
function mergeFixture(base, overrides = {}) {
return {
...base,
...overrides,
previewByKey: {
...(base.previewByKey || {}),
...(overrides.previewByKey || {}),
},
summaryBySessionId: {
...(base.summaryBySessionId || {}),
...(overrides.summaryBySessionId || {}),
},
summaryByKey: {
...(base.summaryByKey || {}),
...(overrides.summaryByKey || {}),
},
answerResponseByKey: {
...(base.answerResponseByKey || {}),
...(overrides.answerResponseByKey || {}),
},
attachmentsByTaskId: {
...(base.attachmentsByTaskId || {}),
...(overrides.attachmentsByTaskId || {}),
},
attachmentDownloadByKey: {
...(base.attachmentDownloadByKey || {}),
...(overrides.attachmentDownloadByKey || {}),
},
};
}
function createSelfServeFixture(overrides = {}) {
const departments = [
{
id: 1,
name: "Copenhagen",
address: "Alpha 1",
latitude: 55.6761,
longitude: 12.5683,
self_serve_enabled: true,
lanes: [
{
id: 7,
department: 1,
name: "7",
status: "AVAILABLE",
machine_available: true,
products: [2, 3],
relay_in_id: "IN-7",
relay_out_id: "OUT-7",
relay_machine_id: "M-7",
dynamic_image_id: 77,
machine_type_id: null,
},
{
id: 8,
department: 1,
name: "8",
status: "FAULT",
machine_available: false,
products: [2, 3],
relay_in_id: "IN-8",
relay_out_id: "OUT-8",
relay_machine_id: "M-8",
dynamic_image_id: 78,
machine_type_id: null,
},
],
},
{
id: 2,
name: "Odense",
address: "Beta 2",
latitude: 55.4038,
longitude: 10.4024,
self_serve_enabled: true,
lanes: [
{
id: 9,
department: 2,
name: "9",
status: "AVAILABLE",
machine_available: true,
products: [2, 4],
relay_in_id: "IN-9",
relay_out_id: "OUT-9",
relay_machine_id: "M-9",
dynamic_image_id: 79,
machine_type_id: null,
},
],
},
{
id: 3,
name: "Aarhus",
address: "Gamma 3",
latitude: 56.1629,
longitude: 10.2039,
self_serve_enabled: true,
lanes: [
{
id: 10,
department: 3,
name: "10",
status: "AVAILABLE",
machine_available: false,
products: [3, 4],
relay_in_id: "IN-10",
relay_out_id: "OUT-10",
relay_machine_id: "M-10",
dynamic_image_id: 80,
machine_type_id: null,
},
],
},
];
const products = [
{ id: 2, name: "Truck", price: 100, description: "Large truck", piktogram: "truck", is_wash: true, subscription_allowed: true, category: 4 },
{ id: 3, name: "Van", price: 80, description: "Medium van", piktogram: "van", is_wash: true, subscription_allowed: true, category: 4 },
{ id: 4, name: "Car", price: 60, description: "Small car", piktogram: "car", is_wash: true, subscription_allowed: true, category: 4 },
];
const baseFixture = {
departments,
departmentLanes: departments.flatMap((department) => department.lanes),
products,
customerVehicles: [
{ id: 1, reg: "AB12345", type: 2 },
{ id: 2, reg: "CD67890", type: 3 },
],
previewByKey: {
"7:AB12345": {
allowed: true,
machine_available: true,
lane: { id: 7, name: "7" },
session: { id: 501, status: "IN_PROGRESS", allowed: true },
questions: [
{ id: 11, question: "Is the tarp removed?", description: "Required before machine wash.", answer: null, order_priority: 1 },
],
conditions: [],
rules: [],
tasks: [
{ id: 9001, task: "Prepare the truck", description: "Complete the pre-wash checks.", order_priority: 1, services: ["MACHINE"], buttons: [1] },
],
allowed_services: ["MACHINE"],
},
"7:ZZ00000": {
allowed: true,
machine_available: true,
lane: { id: 7, name: "7" },
session: { id: 601, status: "IN_PROGRESS", allowed: true },
questions: [
{ id: 21, question: "Ready for direct wash?", answer: null, order_priority: 1 },
],
conditions: [],
rules: [],
tasks: [],
allowed_services: ["MACHINE"],
},
},
summaryBySessionId: {
501: {
session: { id: 501, status: "IN_PROGRESS", allowed: true },
lane: { id: 7, name: "7" },
questions: [
{ id: 11, question: "Is the tarp removed?", description: "Required before machine wash.", answer: null, order_priority: 1 },
],
conditions: [],
rules: [],
tasks: [
{ id: 9001, task: "Prepare the truck", description: "Complete the pre-wash checks.", order_priority: 1, services: ["MACHINE"], buttons: [1] },
],
events: [
{ id: 1, type: "STARTED", created_at: "2026-01-01T10:00:00.000Z" },
],
},
601: {
session: { id: 601, status: "IN_PROGRESS", allowed: true },
lane: { id: 7, name: "7" },
questions: [
{ id: 21, question: "Ready for direct wash?", answer: null, order_priority: 1 },
],
conditions: [],
rules: [],
tasks: [],
events: [
{ id: 2, type: "STARTED", created_at: "2026-01-01T11:00:00.000Z" },
],
},
},
summaryByKey: {},
answerResponseByKey: {
"7:AB12345:11:true": {
session: { id: 501, status: "IN_PROGRESS", allowed: true },
lane: { id: 7, name: "7" },
questions: [
{ id: 11, question: "Is the tarp removed?", description: "Required before machine wash.", answer: true, order_priority: 1 },
],
conditions: [],
rules: [],
tasks: [
{ id: 9001, task: "Prepare the truck", description: "Complete the pre-wash checks.", order_priority: 1, services: ["MACHINE"], buttons: [1] },
],
events: [
{ id: 3, type: "QUESTION_ANSWERED", created_at: "2026-01-01T10:01:00.000Z" },
],
},
"7:ZZ00000:21:true": {
session: { id: 601, status: "IN_PROGRESS", allowed: true },
lane: { id: 7, name: "7" },
questions: [
{ id: 21, question: "Ready for direct wash?", answer: true, order_priority: 1 },
],
conditions: [],
rules: [],
tasks: [],
events: [
{ id: 4, type: "QUESTION_ANSWERED", created_at: "2026-01-01T11:01:00.000Z" },
],
},
},
attachmentsByTaskId: {
9001: [
{ id: 301, content: { other: "prep.pdf" } },
{ id: 302, content: { other: "prep.jpg" } },
],
},
attachmentDownloadByKey: {
"9001:301": { download_link: "https://cdn.example.test/prep.pdf" },
"9001:302": { download_link: "https://cdn.example.test/prep.jpg" },
},
laneAllowedServices: ["MACHINE"],
commandResponse: { success: true },
relayResponse: { success: true },
dynamicImage: TINY_PNG,
};
const fixture = mergeFixture(baseFixture, overrides);
fixture.departmentLanes = fixture.departments.flatMap((department) => department.lanes || []);
fixture.laneById = fixture.departmentLanes.reduce((accumulator, lane) => {
accumulator[String(lane.id)] = lane;
return accumulator;
}, {});
return fixture;
}
export async function mockApi(page, options = {}) {
const selfServe = options.selfServe ? createSelfServeFixture(options.selfServe === true ? {} : options.selfServe) : null;
await page.route(API_HOST, async (route) => {
const request = route.request();
const url = request.url();
const parsedUrl = new URL(url);
const pathname = parsedUrl.pathname;
const method = request.method();
if (url.includes("/auth/recaptcha/pre-check") && method === "GET") {
await route.fulfill(
json({
data: {
recaptcha: {
enabled: false,
site_key: ""
},
rate_limit: {
enabled: false,
limit: 0,
remaining: 0,
reset: 0,
warning: null
}
}
})
);
return;
}
if (url.includes("/auth/login") && method === "POST") {
await route.fulfill(
json({
data: {
token: options.loginToken || "e2e-token"
}
})
);
return;
}
if (url.includes("/auth/session") && method === "GET") {
if (!options.authenticated) {
await route.fulfill(json({ message: "Unauthenticated" }, 401));
return;
}
const defaultSession = {
id: 1,
customer_number: 12345,
group_id: 1,
email: "e2e@example.com",
phone: {
number: "12345678",
country_code: 45
},
notifications: {
wash_certificate_email: null,
email_notifications_enabled: true,
sms_notifications_enabled: false
},
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-01T00:00:00.000Z",
display_name: "E2E User",
permissions: options.permissions || ["user"],
economic_customer: []
};
const sessionData = {
...defaultSession,
...(options.sessionData || {})
};
if (options.permissions) {
sessionData.permissions = options.permissions;
}
await route.fulfill(
json({
data: sessionData
})
);
return;
}
if (pathname.endsWith("/workfeed/config")) {
const workfeedConfig = [
{ module: "workfeed", variable: "enabled", type: "bool", value: true },
{ module: "workfeed", variable: "api_url", type: "string", value: "https://api.workfeed.test" },
{ module: "workfeed", variable: "api_key", type: "string", value: "test-api-key" },
{ module: "workfeed", variable: "CompanyID", type: "string", value: "123456" },
];
if (method === "GET") {
const variable = parsedUrl.searchParams.get("variable");
if (variable) {
const match = workfeedConfig.find((entry) => entry.variable === variable);
await route.fulfill(json({ data: match ? match.value : null }));
return;
}
await route.fulfill(json({ data: workfeedConfig }));
return;
}
if (method === "POST") {
await route.fulfill(json({ data: { updated: true } }));
return;
}
}
if (pathname.endsWith("/modules/workfeed/departments") && method === "GET") {
const departments = [
{
id: "dep_01J5P2B0BC9Q2X7H8Y7JQ1M2N3",
name: "North Facility",
timezone: "Europe/Copenhagen",
active: true,
},
{
id: "dep_01J5P2B0BC9Q2X7H8Y7JQ1M2N4",
name: "South Facility",
timezone: "Europe/Copenhagen",
active: true,
},
];
await route.fulfill(json({
data: {
items: departments,
pagination: { cursor: null, nextCursor: null, limit: 20, total: departments.length },
},
}));
return;
}
if (pathname.endsWith("/modules/workfeed/employees") && method === "GET") {
const employees = [
{
id: "emp_01J5P2F2A0CQKBBX4P8M3Y7R5P",
firstName: "Anne",
lastName: "Nielsen",
fullName: "Anne Nielsen",
email: "anne.nielsen@example.com",
departmentId: "dep_01J5P2B0BC9Q2X7H8Y7JQ1M2N3",
active: true,
},
{
id: "emp_01J5P2F2A0CQKBBX4P8M3Y7R5Q",
firstName: "Mads",
lastName: "Jensen",
fullName: "Mads Jensen",
email: "mads.jensen@example.com",
departmentId: "dep_01J5P2B0BC9Q2X7H8Y7JQ1M2N4",
active: true,
},
];
await route.fulfill(json({
data: {
items: employees,
pagination: { cursor: null, nextCursor: null, limit: 20, total: employees.length },
},
}));
return;
}
if (/\/modules\/workfeed\/employees\/[^/]+$/i.test(pathname) && method === "GET") {
const id = pathname.split("/").pop();
await route.fulfill(json({
data: {
id,
firstName: "Anne",
lastName: "Nielsen",
fullName: "Anne Nielsen",
email: "anne.nielsen@example.com",
departmentId: "dep_01J5P2B0BC9Q2X7H8Y7JQ1M2N3",
active: true,
},
}));
return;
}
if (pathname.endsWith("/modules/workfeed/shifts") && method === "GET") {
const startFrom = parsedUrl.searchParams.get("startFrom");
const startTo = parsedUrl.searchParams.get("startTo");
const employeeID = parsedUrl.searchParams.get("employeeID");
const releasedRaw = parsedUrl.searchParams.get("released");
if (!startFrom) {
await route.fulfill(json({ message: "Missing required query parameter: startFrom" }, 422));
return;
}
if (!startTo) {
await route.fulfill(json({ message: "Missing required query parameter: startTo" }, 422));
return;
}
const shifts = [
{
id: "shf_01J5P3X9D35R9C6BDZ1S0R4V6Q",
title: "Morning Shift",
status: "published",
employeeID: "emp_01J5P2F2A0CQKBBX4P8M3Y7R5P",
released: true,
startAt: "2026-03-24T06:00:00Z",
endAt: "2026-03-24T14:00:00Z",
},
{
id: "shf_01J5P3X9D35R9C6BDZ1S0R4V6R",
title: "Evening Shift",
status: "draft",
employeeID: "emp_01J5P2F2A0CQKBBX4P8M3Y7R5Q",
released: false,
startAt: "2026-03-24T14:00:00Z",
endAt: "2026-03-24T22:00:00Z",
},
];
const released = releasedRaw === null ? undefined : releasedRaw.toLowerCase() === "true";
const filtered = shifts.filter((entry) => {
if (employeeID && entry.employeeID !== employeeID) {
return false;
}
if (released !== undefined && entry.released !== released) {
return false;
}
return true;
});
await route.fulfill(json({
data: {
items: filtered,
pagination: { cursor: null, nextCursor: null, limit: 20, total: filtered.length },
},
}));
return;
}
if (/\/modules\/workfeed\/shifts\/[^/]+$/i.test(pathname) && method === "GET") {
const id = pathname.split("/").pop();
await route.fulfill(json({
data: {
id,
title: "Morning Shift",
status: "published",
employeeID: "emp_01J5P2F2A0CQKBBX4P8M3Y7R5P",
released: true,
startAt: "2026-03-24T06:00:00Z",
endAt: "2026-03-24T14:00:00Z",
},
}));
return;
}
if (selfServe) {
if (pathname.endsWith("/guest/departments") && method === "GET") {
await route.fulfill(json({ data: selfServe.departments }));
return;
}
if (pathname.endsWith("/departments") && method === "GET") {
await route.fulfill(json({ data: selfServe.departments.map(({ lanes, ...department }) => department) }));
return;
}
if (pathname.endsWith("/products") && method === "GET") {
await route.fulfill(json({ data: selfServe.products }));
return;
}
if (pathname.endsWith("/vehicles") && method === "GET") {
await route.fulfill(json({ data: selfServe.customerVehicles }));
return;
}
if (pathname.endsWith("/department/lanes") && method === "GET") {
const laneId = parsedUrl.searchParams.get("id");
if (laneId) {
await route.fulfill(json({ data: selfServe.laneById[laneId] || null }));
return;
}
await route.fulfill(json({ data: selfServe.departmentLanes }));
return;
}
if (pathname.endsWith("/department/selfserve/vehicle/allowed") && method === "GET") {
const laneId = parsedUrl.searchParams.get("lane_id");
const reg = (parsedUrl.searchParams.get("reg") || "").toUpperCase();
const previewData = selfServe.previewByKey[`${laneId}:${reg}`] || null;
await route.fulfill(json({ data: previewData || { allowed: false, questions: [], tasks: [], conditions: [], rules: [] } }));
return;
}
if (pathname.endsWith("/department/selfserve/washes/summary") && method === "GET") {
const sessionId = parsedUrl.searchParams.get("session_id");
const laneId = parsedUrl.searchParams.get("lane_id");
const reg = (parsedUrl.searchParams.get("reg") || "").toUpperCase();
const summaryData = (
(sessionId ? selfServe.summaryBySessionId[sessionId] : null)
|| selfServe.summaryByKey[`${laneId}:${reg}`]
|| null
);
await route.fulfill(json({ data: summaryData || { events: [], questions: [], tasks: [], conditions: [], rules: [] } }));
return;
}
if (pathname.endsWith("/department/selfserve/vehicle/conditions") && method === "POST") {
const body = request.postDataJSON?.() || {};
const key = `${body.lane}:${String(body.reg || "").toUpperCase()}:${body.question}:${body.value}`;
const responseSummary = selfServe.answerResponseByKey[key] || null;
await route.fulfill(json({
data: {
selfserve: responseSummary,
},
}));
return;
}
if (pathname.endsWith("/department/selfserve/tasks/attachments") && method === "GET") {
const taskId = parsedUrl.searchParams.get("id");
await route.fulfill(json({
data: selfServe.attachmentsByTaskId[taskId] || [],
}));
return;
}
if (pathname.endsWith("/department/selfserve/tasks/attachments/download") && method === "GET") {
const taskId = parsedUrl.searchParams.get("task_id");
const attachmentId = parsedUrl.searchParams.get("attachment_id");
await route.fulfill(json(selfServe.attachmentDownloadByKey[`${taskId}:${attachmentId}`] || { download_link: null }));
return;
}
if (pathname.endsWith("/modules/self-serve/lane/services/allowed") && method === "POST") {
await route.fulfill(json({
data: {
allowed_services: selfServe.laneAllowedServices,
},
}));
return;
}
if (pathname.endsWith("/modules/self-serve/lane/relay/machine/enable") && method === "POST") {
await route.fulfill(json(selfServe.relayResponse));
return;
}
if (pathname.endsWith("/modules/self-serve/lane/command") && method === "POST") {
await route.fulfill(json(selfServe.commandResponse));
return;
}
if (pathname.endsWith("/department/lanes/dynamic-image") && method === "GET") {
await route.fulfill(binary(selfServe.dynamicImage));
return;
}
}
if (options.invoiceDistribution) {
const monthFromDate = parsedUrl.searchParams.get("dateFrom");
const monthNumber = monthFromDate ? Number(monthFromDate.split("-")[1]) : 1;
const monthBase = Number.isFinite(monthNumber) ? monthNumber * 10 : 10;
const forceDistributionLegacy = Boolean(options.invoiceDistributionForceLegacyFallback);
const forceCompareLegacy = Boolean(options.invoiceDistributionForceCompareFallback);
if (pathname.endsWith("/orders") && method === "GET") {
await route.fulfill(
json({
data: [
{
id: 1,
created_at: "2026-01-01T00:00:00.000Z"
}
]
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period") && method === "GET") {
await route.fulfill(
json({
data: {
types: {
all: [
{
transactions: [
{ amount: 100 + monthBase, booked: true, excluded: false },
{ amount: 50 + monthBase, booked: true, excluded: false },
{ amount: 25, booked: false, excluded: false }
]
}
]
}
}
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/v2/all") && method === "GET") {
if (forceDistributionLegacy) {
await route.fulfill(json({ message: "v2 distribution temporarily unavailable" }, 500));
return;
}
await route.fulfill(
json({
fixed_pricing: {
customers: [
{
id: 10,
customer_number: 1001,
customer_name: "Acme Transport",
requires_action: false,
transactions: [{ id: 1, date: "2026-01-10T00:00:00.000Z", amount: 80 + monthBase, booked: true, department_id: 1, excluded: false }],
meta: {
fixed_pricing: {
created_at: "2026-01-10T00:00:00.000Z",
price: 80 + monthBase,
original_price: 120 + monthBase,
department_totals_relative: { 1: 40 + monthBase, 2: 40 }
}
}
}
],
collective_results: {
total_fixed_price: 80 + monthBase,
total_department_totals_relative_parsed: {
Copenhagen: 40 + monthBase,
Odense: 40
}
},
warnings: []
},
wash_subscriptions: {
customers: [
{
id: 11,
customer_number: 1002,
customer_name: "Nordic Haul",
requires_action: false,
transactions: [{ id: 2, date: "2026-01-05T00:00:00.000Z", amount: 40 + monthBase, booked: true, department_id: 1, excluded: false }],
meta: {
wash_subscription: {
created_at: "2026-01-05T00:00:00.000Z",
price: 40 + monthBase,
original_price: 55 + monthBase,
department_totals_relative: { 1: 20 + monthBase, 2: 20 }
}
}
}
],
collective_results: {
total_subscription_price: 40 + monthBase,
subscription_price_department_distribution_parsed: {
Copenhagen: 20 + monthBase,
Odense: 20
}
},
warnings: []
},
customer_prices: {
customers: [
{
id: 12,
customer_number: 1003,
customer_name: "Discount Fleet",
requires_action: false,
transactions: [{ id: 3, date: "2026-01-08T00:00:00.000Z", amount: 15 + monthBase, booked: true, department_id: 2, excluded: false }],
meta: {
customer_price: {
created_at: "2026-01-08T00:00:00.000Z",
price: 15 + monthBase,
department_totals_relative: { 2: 15 + monthBase }
}
}
}
],
collective_results: {
total_customer_price: 15 + monthBase,
customer_price_department_distribution_parsed: {
Odense: 15 + monthBase
}
},
warnings: []
}
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/v2/fixed-pricing") && method === "GET") {
if (forceDistributionLegacy) {
await route.fulfill(json({ message: "v2 fixed pricing distribution temporarily unavailable" }, 500));
return;
}
await route.fulfill(
json({
customers: [
{
id: 10,
customer_number: 1001,
customer_name: "Acme Transport",
requires_action: false,
transactions: [{ id: 1, date: "2026-01-10T00:00:00.000Z", amount: 80 + monthBase, booked: true, department_id: 1, excluded: false }],
meta: {
fixed_pricing: {
created_at: "2026-01-10T00:00:00.000Z",
price: 80 + monthBase,
original_price: 120 + monthBase,
department_totals_relative: { 1: 40 + monthBase, 2: 40 }
}
}
}
],
collective_results: {
total_fixed_price: 80 + monthBase,
total_department_totals_relative_parsed: {
Copenhagen: 40 + monthBase,
Odense: 40
}
},
warnings: []
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/v2/wash-subscriptions") && method === "GET") {
if (forceDistributionLegacy) {
await route.fulfill(json({ message: "v2 wash subscriptions distribution temporarily unavailable" }, 500));
return;
}
await route.fulfill(
json({
customers: [
{
id: 11,
customer_number: 1002,
customer_name: "Nordic Haul",
requires_action: false,
transactions: [{ id: 2, date: "2026-01-05T00:00:00.000Z", amount: 40 + monthBase, booked: true, department_id: 1, excluded: false }],
meta: {
wash_subscription: {
created_at: "2026-01-05T00:00:00.000Z",
price: 40 + monthBase,
original_price: 55 + monthBase,
department_totals_relative: { 1: 20 + monthBase, 2: 20 }
}
}
}
],
collective_results: {
total_subscription_price: 40 + monthBase,
subscription_price_department_distribution_parsed: {
Copenhagen: 20 + monthBase,
Odense: 20
}
},
warnings: []
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/v2/customer-prices") && method === "GET") {
if (forceDistributionLegacy) {
await route.fulfill(json({ message: "v2 customer prices distribution temporarily unavailable" }, 500));
return;
}
await route.fulfill(
json({
customers: [
{
id: 12,
customer_number: 1003,
customer_name: "Discount Fleet",
requires_action: false,
transactions: [{ id: 3, date: "2026-01-08T00:00:00.000Z", amount: 15 + monthBase, booked: true, department_id: 2, excluded: false }],
meta: {
customer_price: {
created_at: "2026-01-08T00:00:00.000Z",
price: 15 + monthBase,
department_totals_relative: { 2: 15 + monthBase }
}
}
}
],
collective_results: {
total_customer_price: 15 + monthBase,
customer_price_department_distribution_parsed: {
Odense: 15 + monthBase
}
},
warnings: []
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/fixed-pricing") && method === "GET") {
await route.fulfill(
json({
data: [
{
id: 10,
customer_number: 1001,
customer_name: "Acme Transport",
meta: {
fixed_pricing: {
created_at: "2026-01-10T00:00:00.000Z",
price: 80 + monthBase,
original_price: 120 + monthBase,
department_totals_relative: { 1: 40 + monthBase, 2: 40 }
}
}
}
],
includes: {
collective_fixed_pricing_results: {
total_fixed_price: 80 + monthBase,
total_department_totals_relative_parsed: {
Copenhagen: 40 + monthBase,
Odense: 40
}
}
}
})
);
return;
}
if (pathname.endsWith("/superuser/invoicing/period/distribution/wash-subscriptions") && method === "GET") {
await route.fulfill(
json({
data: [
{
id: 11,
customer_number: 1002,
customer_name: "Nordic Haul",
meta: {
wash_subscription: {
created_at: "2026-01-05T00:00:00.000Z",
price: 40 + monthBase,
original_price: 55 + monthBase,
department_totals_relative: { 1: 20 + monthBase, 2: 20 }
}
}
}
],
includes: {
collective_subscription_results: {
total_subscription_price: 40 + monthBase,
subscription_price_department_distribution_parsed: {
Copenhagen: 20 + monthBase,
Odense: 20
}
}
}
})
);
return;
}
if (pathname.endsWith("/departments") && method === "GET") {
await route.fulfill(
json({
data: [
{ id: 1, name: "Copenhagen" },
{ id: 2, name: "Odense" }
]
})
);
return;
}
if (pathname.endsWith("/collected-invoices") && method === "GET") {
const invoices = [101, 102, 103, 104, 105, 106].map((invoiceId, index) => ({
id: invoiceId,
customer_number: 1001 + index,
customer_name: `Customer ${invoiceId}`,
total_net_amount: 100 + (index * 10)
}));
await route.fulfill(
json({
data: invoices
})
);
return;
}
if (pathname.endsWith("/collected-invoices/economic/compare") && method === "GET") {
const invoiceId = Number(parsedUrl.searchParams.get("collected_invoice_id") || 0);
const mismatch = invoiceId === 101 || invoiceId === 103;
await new Promise((resolve) => setTimeout(resolve, 600));
await route.fulfill(
json({
data: {
collected_invoice_id: invoiceId,
warnings: mismatch ? ["Line mismatch found"] : [],
internal_total: mismatch ? 150 : 120,
booked_total: mismatch ? 145 : 120,
draft_total: null,
difference: mismatch ? 5 : 0,
order_ids: mismatch ? [1, 2] : [3]
}
})
);
return;
}
if (pathname.endsWith("/collected-invoices/economic/v2/compare/bulk") && method === "POST") {
if (forceCompareLegacy) {
await route.fulfill(json({ message: "v2 compare bulk temporarily unavailable" }, 500));
return;
}
const body = request.postDataJSON?.() || {};
const ids = Array.isArray(body.collected_invoice_ids) ? body.collected_invoice_ids : [];
const results = ids.map((invoiceId) => {
const mismatch = Number(invoiceId) === 101 || Number(invoiceId) === 103;
const internalTotal = mismatch ? 150 : 120;
const targetTotal = mismatch ? 145 : 120;
return {
collected_invoice_id: Number(invoiceId),
warnings: mismatch ? ["Line mismatch found"] : [],
details: {
order_ids: mismatch ? [1, 2] : [3],
customer: {
internal_customer_number: 4000 + Number(invoiceId),
name: `Customer ${invoiceId}`
},
internal: {
normalized: {
totals: {
net_total: internalTotal,
billable_line_count: mismatch ? 2 : 1
}
}
}
},
comparison: {
totals: {
internal_net_total: internalTotal
},
targets: {
booked: {
target: "booked",
status: mismatch ? "partial_mismatch" : "exact_match",
overall_match: !mismatch,
totals: {
target_net_total: targetTotal
},
mismatch_reasons: mismatch ? ["department_total_mismatch"] : [],
warnings: mismatch ? ["Line mismatch found"] : [],
lines: {
summary: {
internal_billable_count: mismatch ? 2 : 1,
target_billable_count: mismatch ? 2 : 1,
mismatch_count: mismatch ? 1 : 0
}
}
}
},
warnings: mismatch ? ["comparison warning"] : []
}
};
});
await route.fulfill(
json({
requested: ids.length,
compared: ids.length,
failed: 0,
results,
errors: []
})
);
return;
}
}
if (options.fallbackPassthrough) {
await route.fallback();
return;
}
await route.fulfill(json({ data: [] }));
});
}
export async function seedAuthenticatedState(page, token = "e2e-token") {
await page.addInitScript((value) => {
window.localStorage.setItem("token", value);
}, token);
}