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
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ npm run test:watch

### UI Testing Expectations

- Before touching a screen, check its row in the
[Screen Test Matrix](docs/screen-test-matrix.md) for the test types it's
expected to have and its current coverage status. If you add a new screen,
add a row for it in the same PR.
- All new screens and interactive components **must include tests**.
- Use `@testing-library/react-native` to render components and interact with them via accessible queries (`getByRole`, `getByLabelText`, etc.).
- Test the key user-facing behaviours: form validation, loading states, error messages, and successful flows.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ PocketPay Mobile is part of a broader PocketPay stack:
## Documentation

- [Screen Inventory](docs/screen-inventory.md) - A map of the main screens and routes in the app.
- [Screen Test Matrix](docs/screen-test-matrix.md) - Which test types and visual verification each screen requires, and current coverage gaps
- [Mobile Onboarding Checklist](docs/mobile-onboarding-checklist.md) - Quick-reference setup checklist for new contributors
- [Evaluation Readiness Checklist](docs/evaluation-readiness-checklist.md) - Contract-issue review checklist for GrantFox contributors before payment evaluation
- [UI State Catalogue](docs/ui-states.md) and [Accessibility Checklist](docs/accessibility.md) - Governance for major-screen states, shared component contracts, and accessible review evidence
Expand Down
156 changes: 156 additions & 0 deletions docs/screen-test-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Screen Test Matrix

A screen-by-screen map of what testing and visual verification each part of
the wallet is expected to have, and what actually exists today. Use this
before opening a PR that touches a screen: find its row, do what the row
says, and if you're adding a new screen, add a row for it in the same PR.

