E2E Test Plan
E2E Test Plan: Drop — Fintech Payment AppPlan
Project:
Drop — Remittance + QR Payments{{PROJECT_NAME}} Version:1.0{{VERSION}} Date:2026-02-23{{DATE}} Author:John (AI Director){{AUTHOR}} Status: Draft | In Review | Approved Reviewers:Alem Bašić (CEO){{REVIEWERS}}
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | Initial |
1. E2E Testing Objectives
Objectives:
- Validate that the
5{{COUNT}} most criticalDropuser journeys work end-to-endoninstaginga production-like environment - Catch integration failures between
Next.jsfrontend,API routes, SQLite DB,backend, andmockthird-partyBaaSservices that unit/integration tests cannot detect - Provide confidence before every production deployment
via post-staging deploy gate - Serve as living documentation of critical user flows
for CEO UAT
What E2E tests are NOT for:
- Complete feature coverage (
handledthat'sbyfor unit/integrationtests — 40+ Vitesttests) - Testing every error state (too expensive to
maintain; covered by input-chaos.spec.ts for validation)maintain) - Performance benchmarking (
handledusebyperformanceapi-benchmarks.test.ts)tests) - Visual pixel-perfection (
Figmausesourcevisualofregressiontruth:testsmockups/figma-make-export/)separately)
2. Critical User Journeys
Journey 1: User Registration — 3-Step Onboarding{{JOURNEY_1_NAME}}
| Field | Value |
|---|---|
| Priority | Critical |
| Business Impact | |
| Frequency | |
| Test File | |
Steps:
Navigate{{STEP_1}}to https://drop-staging.fly.dev/Click{{STEP_2}}"Registrer deg" — registration form opensEnter{{STEP_3}}valid data: name (with Unicode: Amir Hasić), email, password ≥ 8 chars, Norwegian phone (+47), DOB ≥ 18 yearsSubmit{{STEP_4}}form — OTP sent to phone, OTP input shownEnter{{STEP_5}}correct 6-digit OTPEnter{{STEP_6}}and confirm 4-digit PINRedirect to dashboard — account activated
Assertions:
201{{ASSERTION_1}}Created on registration (no password hash in response)JWT{{ASSERTION_2}}httpOnly cookie set after PIN setupDashboard{{ASSERTION_3}}visible with user's nameAudit log entry created
Journey 2: User Login + Dashboard Access{{JOURNEY_2_NAME}}
| Field | Value |
|---|---|
| Priority | Critical |
| Business Impact | |
| Frequency | |
| Test File | |
Steps:
Navigate to /login{{STEP_1}}Enter valid email and password{{STEP_2}}SubmitRedirected to dashboardBank balance shown (from mock AISP)Recent transactions visible{{STEP_3}}
Assertions:
200 on login; JWT cookie set; SameSite=Strict; httpOnlyGET /api/auth/me returns user object (no password hash)Balance shown is from mock BaaS (not stored in Drop DB)
Journey 3: Remittance — NOK to RSD
|
Steps:
Log in as KYC-approved consumerNavigate to "Send penger" (Send Money)Select recipient; enter amount = 1,000 NOK; select currency = RSDReview: fee shown as 5 NOK (0.5%); exchange rate shownConfirm and submitTransaction created with status = completedNavigate to Transaction History — transaction visible
Assertions:
201 Created; transaction in DB with fee = 5 NOKTransaction history shows amount, fee, currency, statusNo balance change in Drop DB (pass-through — balance read from mock AISP)
Journey 4: QR Payment — Scan + Pay
|
Steps:
Log in as KYC-approved consumerNavigate to "Scan QR"Enter valid merchantId (pre-seeded merchant)Enter amount = 200 NOKReview: merchant_fee = 2 NOK (1%)Confirm paymentTransaction created; visible in history
Assertions:
201 Created; merchant_fee = 2 NOK; amount = 200 NOKTransaction visible in consumer historyMock PISP payment recorded
Journey 5: Merchant Registration + QR Generation
|
Steps:
Log in as user (not yet a merchant)Navigate to "Bli Merchant" (Become Merchant)Enter business_name and bank_accountSubmit — merchant created with unique QR code valueNavigate to merchant dashboard — QR code visible
Assertions:
201 Created; merchant has unique qr_code_valueGET /api/merchants/me returns merchant details + QR codeQR code is unique (no two merchants share a code)
All Journeys Summary
| Journey | Priority | Est. Duration | Automated | |
|---|---|---|---|---|
| Critical | Yes | |||
| {{JOURNEY_2_NAME}} | Critical | {{DURATION}}s | Yes | {{DATE}} |
| User |
Critical | Yes | ||
| Yes | — | |||
| Account settings update | Medium | 30s | Yes | |
3. Browser & Device Matrix
Desktop Browsers
| Browser | Version | OS | Priority | Notes |
|---|---|---|---|---|
| Chrome | Latest stable | Critical | ||
| Firefox | Latest stable | Windows, macOS | High | |
| Safari | Latest stable | macOS | High | WebKit engine |
| Edge | Latest |
Windows | Medium | Chromium-based |
Mobile Browsers
| Browser | Platform | Version | Priority | Notes |
|---|---|---|---|---|
| Safari | iOS | Latest | Critical | |
| Chrome | Android | Latest |
Screen Resolutions (configured in playwright.config.ts)
| Category | Resolution | Test Priority | |
|---|---|---|---|
| Critical | |||
| 1366×768 | High | ||
| Laptop | 1280×800 | Medium | |
| Tablet (landscape) | 1024×768 | High | |
| Mobile L |
428×926 | Critical | |
Matrix in CI: ChromiumRun +Critical WebKitpriority (mobilebrowser/resolution viewports 375px and 428px)combinations on every deployment. Full matrix on nightly schedule.
4. Test Data Setup & Teardown
Setup Strategy
| Data Type | Method | Scope |
|---|---|---|
Pre-seeded test |
Test suite | |
Created via API | ||
beforeAll |
Test suite | |
| Isolated |
Created via API in beforeEach |
Individual test |
Seed command: npmbash runscripts/e2e-seed.sh db:seed{{ENVIRONMENT}}(populatesSeed stagingverification: SQLitebash withscripts/verify-seed.sh synthetic test data){{ENVIRONMENT}}
Teardown Strategy
| Cleanup Item | Method | Trigger |
|---|---|---|
| DELETE via API | afterEach |
|
| Test session data | Clear browser storage | afterEach |
afterAll |
||
| Payment test data | Stripe test mode auto-cleanup | Daily job |
Rule: Tests must not leave state that affects other tests. EachTests testmust createsbe itsexecutable ownin isolatedany data.order.
Test Accounts
| Account | Role | Purpose | |
|---|---|---|---|
e2e- |
Standard |
||
e2e- |
|||
e2e- |
Credentials stored in: Vaultwarden{{CREDENTIAL_STORE}} (vault.basicconsulting.no)never →hardcoded "Dropin E2E Test Accounts"tests)
5. Authentication Handling in Tests
Strategy: API-based{{AUTH_STRATEGY}} auth
Recommended authenticateapproach (Playwright):
// tests/fixtures/auth.ts // Authenticate via— wait for elements/network, notPOST /api/auth/logininbeforeAll,API, save storagestate,statereuse//forReuseallstate across tests in thesuite.same suite // Only use UI login when testing the login flowitself.// playwright.config.ts approach: // 1. beforeAll: POST /api/auth/login → save JWT cookie to storage state // 2. Each test that needs auth: restore storage state (< 1ms overhead) // 3. Tests for login UI: always fresh (no saved state)itselfSession reuse:
- Session state saved per role
(consumer, merchant)after first auth- Reused for all tests requiring that role
JWTInvalidatedexpiresandinrefreshed7ifdays — within test run durationexpired
6. Test Environment Requirements
Requirement Specification Environment Staging ( )https://drop-staging.fly.dev/{{DOMAIN}}Database StagingDedicatedSQLiteE2Ewithdatabase,syntheticseededseedbeforedata onlyrunExternal services MockSandboxBaaS/ test mode (NEXT_PUBLIC_SERVICE_MODE=mock);Stripemocktest,Sumsubemail sandbox)Stability No active deployments during E2E run (Fly.io deployment check)Data persistence Isolated from— not shared with manual testingduring E2E runResponse times < 5,000ms{{TIMEOUT}}s for all endpoints (PlaywrightE2Edefaultassertionstimeout)time out at 2× this)
Critical:Environment locking: CI acquires a lock before E2Etests must NOTrunagainsttoapreventproductionconcurrentenvironment.deploymentsDropLockhandlesmechanism:financial data — all E2E tests use mock BaaS exclusively.{{LOCK_MECHANISM}}
7. Flaky Test Management Strategy
Definition: A test that fails inconsistently for the same
code.codePrevention:
- Use explicit
waits:waits (not)waitForResponse('/api/...'sleeptimepage.waitForTimeout(1000)
e2e-fresh-{Date.now()}@test.alai.nodata-testidprefers-reduced-motion in test envprefers-reduced-motionDetection:
- Track flakiness rate per test in CI
- Any test failing >
5%{{FLAKY_THRESHOLD}}% of runs without code changes = flaky Playwright HTML report shows flaky tests in CI artifacts
Response:
TagImmediately tag flaky test withannotationtest.fixme()@flakyin Playwright- Create
MissionbugControl taskticket withP2priorityprioritybased on journey criticality - Quarantine (run separately, don't block CI) while fixing
- Fix within
3{{FLAKY_FIX_SLA}} days or remove the test - Monthly flaky test review
during sprint retrospective
8. Visual Regression Testing
Approach:Tool: Playwright{{VIS_TOOL}} screenshot comparisons on critical screens.
Baseline: Stored in {{BASELINE_STORAGE}}
src/drop-app/__tests__/e2e/snapshots/(committedThreshold: Allow up to repo){{VIS_THRESHOLD}}% Sourcepixel ofdifference Truth:for Figmafont Make export: renderingmockups/figma-make-export/src/components/
| Check | Scope | Trigger |
|---|---|---|
Review process: ScreenshotVisual diffs requirein BuilderPR +requiring Validatorexplicit sign-offapproval before merge. Figma is the source of truth — any diff vs Figma is a regression.merge
9. Performance Assertions Within E2E
| Assertion | Metric | Threshold | Journey |
|---|---|---|---|
| Page load | Time to Interactive | < |
|
| Core Web Vitals — LCP | Largest Contentful Paint | < |
Homepage, |
| Core Web Vitals — CLS | Cumulative Layout Shift | < {{CLS}} | All pages |
| API response | < |
||
| API | |||
10. CI Integration
Trigger: Post-staging deployment (GitHub Actions workflow: .github/workflows/e2e.yml)
Parallelization:
3TestPlaywrightsharding:projects{{SHARD_COUNT}}run in parallel: user-flows, full-flows, input-chaosshards2Browser parallelism: {{BROWSER_WORKERS}} workers perproject on CI (total: 6 parallel Playwright workers)shard- Estimated total time:
< 10{{E2E_TOTAL_TIME}} minutes
CI configuration:
# .github/workflows/e2e.ymlReference: {{CI_CONFIG_PATH}}
# Key settings:
# - Projects:Shards: user-flows, full-flows, input-chaos{{SHARD_COUNT}}
# - Retries: 1{{RETRY_COUNT}} (for non-flaky detection;tests not masking failures)only)
# - Timeout per test: 30,000ms{{TEST_TIMEOUT}}ms
# - Timeout total: 600,000ms (10 min)
# - Base URL: https://drop-staging.fly.dev/{{SUITE_TIMEOUT}}min
On failure:
- Collect screenshots, videos,
Playwrighttraces asCIartifacts - Retain artifacts for
7{{ARTIFACT_RETENTION}} days - Alert Slack
#drop-e2e-channel#e2e-failureson alai-talk.slack.com Block production deployment until fixed
11. Test Report Format & Artifacts
Report format: Playwright{{REPORT_FORMAT}} HTML report + JUnit XML for CI
Report location: GitHub{{REPORT_URL}} Actionsor CI artifacts (7-day retention)
Artifacts collected on failure:
| Artifact | Format | When Collected |
|---|---|---|
| Screenshot | PNG | On step failure |
| Screen recording | WebM video | On test failure |
| Network trace | HAR file | On test failure |
| Browser console log | JSON | On test failure |
| Playwright trace | .zip ( |
On test failure |
12. Maintenance Strategy
Page Object Pattern:
- All page interactions wrapped in
lightweightPage Object classes - Selectors centralized — change in
one placetests/e2e/pages/ - Location:
src/drop-app/__tests__/tests/e2e/pages/
Selector strategy (Drop-specific):strategy:
data-testidattributes (preferred —preferredstable,(e.g.,data-testid="remittance-amount")intent-clear)- ARIA roles + accessible name (
e.g.,goodgetByRole('button',for{accessibilityname: 'Send penger' }))alignment) - Text content
—(acceptable forstablestaticNorwegian/English texttext) - CSS class names (avoid —
avoidcoupled(Tailwindtoclasses can change)styling) - XPath (never —
neverfragile)
Review cadence:
E2ETest suite reviewed monthly for relevance- Remove tests
updatedforindeprecatedsamefeaturesPRimmediately - Update tests before merging feature changes (tests are part of the PR)
Monthly review: remove tests for deprecated featuresFigma-make-export checked after every UI change
Related Documents
- Test Strategy
- Test Plan
- Test Case Template
PerformanceCI/CDTest PlanTesting GuideTest InventoryPipeline
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | |||