Skip to content

test(e2e): donation flow tests — CI UI suite + DEV payment suite#150

Open
mlehotskylf wants to merge 4 commits into
mainfrom
worktree-test+e2e-donation-flows
Open

test(e2e): donation flow tests — CI UI suite + DEV payment suite#150
mlehotskylf wants to merge 4 commits into
mainfrom
worktree-test+e2e-donation-flows

Conversation

@mlehotskylf

Copy link
Copy Markdown
Contributor

Summary

Extends the Playwright E2E suite for donation flows with two tiers of tests:

  • CI suite — UI validation tests that run in every PR against the local stack (no real Stripe charges). Covers drawer visibility, step validation, and Cancel.
  • DEV payment suite (@dev tag) — real card payment flows against crowdfunding.dev.lfx.dev using the test-html-text initiative (which has a real Stripe test-mode product ID). Skipped in CI automatically.

New files

  • frontend/e2e/fixtures/stripe.tsfillStripeCard(), complete3DS(), and STRIPE_CARDS test card constants
  • frontend/e2e/fixtures/seed.ts — adds DEV_PAYMENT_INITIATIVE_SLUG = 'test-html-text'
  • frontend/playwright.config.ts — documents DEV mode usage in comments

Card scenarios covered (@dev)

Card Expected result
4242 4242 4242 4242 (valid) Thank-you screen
4000 0000 0000 0069 (expired) "Your card is expired" error
4000 0000 0000 0002 (declined) Decline error message
4000 0000 0000 3220 (3DS) + COMPLETE Thank-you screen
4000 0000 0000 3220 (3DS) + FAIL Authentication error

Running the DEV suite

E2E_BASE_URL=https://crowdfunding.dev.lfx.dev pnpm test:e2e --grep "@dev"

Requires NUXT_E2E_TEST_MODE=true on the DEV frontend deployment (for the e2e-auth endpoint).

Test plan

  • CI E2E job passes (UI-only tests, no Stripe charges)
  • @dev suite run manually against DEV confirms all 5 card scenarios pass

🤖 Generated with Claude Code

…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>
Copilot AI review requested due to automatic review settings June 14, 2026 00:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts to 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();
});
}
@mlehotskylf

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Signed-off-by: GitHub <noreply@github.com>

Copilot AI commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved by merging origin/main and fixing the conflict in frontend/e2e/tests/donate.spec.ts in commit a091266.

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>
Copilot AI review requested due to automatic review settings June 14, 2026 05:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants