Skip to content

Commit 9f3cadd

Browse files
committed
Add e2e testing plan and link from README
1 parent 2a9ce45 commit 9f3cadd

1 file changed

Lines changed: 259 additions & 0 deletions

File tree

docs/e2e-testing.md

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
# End-to-End Testing Plan
2+
3+
## Overview
4+
5+
The CK Join Form plugin ships with a Playwright end-to-end test suite that exercises the React join flow inside a real WordPress environment. Tests run against a wp-env (Docker WordPress) instance seeded with purpose-built test pages.
6+
7+
### Test approach
8+
9+
The suite validates the join form's UI behaviour and the data contracts it sends to the backend. It does not exercise live payment provider APIs. Instead, it uses helpers that simulate the outcome of payment flows (redirect-based success) and captures the `/join` request body to assert correctness.
10+
11+
This is a deliberate trade-off: payment provider SDKs require live credentials, load scripts from CDNs, and introduce external redirect flows that are fragile in CI. By mocking the REST layer and simulating payment completion, the tests stay fast, deterministic, and credential-free while still covering the most important contract: what the frontend sends to the backend.
12+
13+
### Infrastructure
14+
15+
| Component | Location | Purpose |
16+
|-----------|----------|---------|
17+
| Test suite | `packages/join-e2e/tests/` | Playwright spec files |
18+
| Helpers | `packages/join-e2e/tests/helpers.ts` | Shared utilities for mocking, env injection, and payment simulation |
19+
| Seed script | `packages/join-e2e/scripts/setup.php` | Creates test pages with specific block configurations in WordPress |
20+
| Playwright config | `packages/join-e2e/playwright.config.ts` | Single Chromium project, serial execution, base URL `localhost:8889` |
21+
22+
### Key helpers
23+
24+
**`mockRestEndpoints(page)`** intercepts `/wp-json/join/v1/step` and `/wp-json/join/v1/join` with empty 200 responses so network errors never block form progression.
25+
26+
**`injectEnvOverrides(page, urlPattern, overrides)`** intercepts the page HTML response and patches the `<script id="env">` JSON block before the browser receives it. This lets tests toggle feature flags (e.g. `USE_STRIPE`, `STRIPE_DIRECT_DEBIT_ONLY`) without changing WordPress plugin settings.
27+
28+
**`captureJoinBodyViaStripeRedirect(page, pageUrl)`** simulates a completed Stripe payment by injecting a fake `stripePaymentIntentId` into `sessionStorage` and reloading with `?stripe_success=true`. The app detects this and jumps to the confirm stage. The helper intercepts the resulting `/join` POST and returns its body for assertions.
29+
30+
### Test data
31+
32+
Building with `USE_TEST_DATA=true` pre-fills all personal detail fields (name, email, phone, address). This lets tests skip manual field entry when the details page is not the focus.
33+
34+
### Seed pages
35+
36+
The setup script (`scripts/setup.php`) creates these WordPress pages:
37+
38+
| Slug | Configuration |
39+
|------|---------------|
40+
| `e2e-standard-join` | Standard membership, GBP 5/month |
41+
| `e2e-free-join` | Free membership, GBP 0/month |
42+
| `e2e-donation-upsell` | Standard membership with `ask_for_additional_donation` enabled |
43+
| `e2e-supporter` | Supporter mode with 3 tiers (GBP 5, 10, 20/month) |
44+
| `e2e-supporter-custom` | Supporter mode with custom amount allowed |
45+
| `e2e-supporter-no-plans` | Supporter mode with no plans (triggers warning UI) |
46+
47+
48+
## What is covered
49+
50+
The suite currently contains 52 passing tests across 7 spec files.
51+
52+
| Spec file | Area | Tests | What it verifies |
53+
|-----------|------|------:|------------------|
54+
| `01-render.spec.ts` | Rendering | 3 | Form container present, env script injected, first name input visible, no console errors, progress breadcrumb renders |
55+
| `02-form-progression.spec.ts` | Form progression | 7 | Test data pre-fill, required field validation (name, email, phone, address), email format validation, country defaults to GB, details-to-plan advancement, plan selection, `/step` request body |
56+
| `03-free-membership.spec.ts` | Free membership | 4 | Payment stages skipped for GBP 0 plans, confirm stage reached from plan, confirm page content, `/join` request body |
57+
| `04-donation-upsell.spec.ts` | Donation upsell | 6 | Upsell page appears when enabled, "Not right now" skips to payment, tier selection advances, `recurDonation` flag toggling, supporter mode takes precedence over upsell |
58+
| `05-supporter-mode-monthly.spec.ts` | Supporter monthly | 9 | Donation page is first step, breadcrumb text, monthly toggle default, tier selection updates CTA text, donation-to-details progression, `/join` body fields, custom amount input |
59+
| `06-supporter-mode-oneoff.spec.ts` | Supporter one-off | 6 | One-off tab enabled under correct env flags, CTA text changes, `/join` body contains `recurDonation=false`, `paymentMethod=creditCard` forced, custom one-off amounts |
60+
| `07-supporter-mode-edge-cases.spec.ts` | Edge cases | 7 | One-off disabled under `STRIPE_DIRECT_DEBIT_ONLY`, explanatory note shown, monthly still works with DD-only, "no amounts configured" warning, standard vs supporter `/join` body differences |
61+
62+
### Coverage strengths
63+
64+
- **Form navigation logic** is well covered across standard, free, supporter, and upsell flows.
65+
- **Data contracts** (`/step` and `/join` request bodies) are asserted for all major flows.
66+
- **Feature flag behaviour** is tested by injecting env overrides, covering combinations of `USE_STRIPE`, `STRIPE_DIRECT_DEBIT`, `STRIPE_DIRECT_DEBIT_ONLY`, and `ASK_FOR_ADDITIONAL_DONATION`.
67+
- **Validation** for required fields and email format is exercised.
68+
69+
70+
## What is not covered
71+
72+
### Payment UI rendering
73+
74+
The `captureJoinBodyViaStripeRedirect` helper bypasses the payment stage entirely. If Stripe Elements stopped rendering, the payment method selector broke, or the intent creation API call failed, the current tests would not detect it.
75+
76+
### GoCardless flow
77+
78+
Zero coverage. GoCardless is the primary payment method for Direct Debit members. The hosted redirect flow, API flow, and mandate creation are all untested. There is no equivalent of `captureJoinBodyViaStripeRedirect` for GoCardless.
79+
80+
### Chargebee flow
81+
82+
Zero coverage. Neither the hosted pages flow nor the API flow is tested.
83+
84+
### Update flow (`IS_UPDATE_FLOW=true`)
85+
86+
No tests. When `IS_UPDATE_FLOW=true`, personal details are skipped and the form starts at the plan stage, with the member's email pre-filled via URL parameter. This distinct entry point has no coverage.
87+
88+
### Payment method selection page
89+
90+
The `payment-method` step (choosing between Direct Debit and Card when multiple providers are enabled) is never exercised. Tests either mock everything or bypass payment.
91+
92+
### Optional data collection flags
93+
94+
The following flags control whether additional fields appear on the details page, but none are tested:
95+
96+
- `COLLECT_DATE_OF_BIRTH`
97+
- `COLLECT_COUNTY`
98+
- `COLLECT_HEAR_ABOUT_US`
99+
- `COLLECT_PHONE_AND_EMAIL_CONTACT_CONSENT`
100+
101+
### Custom fields
102+
103+
`CUSTOM_FIELDS` allows arbitrary text, checkbox, number, select, and radio fields on the details page. Not tested.
104+
105+
### Address lookup
106+
107+
`USE_POSTCODE_LOOKUP` enables a postcode-to-address autocomplete. Not tested.
108+
109+
### `/join` response handling
110+
111+
Tests verify what is sent to `/join` but not what happens with the response. Success redirects, error message rendering, and retry behaviour are all unverified.
112+
113+
### Success redirect
114+
115+
The `SUCCESS_REDIRECT` / `joined_page` block setting controls post-join navigation. Not tested.
116+
117+
### Error states
118+
119+
Network errors, server validation errors, Stripe card declines, and GoCardless mandate failures have no coverage.
120+
121+
### Skip payment button
122+
123+
`INCLUDE_SKIP_PAYMENT_BUTTON` renders a "skip to thank you" button. Not tested.
124+
125+
### Backend integrations (out of scope)
126+
127+
Mailchimp, Action Network, Zetkin, Auth0, and lapsing/unlapsing webhooks are backend concerns. Mailchimp is covered by PHPUnit (`JoinServiceMailchimpTest.php`). These are not appropriate for frontend e2e tests.
128+
129+
130+
## Future coverage workstreams
131+
132+
Ordered roughly by value and feasibility. Each item is designed to be tackled independently.
133+
134+
### 1. Update flow
135+
136+
**Value:** High. This is a distinct user-facing flow with no coverage at all.
137+
138+
**What is needed:**
139+
- [ ] Seed a new test page (`e2e-update-flow`) via `setup.php`
140+
- [ ] Use `injectEnvOverrides` to set `IS_UPDATE_FLOW=true`
141+
- [ ] Pass an `email` URL parameter to pre-fill the member email
142+
- [ ] Verify the form starts at the plan step (details page skipped)
143+
- [ ] Verify the pre-filled email appears on the confirm page
144+
- [ ] Assert the `/join` request body includes the URL-provided email
145+
146+
### 2. Optional data collection fields
147+
148+
**Value:** Medium. These are simple show/hide toggles but affect data sent to the backend.
149+
150+
**What is needed:**
151+
- [ ] Use `injectEnvOverrides` to enable each flag individually
152+
- [ ] Verify the corresponding input fields appear on the details page
153+
- [ ] Fill in values and confirm they are included in the `/step` request body
154+
- [ ] Verify fields are absent when the flag is disabled
155+
156+
Flags to cover: `COLLECT_DATE_OF_BIRTH`, `COLLECT_COUNTY`, `COLLECT_HEAR_ABOUT_US`, `COLLECT_PHONE_AND_EMAIL_CONTACT_CONSENT`.
157+
158+
### 3. Payment method selection page
159+
160+
**Value:** Medium. This is the gate between choosing Direct Debit vs Card when multiple providers are active.
161+
162+
**What is needed:**
163+
- [ ] Use `injectEnvOverrides` to enable both `USE_GOCARDLESS=true` and `USE_STRIPE=true`
164+
- [ ] Verify the payment-method step appears between donation/plan and payment-details
165+
- [ ] Verify selecting "Direct Debit" and "Card" each advance to the correct next stage
166+
- [ ] Assert the selected `paymentMethod` value is stored in session state
167+
168+
### 4. GoCardless redirect simulation
169+
170+
**Value:** High, but requires more infrastructure.
171+
172+
**What is needed:**
173+
- [ ] Write a `captureJoinBodyViaGoCardlessRedirect` helper that injects a fake `billing_request_id` into session state and reloads with `?gocardless_success=true`
174+
- [ ] Verify the confirm page renders and the `/join` request body includes GoCardless-specific fields
175+
- [ ] Determine the exact query parameters and session keys the app expects on GoCardless return (check the `useGoCardless` hook or equivalent)
176+
177+
### 5. Chargebee redirect simulation
178+
179+
**Value:** Medium. Similar pattern to GoCardless.
180+
181+
**What is needed:**
182+
- [ ] Write a `captureJoinBodyViaChargebeeRedirect` helper that simulates the Chargebee hosted page return
183+
- [ ] Determine the expected query parameters on return (`?chargebee_success=true` or similar)
184+
- [ ] Use `injectEnvOverrides` to set `USE_CHARGEBEE=true` (and optionally `USE_CHARGEBEE_HOSTED_PAGES=true`)
185+
- [ ] Assert `/join` body includes Chargebee-specific fields
186+
187+
### 6. `/join` response handling and error states
188+
189+
**Value:** High for user experience confidence. Moderate implementation effort.
190+
191+
**What is needed:**
192+
- [ ] Mock `/join` to return an error response (e.g. `{ success: false, message: "Email already registered" }`)
193+
- [ ] Verify the error message is displayed to the user
194+
- [ ] Mock `/join` to return a network error (use `route.abort()`)
195+
- [ ] Verify appropriate error UI is shown
196+
- [ ] Mock `/join` to return success and verify redirect to `SUCCESS_REDIRECT` URL
197+
198+
### 7. Success redirect
199+
200+
**Value:** Medium. Validates the post-join experience.
201+
202+
**What is needed:**
203+
- [ ] Seed a test page with a `joined_page` / `SUCCESS_REDIRECT` pointing to a known URL
204+
- [ ] Complete a flow (e.g. free membership) and verify `page.url()` matches the redirect target
205+
- [ ] Test with and without a configured redirect
206+
207+
### 8. Custom fields
208+
209+
**Value:** Low-medium. Affects organisations that configure custom fields.
210+
211+
**What is needed:**
212+
- [ ] Use `injectEnvOverrides` to inject a `CUSTOM_FIELDS` configuration with at least one of each type: text, checkbox, number, select, radio
213+
- [ ] Verify each field renders on the details page
214+
- [ ] Fill in values and verify they appear in the `/step` or `/join` request body
215+
- [ ] Verify validation (e.g. required custom fields block progression)
216+
217+
### 9. Address lookup
218+
219+
**Value:** Low. Nice to have, but the feature depends on a third-party postcode API.
220+
221+
**What is needed:**
222+
- [ ] Mock the postcode lookup API endpoint
223+
- [ ] Use `injectEnvOverrides` to set `USE_POSTCODE_LOOKUP=true`
224+
- [ ] Type a postcode and verify the autocomplete dropdown appears with mocked results
225+
- [ ] Select an address and verify the address fields are populated
226+
227+
### 10. Skip payment button
228+
229+
**Value:** Low. Single feature flag toggle.
230+
231+
**What is needed:**
232+
- [ ] Use `injectEnvOverrides` to set `INCLUDE_SKIP_PAYMENT_BUTTON=true`
233+
- [ ] Verify the skip button appears on the payment page
234+
- [ ] Click it and verify the user reaches the confirm page
235+
- [ ] Assert the `/join` body reflects that payment was skipped
236+
237+
238+
## Running the tests
239+
240+
```bash
241+
# Start the wp-env environment
242+
yarn wp-env start
243+
244+
# Seed the test pages
245+
yarn wp-env run tests-cli wp eval-file /var/www/html/wp-content/e2e-scripts/setup.php
246+
247+
# Build the frontend with test data pre-fill
248+
USE_TEST_DATA=true yarn build
249+
250+
# Run the suite
251+
cd packages/join-e2e
252+
npx playwright test
253+
254+
# Run a specific spec
255+
npx playwright test tests/03-free-membership.spec.ts
256+
257+
# View the HTML report after a run
258+
npx playwright show-report
259+
```

0 commit comments

Comments
 (0)