Merge pull request 'fix(cart): say what the demo button does rather than what the shop does (#203)' (#214) from feature/203-demo-notice-wording into main
Linting / lint (push) Successful in 2m3s
SonarQube Analysis / sonarqube (push) Failing after 17m56s

Reviewed-on: #214
This commit was merged in pull request #214.
This commit is contained in:
2026-08-28 14:05:21 -05:00
3 changed files with 20 additions and 5 deletions
+12 -2
View File
@@ -244,14 +244,24 @@ export default function Cart() {
and whether or not PayPal is configured. The word on the button 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 is the smaller half of this — it asks the customer to notice a
parenthesis on the control they have already decided to press. 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 && ( {config?.demoMode && (
<Alert <Alert
type="warning" type="warning"
showIcon showIcon
style={{ marginBottom: 12 }} style={{ marginBottom: 12 }}
message="Demonstration only" message="Demo checkout"
description="This shop is not taking payments at the moment. Placing an order here costs nothing, and nothing will be shipped." description="The Checkout (Demo) button places a pretend order: nothing is charged, and nothing will be shipped."
/> />
)} )}
{!selectedAddressId && <Text type="warning">Select a shipping address to check out.</Text>} {!selectedAddressId && <Text type="warning">Select a shipping address to check out.</Text>}
+7 -2
View File
@@ -51,14 +51,19 @@ test.describe('Demo mode says so to the customer', () => {
const name = `Demo n${uniqueSuffix()}`; const name = `Demo n${uniqueSuffix()}`;
const item = await createItem(page.request, { name, price: '80' }); 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/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(); await cart.goto();
// A label on the button alone asks the customer to notice a parenthesis on // 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 // 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.demoNotice).toBeVisible({ timeout: 20000 });
await expect(cart.checkoutButton).toHaveCount(0);
}); });
// The toast is three seconds; this is the record the customer comes back to // The toast is three seconds; this is the record the customer comes back to
+1 -1
View File
@@ -21,7 +21,7 @@ export class CartPage {
// That is the point of #195: the label was wrong, and a locator naming it in // 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. // full would have gone looking for the right button and found nothing.
this.checkoutButton = page.getByRole('button', { name: /^Checkout/ }); this.checkoutButton = page.getByRole('button', { name: /^Checkout/ });
this.demoNotice = page.getByText('Demonstration only'); this.demoNotice = page.getByText('Demo checkout');
} }
async goto(): Promise<void> { async goto(): Promise<void> {