* Fix POS step metadata persistence * Preserve registration save payload formatting --------- Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
540 lines
21 KiB
JavaScript
540 lines
21 KiB
JavaScript
import { expect, test } from "@playwright/test";
|
|
import { createPosFixture, mockApi, primeMockSession } from "./support/network.js";
|
|
|
|
const ORDER_ID = 54518;
|
|
const DEPARTMENT_ID = 12;
|
|
const CARD_CUSTOMER_ID = 999;
|
|
const CARD_CUSTOMER_NAME = "Card Terminal Customer";
|
|
const CARD_ORDER_REFERENCE = "CARD-REF-54518";
|
|
const DESKTOP_POS_PERMISSIONS = [
|
|
"admin",
|
|
`department_access_${DEPARTMENT_ID}`,
|
|
"delete_order",
|
|
"edit_order",
|
|
"edit_order_items",
|
|
"get_user",
|
|
"list_customer_notes",
|
|
"list_customer_attributes",
|
|
"get_custom_prices_other",
|
|
];
|
|
const SUPERUSER_POS_PERMISSIONS = [...DESKTOP_POS_PERMISSIONS, "superuser"];
|
|
const SETUP_REQUIRED_MESSAGE =
|
|
"Card payments are not ready for this department. Open Stripe setup and choose a terminal location.";
|
|
|
|
function suppressVueDevtoolsOverlay(page) {
|
|
return page.addInitScript(() => {
|
|
window.__TW_POS_STRIPE_EMAIL_POLLING_INTERVAL_MS__ = 250;
|
|
const injectStyle = () => {
|
|
const root = document.documentElement;
|
|
if (!root) {
|
|
return;
|
|
}
|
|
const style = document.createElement("style");
|
|
style.textContent =
|
|
"#__vue-devtools-container__, .vue-devtools__anchor-btn, .vue-devtools__panel-content { display: none !important; visibility: hidden !important; pointer-events: none !important; }";
|
|
root.appendChild(style);
|
|
};
|
|
if (document.documentElement) {
|
|
injectStyle();
|
|
} else {
|
|
document.addEventListener("DOMContentLoaded", injectStyle, { once: true });
|
|
}
|
|
});
|
|
}
|
|
|
|
function createCardOrderFixture(overrides = {}) {
|
|
return createPosFixture({
|
|
ordersById: {
|
|
[ORDER_ID]: {
|
|
id: ORDER_ID,
|
|
customer_id: CARD_CUSTOMER_ID,
|
|
department_id: DEPARTMENT_ID,
|
|
reference: "CARD-REF-54518",
|
|
po: "",
|
|
safety_seal: "",
|
|
notes: "Paid at terminal",
|
|
reg_1: "ZZ99999",
|
|
reg_2: "",
|
|
reg_3: "",
|
|
invoice_collection_id: 200,
|
|
booking_id: null,
|
|
completed_at: null,
|
|
closed_at: null,
|
|
created_at: "2026-04-08 08:44:07",
|
|
},
|
|
},
|
|
...overrides,
|
|
});
|
|
}
|
|
|
|
function createStoredPaymentIntent(orderId, overrides = {}) {
|
|
const status = overrides.status || "requires_capture";
|
|
const amount = Number(overrides.amount || 124800);
|
|
const isCaptured = status === "succeeded";
|
|
|
|
return {
|
|
id: overrides.id || `pi_${orderId}`,
|
|
amount,
|
|
amount_capturable: isCaptured ? 0 : amount,
|
|
amount_received: isCaptured ? amount : 0,
|
|
currency: overrides.currency || "dkk",
|
|
status,
|
|
metadata: {
|
|
order_id: String(orderId),
|
|
reader_id: String(overrides.readerId || "reader_online_1"),
|
|
tax_percentage: String(overrides.taxPercentage || 25),
|
|
...(overrides.metadata || {}),
|
|
},
|
|
};
|
|
}
|
|
|
|
function createHostedInvoice(orderId, overrides = {}) {
|
|
const amountDue = Number(overrides.amount_due ?? 1562);
|
|
const paid = Boolean(overrides.paid ?? false);
|
|
return {
|
|
id: orderId,
|
|
invoice_id: overrides.invoice_id || `in_${orderId}`,
|
|
customer_id: Number(overrides.customer_id ?? CARD_CUSTOMER_ID),
|
|
url: overrides.url || `https://stripe.example.test/invoices/${orderId}`,
|
|
created_at: overrides.created_at || "2026-04-08 08:44:07",
|
|
paid,
|
|
status: overrides.status || (paid ? "paid" : "open"),
|
|
amount_due: amountDue,
|
|
amount_paid: Number(overrides.amount_paid ?? (paid ? amountDue : 0)),
|
|
};
|
|
}
|
|
|
|
async function primeSession(page, token = "desktop-card-token") {
|
|
await primeMockSession(page, { token });
|
|
}
|
|
|
|
async function bootDesktopCardPayment(page, fixture, permissions = DESKTOP_POS_PERMISSIONS) {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions,
|
|
edgeGateways: false,
|
|
pos: fixture,
|
|
});
|
|
await primeSession(page, `desktop-card-${Date.now()}`);
|
|
await page.goto(`/admin/${DEPARTMENT_ID}/modules/pos?id=${ORDER_ID}&customer_id=${CARD_CUSTOMER_ID}&step=3`);
|
|
const stepThree = page.getByTestId("pos-step-3");
|
|
await expect(stepThree).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-panel-cart")).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-metadata-grid")).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-rail")).toBeVisible({ timeout: 10_000 });
|
|
await expect(
|
|
stepThree
|
|
.locator(
|
|
'[data-testid="pos-stripe-setup-required"], [data-testid="pos-stripe-create-intent"], [data-testid="pos-stripe-capture-intent"], [data-testid="pos-stripe-no-readers"], [data-testid="pos-stripe-payment-in-progress"], [data-testid="pos-stripe-payment-succeeded"], [data-testid="pos-stripe-error"]'
|
|
)
|
|
.first()
|
|
).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-customer-name")).toContainText(CARD_CUSTOMER_NAME, { timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-customer-card")).toContainText(CARD_CUSTOMER_NAME, { timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-customer-wishes-reference")).toContainText(CARD_ORDER_REFERENCE, {
|
|
timeout: 10_000,
|
|
});
|
|
return stepThree;
|
|
}
|
|
|
|
test.describe("POS desktop card payments", () => {
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
test.skip(!testInfo.project.name.includes("desktop"), "Desktop-only POS card payment coverage");
|
|
await suppressVueDevtoolsOverlay(page);
|
|
});
|
|
|
|
test("misconfigured terminal location shows guided blocking state with setup CTA for superusers", async ({
|
|
page,
|
|
}) => {
|
|
const fixture = createCardOrderFixture({
|
|
stripeReadersError: {
|
|
status: 409,
|
|
code: "stripe_terminal_setup_required",
|
|
message: SETUP_REQUIRED_MESSAGE,
|
|
},
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture, SUPERUSER_POS_PERMISSIONS);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-setup-required")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-setup-required")).toContainText(SETUP_REQUIRED_MESSAGE);
|
|
await expect(stepThree.getByTestId("pos-stripe-open-setup")).toHaveAttribute(
|
|
"href",
|
|
`/superuser/departments/${DEPARTMENT_ID}/stripe/setup`
|
|
);
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toHaveCount(0);
|
|
await expect(stepThree.getByTestId("pos-stripe-capture-intent")).toHaveCount(0);
|
|
});
|
|
|
|
test("misconfigured terminal location falls back to contact-superuser guidance for department operators", async ({
|
|
page,
|
|
}) => {
|
|
const fixture = createCardOrderFixture({
|
|
stripeReadersError: {
|
|
status: 409,
|
|
code: "stripe_terminal_setup_required",
|
|
message: SETUP_REQUIRED_MESSAGE,
|
|
},
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-setup-required")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-contact-superuser")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-open-setup")).toHaveCount(0);
|
|
});
|
|
|
|
test("setup-required desktop flow still exposes the inline email card payment action", async ({ page }) => {
|
|
const fixture = createCardOrderFixture({
|
|
stripeReadersError: {
|
|
status: 409,
|
|
code: "stripe_terminal_setup_required",
|
|
message: SETUP_REQUIRED_MESSAGE,
|
|
},
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-setup-required")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-email-action")).toBeVisible();
|
|
|
|
await stepThree.getByTestId("pos-stripe-email-action").click();
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-email-input")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-email-submit")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-setup-required")).toBeVisible();
|
|
});
|
|
|
|
test("configured department with zero readers keeps the unavailable-reader state", async ({ page }) => {
|
|
const fixture = createCardOrderFixture({
|
|
readers: [],
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-no-readers")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-setup-required")).toHaveCount(0);
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toHaveCount(0);
|
|
});
|
|
|
|
test("reloading step 3 keeps the customer summary hydrated", async ({ page }) => {
|
|
const fixture = createCardOrderFixture();
|
|
|
|
await bootDesktopCardPayment(page, fixture);
|
|
|
|
await page.reload();
|
|
|
|
const stepThree = page.getByTestId("pos-step-3");
|
|
await expect(stepThree).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-customer-name")).toContainText(CARD_CUSTOMER_NAME, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect(stepThree.getByTestId("pos-customer-card")).toContainText(CARD_CUSTOMER_NAME, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect(stepThree.getByTestId("pos-order-customer-wishes-reference")).toContainText(CARD_ORDER_REFERENCE, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
test("step 2 customer wishes stay visible after moving to card payment step 3", async ({ page }) => {
|
|
const stepTwoReference = "CARD-STEP-2-REF";
|
|
const stepTwoPo = "PO-CARD-STEP-2";
|
|
const fixture = createCardOrderFixture({
|
|
ordersById: {
|
|
[ORDER_ID]: {
|
|
...createCardOrderFixture().ordersById[ORDER_ID],
|
|
reference: "",
|
|
po: "",
|
|
},
|
|
},
|
|
});
|
|
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: DESKTOP_POS_PERMISSIONS,
|
|
edgeGateways: false,
|
|
pos: fixture,
|
|
});
|
|
await primeSession(page, `desktop-card-step2-${Date.now()}`);
|
|
await page.goto(`/admin/${DEPARTMENT_ID}/modules/pos?id=${ORDER_ID}&customer_id=${CARD_CUSTOMER_ID}&step=2`);
|
|
|
|
const stepTwo = page.getByTestId("pos-step-2");
|
|
await expect(stepTwo).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepTwo.getByTestId("pos-order-metadata-grid")).toBeVisible({ timeout: 10_000 });
|
|
|
|
await stepTwo.getByTestId("pos-order-customer-wishes-reference").click();
|
|
await stepTwo.getByTestId("pos-order-customer-wishes-reference-input").fill(stepTwoReference);
|
|
await stepTwo.getByTestId("pos-order-customer-wishes-po").click();
|
|
await stepTwo.getByTestId("pos-order-customer-wishes-po-input").fill(stepTwoPo);
|
|
await stepTwo.getByTestId("pos-next-step").click();
|
|
|
|
const stepThree = page.getByTestId("pos-step-3");
|
|
await expect(stepThree).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-order-customer-wishes-reference")).toContainText(stepTwoReference, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect(stepThree.getByTestId("pos-order-customer-wishes-po")).toContainText(stepTwoPo, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect.poll(() => fixture.ordersById[ORDER_ID].reference, { timeout: 10_000 }).toBe(stepTwoReference);
|
|
await expect.poll(() => fixture.ordersById[ORDER_ID].po, { timeout: 10_000 }).toBe(stepTwoPo);
|
|
|
|
await page.reload();
|
|
|
|
const reloadedStepThree = page.getByTestId("pos-step-3");
|
|
await expect(reloadedStepThree).toBeVisible({ timeout: 10_000 });
|
|
await expect(reloadedStepThree.getByTestId("pos-order-customer-wishes-reference")).toContainText(stepTwoReference, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect(reloadedStepThree.getByTestId("pos-order-customer-wishes-po")).toContainText(stepTwoPo, {
|
|
timeout: 10_000,
|
|
});
|
|
});
|
|
|
|
test("idle state renders grouped terminal statuses and only keeps ready terminals selectable", async ({ page }) => {
|
|
const fixture = createCardOrderFixture({
|
|
readers: [
|
|
{
|
|
id: "reader_ready",
|
|
label: "Aabenraa",
|
|
status: "online",
|
|
action: null,
|
|
},
|
|
{
|
|
id: "reader_in_use",
|
|
label: "Busy reader",
|
|
status: "online",
|
|
action: { type: "process_payment_intent" },
|
|
},
|
|
{
|
|
id: "reader_offline",
|
|
label: "Offline reader",
|
|
status: "offline",
|
|
action: null,
|
|
},
|
|
],
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toContainText("Betal med Stripe");
|
|
const desktopLayout = stepThree.getByTestId("pos-stripe-desktop-layout");
|
|
await expect(desktopLayout).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-trigger-label")).toContainText("Aabenraa");
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-trigger-dot")).toHaveAttribute("data-status-key", "ready");
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-trigger-dot")).toHaveCSS(
|
|
"background-color",
|
|
"rgb(22, 163, 74)"
|
|
);
|
|
const taxSelect = stepThree.getByTestId("pos-stripe-tax-select");
|
|
await expect(taxSelect).toHaveValue("1");
|
|
await expect(taxSelect).not.toContainText("pos.stripe");
|
|
await expect(stepThree).not.toContainText("pos.pay_with_stripe");
|
|
|
|
const taxField = stepThree.getByTestId("pos-stripe-desktop-tax-field");
|
|
const readerField = stepThree.getByTestId("pos-stripe-desktop-reader-field");
|
|
const actionField = stepThree.getByTestId("pos-stripe-desktop-action-field");
|
|
const [taxBox, readerBox, actionBox] = await Promise.all([
|
|
taxField.boundingBox(),
|
|
readerField.boundingBox(),
|
|
actionField.boundingBox(),
|
|
]);
|
|
|
|
expect(taxBox).not.toBeNull();
|
|
expect(readerBox).not.toBeNull();
|
|
expect(actionBox).not.toBeNull();
|
|
expect(taxBox.y).toBeLessThan(readerBox.y);
|
|
expect(readerBox.y).toBeLessThan(actionBox.y);
|
|
expect(Math.abs(taxBox.width - readerBox.width)).toBeLessThan(2);
|
|
expect(Math.abs(readerBox.width - actionBox.width)).toBeLessThan(2);
|
|
expect(Math.abs(taxBox.x - readerBox.x)).toBeLessThan(2);
|
|
expect(Math.abs(readerBox.x - actionBox.x)).toBeLessThan(2);
|
|
|
|
await stepThree.getByTestId("pos-stripe-terminal-trigger").click();
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-menu")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-group-ready")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-group-in_use")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-group-offline")).toBeVisible();
|
|
|
|
const optionOrder = await stepThree
|
|
.locator('[role="option"][data-testid^="pos-stripe-terminal-option-"]')
|
|
.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("data-testid")));
|
|
expect(optionOrder).toEqual([
|
|
"pos-stripe-terminal-option-reader_ready",
|
|
"pos-stripe-terminal-option-reader_in_use",
|
|
"pos-stripe-terminal-option-reader_offline",
|
|
]);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-option-reader_ready")).toHaveAttribute(
|
|
"aria-disabled",
|
|
"false"
|
|
);
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-option-reader_in_use")).toHaveAttribute(
|
|
"aria-disabled",
|
|
"true"
|
|
);
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-option-reader_offline")).toHaveAttribute(
|
|
"aria-disabled",
|
|
"true"
|
|
);
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-option-dot-reader_in_use")).toHaveCSS(
|
|
"background-color",
|
|
"rgb(245, 158, 11)"
|
|
);
|
|
await expect(stepThree.getByTestId("pos-stripe-terminal-option-dot-reader_offline")).toHaveCSS(
|
|
"background-color",
|
|
"rgb(220, 38, 38)"
|
|
);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toBeEnabled();
|
|
});
|
|
|
|
test("existing requires_capture intent on reload restores the capture action", async ({ page }) => {
|
|
const fixture = createCardOrderFixture({
|
|
paymentIntentsByOrderId: {
|
|
[ORDER_ID]: createStoredPaymentIntent(ORDER_ID, {
|
|
status: "requires_capture",
|
|
}),
|
|
},
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-capture-intent")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toHaveCount(0);
|
|
});
|
|
|
|
test("existing succeeded intent on reload restores the completion CTA", async ({ page }) => {
|
|
const fixture = createCardOrderFixture({
|
|
paymentIntentsByOrderId: {
|
|
[ORDER_ID]: createStoredPaymentIntent(ORDER_ID, {
|
|
status: "succeeded",
|
|
}),
|
|
},
|
|
});
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-payment-succeeded")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-next-step")).toBeVisible();
|
|
expect(fixture.ordersById[ORDER_ID].completed_at).toBeNull();
|
|
});
|
|
|
|
test("happy path create and capture exposes completion without auto-completing the order", async ({ page }) => {
|
|
const fixture = createCardOrderFixture();
|
|
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-create-intent")).toBeVisible();
|
|
await stepThree.getByTestId("pos-stripe-create-intent").click();
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-capture-intent")).toBeVisible({ timeout: 10_000 });
|
|
expect(fixture.ordersById[ORDER_ID].completed_at).toBeNull();
|
|
|
|
await stepThree.getByTestId("pos-stripe-capture-intent").click();
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-payment-succeeded")).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-next-step")).toBeVisible({ timeout: 10_000 });
|
|
await page.waitForTimeout(750);
|
|
expect(fixture.ordersById[ORDER_ID].completed_at).toBeNull();
|
|
});
|
|
|
|
test("email card payment shows inline status and updates after polling", async ({ page }) => {
|
|
const fixture = createCardOrderFixture();
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
const paidOrderPoll = new Promise((resolve, reject) => {
|
|
const timeoutId = setTimeout(() => {
|
|
page.off("response", handleResponse);
|
|
reject(new Error("Timed out waiting for the paid Stripe order poll response."));
|
|
}, 10_000);
|
|
|
|
const handleResponse = async (response) => {
|
|
if (response.request().method() !== "GET") {
|
|
return;
|
|
}
|
|
|
|
const url = new URL(response.url());
|
|
if (!url.pathname.endsWith("/order")) {
|
|
return;
|
|
}
|
|
if (url.searchParams.get("id") !== String(ORDER_ID) || url.searchParams.get("include_all") !== "true") {
|
|
return;
|
|
}
|
|
|
|
const payload = await response.json().catch(() => null);
|
|
const stripeModuleOrders =
|
|
payload?.data?.includes?.stripeModuleOrders ||
|
|
payload?.includes?.stripeModuleOrders ||
|
|
payload?.data?.stripeModuleOrders ||
|
|
{};
|
|
|
|
if (String(stripeModuleOrders?.status || "") !== "paid") {
|
|
return;
|
|
}
|
|
|
|
clearTimeout(timeoutId);
|
|
page.off("response", handleResponse);
|
|
resolve(stripeModuleOrders);
|
|
};
|
|
|
|
page.on("response", handleResponse);
|
|
});
|
|
|
|
await stepThree.getByTestId("pos-stripe-email-action").click();
|
|
await stepThree.getByTestId("pos-stripe-email-input").fill("card-email@example.com");
|
|
await stepThree.getByTestId("pos-stripe-email-submit").click();
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-email-status")).toHaveText(/Afventer betaling|Awaiting payment/);
|
|
|
|
fixture.stripeModuleOrdersByOrderId[ORDER_ID] = createHostedInvoice(ORDER_ID, {
|
|
...fixture.stripeModuleOrdersByOrderId[ORDER_ID],
|
|
status: "paid",
|
|
paid: true,
|
|
amount_due: 0,
|
|
amount_paid: 1562,
|
|
});
|
|
|
|
await paidOrderPoll;
|
|
await expect(stepThree.getByTestId("pos-stripe-email-status")).toHaveText(/Betalt|Paid/, { timeout: 1_000 });
|
|
await expect(stepThree.getByTestId("pos-next-step")).toBeVisible({ timeout: 1_000 });
|
|
});
|
|
|
|
test("email card payment can be cancelled before submit and returns to the idle action", async ({ page }) => {
|
|
const fixture = createCardOrderFixture();
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await stepThree.getByTestId("pos-stripe-email-action").click();
|
|
await expect(stepThree.getByTestId("pos-stripe-email-input")).toBeVisible();
|
|
|
|
await stepThree.getByTestId("pos-stripe-email-cancel").click();
|
|
|
|
await expect(stepThree.getByTestId("pos-stripe-email-action")).toBeVisible();
|
|
await expect(stepThree.getByTestId("pos-stripe-email-input")).toHaveCount(0);
|
|
});
|
|
|
|
test("email card payment cancel clears the hosted invoice selection and restores the idle action", async ({
|
|
page,
|
|
}) => {
|
|
const fixture = createCardOrderFixture();
|
|
const stepThree = await bootDesktopCardPayment(page, fixture);
|
|
|
|
await stepThree.getByTestId("pos-stripe-email-action").click();
|
|
await stepThree.getByTestId("pos-stripe-email-input").fill("cancel-email@example.com");
|
|
await stepThree.getByTestId("pos-stripe-email-submit").click();
|
|
|
|
await expect
|
|
.poll(() => fixture.stripeModuleOrdersByOrderId[ORDER_ID]?.invoice_id || null, { timeout: 10_000 })
|
|
.not.toBeNull();
|
|
|
|
await stepThree.getByTestId("pos-stripe-email-cancel").click();
|
|
|
|
await expect
|
|
.poll(() => Object.keys(fixture.stripeModuleOrdersByOrderId[ORDER_ID] || {}).length, { timeout: 10_000 })
|
|
.toBe(0);
|
|
await expect(stepThree.getByTestId("pos-stripe-email-action")).toBeVisible({ timeout: 10_000 });
|
|
await expect(stepThree.getByTestId("pos-stripe-email-status")).toHaveCount(0);
|
|
});
|
|
});
|