Files
pleno-vue/tests/e2e/pos.visual.spec.js
T
Jeppe Bundgaard e8bd2f5478 Add employee fixtures, mobile POS customer note popup, and alignment updates:
- Introduced employee fixtures in `mobilePos.js` for testing workflows involving employee data.
- Enhanced `PosDepartmentStep1MobileCustomerNotes.vue` with translated labels and data-test attributes.
- Added e2e tests for customer notes popup and translated label validation.
- Improved alignment and responsiveness for `PaginationDisplayGeneralSearchReload.vue` and related e2e tests.
- Updated button logic and styles in `GenericButton.vue` and other mobile POS components for consistent UI behavior.
2026-04-09 14:47:43 +02:00

593 lines
20 KiB
JavaScript

import { expect, test } from "@playwright/test";
import { createPosFixture, mockApi, seedAuthenticatedState } from "./support/network.js";
import { createMobilePosFixture, REGULAR_CUSTOMER_ID, setupMobilePosPage } from "./support/mobilePos.js";
const POS_PERMISSIONS = [
"admin",
"department_access_12",
"edit_order_items",
"get_user",
"list_customer_notes",
"list_customer_attributes",
"get_custom_prices_other",
];
async function primeSession(page, token = "pos-visual-token") {
await seedAuthenticatedState(page, token);
const sessionRequest = page.waitForResponse((response) => {
return response.request().method() === "GET" && response.url().includes("/auth/session");
});
await page.goto("/login");
await sessionRequest;
}
async function seedMobileStepTwoState(page, { customerId = 12345679, orderId = 54518 } = {}) {
await page.addInitScript(({ activeCustomerId, activeOrderId }) => {
window.localStorage.removeItem("pos_order_id");
window.localStorage.setItem("pos", JSON.stringify({
vehicles: {
vehicle_1: {
reg: "EC21235",
status: "verified",
reference: "EC21233 - Test Ref. / Intern nummer",
type: 53,
customer_id: activeCustomerId,
},
vehicle_2: null,
vehicle_3: null,
activeVehicleIndex: 1,
},
views: {
manualInput: false,
vehicleSelection: false,
additionalItemSelection: false,
transactionHistoryView: false,
},
transactionItems: {
primaryItem: {
id: 53,
name: "Forvogn",
price: 649,
addons: [
{
id: 63,
quantity: 1,
product: {
id: 63,
name: "Indvendig vask Forvogn",
price: 399,
},
},
],
},
additionalItems: [
{
id: 65,
name: "Ekstraordinær pr. 10 min inkl. kemi",
price: 299,
quantity: 1,
},
],
},
categories: {
list: [],
selected: null,
},
productList: {
list: [],
},
metadata: {
customerId: activeCustomerId,
notes: "Visual regression note",
reference: "EC21233 - Test Ref. / Intern nummer",
washId: null,
bookingId: null,
laneId: null,
},
attachments: {
files: [],
base64: [],
wash_certificate: false,
},
transactionHistory: [],
lastVehicleOrders: {
vehicle_1: {
id: activeOrderId,
created_at: "2026-04-08 08:44:07",
items: [],
},
vehicle_2: null,
vehicle_3: null,
},
timestamp: Date.now(),
}));
}, {
activeCustomerId: customerId,
activeOrderId: orderId,
});
}
async function expectCustomerTabsDividerSpacing(scope) {
const tabs = scope.locator(".pos-selected-customer__tabs").first();
await expect(tabs).toBeVisible();
const spacing = await tabs.evaluate((element) => Number.parseFloat(window.getComputedStyle(element).marginBottom));
expect(spacing).toBeGreaterThan(5);
}
async function expectPosCardTabsDividerSpacing(scope) {
const tabs = scope.locator(".pos-card-tabs > .tabs").first();
await expect(tabs).toBeVisible();
const spacing = await tabs.evaluate((element) => Number.parseFloat(window.getComputedStyle(element).paddingBottom));
expect(spacing).toBeGreaterThan(5);
}
async function getElementMetrics(locator) {
await expect(locator).toBeVisible();
return locator.evaluate((element) => {
const styles = window.getComputedStyle(element);
const rect = element.getBoundingClientRect();
return {
fontSize: Number.parseFloat(styles.fontSize),
lineHeight: Number.parseFloat(styles.lineHeight),
height: rect.height,
};
});
}
async function getSidebarTabMetrics(locator) {
await expect(locator).toBeVisible();
return locator.evaluate((element) => {
const styles = window.getComputedStyle(element);
const rect = element.getBoundingClientRect();
return {
color: styles.color,
backgroundColor: styles.backgroundColor,
borderRadius: styles.borderRadius,
minHeight: styles.minHeight,
paddingTop: styles.paddingTop,
paddingRight: styles.paddingRight,
paddingBottom: styles.paddingBottom,
paddingLeft: styles.paddingLeft,
height: rect.height,
};
});
}
async function getSidebarTabListMetrics(locator) {
await expect(locator).toBeVisible();
return locator.evaluate((element) => {
const styles = window.getComputedStyle(element);
return {
gap: styles.gap,
rowGap: styles.rowGap,
columnGap: styles.columnGap,
};
});
}
async function openOrderDetailAddItems(page) {
const addItemsPanel = page.locator('[data-testid="pos-order-add-items-panel"]:visible').first();
const addItemsBackButton = page.locator('[data-testid="pos-order-add-items-back"]:visible').first();
await page.goto("/admin/12/modules/pos/orders/54518");
await expect(page.getByTestId("pos-order-detail")).toBeVisible();
await page.getByTestId("pos-order-add-item").click();
await expect(addItemsPanel).toBeVisible();
await expect(addItemsBackButton).toBeVisible();
}
function getVisibleTestId(page, testId) {
return page.locator(`[data-testid="${testId}"]:visible`).first();
}
test.describe("POS visuals", () => {
test("desktop step 1 snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
const posFixture = createPosFixture({
orderBookings: [
{
id: 77,
reg_1: "AB12345",
reg_2: "",
},
],
});
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: posFixture,
});
await primeSession(page, "pos-visual-desktop-step-1-token");
await page.goto("/admin/12/modules/pos");
await expect(page.getByTestId("pos-step-1")).toBeVisible();
await expectPosCardTabsDividerSpacing(page.getByTestId("pos-step-1"));
await expect(page.getByTestId("pos-recent-scan-row-801")).toBeVisible();
await expect(page.getByTestId("pos-step-1")).toHaveScreenshot("pos-step-1-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop step 1 expanded recent scan snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
const posFixture = createPosFixture({
orderBookings: [
{
id: 77,
reg_1: "AB12345",
reg_2: "",
},
],
});
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: posFixture,
});
await primeSession(page, "pos-visual-desktop-step-1-expanded-token");
await page.goto("/admin/12/modules/pos");
await expect(page.getByTestId("pos-step-1")).toBeVisible();
await expect(page.getByTestId("pos-recent-scan-details-801")).toHaveCount(0);
await page.getByTestId("pos-recent-scan-row-801").click();
await expect(page.locator("#reg_1")).toHaveValue("AB12345");
await expect(page.getByTestId("pos-recent-scan-details-801")).toContainText("RENAULT");
await expect(page.getByTestId("pos-recent-scan-details-801")).toContainText("Captur");
await expect(page.getByTestId("pos-recent-scan-details-801")).toContainText("Booking");
await expect(page.getByTestId("pos-step-1")).toHaveScreenshot("pos-step-1-desktop-expanded-scan.png", {
maxDiffPixels: 300,
});
});
test("desktop step 1 customer snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-step-1-customer-token");
await page.goto("/admin/12/modules/pos");
await expect(page.getByTestId("pos-step-1")).toBeVisible();
await page.locator("#reg_1").fill("EC21235");
await expect(page.getByTestId("pos-step-1").locator(".pos-card-tabs > .tabs li.is-active")).toContainText("Kunde");
await expectPosCardTabsDividerSpacing(page.getByTestId("pos-step-1"));
await expectCustomerTabsDividerSpacing(page.getByTestId("pos-step-1"));
await expect(page.getByTestId("pos-step-1")).toHaveScreenshot("pos-step-1-customer-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop step 1 keeps customer notes visible without refetching on blur", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
const customerNoteRequests = [];
page.on("request", (request) => {
if (
request.method() === "GET" &&
request.url().includes("/customer/notes?customer_number=12345679")
) {
customerNoteRequests.push(request.url());
}
});
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture({
customerNotesByNumber: {
12345679: [
{
id: 401,
created_at: "2026-04-09 09:15:00",
note: "Ring inden vask",
cashier_id: null,
},
],
},
}),
});
await primeSession(page, "pos-visual-desktop-step-1-notes-token");
await page.goto("/admin/12/modules/pos");
await expect(page.getByTestId("pos-step-1")).toBeVisible();
const stepOne = page.getByTestId("pos-step-1");
const registrationInput = page.locator("#reg_1");
const referenceInput = page.locator("#reference");
const noteRow = stepOne.getByTestId("pos-customer-note-401");
await registrationInput.fill("EC21235");
await expect(noteRow).toBeVisible();
await expect(noteRow).toContainText("Ring inden vask");
await expect.poll(() => customerNoteRequests.length).toBe(1);
await referenceInput.click();
await expect(noteRow).toBeVisible();
await expect(noteRow).toContainText("Ring inden vask");
await page.waitForTimeout(250);
expect(customerNoteRequests).toHaveLength(1);
});
test("desktop order detail snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-token");
await page.goto("/admin/12/modules/pos/orders/54518");
await expect(page.getByTestId("pos-order-detail")).toBeVisible();
await expect(page.locator('[data-auto-excel-export-button="1"]')).toHaveCount(0);
await expect(page.getByTestId("pos-order-registration-add-2")).toBeVisible();
await expect(page.getByTestId("pos-order-note-empty-state")).toBeVisible();
await expect(page.getByTestId("pos-order-item-edit-9101")).toBeVisible();
await expect(page.getByTestId("pos-order-add-item")).toBeVisible();
await expect(page.getByTestId("pos-order-total")).toContainText("1372 DKK");
await expect(page.getByTestId("pos-order-detail")).toHaveScreenshot("pos-order-detail-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop step 2 shared workspace snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-step-2-title-token");
await page.goto("/admin/12/modules/pos");
await expect(page.getByTestId("pos-step-1")).toBeVisible();
await page.locator("#reg_1").fill("EC21235");
await expect(page.getByTestId("pos-step-1").locator(".pos-card-tabs > .tabs li.is-active")).toContainText("Kunde");
await page.locator('[data-testid="pos-next-step"]:visible').click();
const stepTwo = page.getByTestId("pos-step-2");
await expect(stepTwo).toBeVisible();
await expect(stepTwo.getByTestId("pos-order-add-items-panel")).toBeVisible();
await expect(stepTwo.getByTestId("pos-order-panel-cart")).toBeVisible();
await expect(stepTwo.getByTestId("pos-order-metadata-grid")).toBeVisible();
await expect(stepTwo).toHaveScreenshot("pos-step-2-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop step 3 shared workspace snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-step-3-token");
await page.goto("/admin/12/modules/pos?id=54518&customer_id=12345679&step=3");
const stepThree = page.getByTestId("pos-step-3");
await expect(stepThree).toBeVisible();
await expect(stepThree.getByTestId("pos-order-panel-cart")).toBeVisible();
await expect(stepThree.getByTestId("pos-order-metadata-grid")).toBeVisible();
await expect(stepThree.getByTestId("pos-order-rail")).toBeVisible();
await expect(stepThree).toHaveScreenshot("pos-step-3-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop step 4 shared workspace snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-step-4-token");
await page.goto("/admin/12/modules/pos?id=54518&customer_id=12345679&step=4");
const stepFour = page.getByTestId("pos-step-4");
await expect(stepFour).toBeVisible();
await expect(stepFour.getByTestId("pos-order-panel-cart")).toBeVisible();
await expect(stepFour.getByTestId("pos-order-metadata-grid")).toBeVisible();
await expect(stepFour.getByTestId("pos-order-rail")).toBeVisible();
await expect(stepFour).toHaveScreenshot("pos-step-4-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop order detail add-items workspace snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-add-items-token");
await openOrderDetailAddItems(page);
await expect(getVisibleTestId(page, "pos-order-workspace")).toHaveScreenshot("pos-order-detail-add-items-desktop.png", {
maxDiffPixels: 300,
});
});
test("desktop order detail edit modal snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "Desktop only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-desktop-edit-modal-token");
await page.goto("/admin/12/modules/pos/orders/54518");
await expect(page.getByTestId("pos-order-detail")).toBeVisible();
await page.getByTestId("pos-order-item-edit-9101").click();
await expect(page.getByTestId("pos-order-item-edit-modal")).toBeVisible();
await expect(page.getByTestId("pos-order-item-edit-modal")).toHaveScreenshot("pos-order-detail-edit-modal-desktop.png", {
maxDiffPixels: 300,
});
});
test("mobile step 2 snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("mobile"), "Mobile only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await seedMobileStepTwoState(page);
await primeSession(page, "pos-visual-mobile-step-2-token");
await page.goto("/admin/12/modules/pos?id=54518&customer_id=12345679&step=2");
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible();
await expect(page.getByTestId("pos-mobile-step-2")).toHaveScreenshot("pos-mobile-step-2.png", {
maxDiffPixels: 300,
});
});
test("mobile step 1 selected customer CTA snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("mobile"), "Mobile only");
const longCustomerName = "(TEST) Pleno Vognmandsforretning";
const fixture = createMobilePosFixture({
customersByNumber: {
[REGULAR_CUSTOMER_ID]: {
id: REGULAR_CUSTOMER_ID,
customerNumber: REGULAR_CUSTOMER_ID,
name: longCustomerName,
address: "Demo Street 1",
zip: "2630",
city: "Taastrup",
mobilePhone: "12345678",
email: "pos-mobile@example.com",
corporateIdentificationNumber: "00012345",
economic_customer: REGULAR_CUSTOMER_ID,
barred: false,
},
},
});
await setupMobilePosPage(page, fixture, {
token: "pos-visual-mobile-step-1-customer-token",
permissions: ["admin", "department_access_1"],
seedState: {
customerId: REGULAR_CUSTOMER_ID,
reg: "EC21235",
reference: "EC21233 - Test Ref. / Intern nummer",
includePrimaryItem: false,
vehicleType: null,
lastOrderId: null,
},
route: {
step: 1,
},
});
await expect(page.getByTestId("pos-mobile-step-1")).toBeVisible();
await expect(page.getByTestId("pos-mobile-next-step")).toContainText(longCustomerName);
await expect(page.getByTestId("pos-mobile-fixed-actions")).toHaveScreenshot("pos-mobile-step-1-fixed-actions.png", {
maxDiffPixels: 300,
});
});
test("mobile order detail add-items workspace snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("mobile"), "Mobile only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture(),
});
await primeSession(page, "pos-visual-mobile-add-items-token");
await openOrderDetailAddItems(page);
await expect(getVisibleTestId(page, "pos-order-workspace")).toHaveScreenshot("pos-order-detail-add-items-mobile.png", {
maxDiffPixels: 300,
});
});
test("mobile step 3 snapshot", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("mobile"), "Mobile only");
await mockApi(page, {
authenticated: true,
permissions: POS_PERMISSIONS,
edgeGateways: false,
pos: createPosFixture({
ordersById: {
54518: {
id: 54518,
customer_id: 999,
department_id: 12,
reference: "CARD-REF-54518",
notes: "Paid at terminal",
reg_1: "EC21235",
reg_2: "",
reg_3: "",
invoice_collection_id: null,
booking_id: null,
completed_at: null,
closed_at: null,
created_at: "2026-04-08 08:44:07",
},
},
paymentIntentsByOrderId: {
54518: {
id: "pi_54518",
amount: 123400,
amount_capturable: 123400,
amount_received: 0,
currency: "dkk",
status: "requires_capture",
metadata: {
order_id: "54518",
reader_id: "reader_online_1",
tax_percentage: "25",
},
},
},
}),
});
await seedMobileStepTwoState(page, { customerId: 999 });
await primeSession(page, "pos-visual-mobile-step-3-token");
await page.goto("/admin/12/modules/pos?id=54518&customer_id=999&step=3");
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible();
await expect(page.getByTestId("pos-mobile-step-3")).toHaveScreenshot("pos-mobile-step-3.png", {
maxDiffPixels: 300,
});
});
});