Skip to content

test(e2e): Wave 4b — UI-driven Razorpay TEST-card payment E2E (free → upgrade → Pro)#194

Merged
mastermanas805 merged 2 commits into
mainfrom
ci/wave4b-ui-payment-spec
Jun 6, 2026
Merged

test(e2e): Wave 4b — UI-driven Razorpay TEST-card payment E2E (free → upgrade → Pro)#194
mastermanas805 merged 2 commits into
mainfrom
ci/wave4b-ui-payment-spec

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

Wave 4b (Part 2 of 2) — web: the UI-driven Razorpay TEST-card payment E2E

The CEO ask: "There is no CI that actually tries to create a test user, then tries to upgrade and enter test details and check everything is working fine." This adds that real UI loop. Pairs with the api Wave 4b PR (InstaNode-dev/api#268 — cohort test-mode rzp_test_* checkout routing). Design: docs/ci/01-CI-INTEGRATION-DESIGN.md §"Razorpay test-card payment E2E" (Approach A).

e2e/live-ui-payment.spec.ts — two tests, two lanes

1. @pr-smoke CONTRACT-ONLY (PR-able, no card entry). Mint a free cohort user via the factory → drive the dashboard /app/checkout?plan=pro UI → createCheckout → assert the SPA reaches one of: a Razorpay checkout URL (test keys wired), the inert cohort path (synthetic_test_cohort 403, when rzp_test_* keys aren't wired — prod today), or the honest billing-not-configured fallback. All three prove the UI→api→checkout wiring end-to-end against the real api. Reaped.

2. FULL card-entry (nightly only, gated E2E_RAZORPAY_TEST_MODE=1). Same up to the Razorpay page, then fill the subscription test card 4718 6091 0820 4366 + OTP 1234 (cross-origin iframes + popup handled) → submit → poll /api/v1/capabilities for tier=pro (the real TEST-mode webhook drives the upgrade). RESILIENT + SOFT-FAILS (skip-with-reason) on a Razorpay markup change or an unreachable webhook — a Razorpay UI change must never red the nightly. Deterministic upgrade is covered by the api webhook-injection suite.

Wiring

  • The spec matches live-*.spec.ts → runs in e2e-prod.yml (30-min) + e2e-live.yml (nightly staging). The @pr-smoke leg runs per-PR via e2e-pr-smoke.yml.
  • Both nightly workflows now pass E2E_RAZORPAY_TEST_MODE from a repo var (inert until the operator wires test keys) + arm the factory token.

Gating + live verification

Skips clean until E2E_LIVE/E2E_ACCOUNT_TOKEN (and, for the card leg, E2E_RAZORPAY_TEST_MODE) are set. Verified live: the @pr-smoke contract-only test PASSES against prod api.instanode.dev — minted a free cohort user → drove the dashboard UI → reached the inert cohort path (synthetic_test_cohort 403, since prod has no test keys yet) → reaped clean. npm run gate green (tsc + build + 1129 vitest).

Operator enable (no approval wait)

After the api Wave 4b PR is merged + the operator sets RAZORPAY_TEST_* on the api: set repo var E2E_RAZORPAY_TEST_MODE=1 (+ E2E_ACCOUNT_TOKEN secret already present) → the nightly card-entry leg goes live. Until then it's shipped + inert.

🤖 Generated with Claude Code

… upgrade → Pro)

The CEO ask: "There is no CI that actually tries to create a test user, then
tries to upgrade and enter test details and check everything is working fine."
This adds the real UI loop. Design: docs/ci/01-CI-INTEGRATION-DESIGN.md
§"Razorpay test-card payment E2E" (Approach A) + the api Wave 4b PR (cohort
test-mode rzp_test_* checkout routing).

e2e/live-ui-payment.spec.ts — TWO tests, two lanes:
  1. @pr-smoke CONTRACT-ONLY (PR-able, no card entry): mint a free cohort user
     via the factory → drive the dashboard /app/checkout?plan=pro UI →
     createCheckout → assert the SPA reaches a Razorpay checkout URL, OR the
     inert cohort path (synthetic_test_cohort 403, when rzp_test_* keys aren't
     wired), OR the honest billing-not-configured fallback. All three prove the
     UI→api→checkout wiring end-to-end against the REAL api. Reaped.
  2. FULL card-entry (nightly only, gated on E2E_RAZORPAY_TEST_MODE=1): same up
     to the Razorpay page, then fill the subscription test card
     4718 6091 0820 4366 + OTP 1234 (cross-origin iframes + popup handled), submit
     → poll /api/v1/capabilities for tier=pro (the real TEST-mode webhook drives
     the upgrade). RESILIENT + SOFT-FAILS (skip-with-reason) on a Razorpay
     markup change or an unreachable webhook so a UI change never reds the
     nightly. Deterministic upgrade is covered by the api webhook-injection suite.

Wiring: the spec matches live-*.spec.ts so it runs in e2e-prod.yml (30-min) +
e2e-live.yml (nightly staging); the @pr-smoke leg runs per-PR via
e2e-pr-smoke.yml. Both nightly workflows now pass E2E_RAZORPAY_TEST_MODE from a
repo var (inert until the operator wires test keys) + arm the factory token.

Gating: skips clean until E2E_LIVE/E2E_ACCOUNT_TOKEN (and, for the card leg,
E2E_RAZORPAY_TEST_MODE) are set. Verified live: the @pr-smoke contract-only test
PASSES against prod api.instanode.dev (minted free cohort → UI → reached the
inert cohort path, reaped). npm run gate green (tsc + build + 1129 vitest).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 enabled auto-merge (squash) June 6, 2026 03:42
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
dist/assets/index-DV4gJTWd.js 162.15 KB (0%)
dist/assets/index-BsJUZYRr.css 6.13 KB (0%)

Comment thread e2e/live-ui-payment.spec.ts Fixed
…egexp-anchor

CodeQL flagged the unanchored /(razorpay\.com|rzp\.io)/i regex used to validate
a reached checkout URL: an unanchored substring match means a hostile URL like
https://evil.com/?x=razorpay.com would pass. Replace the assertion with a parsed
isRazorpayCheckoutHost() that inspects the URL hostname (exact apex or subdomain
match), and anchor the Playwright route() regex on scheme + exact host segment.
No security weakening — strictly tightens the host check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 1a11604 into main Jun 6, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants