#195 and #203 made the cart say a demo order is a demo order. That message is an antd toast lasting about three seconds, after which the cart empties and the card unmounts. Order history is what the customer comes back to when they wonder where their item is, and it said nothing. A demo row was a real row: item name, `$80.00`, status `completed` rendered as a neutral tag because `STATUS_COLORS` has no `completed` key, and `demo` printed raw under a heading reading "Processor". That is not an explanation — a customer has no reason to read `demo` as "this did not happen", and "processor" is not a word they have any reason to know. Two things now say it, for the same reason the cart needed two. The `demo` cell renders as a tag reading "Demo (not charged)", which marks *which* order. A notice above the table, shown only when there is one, says what that means — a tag reading "Demo" still assumes the reader knows what a demo order is, and what they actually want to know is whether to expect a parcel. Nothing changes on the backend: `orders.processor = 'demo'` was already written at checkout and already selected for this page. The row is a real row in a real table and stays visible, because hiding it would be its own kind of lie — the customer did do something, and it did have an effect on the catalogue. Written test-first against a browser: the new case drives a real demo purchase through the cart, opens `/orders`, and failed on both assertions before the change. Verified: 4 end-to-end tests in this spec pass, the 5 existing order-history tests still pass, frontend build clean, lint 0 errors (2 pre-existing warnings in `src/filters.ts`). Closes #205 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
107 lines
4.4 KiB
TypeScript
107 lines
4.4 KiB
TypeScript
import { test, expect, createItem, uniqueSuffix } from './fixtures';
|
|
|
|
/**
|
|
* Demo mode as production actually runs it: `DEMO_MODE=true` with no PayPal
|
|
* credentials configured, which is the interim #191 put in place and the shape
|
|
* this suite already runs in.
|
|
*
|
|
* That combination used to render a full-width primary button reading plainly
|
|
* "Checkout". The "(Demo)" suffix was gated on a PayPal client id being present,
|
|
* so the one configuration that needs the label was the only one that did not
|
|
* get it — and the button is not decorative, it marks real inventory sold and
|
|
* emails everyone who favorited it. See #195.
|
|
*
|
|
* These assert what a customer can see, because that is the whole defect. The
|
|
* server was always right about what it was doing.
|
|
*/
|
|
|
|
const ADDRESS = {
|
|
fullName: 'Dana Holt',
|
|
addressLine1: '118 Cedar Street',
|
|
city: 'Asheville',
|
|
state: 'NC',
|
|
postalCode: '28801',
|
|
isDefault: true
|
|
};
|
|
|
|
test.describe('Demo mode says so to the customer', () => {
|
|
test('labels the checkout button as a demo when no PayPal is configured', async ({
|
|
page,
|
|
customer,
|
|
cart
|
|
}) => {
|
|
const name = `Demo c${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();
|
|
|
|
// The cart selects the default address on load, so the checkout controls
|
|
// render with nothing clicked.
|
|
await expect(cart.checkoutButton).toBeVisible({ timeout: 20000 });
|
|
await expect(cart.checkoutButton).toHaveText('Checkout (Demo)');
|
|
});
|
|
|
|
test('says so before the customer commits, not only on the button', async ({
|
|
page,
|
|
customer,
|
|
cart
|
|
}) => {
|
|
const name = `Demo n${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();
|
|
|
|
// A label on the button alone asks the customer to notice a parenthesis on
|
|
// the thing they are already clicking. The notice is the part that has to
|
|
// survive someone not reading carefully.
|
|
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' });
|
|
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();
|
|
|
|
// "Order complete!" is true and is exactly what a real order would say. The
|
|
// item really is marked sold and favoriters really are emailed, so the one
|
|
// moment the customer is told what happened has to distinguish the two.
|
|
await expect(page.getByText(/nothing was charged/i)).toBeVisible({ timeout: 20000 });
|
|
});
|
|
});
|