|
|
|
@@ -0,0 +1,172 @@
|
|
|
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
|
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
|
|
|
|
import { isDesktopProject } from "./support/projects";
|
|
|
|
|
|
|
|
|
|
const XL_VASK_USAGE_LOG_ID = 55;
|
|
|
|
|
const PERIOD_FROM_DATE = "2026-07-01";
|
|
|
|
|
const PERIOD_TO_DATE = "2026-07-31";
|
|
|
|
|
|
|
|
|
|
const PERIOD_VIEW_URL = `/superuser/invoices?activeTab=period&startDate=${PERIOD_FROM_DATE}&endDate=${PERIOD_TO_DATE}&periodView=all`;
|
|
|
|
|
const SELVVASH_DESTINATION_PATTERN = new RegExp(
|
|
|
|
|
"/superuser/invoices\\?activeTab=period&periodView=self_wash&xlvaskUsageLogId=" + XL_VASK_USAGE_LOG_ID
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
function json(body: unknown, status = 200) {
|
|
|
|
|
return {
|
|
|
|
|
status,
|
|
|
|
|
contentType: "application/json",
|
|
|
|
|
body: JSON.stringify(body),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createXlVaskFlaggedPeriodPayload({ dateFrom = PERIOD_FROM_DATE } = {}) {
|
|
|
|
|
const fixtureDate = String(dateFrom).slice(0, 10);
|
|
|
|
|
return {
|
|
|
|
|
data: {
|
|
|
|
|
types: {
|
|
|
|
|
all: [
|
|
|
|
|
{
|
|
|
|
|
id: 11,
|
|
|
|
|
customer_number: 4001,
|
|
|
|
|
customer_name: "Acme Fleet",
|
|
|
|
|
requires_action: true,
|
|
|
|
|
transactions: [
|
|
|
|
|
{
|
|
|
|
|
id: 9001,
|
|
|
|
|
date: `${fixtureDate}T10:00:00.000Z`,
|
|
|
|
|
amount: 120,
|
|
|
|
|
booked: false,
|
|
|
|
|
excluded: false,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
queue: { has_active_job: false, statuses: [], invoice_collection_ids: [], is_action_blocked: false },
|
|
|
|
|
meta: {},
|
|
|
|
|
flags: [
|
|
|
|
|
{
|
|
|
|
|
id: "auto-xlvask-missing-1",
|
|
|
|
|
source: "automatic",
|
|
|
|
|
severity: "yellow",
|
|
|
|
|
status: "active",
|
|
|
|
|
target_type: "xlvask_usage_log",
|
|
|
|
|
target_id: XL_VASK_USAGE_LOG_ID,
|
|
|
|
|
customer_number: 4001,
|
|
|
|
|
definition_key: "xlvask_missing_order_link",
|
|
|
|
|
fingerprint: "xlvask-missing-fingerprint-1",
|
|
|
|
|
message_key: "invoice_period.flags.automatic.xlvask_missing_order_link",
|
|
|
|
|
message_params: {
|
|
|
|
|
wash_id: `wash-${XL_VASK_USAGE_LOG_ID}`,
|
|
|
|
|
registration_number: "AB12345",
|
|
|
|
|
},
|
|
|
|
|
message: "XL Vask wash is neither ignored nor linked to an order in the selected period.",
|
|
|
|
|
context: {
|
|
|
|
|
customer_name: "Acme Fleet",
|
|
|
|
|
xlvask_usage_log_id: XL_VASK_USAGE_LOG_ID,
|
|
|
|
|
wash_id: `wash-${XL_VASK_USAGE_LOG_ID}`,
|
|
|
|
|
registration_number: "AB12345",
|
|
|
|
|
start_time: `${fixtureDate}T10:00:00.000Z`,
|
|
|
|
|
},
|
|
|
|
|
xlvask_usage_log_id: XL_VASK_USAGE_LOG_ID,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
flag_counts: { manual: 0, automatic: 1, total: 1 },
|
|
|
|
|
status_indicator: "flag_yellow",
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
invoice_per_order: [],
|
|
|
|
|
fixed_pricing: [],
|
|
|
|
|
tank_cleaning: [],
|
|
|
|
|
special_arrangements: [],
|
|
|
|
|
vehicle_subscriptions: [],
|
|
|
|
|
possible_duplicates: [],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function suppressVueDevtoolsOverlay(page) {
|
|
|
|
|
await page.addInitScript(() => {
|
|
|
|
|
const STYLE_ID = "__e2e-hide-vue-devtools";
|
|
|
|
|
localStorage.setItem("lastVersionCheck", String(Date.now()));
|
|
|
|
|
|
|
|
|
|
const apply = () => {
|
|
|
|
|
const target = document.head || document.documentElement;
|
|
|
|
|
if (!target) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!document.getElementById(STYLE_ID)) {
|
|
|
|
|
const style = document.createElement("style");
|
|
|
|
|
style.id = STYLE_ID;
|
|
|
|
|
style.textContent =
|
|
|
|
|
"#__vue-devtools-container__, .vue-devtools__anchor-btn, .vue-devtools__panel-content { display: none !important; visibility: hidden !important; pointer-events: none !important; }";
|
|
|
|
|
target.appendChild(style);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
apply();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function primePeriodViewApi(page) {
|
|
|
|
|
return page.route("**/superuser/invoicing/period**", async (route) => {
|
|
|
|
|
if (route.request().method() !== "GET") {
|
|
|
|
|
await route.fallback();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const url = new URL(route.request().url());
|
|
|
|
|
const dateFrom = url.searchParams.get("dateFrom") || PERIOD_FROM_DATE;
|
|
|
|
|
|
|
|
|
|
await route.fulfill(json(createXlVaskFlaggedPeriodPayload({ dateFrom })));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test.describe("XL Vask flag → Selvvash navigation", () => {
|
|
|
|
|
test("@smoke flag token opens the Selvvash tab with the highlighted wash", async ({ page, context }, testInfo) => {
|
|
|
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
|
|
|
|
|
|
await page.setViewportSize({ width: 1440, height: 900 });
|
|
|
|
|
const token = "superuser-xl-vask-navigation-token";
|
|
|
|
|
await suppressVueDevtoolsOverlay(page);
|
|
|
|
|
await seedAuthenticatedState(page, token);
|
|
|
|
|
await mockApi(page, {
|
|
|
|
|
authenticated: true,
|
|
|
|
|
permissions: ["superuser", "user"],
|
|
|
|
|
loginToken: token,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await primePeriodViewApi(page);
|
|
|
|
|
|
|
|
|
|
await context.addInitScript((tokenValue) => {
|
|
|
|
|
window.localStorage.setItem("token", tokenValue);
|
|
|
|
|
}, token);
|
|
|
|
|
|
|
|
|
|
await page.goto(PERIOD_VIEW_URL, { waitUntil: "domcontentloaded" });
|
|
|
|
|
await expect(page).toHaveURL(/activeTab=period/);
|
|
|
|
|
await expect(page.getByTestId("invoicing-period-view-selector-all")).toBeVisible({ timeout: 30_000 });
|
|
|
|
|
|
|
|
|
|
const customerRow = page.getByTestId("invoicing-period-customer-4001");
|
|
|
|
|
await expect(customerRow).toBeVisible();
|
|
|
|
|
|
|
|
|
|
const flagRow = page.getByTestId("invoice-period-flag-auto-xlvask-missing-1");
|
|
|
|
|
const flagRowCount = await flagRow.count();
|
|
|
|
|
test.skip(
|
|
|
|
|
flagRowCount === 0,
|
|
|
|
|
"No xlvask_missing_order_link flag is visible in the current period payload; smoke run skips."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await expect(flagRow).toBeVisible();
|
|
|
|
|
const flagToken = flagRow.locator(".invoice-period-flag-token");
|
|
|
|
|
await expect(flagToken).toBeVisible();
|
|
|
|
|
|
|
|
|
|
const popupPromise = page.waitForEvent("popup", { timeout: 15_000 });
|
|
|
|
|
await flagToken.click();
|
|
|
|
|
const popup = await popupPromise;
|
|
|
|
|
|
|
|
|
|
await expect(popup).toHaveURL(SELVVASH_DESTINATION_PATTERN);
|
|
|
|
|
await popup.waitForLoadState("domcontentloaded").catch(() => {});
|
|
|
|
|
|
|
|
|
|
await popup.close();
|
|
|
|
|
await context.close();
|
|
|
|
|
});
|
|
|
|
|
});
|