575 lines
21 KiB
JavaScript
575 lines
21 KiB
JavaScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
CARD_CUSTOMER_ID,
|
|
DEFAULT_ORDER_ID,
|
|
REGULAR_CUSTOMER_ID,
|
|
createMobilePosFixture,
|
|
createPaymentIntent,
|
|
getStoredPosSnapshot,
|
|
setupMobilePosPage,
|
|
suppressVueDevtoolsOverlay,
|
|
waitForMobileNextStepCooldown,
|
|
} from "./support/mobilePos.js";
|
|
|
|
test.describe("POS mobile card payments", () => {
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
test.skip(testInfo.project.name !== "chromium-mobile", "Mobile card payment suite is scoped to chromium-mobile.");
|
|
await suppressVueDevtoolsOverlay(page);
|
|
});
|
|
|
|
test("card mode entry sets customer 999 and preserves order context", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-entry-token",
|
|
seedState: {
|
|
customerId: null,
|
|
reg: "ZZ00000",
|
|
reference: "CARD-CTX-REF",
|
|
includePrimaryItem: false,
|
|
vehicleType: 53,
|
|
},
|
|
route: {
|
|
step: 1,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-next-step")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect(page.getByTestId("pos-mobile-direct-card-payment")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-direct-card-payment").click();
|
|
await expect(page.getByTestId("pos-mobile-customer-popup")).toBeHidden({ timeout: 10_000 });
|
|
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
const snapshot = await getStoredPosSnapshot(page);
|
|
return snapshot?.metadata?.customerId ?? null;
|
|
},
|
|
{ timeout: 10_000 }
|
|
)
|
|
.toBe(CARD_CUSTOMER_ID);
|
|
|
|
const snapshot = await getStoredPosSnapshot(page);
|
|
expect(snapshot?.vehicles?.vehicle_1?.reg).toBe("ZZ00000");
|
|
expect(snapshot?.metadata?.reference).toBe("CARD-CTX-REF");
|
|
});
|
|
|
|
test("card mode stays disabled when no stripe readers are assigned and re-enables after polling", async ({ page }) => {
|
|
const fixture = createMobilePosFixture({
|
|
readers: [],
|
|
});
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-reader-polling-token",
|
|
seedState: {
|
|
customerId: null,
|
|
reg: "ZZ00000",
|
|
reference: "CARD-POLL-REF",
|
|
includePrimaryItem: false,
|
|
vehicleType: 53,
|
|
},
|
|
route: {
|
|
step: 1,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-next-step")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
|
|
const cardButton = page.getByTestId("pos-mobile-direct-card-payment");
|
|
await expect(cardButton).toBeVisible({ timeout: 10_000 });
|
|
await expect
|
|
.poll(() => fixture.requestCounters.readersGet, { timeout: 10_000 })
|
|
.toBeGreaterThan(0);
|
|
await expect(cardButton).toBeDisabled();
|
|
|
|
fixture.readers = [
|
|
{
|
|
id: "reader_online_2",
|
|
label: "Recovered Reader",
|
|
status: "online",
|
|
action: null,
|
|
},
|
|
];
|
|
|
|
await expect
|
|
.poll(async () => {
|
|
return await cardButton.isEnabled();
|
|
}, { timeout: 12_000 })
|
|
.toBe(true);
|
|
|
|
await cardButton.click();
|
|
await expect(page.getByTestId("pos-mobile-customer-popup")).toBeHidden({ timeout: 10_000 });
|
|
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
const snapshot = await getStoredPosSnapshot(page);
|
|
return snapshot?.metadata?.customerId ?? null;
|
|
},
|
|
{ timeout: 10_000 }
|
|
)
|
|
.toBe(CARD_CUSTOMER_ID);
|
|
});
|
|
|
|
test("@smoke direct card flow bridges step 1 to capture and resets to scanner", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-bridge-token",
|
|
seedState: {
|
|
customerId: null,
|
|
reg: "CD99999",
|
|
reference: "CARD-BRIDGE-REF",
|
|
includePrimaryItem: false,
|
|
vehicleType: 53,
|
|
},
|
|
route: {
|
|
step: 1,
|
|
},
|
|
});
|
|
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect(page.getByTestId("pos-mobile-direct-card-payment")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-direct-card-payment").click();
|
|
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible({ timeout: 10_000 });
|
|
await waitForMobileNextStepCooldown(page);
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect.poll(() => fixture.requestCounters.orderItemsPost, { timeout: 10_000 }).toBe(1);
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-capture-intent").click();
|
|
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCapture, { timeout: 10_000 }).toBe(1);
|
|
await expect.poll(() => fixture.requestCounters.markAsCompleted, { timeout: 10_000 }).toBe(1);
|
|
await expect
|
|
.poll(
|
|
() => {
|
|
const currentUrl = new URL(page.url());
|
|
return currentUrl.searchParams.get("step");
|
|
},
|
|
{ timeout: 12_000 }
|
|
)
|
|
.toBe("1");
|
|
await expect(page.getByTestId("pos-mobile-next-step")).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
test("step 2 transitions to mobile card payment step 3 for customer 999", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-step-transition-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
primaryItemId: 53,
|
|
},
|
|
route: {
|
|
step: 2,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
|
|
await expect.poll(() => fixture.requestCounters.orderItemsPost, { timeout: 10_000 }).toBe(1);
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page).toHaveURL(/step=3/);
|
|
expect(fixture.requestCounters.markAsCompleted).toBe(0);
|
|
});
|
|
|
|
test("reader unavailable state is recoverable and non-fatal", async ({ page }) => {
|
|
const fixture = createMobilePosFixture({
|
|
readers: [
|
|
{
|
|
id: "reader_in_use",
|
|
label: "Busy reader",
|
|
status: "online",
|
|
action: { type: "process_payment_intent" },
|
|
},
|
|
{
|
|
id: "reader_offline",
|
|
label: "Offline reader",
|
|
status: "offline",
|
|
action: null,
|
|
},
|
|
],
|
|
});
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-reader-unavailable-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-no-readers")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-reader-unavailable-message")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toHaveCount(0);
|
|
await expect(page.getByTestId("pos-stripe-error-message")).toHaveCount(0);
|
|
await expect(page.getByTestId("pos-stripe-terminal-trigger-dot")).toHaveAttribute("data-status-key", "in_use");
|
|
await expect(page.getByTestId("pos-stripe-terminal-trigger-dot")).toHaveCSS(
|
|
"background-color",
|
|
"rgb(245, 158, 11)"
|
|
);
|
|
|
|
await page.getByTestId("pos-stripe-terminal-trigger").click();
|
|
await expect(page.getByTestId("pos-stripe-terminal-menu")).toBeVisible();
|
|
await expect(page.getByTestId("pos-stripe-terminal-group-in_use")).toBeVisible();
|
|
await expect(page.getByTestId("pos-stripe-terminal-group-offline")).toBeVisible();
|
|
await expect(page.getByTestId("pos-stripe-terminal-option-reader_in_use")).toHaveAttribute("aria-disabled", "true");
|
|
await expect(page.getByTestId("pos-stripe-terminal-option-reader_offline")).toHaveAttribute("aria-disabled", "true");
|
|
await expect(page.getByTestId("pos-stripe-terminal-option-dot-reader_offline")).toHaveCSS(
|
|
"background-color",
|
|
"rgb(220, 38, 38)"
|
|
);
|
|
|
|
fixture.readers = [
|
|
{
|
|
id: "reader_in_use",
|
|
label: "Busy reader",
|
|
status: "online",
|
|
action: { type: "process_payment_intent" },
|
|
},
|
|
{
|
|
id: "reader_offline",
|
|
label: "Offline reader",
|
|
status: "offline",
|
|
action: null,
|
|
},
|
|
{
|
|
id: "reader_online_2",
|
|
label: "Recovered Reader",
|
|
status: "online",
|
|
action: null,
|
|
},
|
|
];
|
|
|
|
await page.getByTestId("pos-stripe-no-readers").click();
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeEnabled({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-selected-reader")).toContainText("Recovered Reader");
|
|
await expect(page.getByTestId("pos-stripe-terminal-trigger-dot")).toHaveAttribute("data-status-key", "ready");
|
|
await expect(page.getByTestId("pos-stripe-terminal-trigger-dot")).toHaveCSS(
|
|
"background-color",
|
|
"rgb(22, 163, 74)"
|
|
);
|
|
expect(fixture.requestCounters.paymentIntentCreate).toBe(0);
|
|
});
|
|
|
|
test("reader available creates intent once and reaches capture state", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-create-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeEnabled();
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCreate, { timeout: 10_000 }).toBe(1);
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
expect(fixture.requestCounters.markAsCompleted).toBe(0);
|
|
});
|
|
|
|
test("step 3 back offers resume or cancel when payment is active", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-cancel-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.getByTestId("pos-mobile-step-3-back").click();
|
|
await expect(page.locator(".swal2-popup")).toBeVisible({ timeout: 10_000 });
|
|
await page.locator(".swal2-confirm").click();
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
expect(fixture.requestCounters.paymentIntentDelete).toBe(0);
|
|
|
|
await page.getByTestId("pos-mobile-step-3-back").click();
|
|
await expect(page.locator(".swal2-popup")).toBeVisible({ timeout: 10_000 });
|
|
await page.locator(".swal2-deny").click();
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentDelete, { timeout: 10_000 }).toBe(1);
|
|
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible({ timeout: 10_000 });
|
|
await expect(page).toHaveURL(/step=2/);
|
|
expect(fixture.requestCounters.markAsCompleted).toBe(0);
|
|
});
|
|
|
|
test("capture success completes once and resets scanner flow", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-capture-success-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.getByTestId("pos-stripe-capture-intent").click();
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCapture, { timeout: 10_000 }).toBe(1);
|
|
await expect.poll(() => fixture.requestCounters.markAsCompleted, { timeout: 10_000 }).toBe(1);
|
|
await expect
|
|
.poll(
|
|
() => {
|
|
const currentUrl = new URL(page.url());
|
|
return currentUrl.searchParams.get("step");
|
|
},
|
|
{ timeout: 12_000 }
|
|
)
|
|
.toBe("1");
|
|
await expect(page.getByTestId("pos-mobile-next-step")).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
test("guardrail: completion never runs before capture success", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-guardrail-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
await expect.poll(() => fixture.requestCounters.markAsCompleted, { timeout: 2_500 }).toBe(0);
|
|
});
|
|
|
|
test("step 3 bootstrap recovery fetches existing requires_capture intent", async ({ page }) => {
|
|
const fixture = createMobilePosFixture({
|
|
paymentIntentsByOrderId: {
|
|
[DEFAULT_ORDER_ID]: createPaymentIntent(DEFAULT_ORDER_ID, {
|
|
status: "requires_capture",
|
|
}),
|
|
},
|
|
});
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-recovery-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentGet, { timeout: 10_000 }).toBeGreaterThan(0);
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
expect(fixture.requestCounters.paymentIntentCreate).toBe(0);
|
|
});
|
|
|
|
test("step 3 bootstrap recovery completes already succeeded intent once", async ({ page }) => {
|
|
const fixture = createMobilePosFixture({
|
|
paymentIntentsByOrderId: {
|
|
[DEFAULT_ORDER_ID]: createPaymentIntent(DEFAULT_ORDER_ID, {
|
|
status: "succeeded",
|
|
}),
|
|
},
|
|
});
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-succeeded-recovery-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentGet, { timeout: 10_000 }).toBeGreaterThan(0);
|
|
await expect.poll(() => fixture.requestCounters.markAsCompleted, { timeout: 10_000 }).toBe(1);
|
|
expect(fixture.requestCounters.paymentIntentCreate).toBe(0);
|
|
expect(fixture.requestCounters.paymentIntentCapture).toBe(0);
|
|
await expect
|
|
.poll(
|
|
() => {
|
|
const currentUrl = new URL(page.url());
|
|
return currentUrl.searchParams.get("step");
|
|
},
|
|
{ timeout: 12_000 }
|
|
)
|
|
.toBe("1");
|
|
});
|
|
|
|
test("returning to step 2 and re-entering step 3 does not duplicate order items", async ({ page }) => {
|
|
const fixture = createMobilePosFixture();
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-step-reentry-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 2,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect.poll(() => fixture.requestCounters.orderItemsPost, { timeout: 10_000 }).toBe(1);
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.getByTestId("pos-mobile-step-3-back").click();
|
|
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.waitForTimeout(2100);
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toBeVisible({ timeout: 10_000 });
|
|
await expect.poll(() => fixture.requestCounters.orderItemsPost, { timeout: 10_000 }).toBe(1);
|
|
await expect.poll(() => fixture.requestCounters.orderItemsGet, { timeout: 10_000 }).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("payment-intent get/create/capture failures surface retryable error states", async ({ page }) => {
|
|
const fixture = createMobilePosFixture({
|
|
failureBudget: {
|
|
paymentIntentGet: 0,
|
|
paymentIntentCreate: 2,
|
|
paymentIntentCapture: 2,
|
|
},
|
|
});
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-card-error-recovery-token",
|
|
seedState: {
|
|
customerId: CARD_CUSTOMER_ID,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 3,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: CARD_CUSTOMER_ID,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCreate, { timeout: 10_000 }).toBe(2);
|
|
await expect(page.getByTestId("pos-stripe-error")).toBeVisible({ timeout: 10_000 });
|
|
|
|
fixture.failureBudget.paymentIntentGet = 2;
|
|
await page.getByTestId("pos-stripe-error").click();
|
|
await expect(page.getByTestId("pos-stripe-error")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-error").click();
|
|
await expect(page.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.getByTestId("pos-stripe-create-intent").click();
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCreate, { timeout: 10_000 }).toBe(3);
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.getByTestId("pos-stripe-capture-intent").click();
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCapture, { timeout: 10_000 }).toBe(2);
|
|
await expect(page.getByTestId("pos-stripe-error")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-stripe-error").click();
|
|
await expect(page.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.getByTestId("pos-stripe-capture-intent").click();
|
|
await expect.poll(() => fixture.requestCounters.paymentIntentCapture, { timeout: 10_000 }).toBe(3);
|
|
await expect.poll(() => fixture.requestCounters.markAsCompleted, { timeout: 10_000 }).toBe(1);
|
|
});
|
|
|
|
test("regression: non-card mobile customer still follows step-2 complete/reset path", async ({ page }) => {
|
|
const regularCustomerId = REGULAR_CUSTOMER_ID;
|
|
const fixture = createMobilePosFixture({
|
|
ordersById: {
|
|
[DEFAULT_ORDER_ID]: {
|
|
id: DEFAULT_ORDER_ID,
|
|
customer_id: regularCustomerId,
|
|
department_id: 1,
|
|
reference: "REF-9201",
|
|
notes: "",
|
|
reg_1: "AB12345",
|
|
reg_2: "",
|
|
reg_3: "",
|
|
invoice_collection_id: null,
|
|
booking_id: null,
|
|
completed_at: null,
|
|
created_at: "2026-01-01T10:00:00.000Z",
|
|
},
|
|
},
|
|
});
|
|
|
|
await setupMobilePosPage(page, fixture, {
|
|
token: "mobile-non-card-regression-token",
|
|
seedState: {
|
|
customerId: regularCustomerId,
|
|
includePrimaryItem: true,
|
|
},
|
|
route: {
|
|
step: 2,
|
|
orderId: DEFAULT_ORDER_ID,
|
|
customerId: regularCustomerId,
|
|
},
|
|
});
|
|
|
|
await expect(page.getByTestId("pos-mobile-step-2")).toBeVisible({ timeout: 10_000 });
|
|
await page.getByTestId("pos-mobile-next-step").click();
|
|
await expect.poll(() => fixture.requestCounters.orderItemsPost, { timeout: 10_000 }).toBe(1);
|
|
await expect
|
|
.poll(
|
|
() => {
|
|
const currentUrl = new URL(page.url());
|
|
return currentUrl.searchParams.get("step");
|
|
},
|
|
{ timeout: 12_000 }
|
|
)
|
|
.toBe("1");
|
|
|
|
expect(fixture.requestCounters.markAsCompleted).toBe(0);
|
|
await expect(page.getByTestId("pos-mobile-step-3")).toHaveCount(0);
|
|
});
|
|
});
|