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 333d398..a638341 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);
});
test('the confirmation says nothing was charged', async ({ page, customer, cart }) => {
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 {