test(e2e): donation flow tests — CI UI suite + DEV payment suite#150
Open
mlehotskylf wants to merge 4 commits into
Open
test(e2e): donation flow tests — CI UI suite + DEV payment suite#150mlehotskylf wants to merge 4 commits into
mlehotskylf wants to merge 4 commits into
Conversation
…t tests Adds a Stripe card helper fixture and a two-tier E2E donation test suite: CI suite (runs in every PR against the local stack): - Donate button visibility on published initiative - Drawer opens on click - Amount step: Continue disabled until amount selected - Contact step: Continue to Payment disabled until name + email filled - Cancel closes the drawer DEV payment suite (tagged @dev, skipped in CI): - Valid card → thank-you screen - Expired card → "Your card is expired" error - Declined card → decline error message - 3DS card → approve → thank-you screen - 3DS card → fail → authentication error The @dev suite runs against crowdfunding.dev.lfx.dev using a dedicated test initiative (test-html-text) that has a real Stripe test-mode product. Trigger with: E2E_BASE_URL=https://crowdfunding.dev.lfx.dev pnpm test:e2e --grep "@dev" New files: - frontend/e2e/fixtures/stripe.ts — fillStripeCard(), complete3DS(), STRIPE_CARDS constants - frontend/e2e/fixtures/seed.ts — adds DEV_PAYMENT_INITIATIVE_SLUG - frontend/playwright.config.ts — documents DEV mode usage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Michal Lehotsky <mlehotsky@linuxfoundation.org>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends the frontend Playwright E2E coverage for donation flows by splitting tests into (1) a CI-safe UI-only suite that runs against the local stack and (2) a DEV-only payment suite (tagged @dev) that runs real Stripe test-mode card scenarios against crowdfunding.dev.lfx.dev.
Changes:
- Expanded
donate.spec.tsto cover drawer UI behavior in CI and full card-payment flows in DEV (@dev). - Added Stripe-specific Playwright helpers (
fillStripeCard,complete3DS, and card constants). - Documented how to run DEV payment tests and added a dedicated DEV initiative slug constant.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| frontend/playwright.config.ts | Adds comments documenting how to run the @dev payment suite against DEV. |
| frontend/e2e/tests/donate.spec.ts | Reworks donation E2Es into CI UI tests + DEV-only Stripe payment flow tests. |
| frontend/e2e/fixtures/stripe.ts | Introduces Stripe helper utilities for filling card details and completing 3DS. |
| frontend/e2e/fixtures/seed.ts | Adds a constant for the DEV initiative slug used by payment E2Es. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+27
to
+41
| /** | ||
| * Fill the Stripe card element iframes on the donate payment step. | ||
| * | ||
| * All three fields (card number, expiry, CVC) live inside a single cross-origin | ||
| * Stripe iframe titled "Secure card number input frame". They are accessed via | ||
| * Playwright's frameLocator and filled with their internal `name` attributes. | ||
| */ | ||
| export async function fillStripeCard(page: Page, card: StripeCard): Promise<void> { | ||
| const [month, year] = card.expiry.split('/'); | ||
| const frame = page.frameLocator('iframe[title="Secure card number input frame"]'); | ||
| await frame.locator('[name="cardnumber"]').fill(card.number); | ||
| await frame.locator('[name="cc-exp-month"]').fill(month); | ||
| await frame.locator('[name="cc-exp-year"]').fill(year); | ||
| await frame.locator('[name="cc-csc"]').fill(card.cvc); | ||
| } |
Comment on lines
+26
to
+36
| // The Donate button has a <span> child that intercepts pointer events, | ||
| // so use a JS dispatch to open the drawer reliably. | ||
| await page.evaluate(() => { | ||
| const btns = document.querySelectorAll('button'); | ||
| for (const b of btns) { | ||
| if (b.textContent?.trim() === 'Donate') { | ||
| b.click(); | ||
| return; | ||
| } | ||
| } | ||
| }); |
Comment on lines
+48
to
+59
| /** Click the primary Donate submit button inside the drawer. */ | ||
| async function clickDrawerDonate(page: Parameters<typeof fillStripeCard>[0]) { | ||
| await page.evaluate(() => { | ||
| const donateBtn = Array.from(document.querySelectorAll('button')).find( | ||
| (b) => | ||
| b.textContent?.trim() === 'Donate' && | ||
| b.className.includes('p-button-primary') && | ||
| !b.className.includes('pill'), | ||
| ); | ||
| donateBtn?.click(); | ||
| }); | ||
| } |
Contributor
Author
|
@copilot resolve the merge conflicts in this pull request |
Signed-off-by: GitHub <noreply@github.com>
Contributor
Resolved by merging |
Structured test plan covering: - Individual donation flows (valid, expired, declined, 3DS approve/fail) - Corporate donation flows (valid, declined) - Recurring subscription (create, cancel) - Initiative creation for all 4 types (Project, OSTIF, General Fund, Event) - Self Serve validation steps - DB verification queries with assertions Includes a Known gaps section tracking issues found during the June 2026 manual QA session (corporate org_id null, payment_method null, OSTIF contact field mapping bug). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Michal Lehotsky <mlehotsky@linuxfoundation.org>
Comment on lines
+34
to
+41
| export async function fillStripeCard(page: Page, card: StripeCard): Promise<void> { | ||
| const [month, year] = card.expiry.split('/'); | ||
| const frame = page.frameLocator('iframe[title="Secure card number input frame"]'); | ||
| await frame.locator('[name="cardnumber"]').fill(card.number); | ||
| await frame.locator('[name="cc-exp-month"]').fill(month); | ||
| await frame.locator('[name="cc-exp-year"]').fill(year); | ||
| await frame.locator('[name="cc-csc"]').fill(card.cvc); | ||
| } |
Comment on lines
+27
to
+33
| /** | ||
| * Fill the Stripe card element iframes on the donate payment step. | ||
| * | ||
| * All three fields (card number, expiry, CVC) live inside a single cross-origin | ||
| * Stripe iframe titled "Secure card number input frame". They are accessed via | ||
| * Playwright's frameLocator and filled with their internal `name` attributes. | ||
| */ |
| // --------------------------------------------------------------------------- | ||
|
|
||
| test.describe('Donate — payment flows @dev', () => { | ||
| test.skip(!isDevRun, 'Payment tests only run against the DEV environment (@dev)'); |
Comment on lines
+28
to
+46
| await page.evaluate(() => { | ||
| const btns = document.querySelectorAll('button'); | ||
| for (const b of btns) { | ||
| if (b.textContent?.trim() === 'Donate') { | ||
| b.click(); | ||
| return; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| // Step 1 — amount | ||
| await page.getByRole('button', { name: '$10', exact: true }).click(); | ||
| await page.getByRole('button', { name: 'Continue' }).click(); | ||
|
|
||
| // Step 2 — contact | ||
| await page.locator('input[type="text"]').fill('E2E Test User'); | ||
| await page.locator('input[type="email"]').fill(opts.email ?? 'e2e@example.com'); | ||
| await page.getByRole('button', { name: 'Continue to Payment' }).click(); | ||
| } |
Comment on lines
+50
to
+58
| await page.evaluate(() => { | ||
| const donateBtn = Array.from(document.querySelectorAll('button')).find( | ||
| (b) => | ||
| b.textContent?.trim() === 'Donate' && | ||
| b.className.includes('p-button-primary') && | ||
| !b.className.includes('pill'), | ||
| ); | ||
| donateBtn?.click(); | ||
| }); |
The secondary contact field mapping issue was caused by test automation filling inputs by array index rather than by label. The application code (fundraise-contact-person-group.vue, buildContacts server fn) is correct. Removed it from the known issues table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Michal Lehotsky <mlehotsky@linuxfoundation.org>
lewisojile
approved these changes
Jun 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends the Playwright E2E suite for donation flows with two tiers of tests:
@devtag) — real card payment flows againstcrowdfunding.dev.lfx.devusing thetest-html-textinitiative (which has a real Stripe test-mode product ID). Skipped in CI automatically.New files
frontend/e2e/fixtures/stripe.ts—fillStripeCard(),complete3DS(), andSTRIPE_CARDStest card constantsfrontend/e2e/fixtures/seed.ts— addsDEV_PAYMENT_INITIATIVE_SLUG = 'test-html-text'frontend/playwright.config.ts— documents DEV mode usage in commentsCard scenarios covered (@dev)
4242 4242 4242 4242(valid)4000 0000 0000 0069(expired)4000 0000 0000 0002(declined)4000 0000 0000 3220(3DS) + COMPLETE4000 0000 0000 3220(3DS) + FAILRunning the DEV suite
E2E_BASE_URL=https://crowdfunding.dev.lfx.dev pnpm test:e2e --grep "@dev"Requires
NUXT_E2E_TEST_MODE=trueon the DEV frontend deployment (for thee2e-authendpoint).Test plan
@devsuite run manually against DEV confirms all 5 card scenarios pass🤖 Generated with Claude Code