This is a coverage map, not a spec of *how* to write the tests — see
[CONTRIBUTING.md](../CONTRIBUTING.md#testing) for the testing setup and
[UI State Catalogue](ui-states.md) for the six required states
(Loading/Empty/Error/Success/Disabled/Pending) each screen's *behavior*
should satisfy regardless of whether an automated test exists yet.

## Test Types Referenced Below

| Type | What it means | Tooling |
|---|---|---|
| **Component test** | Renders the screen (or a meaningful slice of it) with Testing Library and asserts on states, interactions, and accessible queries. | `@testing-library/react-native`, co-located in `__tests__/` or `tests/` |
| **Store/logic test** | Unit-tests the Zustand store or utility function the screen depends on, independent of rendering. | Jest |
| **Flow test** | Exercises a multi-screen journey end-to-end within RTL (e.g. create → success, send → review → sign-confirmation). | `@testing-library/react-native` + `expo-router` mock |
| **Visual verification** | Manual check against [Design System](design-system.md) tokens, the applicable row in [UI State Catalogue](ui-states.md), and [Accessibility Checklist](accessibility.md). Required on every PR that changes a screen's UI — this project has no automated visual-regression/snapshot tooling, so this step is not optional. |

**Status legend:** ✅ covered · ⚠️ partial (logic tested, screen render/flow is not, or vice versa) · ❌ gap — no automated test exists.

---

## 1. Wallet Creation & Import (Onboarding)

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Welcome | `app/(auth)/index.tsx` | Component, Visual | None found | ❌ |
| Create Wallet | `app/(auth)/create.tsx` | Component, Flow, Visual | None found | ❌ |
| Import Wallet | `app/(auth)/import.tsx` | Component, Flow, Visual | None found | ❌ |
| Wallet Creation Success | `app/(auth)/wallet-creation-success.tsx` | Component, Visual | None found | ❌ |

**Visual verification expectations:** secret key / recovery phrase text must never be logged, screenshotted in CI artifacts, or left visible after navigating away (see [Security Guide](security.md)); "I've backed this up" / confirmation controls meet the 44×44 dp touch target and 4.5:1 contrast minimums from the [Accessibility Checklist](accessibility.md); the create → success handoff must not allow the success screen to be reached without a wallet actually existing in the store.

This is the largest gap in the app: onboarding has no automated coverage at
all today. A PR touching any of these four screens should add at least a
component test for that screen before merging.

## 2. Balance (Home / Wallet)

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Home / Wallet | `app/(tabs)/index.tsx` | Component, Store, Visual | `__tests__/home.pullToRefresh.test.tsx`, `__tests__/walletStore.test.ts`, `__tests__/walletStore.pagination.test.ts` | ✅ |

**Visual verification expectations:** all six [UI State Catalogue](ui-states.md#wallet-home) rows for Wallet (Loading/Empty/Error/Success/Disabled/Pending); balance formatting and sent/received sign conventions.

## 3. Send

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Send (compose) | `app/send.tsx` | Component, Visual | `__tests__/send.test.tsx` | ✅ |
| Review Transaction | `app/review-transaction.tsx` | Component, Flow, Visual | `__tests__/ReviewConfirm.test.tsx`, `__tests__/signerStore.confirming.test.ts`, `__tests__/signerStore.cancellation.test.ts`, `__tests__/signerStore.cancelled.test.ts` | ✅ |
| Sign Confirmation | `app/sign-confirmation.tsx` | Component, Visual | `__tests__/screens/sign-confirmation.test.tsx` | ✅ |
| Payment Success | `app/payment-success.tsx` | Component, Visual | `__tests__/paymentSuccess.test.tsx` | ✅ |

**Visual verification expectations:** the four signer phases (Reviewing →
Signing → Submitting → Waiting for confirmation) from
[UI State Catalogue](ui-states.md#send) must each be visually distinct and
non-duplicable; validation errors render beside the offending field, not as
a generic banner.

## 4. Receive & QR

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Receive | `app/receive.tsx` | Component, Visual | Covered indirectly via `__tests__/qrPayload.test.ts` (payload logic only) | ⚠️ |
| Scan (generic) | `app/scan.tsx` | Component, Visual | None found | ❌ |
| Scan to Pay | `app/scan-pay.tsx` | Component, Flow, Visual | `__tests__/contacts.scan.test.tsx` covers scan-to-contact, not scan-to-pay | ⚠️ |

**Visual verification expectations:** QR code renders the correct
address-only or SEP-0007 payload per [QR Payment Requests](qr-payment-requests.md);
camera-permission-denied state is handled visibly, not a blank screen; scan
never auto-submits a payment — see [Scan-to-Pay Review](scan-to-pay.md) for
the required "cancel before payment" behavior.

## 5. Activity / Transaction Detail

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Activity (history) | `app/(tabs)/history.tsx` | Component, Visual | `__tests__/history.pagination.test.tsx` | ✅ |
| Transaction Detail | `app/transaction/[id].tsx` | Component, Flow, Visual | `__tests__/transactionDetail.test.tsx`, `__tests__/transactionDetail.deeplink.test.tsx` | ✅ |

## 6. Address Book (Contacts)

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Contacts (address book) | `app/contacts.tsx` | Component, Store, Flow, Visual | `__tests__/ContactForm.test.tsx`, `__tests__/ContactPicker.test.tsx`, `__tests__/contactStore.test.ts`, `tests/contact-delete-confirmation.test.tsx` | ✅ |

**Visual verification expectations:** deleting a contact requires the
confirmation dialog every time (no accidental data loss); a contact picked
from scan or history correctly resolves to its saved label on Send/Receive.

## 7. Vault (Placeholder)

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Vault (main) | `app/(tabs)/vault.tsx` | Component, Store, Visual | `__tests__/vault.test.tsx`, `__tests__/VaultUnavailableState.test.tsx` | ✅ |
| Vault Detail | `app/vault/[id].tsx` | Component, Visual | None found | ❌ |
| Vault Lock Detail | `app/vault-lock/[id].tsx` | Component, Flow, Visual | `__tests__/maturedLockWithdrawal.test.ts` (store logic), `__tests__/maturedLockWithdrawalFlow.test.tsx` (flow), `__tests__/VaultLockEducationModal.test.tsx` | ⚠️ |

**Visual verification expectations:** the vault must clearly read as
**mock/Testnet-only** whenever `EXPO_PUBLIC_VAULT_CONTRACT_ID` is unset — see
[Vault UI Guidance](vault-ui-guidance.md); lock statuses (locked, matured,
withdrawn) each have a visually distinct badge; `app/vault/[id].tsx` has no
dedicated screen test today even though the store and lock-detail flow do —
don't assume vault coverage is complete just because `vault.test.tsx` passes.

## 8. Settings

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Settings (main) | `app/(tabs)/settings.tsx` | Component, Visual | None found | ❌ |
| Feature Flags | `app/(tabs)/settings/flags.tsx` | Component, Visual | None found | ❌ |

**Visual verification expectations:** destructive settings actions (wallet
reset) require `WalletResetConfirmModal` — see
`__tests__/WalletResetConfirmModal.test.tsx` for the confirmation contract
itself, even though the Settings screen that launches it is untested;
toggling a feature flag takes effect without requiring an app restart, or
the UI explains that it does.

## 9. Diagnostics

| Screen | Route | Test Types Required | Existing Coverage | Status |
|---|---|---|---|---|
| Diagnostics | `app/diagnostics.tsx` | Component, Store/logic, Visual | `__tests__/diagnostics.test.ts` (covers `getDiagnostics()` utility only, not the screen render) | ⚠️ |

---

## Cross-Cutting Checks (every screen, regardless of row above)

These apply on top of whatever this screen's row requires — they're not a
substitute for it:

- [ ] Every interactive element has `accessible={true}` and a descriptive
`accessibilityLabel` (see [Accessibility Checklist](accessibility.md)).
- [ ] The screen satisfies all six [UI State Catalogue](ui-states.md) rows
that apply to it, including any explicitly marked *Not applicable* with a
stated reason.
- [ ] Design tokens come from `src/constants/theme.ts` — no hardcoded hex
colors or pixel sizes (see [Design System Guide](design-system.md)).
- [ ] No secret key, seed phrase, or full transaction signature is ever
logged or left in a test snapshot/fixture.
- [ ] `npm run typecheck`, `npm run lint`, and `npm test` all pass — see the
[CI Troubleshooting Guide](ci-troubleshooting.md) if any of them fail for
a reason you can't immediately explain.

## Keeping This Matrix Current

If you add a new screen under `app/`, add a row for it here in the same PR
— don't leave it for someone else to discover the gap. If you add a test for
a screen marked ❌ or ⚠️ above, update its Status and Existing Coverage
columns in the same PR that adds the test.