diff --git a/frontend/src/customer/Orders.tsx b/frontend/src/customer/Orders.tsx index 1bd22ea..b70774c 100644 --- a/frontend/src/customer/Orders.tsx +++ b/frontend/src/customer/Orders.tsx @@ -38,7 +38,16 @@ const COLUMNS = [ dataIndex: 'status', render: (v: string) => {v} }, - { title: 'Processor', dataIndex: 'processor' }, + { + title: 'Processor', + dataIndex: 'processor', + // `demo` used to render as a raw column value, which is not an explanation: + // a customer has no reason to read it as "this did not happen", and the row + // was otherwise identical to a real one — real price, `completed` status, + // same neutral tag. The cart says it is a demo (#195, #203) for about three + // seconds; this is the record they come back to. See #205. + render: (v: string) => (v === 'demo' ? Demo (not charged) : v) + }, { title: 'Date', dataIndex: 'created_at', render: (v: string) => new Date(v).toLocaleDateString() } ]; @@ -69,6 +78,12 @@ function OrdersBody({ loading, error, orders, onRetry }: BodyProps) { ); } + // Shown only when there is one to explain. The per-row tag says which order, + // this says what it means — a tag reading "Demo" still assumes the reader + // knows what a demo order is, and the thing they actually want to know is + // whether to expect a parcel. + const hasDemoOrder = orders.some(o => o.processor === 'demo'); + if (orders.length === 0) { return ( @@ -79,15 +94,26 @@ function OrdersBody({ loading, error, orders, onRetry }: BodyProps) { } return ( - + <> + {hasDemoOrder && ( + + )} +
+ ); } diff --git a/frontend/tests/e2e/demo-checkout.spec.ts b/frontend/tests/e2e/demo-checkout.spec.ts index 333d398..70ab312 100644 --- a/frontend/tests/e2e/demo-checkout.spec.ts +++ b/frontend/tests/e2e/demo-checkout.spec.ts @@ -61,6 +61,32 @@ test.describe('Demo mode says so to the customer', () => { await expect(cart.demoNotice).toBeVisible({ timeout: 20000 }); }); + // The toast is three seconds; this is the record the customer comes back to + // when they wonder where their item is. It showed `demo` as a raw value under + // a "Processor" heading, beside a real price and a neutral `completed` tag — + // nothing a customer would read as "this did not happen". See #205. + test('order history marks the demo order rather than showing it as a real one', async ({ + page, + customer, + cart, + orders + }) => { + const name = `Demo h${uniqueSuffix()}`; + const item = await createItem(page.request, { name, price: '80' }); + expect((await page.request.post(`/api/cart/items/${item.id}`)).status()).toBe(201); + expect((await page.request.post('/api/customers/me/addresses', { data: ADDRESS })).ok()).toBe(true); + + await cart.goto(); + await expect(cart.checkoutButton).toBeVisible({ timeout: 20000 }); + await cart.checkoutButton.click(); + await expect(page.getByText(/nothing was charged/i)).toBeVisible({ timeout: 20000 }); + + await orders.goto(); + + await expect(orders.demoOrderMarker).toBeVisible({ timeout: 20000 }); + await expect(orders.demoNotice).toBeVisible(); + }); + test('the confirmation says nothing was charged', async ({ page, customer, cart }) => { const name = `Demo p${uniqueSuffix()}`; const item = await createItem(page.request, { name, price: '80' }); diff --git a/frontend/tests/e2e/pages/OrdersPage.ts b/frontend/tests/e2e/pages/OrdersPage.ts index b13357e..cc49341 100644 --- a/frontend/tests/e2e/pages/OrdersPage.ts +++ b/frontend/tests/e2e/pages/OrdersPage.ts @@ -14,6 +14,8 @@ export class OrdersPage { readonly continueShoppingButton: Locator; readonly backToShopButton: Locator; readonly anyDialog: Locator; + readonly demoNotice: Locator; + readonly demoOrderMarker: Locator; constructor(private readonly page: Page) { this.heading = page.getByRole('heading', { name: 'Order History' }); @@ -21,6 +23,8 @@ export class OrdersPage { this.continueShoppingButton = page.getByRole('button', { name: 'Continue Shopping' }); this.backToShopButton = page.getByRole('button', { name: 'Back to Shop' }); this.anyDialog = page.getByRole('dialog'); + this.demoNotice = page.getByText('Some of these are demo orders'); + this.demoOrderMarker = page.getByText('Demo (not charged)'); } async goto(): Promise {