Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 67 additions & 73 deletions docs/payment-e2e-skip-index.md
Original file line number Diff line number Diff line change
@@ -1,102 +1,96 @@
# Payment E2E `test.skip` Index (#53)

**Generated 2026-06-05.** Indexes every skipped test in `tests/e2e/payment/` by its
**blocker**, so it's clear what must ship before each can be un-skipped — and that
none are skipped without a tracked reason.
**Regenerated 2026-06-08.** Indexes every skipped test in `tests/e2e/payment/` by
its **blocker**, so it's clear what must ship before each can be un-skipped — and
that none are skipped without a tracked reason. Line numbers and reasons below are
the ground truth in the specs as of this date (verified by grepping
`test.skip(true, ...)`), not a hand-maintained guess.

## Summary

`tests/e2e/payment/` contains **34 skipped tests** (via `test.skip(true, '<reason>')`),
across 7 spec files. They fall into these blocker buckets:

| Blocker | Count | Unblocked by |
| ------------------------------ | ----- | ----------------------------------------------------- |
| Other missing UI feature | 10 | various small UI surfaces |
| Route `/payment/subscriptions` | 5 | SUBSCRIPTION-MGMT (#5) |
| Offline-queue UI | 5 | PAYMENT-OFFLINE-UI (#4) |
| Live provider keys / webhooks | 4 | PayPal/Stripe **sandbox credentials** (deploy-time) |
| Route `/payment/dashboard` | 2 | PAYMENT-DASHBOARD (#3) |
| Route `/payment/history` | 2 | PAYMENT-DASHBOARD (#3) |
| Realtime dashboard UI | 2 | PAYMENT-DASHBOARD (#3 / #6) |
| Grace-period wiring | 1 | SUBSCRIPTION-MGMT (#5) |
| Duplicate-prevention | 1 | dup-prevention feature |
| Test-env limitation | 2 | won't-fix in E2E (FPS / bundling — covered elsewhere) |
`tests/e2e/payment/` contains **27 skipped tests** (each via
`test.skip(true, '<reason>')`), across 7 spec files. They fall into these blocker
buckets:

**Key takeaways for un-skipping:**

- The biggest unlocks are the **payment UI routes** (#3 PAYMENT-DASHBOARD, #4
PAYMENT-OFFLINE-UI, #5 SUBSCRIPTION-MGMT) — shipping those routes un-blocks ~17 tests.
- **4 tests need live sandbox credentials** (full Stripe/PayPal redirect + webhook
flows) — these stay skipped in CI by design; run them locally with sandbox keys.
- The 2 test-env-limitation skips (FPS measurement, script bundling) are not real
product gaps; leave skipped.

> Note: the Edge Functions those flows call (PayPal order/capture/subscription,
> cancel/resume) shipped in #130 — so the remaining payment blockers are the
> **front-end routes** + **sandbox creds**, not the backend.

## Full index by blocker

### Route /payment/subscriptions — SUBSCRIPTION-MGMT (#5) — 5 skipped

- `02-paypal-subscription.spec.ts:85` — Subscription management page not yet implemented
- `02-paypal-subscription.spec.ts:90` — Subscription management page not yet implemented
- `02-paypal-subscription.spec.ts:95` — Subscription management page not yet implemented
- `03-failed-payment-retry.spec.ts:136` — Subscription management page not yet implemented
- `06-realtime-dashboard.spec.ts:99` — Subscription management page not yet implemented
| Blocker | Count | Unblocked by |
| ----------------------------------- | ----- | ------------------------------------------------------------------- |
| Unimplemented dashboard/realtime UI | 11 | small UI surfaces on `/payment/dashboard` + realtime widgets (#6) |
| Live provider keys / webhooks | 6 | Stripe/PayPal **sandbox credentials** set on the deployed functions |
| Offline-queue seed fixture | 4 | an in-page Dexie queue-seed fixture (autonomous; see note below) |
| Unimplemented route/page | 3 | `/payment/subscriptions`, `/payment/history` routes |
| Won't-fix in E2E | 2 | FPS measurement + script-bundling (covered elsewhere / unreliable) |
| Edge-function flow | 1 | exercising the `cancel-subscription` Edge Function end-to-end |

### Route /payment/dashboard — PAYMENT-DASHBOARD (#3) — 2 skipped
**Just un-skipped (2026-06-08, no creds):** the **grace-period countdown** and
**duplicate-prevention (23505)** tests in `02-paypal-subscription.spec.ts` now run
against a seeded subscription row via the new `seedIsolatedSubscription` fixture
(`tests/e2e/utils/test-user-factory.ts`). They needed a fixture, not credentials.

- `07-performance.spec.ts:26` — Payment dashboard page not yet implemented
- `07-performance.spec.ts:33` — Payment dashboard page not yet implemented

### Route /payment/history — PAYMENT-DASHBOARD (#3) — 2 skipped

- `05-offline-queue.spec.ts:115` — Payment history page not yet implemented
- `07-performance.spec.ts:40` — Payment history page not yet implemented

### Offline-queue UI — PAYMENT-OFFLINE-UI (#4) — 5 skipped

- `01-stripe-onetime.spec.ts:149` — Offline queue feature not yet implemented
- `05-offline-queue.spec.ts:67` — Offline queue status UI not yet implemented
- `05-offline-queue.spec.ts:75` — Queue sync status UI not yet implemented
- `05-offline-queue.spec.ts:91` — Queue persistence UI not yet implemented
- `07-performance.spec.ts:48` — Offline queue sync UI not yet implemented
**Key takeaways for un-skipping:**

### Realtime dashboard UI — PAYMENT-DASHBOARD (#3/#6) — 2 skipped
- **6 tests need live sandbox credentials** (full Stripe/PayPal redirect + webhook
flows). With the Edge Functions now deployed to prod, the only remaining gate is
setting the provider secrets (`STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`,
`PAYPAL_CLIENT_SECRET`, `PAYPAL_WEBHOOK_ID`) via the Management API `/secrets`
endpoint. These stay skipped in CI by design.
- **4 offline-queue tests need a Dexie seed fixture.** These are empty stubs whose
bodies must also be written; the queue is client-side (`PaymentQueueV2` /
`queuedOperations` Dexie store, via `@/lib/offline-queue/payment-adapter`), so the
fixture must seed IndexedDB in-page (call the app's own `paymentQueue` API), not
the server `payment_intents` table. Autonomous but non-trivial — deferred.
- **The remaining ~16** are genuinely unimplemented UI surfaces or routes
(realtime widgets, `/payment/subscriptions`, `/payment/history`) and 2
won't-fix-in-E2E perf cases. They are NOT credential-blocked.

> Note: the Edge Functions these flows call (Stripe checkout/subscription/verify,
> PayPal order/capture/subscription, cancel/resume, the webhooks) are all DEPLOYED
> to prod as of 2026-06-08 (via the Supabase Management API). So the remaining
> payment blockers are **sandbox creds** + **a few front-end surfaces**, not the
> backend code.

- `06-realtime-dashboard.spec.ts:104` — Transaction counter not yet implemented
- `06-realtime-dashboard.spec.ts:130` — Real-time error notifications not yet implemented
## Full index by blocker

### Live provider keys / webhooks (sandbox creds) — 4 skipped
### Live provider keys / webhooks (sandbox creds) — 6 skipped

- `02-paypal-subscription.spec.ts:49` — PayPal API keys not configured - skipping flow test
- `02-paypal-subscription.spec.ts:56` — PayPal API keys not configured - skipping flow test
- `02-paypal-subscription.spec.ts:124` — Needs a seeded past_due/grace row + PayPal sandbox keys
- `06-realtime-dashboard.spec.ts:85` — Payment list updates require actual Stripe integration
- `06-realtime-dashboard.spec.ts:92` — Webhook verification requires actual Stripe webhooks
- `07-performance.spec.ts:19` — Stripe API keys not configured - use k6 for load testing
- `07-performance.spec.ts:116` — Script bundling test requires Stripe integration

### Grace-period wiring (#5) — 1 skipped
### Offline-queue seed fixture (autonomous — Dexie) — 4 skipped

- `02-paypal-subscription.spec.ts:100` — Grace period feature not yet implemented
- `05-offline-queue.spec.ts:102` — Needs a queue-seed fixture or provider creds to enqueue
- `05-offline-queue.spec.ts:110` — Needs a queue-seed fixture or provider creds to enqueue
- `05-offline-queue.spec.ts:118` — Needs a queue-seed fixture to enqueue multiple items
- `05-offline-queue.spec.ts:137` — Needs a seeded failed item to exercise backoff/retry UI

### Duplicate-prevention feature — 1 skipped
### Edge-function flow — 1 skipped

- `02-paypal-subscription.spec.ts:105` — Duplicate subscription prevention not yet implemented
- `02-paypal-subscription.spec.ts:120` — Cancel drives the cancel-subscription Edge Function

### Other missing UI feature — 10 skipped
### Unimplemented route/page — 3 skipped

- `03-failed-payment-retry.spec.ts:136` — Subscription management page not yet implemented
- `06-realtime-dashboard.spec.ts:99` — Subscription management page not yet implemented
- `07-performance.spec.ts:40` — Payment history page not yet implemented

### Unimplemented dashboard / realtime UI — 11 skipped

- `01-stripe-onetime.spec.ts:149` — Offline queue feature not yet implemented
- `04-gdpr-consent.spec.ts:232` — Consent reset feature not yet implemented
- `05-offline-queue.spec.ts:83` — Queue count display not yet implemented
- `05-offline-queue.spec.ts:99` — Retry status UI not yet implemented
- `05-offline-queue.spec.ts:107` — Max retry UI not yet implemented
- `05-offline-queue.spec.ts:123` — Queue overflow handling not yet implemented
- `05-offline-queue.spec.ts:128` — Clear queue button not yet implemented
- `06-realtime-dashboard.spec.ts:104` — Transaction counter not yet implemented
- `06-realtime-dashboard.spec.ts:112` — Offline status indicator not yet implemented
- `06-realtime-dashboard.spec.ts:120` — Reconnection UI not yet implemented
- `06-realtime-dashboard.spec.ts:125` — Batch update UI not yet implemented
- `06-realtime-dashboard.spec.ts:130` — Real-time error notifications not yet implemented
- `06-realtime-dashboard.spec.ts:135` — Payment chart not yet implemented
- `07-performance.spec.ts:26` — Payment dashboard page not yet implemented
- `07-performance.spec.ts:33` — Payment dashboard page not yet implemented
- `07-performance.spec.ts:48` — Offline queue sync UI not yet implemented

### Uncategorized (test-env limitations) — 2 skipped
### Won't-fix in E2E — 2 skipped

- `05-offline-queue.spec.ts:167` — Overflow/storage-warning handling not in scope for #4
- `07-performance.spec.ts:111` — FPS testing is not reliable in E2E tests
- `07-performance.spec.ts:116` — Script bundling test requires Stripe integration
107 changes: 86 additions & 21 deletions tests/e2e/payment/02-paypal-subscription.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
*/

import { test, expect } from '@playwright/test';
import { dismissCookieBanner } from '../utils/test-user-factory';
import {
dismissCookieBanner,
getAdminClient,
seedIsolatedSubscription,
deleteIsolatedSubscription,
openSubscriptionsAs,
type IsolatedSubscription,
} from '../utils/test-user-factory';

test.describe('PayPal Subscription Creation Flow', () => {
test.describe.configure({ timeout: 60000 });
Expand Down Expand Up @@ -100,34 +107,92 @@ test.describe('PayPal Subscription Creation Flow', () => {
).toBeVisible();
});

// The flows below assert behavior against a SEEDED subscription row (cancel,
// grace-period countdown, duplicate-prevention). They need the per-test
// subscription-seeding fixture (service-role insert + cleanup) which isn't
// wired yet — the route + grace UI + 23505 guard themselves are covered by
// SubscriptionManager.test.tsx and the migration. Un-skip once seeding lands.
// The grace-period + duplicate-prevention flows below run against a SEEDED
// subscription row via the per-test fixture (seedIsolatedSubscription —
// service-role insert + cleanup, no provider creds). Cancel + failed-retry
// remain skipped: cancel drives the cancel-subscription edge function and
// failed-retry needs PayPal sandbox keys.

test.skip('should allow subscription cancellation', async ({ page }) => {
test.skip(
true,
'Needs a seeded subscription row (per-test fixture) to drive cancel'
);
// Fixture exists (seedIsolatedSubscription) but cancel drives the
// cancel-subscription Edge Function end-to-end; un-skip once that flow is
// exercised against a deployed function with provider config.
test.skip(true, 'Cancel drives the cancel-subscription Edge Function');
});

test.skip('should handle failed payment retry logic', async ({ page }) => {
test.skip(true, 'Needs a seeded past_due/grace row + PayPal sandbox keys');
});

test.skip('should show grace period warning', async ({ page }) => {
// Covered as a component test in SubscriptionManager.test.tsx; an E2E here
// needs a seeded grace_period row.
test.skip(true, 'Needs a seeded grace_period subscription row');
test('should show grace period warning', async ({ browser }) => {
// Seed a grace_period subscription (expires in 5 days) for a throwaway user,
// open /account/subscriptions AS that user, and assert the countdown + badge
// render — driven by a real row through real RLS (no provider creds needed).
let fixture: IsolatedSubscription | null = null;
let opened: Awaited<ReturnType<typeof openSubscriptionsAs>> | null = null;
try {
fixture = await seedIsolatedSubscription('grace_period', {
provider: 'stripe',
graceDays: 5,
});
// No admin client / anon key (unconfigured env) → skip rather than fail.
test.skip(!fixture, 'Admin client unavailable to seed subscription');
if (!fixture) return;

opened = await openSubscriptionsAs(browser, fixture);
const { page } = opened;

await expect(
page.getByRole('heading', { name: 'Subscriptions', level: 1 })
).toBeVisible({ timeout: 30000 });

// Grace-period alert with the day countdown (graceDays=5).
await expect(
page.getByText(/Grace period: 5 days remaining/i)
).toBeVisible({ timeout: 30000 });
// The status badge.
await expect(page.getByText(/Grace Period/i).first()).toBeVisible();
} finally {
if (opened) await opened.close();
await deleteIsolatedSubscription(fixture);
}
});

test.skip('should prevent duplicate subscriptions', async ({ page }) => {
// The DB-level guard (idx_subscriptions_one_live_per_user) + webhook 23505
// catch enforce this server-side; a browser E2E needs a seeded live row.
test.skip(
true,
'Needs a seeded live subscription row to trigger the guard'
);
test('should prevent duplicate subscriptions', async () => {
// The one-live-per-user guard (idx_subscriptions_one_live_per_user) is
// enforced server-side. Seed one live (active) row, then attempt a SECOND
// live row for the same user via the service-role client and assert it is
// rejected with Postgres unique_violation (23505) — the same rejection the
// webhook upsert catches in prod. No provider creds needed.
let fixture: IsolatedSubscription | null = null;
try {
fixture = await seedIsolatedSubscription('active', {
provider: 'stripe',
});
const admin = getAdminClient();
test.skip(
!fixture || !admin,
'Admin client unavailable to seed subscription'
);
if (!fixture || !admin) return;

const { error } = await admin.from('subscriptions').insert({
template_user_id: fixture.user.id,
provider: 'paypal',
provider_subscription_id: `iso_dup_${Date.now()}`,
customer_email: fixture.user.email,
plan_amount: 999,
plan_interval: 'month',
status: 'active', // second LIVE row for the same user → must be rejected
failed_payment_count: 0,
});

expect(error).not.toBeNull();
// Postgres unique_violation, on the one-live-per-user partial index.
expect(error?.code).toBe('23505');
expect(error?.message).toMatch(/idx_subscriptions_one_live_per_user/i);
} finally {
await deleteIsolatedSubscription(fixture);
}
});
});
Loading
Loading