diff --git a/frontend/src/cart/Cart.tsx b/frontend/src/cart/Cart.tsx index ae85d12..f1be4f1 100644 --- a/frontend/src/cart/Cart.tsx +++ b/frontend/src/cart/Cart.tsx @@ -244,14 +244,24 @@ export default function Cart() { and whether or not PayPal is configured. The word on the button is the smaller half of this — it asks the customer to notice a parenthesis on the control they have already decided to press. + + It describes the button rather than the shop, and that is the + point rather than a phrasing preference. demoMode and + paypalClientId are independent — checkPayPal only *requires* + credentials when DEMO_MODE=false, it never forbids them when it + is true — and the documented cutover order is to populate the + PayPal secrets while demo mode is still on, then flip. In that + window live PayPal buttons render directly below this notice, so + anything claiming the shop is not taking payments would be false + exactly where a customer can be charged. See #203. */} {config?.demoMode && ( )} {!selectedAddressId && Select a shipping address to check out.} diff --git a/frontend/tests/e2e/demo-checkout.spec.ts b/frontend/tests/e2e/demo-checkout.spec.ts index 70ab312..d8ef98f 100644 --- a/frontend/tests/e2e/demo-checkout.spec.ts +++ b/frontend/tests/e2e/demo-checkout.spec.ts @@ -51,14 +51,19 @@ test.describe('Demo mode says so to the customer', () => { 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); + // Deliberately NO address. The cart auto-selects the default one on load, + // so seeding an address here would mean the button is already rendered and + // this would pass even with the notice moved inside the selectedAddressId + // guard — which is the regression it exists to catch. #203. 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. + // survive someone not reading carefully, so it has to be up before there is + // anything to press. await expect(cart.demoNotice).toBeVisible({ timeout: 20000 }); + await expect(cart.checkoutButton).toHaveCount(0); }); // The toast is three seconds; this is the record the customer comes back to diff --git a/frontend/tests/e2e/pages/CartPage.ts b/frontend/tests/e2e/pages/CartPage.ts index d4ceaa2..0bf77e6 100644 --- a/frontend/tests/e2e/pages/CartPage.ts +++ b/frontend/tests/e2e/pages/CartPage.ts @@ -21,7 +21,7 @@ export class CartPage { // That is the point of #195: the label was wrong, and a locator naming it in // full would have gone looking for the right button and found nothing. this.checkoutButton = page.getByRole('button', { name: /^Checkout/ }); - this.demoNotice = page.getByText('Demonstration only'); + this.demoNotice = page.getByText('Demo checkout'); } async goto(): Promise {