Add unit tests and e2e coverage for POS mobile add-customer autofill functionality:
- Added unit tests for `applyPosAddCustomerSearchResult` to handle autofill field updates, manual overrides, and stale response rejection. - Implemented a new e2e scenario for preserving manual edits during autofill updates and ignoring stale delayed responses. - Refactored `mobilePos.js` fixture setup to include CVR search and customer registration mock logic. - Added and integrated `PosDesktopCustomerConflictModal.vue` for UI handling of customer conflicts.
This commit is contained in:
@@ -53,6 +53,17 @@ async function createDisposableOrder(page: Page) {
|
||||
};
|
||||
}
|
||||
|
||||
async function selectStepOneCustomer(
|
||||
page: Page,
|
||||
customerNumber: number | string,
|
||||
expectedName: string | RegExp = /\(TEST\) Pleno Vognmandsforretning/
|
||||
) {
|
||||
await page.locator("#pos_select_customer_input").fill(String(customerNumber));
|
||||
await expect(page.locator(".customer-drop-down-select").first()).toBeVisible();
|
||||
await page.locator(".customer-drop-down-select").first().click();
|
||||
await expect(page.locator(".field.has-addons input[disabled]").last()).toHaveValue(expectedName);
|
||||
}
|
||||
|
||||
async function clickVisibleTestId(page: Page, testId: string) {
|
||||
const locator = page.getByTestId(testId);
|
||||
const count = await locator.count();
|
||||
@@ -275,6 +286,47 @@ function createRequiredWarningsPosFixture() {
|
||||
});
|
||||
}
|
||||
|
||||
function createCustomerConflictPosFixture() {
|
||||
const baseFixture = createPosFixture();
|
||||
const defaultCustomer = baseFixture.customersByNumber[12345679];
|
||||
const vehicleCustomerNumber = 22334455;
|
||||
const vehicleCustomerName = "(TEST) Conflict Fleet";
|
||||
|
||||
return createPosFixture({
|
||||
customersByNumber: {
|
||||
[vehicleCustomerNumber]: {
|
||||
...defaultCustomer,
|
||||
id: 3,
|
||||
customerNumber: vehicleCustomerNumber,
|
||||
economic_customer: vehicleCustomerNumber,
|
||||
name: vehicleCustomerName,
|
||||
email: "conflict@example.com",
|
||||
mobilePhone: "22334455",
|
||||
},
|
||||
},
|
||||
vehicles: [
|
||||
...baseFixture.vehicles.filter((vehicle) => vehicle.reg !== "CONFLICT1"),
|
||||
{
|
||||
id: 7701,
|
||||
reg: "CONFLICT1",
|
||||
customer_id: vehicleCustomerNumber,
|
||||
customer_name: vehicleCustomerName,
|
||||
type: 53,
|
||||
status: "verified",
|
||||
barred: false,
|
||||
wash_subscription: false,
|
||||
addons: {
|
||||
enabled: 0,
|
||||
available: 0,
|
||||
list: [],
|
||||
},
|
||||
reference: "CONFLICT-VEHICLE-REF",
|
||||
last_order_id: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("Admin POS Orders - desktop settings", () => {
|
||||
test.beforeEach(async ({ page }, testInfo) => {
|
||||
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only order settings coverage");
|
||||
@@ -1426,6 +1478,113 @@ test.describe("Admin POS Orders - desktop required warning states", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Admin POS Orders - desktop step 1 customer and vehicle ownership", () => {
|
||||
test.beforeEach(async ({ page }, testInfo) => {
|
||||
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only step 1 customer ownership coverage");
|
||||
});
|
||||
|
||||
test("preserves a manually selected required-reference customer and manual reference while editing reg_1", async ({
|
||||
page,
|
||||
}) => {
|
||||
await mockApi(page, {
|
||||
authenticated: true,
|
||||
permissions: POS_PERMISSIONS,
|
||||
edgeGateways: false,
|
||||
pos: createRequiredWarningsPosFixture(),
|
||||
});
|
||||
await primeOperatorSession(page, "pos-orders-manual-customer-reg-edit-token");
|
||||
|
||||
await page.goto("/admin/12/modules/pos?step=1");
|
||||
await expect(page.getByTestId("pos-step-1")).toBeVisible();
|
||||
|
||||
await page.locator("#reg_1").fill("MAN");
|
||||
await selectStepOneCustomer(page, 12345679);
|
||||
|
||||
const referenceInput = page.locator("#reference");
|
||||
await referenceInput.fill("MANUAL-REF-001");
|
||||
|
||||
await page.locator("#reg_1").fill("MANUAL1");
|
||||
await expect(page.locator(".pos-selected-customer__title").filter({ hasText: /\(TEST\) Pleno/ }).first()).toBeVisible();
|
||||
await expect(referenceInput).toHaveValue("MANUAL-REF-001");
|
||||
|
||||
await page.locator("#reg_1").fill("");
|
||||
await expect(page.locator(".pos-selected-customer__title").filter({ hasText: /\(TEST\) Pleno/ }).first()).toBeVisible();
|
||||
await expect(referenceInput).toHaveValue("MANUAL-REF-001");
|
||||
|
||||
await page.locator("#reg_1").fill("MANUAL2");
|
||||
await expect(page.locator(".pos-selected-customer__title").filter({ hasText: /\(TEST\) Pleno/ }).first()).toBeVisible();
|
||||
await expect(referenceInput).toHaveValue("MANUAL-REF-001");
|
||||
});
|
||||
|
||||
test("asks whether to keep the selected customer when an exact plate belongs to another customer", async ({
|
||||
page,
|
||||
}) => {
|
||||
const fixture = createCustomerConflictPosFixture();
|
||||
|
||||
await mockApi(page, {
|
||||
authenticated: true,
|
||||
permissions: POS_PERMISSIONS,
|
||||
edgeGateways: false,
|
||||
pos: fixture,
|
||||
});
|
||||
await primeOperatorSession(page, "pos-orders-customer-conflict-keep-token");
|
||||
|
||||
await page.goto("/admin/12/modules/pos?step=1");
|
||||
await expect(page.getByTestId("pos-step-1")).toBeVisible();
|
||||
|
||||
await page.locator("#reg_1").fill("CON");
|
||||
await selectStepOneCustomer(page, 12345679);
|
||||
await page.locator("#reference").fill("KEEP-REF");
|
||||
|
||||
await page.locator("#reg_1").fill("CONFLICT1");
|
||||
const activeConflictModal = page.locator('[data-testid="pos-desktop-customer-conflict-modal"].is-active');
|
||||
await expect(activeConflictModal).toBeVisible();
|
||||
|
||||
await activeConflictModal.getByTestId("pos-desktop-customer-conflict-keep").click();
|
||||
await expect(activeConflictModal).toHaveCount(0);
|
||||
|
||||
await expect(page.locator(".pos-selected-customer__title").filter({ hasText: /\(TEST\) Pleno/ }).first()).toBeVisible();
|
||||
await expect(page.locator("#reference")).toHaveValue("KEEP-REF");
|
||||
await expect(page.locator("#reg_1")).toHaveValue("CONFLICT1");
|
||||
|
||||
await page.getByTestId("pos-step-1").getByTestId("pos-next-step").click();
|
||||
await expect(page).toHaveURL(/\/admin\/12\/modules\/pos\?id=\d+&customer_id=12345679&step=2/);
|
||||
});
|
||||
|
||||
test("can switch to the matched vehicle customer from the conflict modal", async ({ page }) => {
|
||||
const fixture = createCustomerConflictPosFixture();
|
||||
|
||||
await mockApi(page, {
|
||||
authenticated: true,
|
||||
permissions: POS_PERMISSIONS,
|
||||
edgeGateways: false,
|
||||
pos: fixture,
|
||||
});
|
||||
await primeOperatorSession(page, "pos-orders-customer-conflict-use-vehicle-token");
|
||||
|
||||
await page.goto("/admin/12/modules/pos?step=1");
|
||||
await expect(page.getByTestId("pos-step-1")).toBeVisible();
|
||||
|
||||
await page.locator("#reg_1").fill("CON");
|
||||
await selectStepOneCustomer(page, 12345679);
|
||||
await page.locator("#reference").fill("MANUAL-REF");
|
||||
|
||||
await page.locator("#reg_1").fill("CONFLICT1");
|
||||
const activeConflictModal = page.locator('[data-testid="pos-desktop-customer-conflict-modal"].is-active');
|
||||
await expect(activeConflictModal).toBeVisible();
|
||||
|
||||
await activeConflictModal.getByTestId("pos-desktop-customer-conflict-use-vehicle").click();
|
||||
await expect(activeConflictModal).toHaveCount(0);
|
||||
|
||||
await expect(page.locator(".pos-selected-customer__title").filter({ hasText: /\(TEST\) Conflict Fleet/ }).first()).toBeVisible();
|
||||
await expect(page.locator("#reference")).toHaveValue("CONFLICT-VEHICLE-REF");
|
||||
await expect(page.locator("#reg_1")).toHaveValue("CONFLICT1");
|
||||
|
||||
await page.getByTestId("pos-step-1").getByTestId("pos-next-step").click();
|
||||
await expect(page).toHaveURL(/\/admin\/12\/modules\/pos\?id=\d+&customer_id=22334455&step=2/);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Admin POS Orders - desktop attachment discovery", () => {
|
||||
test.beforeEach(async ({ page }, testInfo) => {
|
||||
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only attachment discovery coverage");
|
||||
|
||||
@@ -139,6 +139,14 @@ async function selectCustomerFromPopup(page, customerNumber = REGULAR_CUSTOMER_I
|
||||
await expect(page.getByTestId("pos-mobile-customer-popup")).toBeHidden({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
async function openAddCustomerPopup(page) {
|
||||
const popup = page.locator('[data-testid="pos-mobile-popup"][data-popup-id="select_customer"]');
|
||||
|
||||
await expect(page.getByTestId("pos-mobile-customer-popup")).toBeVisible({ timeout: 10_000 });
|
||||
await popup.getByText("Ny kunde", { exact: true }).click();
|
||||
await expect(page.getByTestId("pos-mobile-add-customer-popup")).toBeVisible({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
async function waitForStepReset(page) {
|
||||
await expect
|
||||
.poll(
|
||||
@@ -404,6 +412,152 @@ test.describe("POS mobile order flow", () => {
|
||||
await expect(page.getByTestId("pos-mobile-customer-popup")).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test("add-customer popup preserves manual edits while refreshing untouched fields from a later CVR lookup", async ({
|
||||
page,
|
||||
}) => {
|
||||
const fixture = createMobilePosFixture({
|
||||
cvrSearchResponses: {
|
||||
"41004355": {
|
||||
data: {
|
||||
vat: 41004355,
|
||||
status: "Normal",
|
||||
name: "Truckwash ApS",
|
||||
address: "Letland Alle 2",
|
||||
zipcode: 2630,
|
||||
city: "Taastrup",
|
||||
phone: "21754690",
|
||||
email: "mikkel@truckwash.dk",
|
||||
},
|
||||
},
|
||||
"43423010": {
|
||||
data: {
|
||||
vat: 43423010,
|
||||
status: "Normal",
|
||||
name: "Wash Group ApS",
|
||||
address: "Nordhavn 4",
|
||||
zipcode: 2100,
|
||||
city: "Kobenhavn O",
|
||||
phone: "42331128",
|
||||
email: "billing@wash-group.test",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await setupMobilePosPage(page, fixture, {
|
||||
token: "mobile-add-customer-reset-fix-token",
|
||||
seedState: {
|
||||
customerId: null,
|
||||
reg: "FREE123",
|
||||
reference: "ADD-CUSTOMER-REF",
|
||||
includePrimaryItem: false,
|
||||
vehicleType: null,
|
||||
lastOrderId: null,
|
||||
},
|
||||
route: {
|
||||
step: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await page.getByTestId("pos-mobile-next-step").click();
|
||||
await openAddCustomerPopup(page);
|
||||
|
||||
const searchInput = page.getByTestId("pos-mobile-add-customer-search-input");
|
||||
const companyPhone = page.getByTestId("pos-mobile-add-customer-company-phone");
|
||||
const invoiceEmail = page.getByTestId("pos-mobile-add-customer-invoice-email");
|
||||
const contactEmail = page.getByTestId("pos-mobile-add-customer-contact-email");
|
||||
const contactPhone = page.getByTestId("pos-mobile-add-customer-contact-phone");
|
||||
const submitButton = page.getByTestId("pos-mobile-add-customer-submit");
|
||||
|
||||
await searchInput.fill("41004355");
|
||||
await expect(companyPhone).toHaveValue("21754690", { timeout: 10_000 });
|
||||
await expect(invoiceEmail).toHaveValue("mikkel@truckwash.dk");
|
||||
await expect(contactEmail).toHaveValue("mikkel@truckwash.dk");
|
||||
await expect(contactPhone).toHaveValue("21754690");
|
||||
await expect(submitButton).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
await companyPhone.fill("55550000");
|
||||
await contactEmail.fill("dispatch@truckwash.test");
|
||||
|
||||
await searchInput.fill("43423010");
|
||||
|
||||
await expect(companyPhone).toHaveValue("55550000", { timeout: 10_000 });
|
||||
await expect(contactEmail).toHaveValue("dispatch@truckwash.test");
|
||||
await expect(invoiceEmail).toHaveValue("billing@wash-group.test");
|
||||
await expect(contactPhone).toHaveValue("42331128");
|
||||
await expect(submitButton).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test("add-customer popup ignores stale delayed CVR responses after a newer lookup resolves", async ({ page }) => {
|
||||
const fixture = createMobilePosFixture({
|
||||
cvrSearchResponses: {
|
||||
"41004355": {
|
||||
delayMs: 700,
|
||||
data: {
|
||||
vat: 41004355,
|
||||
status: "Normal",
|
||||
name: "Truckwash ApS",
|
||||
address: "Letland Alle 2",
|
||||
zipcode: 2630,
|
||||
city: "Taastrup",
|
||||
phone: "21754690",
|
||||
email: "mikkel@truckwash.dk",
|
||||
},
|
||||
},
|
||||
"43423010": {
|
||||
data: {
|
||||
vat: 43423010,
|
||||
status: "Normal",
|
||||
name: "Wash Group ApS",
|
||||
address: "Nordhavn 4",
|
||||
zipcode: 2100,
|
||||
city: "Kobenhavn O",
|
||||
phone: "42331128",
|
||||
email: "billing@wash-group.test",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await setupMobilePosPage(page, fixture, {
|
||||
token: "mobile-add-customer-stale-search-token",
|
||||
seedState: {
|
||||
customerId: null,
|
||||
reg: "FREE123",
|
||||
reference: "ADD-CUSTOMER-RACE-REF",
|
||||
includePrimaryItem: false,
|
||||
vehicleType: null,
|
||||
lastOrderId: null,
|
||||
},
|
||||
route: {
|
||||
step: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await page.getByTestId("pos-mobile-next-step").click();
|
||||
await openAddCustomerPopup(page);
|
||||
|
||||
const searchInput = page.getByTestId("pos-mobile-add-customer-search-input");
|
||||
const companyPhone = page.getByTestId("pos-mobile-add-customer-company-phone");
|
||||
const invoiceEmail = page.getByTestId("pos-mobile-add-customer-invoice-email");
|
||||
const contactPhone = page.getByTestId("pos-mobile-add-customer-contact-phone");
|
||||
|
||||
await searchInput.fill("41004355");
|
||||
await page.waitForTimeout(350);
|
||||
await searchInput.fill("43423010");
|
||||
|
||||
await expect(companyPhone).toHaveValue("42331128", { timeout: 10_000 });
|
||||
await expect(invoiceEmail).toHaveValue("billing@wash-group.test");
|
||||
await expect(contactPhone).toHaveValue("42331128");
|
||||
|
||||
await page.waitForTimeout(900);
|
||||
|
||||
await expect(companyPhone).toHaveValue("42331128");
|
||||
await expect(invoiceEmail).toHaveValue("billing@wash-group.test");
|
||||
await expect(contactPhone).toHaveValue("42331128");
|
||||
await expect.poll(() => fixture.requestCounters.cvrSearchGet, { timeout: 10_000 }).toBe(2);
|
||||
});
|
||||
|
||||
test("transaction history keeps each wash visually separated with a border", async ({ page }) => {
|
||||
const todayDate = new Date().toISOString().split("T")[0];
|
||||
const fixture = createMobilePosFixture({
|
||||
|
||||
@@ -280,6 +280,8 @@ function createRequestCounters(overrides = {}) {
|
||||
attachmentsDelete: 0,
|
||||
attachmentUpload: 0,
|
||||
attachmentDownload: 0,
|
||||
cvrSearchGet: 0,
|
||||
customerRegistrationPost: 0,
|
||||
lprPost: 0,
|
||||
...overrides,
|
||||
};
|
||||
@@ -297,6 +299,8 @@ function createRequestLog(overrides = {}) {
|
||||
bookingOrderAssignments: [],
|
||||
attachmentUploads: [],
|
||||
attachmentDeletes: [],
|
||||
cvrSearches: [],
|
||||
customerRegistrations: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -444,6 +448,32 @@ function buildDefaultFixture() {
|
||||
{ id: 41, name: productById[41].name, price: productById[41].price, quantity: 1 },
|
||||
],
|
||||
});
|
||||
const defaultCvrSearchResponses = {
|
||||
"41004355": {
|
||||
data: {
|
||||
vat: 41004355,
|
||||
status: "Normal",
|
||||
name: "Truckwash ApS",
|
||||
address: "Letland Alle 2",
|
||||
zipcode: 2630,
|
||||
city: "Taastrup",
|
||||
phone: "21754690",
|
||||
email: "mikkel@truckwash.dk",
|
||||
},
|
||||
},
|
||||
"43423010": {
|
||||
data: {
|
||||
vat: 43423010,
|
||||
status: "Normal",
|
||||
name: "Wash Group ApS",
|
||||
address: "Nordhavn 4",
|
||||
zipcode: 2100,
|
||||
city: "Kobenhavn O",
|
||||
phone: "42331128",
|
||||
email: "billing@wash-group.test",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
departmentId: DEFAULT_DEPARTMENT_ID,
|
||||
@@ -622,6 +652,13 @@ function buildDefaultFixture() {
|
||||
8102: nonWashBooking,
|
||||
8103: safetySealBooking,
|
||||
},
|
||||
cvrSearchResponses: defaultCvrSearchResponses,
|
||||
customerRegistrationResponse: {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
pendingBookings: null,
|
||||
bookingStatusById: {
|
||||
[DEFAULT_BOOKING_ID]: "pending",
|
||||
@@ -653,6 +690,8 @@ function normalizeFixture(fixture) {
|
||||
fixture.attachmentsByOrderId = mergeObjectMaps({}, fixture.attachmentsByOrderId);
|
||||
fixture.paymentIntentsByOrderId = mergeObjectMaps({}, fixture.paymentIntentsByOrderId);
|
||||
fixture.bookingsById = mergeObjectMaps({}, fixture.bookingsById);
|
||||
fixture.cvrSearchResponses = mergeObjectMaps({}, fixture.cvrSearchResponses);
|
||||
fixture.customerRegistrationResponse = clone(fixture.customerRegistrationResponse || { status: 200, data: { success: true } });
|
||||
fixture.deletedOrderIds = Array.isArray(fixture.deletedOrderIds) ? fixture.deletedOrderIds : [];
|
||||
fixture.markCompletedOrderIds = Array.isArray(fixture.markCompletedOrderIds) ? fixture.markCompletedOrderIds : [];
|
||||
|
||||
@@ -695,6 +734,8 @@ export function createMobilePosFixture(overrides = {}) {
|
||||
attachmentsByOrderId: mergeObjectMaps(base.attachmentsByOrderId, overrides.attachmentsByOrderId),
|
||||
paymentIntentsByOrderId: mergeObjectMaps(base.paymentIntentsByOrderId, overrides.paymentIntentsByOrderId),
|
||||
bookingsById: mergeObjectMaps(base.bookingsById, overrides.bookingsById),
|
||||
cvrSearchResponses: mergeObjectMaps(base.cvrSearchResponses, overrides.cvrSearchResponses),
|
||||
customerRegistrationResponse: clone(overrides.customerRegistrationResponse ?? base.customerRegistrationResponse),
|
||||
bookingStatusById: mergeObjectMaps(base.bookingStatusById, overrides.bookingStatusById),
|
||||
deletedOrderIds: clone(overrides.deletedOrderIds ?? base.deletedOrderIds),
|
||||
markCompletedOrderIds: clone(overrides.markCompletedOrderIds ?? base.markCompletedOrderIds),
|
||||
@@ -1298,6 +1339,90 @@ export async function mockMobilePosApi(page, fixture) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/cvr/search") && method === "GET") {
|
||||
recordCounter(fixture, "cvrSearchGet");
|
||||
const query = String(parsedUrl.searchParams.get("query") || "").trim();
|
||||
recordLog(fixture, "cvrSearches", { query });
|
||||
|
||||
const responseConfig = fixture.cvrSearchResponses?.[query] || null;
|
||||
const delayMs = Number(responseConfig?.delayMs ?? 0);
|
||||
const status = Number(responseConfig?.status ?? 200);
|
||||
|
||||
if (delayMs > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
|
||||
if (status >= 400) {
|
||||
await route.fulfill(
|
||||
json(
|
||||
{
|
||||
success: false,
|
||||
data: {
|
||||
message: responseConfig?.message || "Unable to load CVR details",
|
||||
},
|
||||
},
|
||||
status
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill(
|
||||
json({
|
||||
success: true,
|
||||
data: clone(responseConfig?.data ?? null),
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/auth/register/cvr") && method === "POST") {
|
||||
recordCounter(fixture, "customerRegistrationPost");
|
||||
recordLog(fixture, "customerRegistrations", body);
|
||||
|
||||
const responseConfig = fixture.customerRegistrationResponse || {};
|
||||
const status = Number(responseConfig.status ?? 200);
|
||||
|
||||
if (status >= 400) {
|
||||
await route.fulfill(
|
||||
json(
|
||||
{
|
||||
success: false,
|
||||
data: {
|
||||
message: responseConfig.message || "Unable to register customer",
|
||||
},
|
||||
},
|
||||
status
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const customerNumber = toPositiveInteger(body.companyPhone);
|
||||
if (customerNumber) {
|
||||
fixture.customersByNumber[customerNumber] = createCustomer(customerNumber, {
|
||||
name: body?.searchResult?.name || `Customer ${customerNumber}`,
|
||||
address: body?.searchResult?.address || "Demo Street 1",
|
||||
zip: String(body?.searchResult?.zipcode || "2630"),
|
||||
city: body?.searchResult?.city || "Taastrup",
|
||||
mobilePhone: String(body.contactPhone || body.companyPhone || ""),
|
||||
email: body.contactEmail || body.invoiceEmail || "",
|
||||
corporateIdentificationNumber: String(body.cvr || "").padStart(8, "0"),
|
||||
economic_customer: customerNumber,
|
||||
});
|
||||
fixture.customerAttributesByNumber[customerNumber] = fixture.customerAttributesByNumber[customerNumber] || [];
|
||||
fixture.customerNotesByNumber[customerNumber] = fixture.customerNotesByNumber[customerNumber] || [];
|
||||
}
|
||||
|
||||
await route.fulfill(
|
||||
json({
|
||||
success: true,
|
||||
data: clone(responseConfig.data ?? { success: true }),
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/users/customer") && method === "GET") {
|
||||
recordCounter(fixture, "usersCustomerGet");
|
||||
const customerNumber =
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
applyPosAddCustomerSearchResult,
|
||||
createPosAddCustomerFields,
|
||||
createPosAddCustomerManualOverrides,
|
||||
createPosAddCustomerRequestState,
|
||||
nextPosAddCustomerRequestState,
|
||||
resolvePosAddCustomerRequestState,
|
||||
setPosAddCustomerManualOverride,
|
||||
} from "@/components/displays/department/pos/steps/mobile/elements/posAddCustomerAutofill.js";
|
||||
|
||||
function createSearchResult(overrides = {}) {
|
||||
return {
|
||||
vat: 41004355,
|
||||
name: "Truckwash ApS",
|
||||
phone: "21754690",
|
||||
email: "mikkel@truckwash.dk",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("POS mobile add-customer autofill", () => {
|
||||
it("fills all eligible fields on the initial successful lookup", () => {
|
||||
const nextFields = applyPosAddCustomerSearchResult({
|
||||
fields: createPosAddCustomerFields(),
|
||||
manualOverrides: createPosAddCustomerManualOverrides(),
|
||||
searchResult: createSearchResult(),
|
||||
notFoundLabel: "Not found",
|
||||
});
|
||||
|
||||
expect(nextFields).toEqual({
|
||||
cvr: "41004355",
|
||||
companyPhone: "21754690",
|
||||
invoiceEmail: "mikkel@truckwash.dk",
|
||||
contactEmail: "mikkel@truckwash.dk",
|
||||
contactPhone: "21754690",
|
||||
});
|
||||
});
|
||||
|
||||
it("updates only untouched fields when a later lookup resolves", () => {
|
||||
const initialFields = applyPosAddCustomerSearchResult({
|
||||
fields: createPosAddCustomerFields(),
|
||||
manualOverrides: createPosAddCustomerManualOverrides(),
|
||||
searchResult: createSearchResult(),
|
||||
notFoundLabel: "Not found",
|
||||
});
|
||||
|
||||
const nextFields = applyPosAddCustomerSearchResult({
|
||||
fields: {
|
||||
...initialFields,
|
||||
companyPhone: "55550000",
|
||||
},
|
||||
manualOverrides: createPosAddCustomerManualOverrides({
|
||||
companyPhone: true,
|
||||
}),
|
||||
searchResult: createSearchResult({
|
||||
vat: 43423010,
|
||||
name: "Wash Group ApS",
|
||||
phone: "42331128",
|
||||
email: "billing@wash-group.test",
|
||||
}),
|
||||
notFoundLabel: "Not found",
|
||||
});
|
||||
|
||||
expect(nextFields).toEqual({
|
||||
cvr: "43423010",
|
||||
companyPhone: "55550000",
|
||||
invoiceEmail: "billing@wash-group.test",
|
||||
contactEmail: "billing@wash-group.test",
|
||||
contactPhone: "42331128",
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves manually edited fields across later autofill passes", () => {
|
||||
const manualOverrides = createPosAddCustomerManualOverrides({
|
||||
companyPhone: true,
|
||||
contactEmail: true,
|
||||
});
|
||||
|
||||
const nextFields = applyPosAddCustomerSearchResult({
|
||||
fields: createPosAddCustomerFields({
|
||||
cvr: "41004355",
|
||||
companyPhone: "99998888",
|
||||
invoiceEmail: "old-invoice@example.com",
|
||||
contactEmail: "dispatch@example.com",
|
||||
contactPhone: "21754690",
|
||||
}),
|
||||
manualOverrides,
|
||||
searchResult: createSearchResult({
|
||||
vat: 43423010,
|
||||
phone: "42331128",
|
||||
email: "billing@wash-group.test",
|
||||
}),
|
||||
notFoundLabel: "Not found",
|
||||
});
|
||||
|
||||
expect(nextFields.companyPhone).toBe("99998888");
|
||||
expect(nextFields.contactEmail).toBe("dispatch@example.com");
|
||||
expect(nextFields.invoiceEmail).toBe("billing@wash-group.test");
|
||||
expect(nextFields.contactPhone).toBe("42331128");
|
||||
expect(nextFields.cvr).toBe("43423010");
|
||||
});
|
||||
|
||||
it("refills a field after the operator clears a manual override", () => {
|
||||
const withManualEdit = setPosAddCustomerManualOverride(
|
||||
createPosAddCustomerManualOverrides({
|
||||
companyPhone: true,
|
||||
}),
|
||||
"companyPhone",
|
||||
""
|
||||
);
|
||||
|
||||
expect(withManualEdit.companyPhone).toBe(false);
|
||||
|
||||
const nextFields = applyPosAddCustomerSearchResult({
|
||||
fields: createPosAddCustomerFields({
|
||||
companyPhone: "",
|
||||
}),
|
||||
manualOverrides: withManualEdit,
|
||||
searchResult: createSearchResult({
|
||||
phone: "42331128",
|
||||
}),
|
||||
notFoundLabel: "Not found",
|
||||
});
|
||||
|
||||
expect(nextFields.companyPhone).toBe("42331128");
|
||||
});
|
||||
|
||||
it("rejects slower stale responses after a newer lookup wins", () => {
|
||||
const initialState = createPosAddCustomerRequestState();
|
||||
const firstRequest = nextPosAddCustomerRequestState(initialState);
|
||||
const secondRequest = nextPosAddCustomerRequestState(firstRequest.state);
|
||||
|
||||
const staleResolution = resolvePosAddCustomerRequestState(secondRequest.state, firstRequest.requestId);
|
||||
const latestResolution = resolvePosAddCustomerRequestState(secondRequest.state, secondRequest.requestId);
|
||||
|
||||
expect(staleResolution.shouldApply).toBe(false);
|
||||
expect(staleResolution.state).toEqual(secondRequest.state);
|
||||
|
||||
expect(latestResolution.shouldApply).toBe(true);
|
||||
expect(latestResolution.state).toEqual({
|
||||
latestRequestedId: secondRequest.requestId,
|
||||
latestAppliedId: secondRequest.requestId,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user