NYM-1199: Realign Node Families E2E strategy with Playwright as primary
Revises the end-to-end testing approach for Node Families. The primary E2E suite now uses Playwright against the mock-wired dev server, ensuring cross-platform coverage including macOS and reusing existing tooling. The WebdriverIO + tauri-driver native-webview suite is re-scoped as an optional, CI-only validation leg. Additionally, an optional read-only sandbox IPC smoke test is introduced for higher fidelity.
This commit is contained in:
@@ -5,65 +5,71 @@ The Node Families feature (parent change `node-families-wallet`) is complete and
|
||||
- `src/pages/families/FamilyPage.tsx` — pure page, consumes `useFamiliesContext()`, no Tauri imports.
|
||||
- `src/context/FamiliesContextProvider.tsx` — the **real** provider; the only families file importing `./main` (Tauri runtime).
|
||||
- `src/context/mocks/families.tsx` (`MockFamiliesContextProvider`) — drives the page from an in-memory contract engine (`familiesMockState.ts`) seeded by `families.fixtures.ts`.
|
||||
- `src/pages/families/FamilyPageRoute.tsx` (`FamilyPageWithProvider`) — wraps `FamilyPage` in the real provider for the live app route.
|
||||
- Storybook flow stories (`FamilyFlows.stories.tsx`) and `e2e/families.spec.ts` (Playwright → Storybook on :6006) already encode the journeys via `data-testid`s.
|
||||
|
||||
Constraints established up front:
|
||||
- **Playwright cannot drive Tauri's WebDriver.** `tauri-driver` exposes the classic W3C WebDriver protocol; Playwright speaks CDP/BiDi. The Tauri-documented clients are Selenium and WebdriverIO.
|
||||
- **`tauri-driver` has no macOS support** (no WKWebView driver). Native-webview e2e therefore runs only on Linux/Windows — in CI, not on the developer's Mac.
|
||||
Environment facts that shape the design:
|
||||
- The wallet dev server is **webpack-dev-server on `http://localhost:9000`** (`historyApiFallback: true`, `hot: true`) — the same `devUrl` Tauri loads. Pointing a browser-based runner here exercises the real app shell + router.
|
||||
- **Playwright cannot drive Tauri's WebDriver.** `tauri-driver` exposes the classic W3C WebDriver protocol; Playwright's only WebDriver story is experimental **BiDi**, a different protocol. The Tauri-documented native clients are WebdriverIO and Selenium.
|
||||
- **`tauri-driver` has no macOS support** (no WKWebView driver). Native-webview e2e runs only on Linux/Windows — in CI.
|
||||
- The node-families **contract is now deployed to sandbox** (one family, one member), enabling an optional real-IPC read smoke.
|
||||
|
||||
Decisions taken with the user: **WebdriverIO + `tauri-driver` in CI** for the native-webview suite, and a **build-time env flag** for mock-provider selection.
|
||||
This design supersedes an earlier framing that made WebdriverIO + `tauri-driver` the primary suite. Per the latest direction, the primary suite is **Playwright against the mock-wired dev server** (cross-platform, runs on macOS), and the native-webview run is an **optional CI validation leg**.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Render the existing Family page inside the real Tauri desktop shell, backed by the Storybook mock providers, with zero chain/IPC dependency.
|
||||
- Render the existing Family page inside the real wallet app shell, backed by the Storybook mock providers, with zero chain/IPC dependency.
|
||||
- A primary e2e suite that runs **everywhere including the developer's Mac**, reusing the existing Playwright dependency, selectors, and journeys.
|
||||
- Keep all mock code out of the production bundle (compile-time elimination).
|
||||
- Replay the owner and operator journeys against the native webview via WebdriverIO + `tauri-driver`, reusing the existing `data-testid` selectors and assertions.
|
||||
- Run the native-webview suite in Linux CI; keep the existing Playwright/Storybook suite as the cross-platform/local check.
|
||||
- Provide an optional native-binary validation leg (WebdriverIO + `tauri-driver`) in Linux CI for higher fidelity.
|
||||
- Make use of the sandbox contract deployment via an optional real-IPC read smoke.
|
||||
|
||||
**Non-Goals:**
|
||||
- No real IPC/chain wiring (remains parent-change tasks 9.4/9.5).
|
||||
- No real IPC/chain *write* wiring (remains parent-change tasks 9.4/9.5); the sandbox tier is read-only.
|
||||
- No macOS native-webview e2e (unsupported by Tauri).
|
||||
- No migration of the existing Playwright/Storybook suite to WebdriverIO — both coexist.
|
||||
- No destructive lifecycle mutations against the shared sandbox.
|
||||
- No change to the Family feature's behavior or its specs.
|
||||
|
||||
## Decisions
|
||||
|
||||
**D1 — WebdriverIO + `tauri-driver` for native-webview e2e (not Playwright).**
|
||||
Playwright cannot connect to `tauri-driver`'s W3C WebDriver endpoint. WebdriverIO is the Tauri-documented client and natively speaks WebDriver classic. The journeys are simple click/type/assert sequences, so the port from Playwright is mechanical. *Alternative considered:* Selenium — also supported, but WebdriverIO's config + assertion ergonomics are closer to the existing Playwright specs, lowering porting cost.
|
||||
**D1 — Playwright against the mock-wired dev server is the PRIMARY suite.**
|
||||
Point Playwright at `http://localhost:9000` with the mock flag on, and replay the journeys against the real app shell + router. Rationale: runs on every OS (incl. the developer's Mac), reuses the already-present `@playwright/test` dep and the existing selectors/specs, and tests the actual app chrome rather than Storybook iframes. *Alternative considered:* keep driving Storybook stories — lower fidelity (no router/app shell) and no closer to the real app. *Alternative considered:* Cypress — no advantage over the Playwright suite already in the repo.
|
||||
|
||||
**D2 — Build-time env flag selects the provider (not runtime).**
|
||||
A webpack `DefinePlugin` constant (e.g. `process.env.WALLET_MOCK_FAMILIES`) gates the import of `MockFamiliesContextProvider` vs `FamiliesContextProvider` behind a `const MOCK = ...` check. With the flag off (default), the dead branch and its transitive mock imports tree-shake out — no mock engine in production. *Alternatives considered:* runtime URL/query flag (ships mock code in prod unless guarded; rejected for prod-safety) and a fully separate entry point (heavier; the flag is enough since the page is already provider-agnostic).
|
||||
A webpack `DefinePlugin` constant (e.g. `WALLET_MOCK_FAMILIES`, tri-state `owner|operator|off`) gates the import of `MockFamiliesContextProvider` vs `FamiliesContextProvider` behind a `const` check; with the flag off (default) the dead branch and its transitive mock imports tree-shake out. The flag both selects the mock provider and chooses the persona seed (`buildOwnerFlowStore` / `buildOperatorFlowStore`). *Alternatives considered:* runtime URL/query flag (ships mock code in prod unless guarded; rejected) and a separate entry point (heavier; unnecessary since the page is already provider-agnostic).
|
||||
|
||||
**D3 — Reuse the existing fixtures and selectors verbatim.**
|
||||
The mock build seeds `buildOwnerFlowStore` / `buildOperatorFlowStore` and the page already exposes the journey `data-testid`s. The WebdriverIO journeys mirror `FamilyFlows.stories.tsx` step-for-step so the two suites stay observably equivalent. Persona/seed selection at launch reuses the same flag mechanism (e.g. `WALLET_MOCK_FAMILIES=owner|operator`) so each journey gets a deterministic start state.
|
||||
The mock-wired dev server seeds the same fixtures as the Storybook flows, and the page already exposes the journey `data-testid`s, so the Playwright journeys mirror `FamilyFlows.stories.tsx` step-for-step. The same selectors then carry over to the optional WebdriverIO leg, keeping all suites observably equivalent.
|
||||
|
||||
**D4 — Mock build artifact for the harness.**
|
||||
`tauri-driver` launches a built binary, so the harness needs a mock-wired build. Options: (a) `tauri dev` with the mock webpack config behind `devUrl`, or (b) a one-off `tauri build` with the mock define set, then point WebdriverIO at the produced binary. CI uses (b) for a stable artifact; local debugging (on Linux) can use (a). The Tauri `frontendDist`/`devUrl` already point at the webpack output, so only the define + entry wiring changes.
|
||||
**D4 — Optional native-webview validation: WebdriverIO + `tauri-driver` in CI (not Playwright).**
|
||||
For "how far can we get against the actual binary," follow the Tauri WebDriver-in-CI flow: Ubuntu runner, `xvfb-run` for a headless display, `libwebkit2gtk-4.1-dev` + `webkit2gtk-driver` + `tauri-driver` (cargo, `--locked`), WebdriverIO driving the packaged app's native WebKitGTK webview. Playwright is not an option here (protocol mismatch, D-context). This leg is optional and starts non-blocking.
|
||||
|
||||
**D5 — Skip-not-fail on unsupported platforms.**
|
||||
The WebdriverIO suite detects macOS (or a missing `tauri-driver`/`WebKitWebDriver`) and skips with a clear message, so `pnpm test:e2e:tauri` on a Mac is a no-op rather than a red failure. CI is the source of truth.
|
||||
**D5 — Skip-not-fail on unsupported platforms / missing tools.**
|
||||
The WebdriverIO leg detects macOS (or a missing `tauri-driver`/`webkit2gtk-driver`) and skips with a clear message, so invoking it locally on a Mac is a no-op rather than a red failure. The Playwright suite is the local source of truth; the native leg is CI-only.
|
||||
|
||||
**D6 — Separate CI job, not a step in `build` (reconciled with recent merge).**
|
||||
The merged `ci-nym-wallet-frontend.yml` has one `build` job (ubuntu-22.04) doing install → tsc → lint → unit tests → build-storybook → upload. The native-webview suite is added as a **separate job** rather than appended to `build`: it additionally needs a Rust/Tauri compile, `WebKitWebDriver`, and `tauri-driver`, which would slow every `build` run and couple unrelated failures. It can `needs: build` (reuse nothing) or run independently. *Also reconciled:* the existing Playwright→Storybook suite (`test:e2e`) is **not yet in CI** — only unit tests + Storybook build are — so the "keep Playwright as the cross-platform check" intent means optionally wiring `test:e2e` into CI too, not assuming it already runs there.
|
||||
**D6 — CI: primary step in `build`, native leg as a separate job.**
|
||||
The merged `ci-nym-wallet-frontend.yml` has one `build` job (ubuntu-22.04: install → tsc → lint → unit tests → build-storybook → upload). Add the **primary Playwright e2e as a step in `build`** (the Playwright suite is not yet in CI — only unit tests + Storybook build are). Add the **native-webview run as a separate job** (`needs: build` or independent) because it adds a Rust/Tauri compile + system webdriver deps that would slow and couple the main job; start it `continue-on-error`.
|
||||
|
||||
**D7 — Sandbox real-IPC smoke is a separate, optional, READ-only tier.**
|
||||
The sandbox contract (one family/one member) lets us smoke the real `FamiliesContextProvider` + `src/requests/families.ts` against a live chain — validating the IPC wiring that the mock deliberately stands in for (parent-change 9.4). Keep it read-only and separate from the deterministic mock e2e: a shared sandbox is a poor place to run create/kick/disband lifecycles, and live-network reads are inherently flakier. *Alternative considered:* fold sandbox into the main e2e — rejected (non-determinism + shared-state mutation).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Native-webview e2e can't run on the developer's Mac** → Keep the Playwright/Storybook suite as the local check (works everywhere); rely on Linux CI for native-webview coverage; D5 makes the local invocation a clean skip.
|
||||
- **WebKitGTK rendering/timing differs from Chromium** → Journeys use explicit waits on `data-testid`s (as the Storybook flows already do) and zero/low mock latency for the auto-run paths; generous step timeouts in CI.
|
||||
- **Build-flag branch could accidentally ship mock code** → Default flag off; add a check (bundle assertion or the existing `check:singletons`-style guard) and the spec scenario "Production build excludes mock code" to lock it in.
|
||||
- **`tauri-driver` + `WebKitWebDriver` version drift in CI** → Pin `tauri-driver` (`cargo install --locked`) and install a known WebKitGTK driver in the CI image; cache cargo bin.
|
||||
- **Duplicated journey logic across two suites drifts over time** → Both target identical `data-testid`s and assert identical outcomes (parity requirement); factor shared selector/step constants if drift appears.
|
||||
- **Provider seam breaks the merged Figma Code Connect mapping** (`FamilyPage.figma.tsx` does `example: () => <FamilyPage />`) → Keep the mock/real selection in a *separate* module (D2); never make `FamilyPage`'s own module depend on the flag or on Tauri, so it stays importable in isolation. The `src/**/*.figma.tsx` include is unaffected.
|
||||
- **Theme swap (Nym 2.0) changing appearance under test** → Confirmed color-only (no DOM/`data-testid` change, families components untouched by the merge); journeys assert visibility/test ids, not pixels, so parity holds across the palette change.
|
||||
- **Browser fidelity ≠ packaged app** → The primary Playwright suite tests the web frontend, not the native binary or real `invoke`; the optional WebdriverIO leg covers the binary, and D7 covers real IPC. The three tiers together close the gap the mock leaves.
|
||||
- **WebKitGTK rendering/timing differs from Chromium (native leg)** → Journeys wait on `data-testid`s (as the Storybook flows do) with low mock latency and generous CI timeouts.
|
||||
- **Build-flag branch could ship mock code** → Default off; bundle/guard check + the spec scenario "Production build excludes mock code" lock it in.
|
||||
- **Provider seam breaks the merged Figma Code Connect mapping** (`FamilyPage.figma.tsx` → `example: () => <FamilyPage />`) → Keep mock/real selection in a *separate* module (D2); never make `FamilyPage`'s module depend on the flag or on Tauri.
|
||||
- **Theme swap (Nym 2.0) under test** → Confirmed color-only (families components untouched by the merge); journeys assert visibility/test ids, not pixels.
|
||||
- **`tauri-driver`/`webkit2gtk-driver` version drift in CI** → Pin `tauri-driver` (`cargo install --locked`), install a known WebKitGTK driver in the runner, cache cargo bin; the leg is `continue-on-error` until stable.
|
||||
- **Sandbox state drifts / contract redeploys** → The read smoke asserts shape/render, not exact contents (or pins to a known family id) so a changed fixture doesn't hard-fail; keep it non-blocking.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Additive only — no rollback of existing behavior needed. Rollout: (1) add the build-time flag + provider seam; (2) add the WebdriverIO config/spec; (3) add the Linux CI job. If the native-webview suite proves flaky in CI, it can be gated/`continue-on-error` while the Playwright suite continues to gate merges, with no impact on the app itself.
|
||||
Additive only. Rollout: (1) build-time flag + provider seam; (2) repoint Playwright at the mock-wired dev server and add it to the `build` CI job; (3) add the optional WebdriverIO native job (`continue-on-error`); (4) add the optional sandbox read smoke. Any optional leg can be dropped/gated without affecting the app or the primary suite.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Build vs dev-server launch for the CI harness (D4 (a) vs (b)) — default to a built binary; revisit if build time in CI is prohibitive.
|
||||
- Whether to also add a Windows CI leg (supported by `tauri-driver`) or keep Linux-only initially — start Linux-only.
|
||||
- Exact flag surface for persona seeding (single tri-state `WALLET_MOCK_FAMILIES=owner|operator|off` vs a separate seed var) — resolve during tasks.
|
||||
- Persona handling for the dev server: one server per persona run (re-launch with a different `WALLET_MOCK_FAMILIES`) vs a single launch with a runtime persona switch — default to per-run env, resolve during tasks.
|
||||
- Whether to add a Windows leg to the native job (supported by `tauri-driver`) or keep Linux-only initially — start Linux-only.
|
||||
- Sandbox smoke placement: CI job vs a documented manual step, and whether it pins a known family id — resolve during tasks.
|
||||
- Whether to retire the Storybook-iframe Playwright specs once the dev-server suite lands, or keep both — lean toward repointing (one suite).
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
## Why
|
||||
|
||||
The Node Families feature is fully built and exercised in Storybook, but its end-to-end journeys have only ever run in a browser (Storybook `play` functions + Playwright against the Storybook iframe). Nothing yet proves the page renders and the owner/operator flows work **inside the real Tauri desktop shell** — the native webview, the app router, the production chrome. We want that confidence without needing a live chain or Rust IPC handlers, so we run the same journeys against the page mounted in the Tauri app but backed by the existing Storybook mock providers.
|
||||
The Node Families feature is fully built and exercised in Storybook, but its end-to-end journeys have only ever run against Storybook iframes. We want to validate the page and the owner/operator flows inside the **real wallet app shell** (the production router and chrome), backed by the existing Storybook mock providers so no live chain or Rust IPC is required — and we want that primary suite to run **everywhere, including the developer's Mac**, with a heavier native-binary check available in CI.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Mount the existing Family page in the Tauri app behind a **build-time mock flag**: when the flag is set, the app wires `MockFamiliesContextProvider` (the in-memory contract engine already used by Storybook) instead of the Tauri-backed `FamiliesContextProvider`; the production bundle tree-shakes the mock code out entirely.
|
||||
- Produce a dedicated **mock-wired Tauri build** the test harness can launch — the real binary and native webview, but seeded with the deterministic Storybook fixture stores (`buildOwnerFlowStore` / `buildOperatorFlowStore`) so flows are reproducible offline.
|
||||
- Add a **WebdriverIO + `tauri-driver`** e2e harness that drives the actual native webview and replays the same journeys currently covered by the Storybook flow stories: owner lifecycle (create → invite → accept → kick → disband) and operator lifecycle (accept → leave, then reject), plus the multi-node invite-states assertion. Selectors reuse the existing `data-testid`s.
|
||||
- Wire the WebdriverIO suite into **CI on Linux** (Tauri WebDriver supports only Linux/Windows; macOS has no WKWebView driver), keeping the existing Playwright-against-Storybook suite as the cross-platform/local check.
|
||||
- **Not** adopting Playwright for the Tauri runtime: Playwright cannot speak the classic W3C WebDriver protocol that `tauri-driver` exposes; WebdriverIO is the supported client.
|
||||
- Mount the existing Family page in the wallet app behind a **build-time mock flag**: when set, the app wires `MockFamiliesContextProvider` (the in-memory contract engine already used by Storybook) instead of the Tauri-backed `FamiliesContextProvider`; the production bundle tree-shakes the mock code out.
|
||||
- **Primary e2e — Playwright against the dev server (mock-wired):** point Playwright at the running webpack dev server (`http://localhost:9000`, the same `devUrl` Tauri loads) with the mock flag enabled, and replay the same journeys currently covered by the Storybook flow stories. This drives the real app shell + router in a real browser (Chromium/WebKit), is cross-platform (runs locally on macOS), and reuses the existing `@playwright/test` dependency and `data-testid` selectors. It supersedes the current Playwright→Storybook-iframe suite by pointing at the app shell instead.
|
||||
- **Optional validation — WebdriverIO + `tauri-driver` in CI:** as a "how far can we get against the actual binary" leg, add the Tauri WebDriver CI flow (Ubuntu + `xvfb-run`, `webkit2gtk-driver` + `tauri-driver`, WebdriverIO) to drive the **packaged app in the native WebKitGTK webview**. Linux/Windows only (macOS has no WKWebView driver), so it lives in CI, not local dev, and starts non-blocking.
|
||||
- **Optional higher-fidelity tier — sandbox real-IPC smoke:** the node-families contract is now deployed to **sandbox** (currently one family, one member). This unlocks a read-only smoke against the real `FamiliesContextProvider` + `src/requests/families.ts` wiring (parent-change task 9.4), separate from the deterministic mock e2e.
|
||||
- **Not** adopting Playwright for the Tauri runtime: Playwright can't speak the classic W3C WebDriver protocol `tauri-driver` exposes (its experimental WebDriver **BiDi** support is a different protocol); WebdriverIO remains the client for the native leg.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `families-app-mock-build`: A build-time flag that mounts the Family page inside the Tauri wallet app with the Storybook mock providers, seeded by deterministic fixtures, while keeping mock code out of production builds.
|
||||
- `families-webdriver-e2e`: A WebdriverIO + `tauri-driver` end-to-end suite that launches the mock-wired Tauri binary and verifies the owner and operator Node Families journeys against the native webview in CI.
|
||||
- `families-app-mock-build`: A build-time flag that mounts the Family page inside the wallet app shell with the Storybook mock providers, seeded by deterministic fixtures, served by the dev server, while keeping mock code out of production builds.
|
||||
- `families-app-e2e`: End-to-end journey coverage of the owner and operator Node Families flows — primarily Playwright against the mock-wired dev server (cross-platform), with an optional WebdriverIO + `tauri-driver` native-webview validation leg in CI.
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- None: the existing node-families-owner / node-families-operator specs describe behavior that this change exercises but does not alter. -->
|
||||
<!-- None: the existing node-families-owner / node-families-operator specs describe behavior this change exercises but does not alter. -->
|
||||
|
||||
## Impact
|
||||
|
||||
- **Frontend (`nym-wallet/src`)**: provider-selection seam (build-time flag) around `FamiliesContextProvider` vs `MockFamiliesContextProvider`; a Family route/entry reachable in the mock build; webpack config gains a mock-flag-driven define + entry path.
|
||||
- **Build/config**: webpack (`webpack.dev.js` / a mock variant), Tauri (`tauri.conf.json` `devUrl`/`frontendDist` or a build profile) so the harness can launch the mock-wired app.
|
||||
- **Tests (`nym-wallet/e2e`)**: new WebdriverIO config + spec(s) mirroring `e2e/families.spec.ts` journeys; `tauri-driver` as a dev/CI dependency (`cargo install tauri-driver`).
|
||||
- **CI (`.github/workflows/ci-nym-wallet-frontend.yml`)**: the existing single `build` job (ubuntu-22.04: install → tsc → lint → unit tests → build-storybook → upload) gains a **separate** native-webview job that installs `WebKitWebDriver` + `tauri-driver`, builds the mock-wired binary, and runs the WebdriverIO suite (kept separate because it adds a Rust/Tauri build + system webdriver deps the `build` job doesn't need). Note: the Playwright→Storybook suite is currently **not** wired into CI — only unit tests + Storybook build are — so this change should also decide whether to add the existing Playwright suite as a CI step.
|
||||
- **Figma Code Connect (recently merged)**: `src/pages/families/FamilyPage.figma.tsx` maps `FamilyPage` via `example: () => <FamilyPage />`. The build-time provider seam MUST keep `FamilyPage` itself provider-agnostic and importable in isolation so this mapping (and the `src/**/*.figma.tsx` config) keeps resolving.
|
||||
- **Theme (recently merged Nym 2.0 swap)**: orthogonal — the swap changed only color values, no DOM/`data-testid`s, so journeys/selectors are unaffected; the mock-wired build simply renders the new dark palette.
|
||||
- **Dependencies**: add `webdriverio` (+ runner) to `nym-wallet` dev deps; `tauri-driver` via cargo. No production dependency or runtime change.
|
||||
- **Out of scope**: real IPC/chain wiring (still task 9.4/9.5 of the parent change); macOS native-webview e2e (unsupported by Tauri); Figma Code Connect publish (separate Tier-1/Hux-gated step).
|
||||
- **Frontend (`nym-wallet/src`)**: provider-selection seam (build-time flag) around `FamiliesContextProvider` vs `MockFamiliesContextProvider`; a Family route reachable in the mock-wired dev server; webpack config gains a mock-flag-driven `DefinePlugin` constant + persona seed.
|
||||
- **Tests (`nym-wallet/e2e`)**: repoint/extend the Playwright config from Storybook (:6006) to the mock-wired dev server (:9000); journeys reuse existing selectors. Add an optional WebdriverIO config + `tauri-driver` (cargo) for the native leg.
|
||||
- **CI (`.github/workflows/ci-nym-wallet-frontend.yml`)**: the existing single `build` job (ubuntu-22.04: install → tsc → lint → unit tests → build-storybook → upload) gains the **primary Playwright e2e step** (currently the Playwright suite is NOT in CI), and a **separate optional** native-webview job (xvfb-run + `webkit2gtk-driver` + `tauri-driver` + Tauri/Rust build, starts `continue-on-error`).
|
||||
- **Figma Code Connect (recently merged)**: `src/pages/families/FamilyPage.figma.tsx` maps `FamilyPage` via `example: () => <FamilyPage />`. The provider seam MUST keep `FamilyPage` itself provider-agnostic and importable in isolation so this mapping (and the `src/**/*.figma.tsx` config) keeps resolving.
|
||||
- **Theme (recently merged Nym 2.0 swap)**: orthogonal — color-only, no DOM/`data-testid` change, so journeys/selectors are unaffected; the mock-wired build simply renders the new dark palette.
|
||||
- **Dependencies**: no new dep for the primary suite (`@playwright/test` already present); `webdriverio` (+ runner) and `tauri-driver` (cargo) only for the optional native leg.
|
||||
- **Out of scope**: full destructive lifecycle mutations against shared sandbox (read smoke only); macOS native-webview e2e (unsupported by Tauri); Figma Code Connect publish (Tier-1/Hux-gated); real IPC wiring beyond the read smoke (parent-change 9.4/9.5).
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Primary e2e against the mock-wired dev server
|
||||
|
||||
The project SHALL provide a Playwright suite that drives the wallet app served by the dev server (`http://localhost:9000`) with the families mock flag enabled, exercising the Family page within the real app shell and router (not a Storybook iframe). The suite MUST run cross-platform (including macOS) and MUST NOT require a live chain or Rust IPC. It SHALL reuse the existing journey `data-testid` selectors.
|
||||
|
||||
#### Scenario: Suite drives the app shell, not Storybook
|
||||
|
||||
- **WHEN** the Playwright suite starts
|
||||
- **THEN** it launches (or reuses) the mock-wired dev server on `:9000` and navigates to the Family page within the app router
|
||||
- **AND** the page renders backed by `MockFamiliesContextProvider` with no IPC call
|
||||
|
||||
#### Scenario: Runs on the developer's platform
|
||||
|
||||
- **WHEN** the suite is run locally on macOS
|
||||
- **THEN** it executes the journeys in a real browser and reports pass/fail (no platform skip)
|
||||
|
||||
### Requirement: Owner lifecycle journey
|
||||
|
||||
The primary suite SHALL replay the owner lifecycle end to end against the app shell: create a family, invite the self-controlled node, accept the invite from the operator tab, kick the member, and disband the family, asserting the same post-step DOM transitions as the Storybook owner-lifecycle flow.
|
||||
|
||||
#### Scenario: Owner create-to-disband completes
|
||||
|
||||
- **WHEN** the suite runs the owner lifecycle against the owner-persona mock build
|
||||
- **THEN** creating a family reveals the owner management page
|
||||
- **AND** the invited node appears as a pending invite, then as a joined member after acceptance
|
||||
- **AND** kicking the member removes it, and disbanding returns the create-family entry point
|
||||
|
||||
### Requirement: Operator lifecycle journey
|
||||
|
||||
The primary suite SHALL replay the operator lifecycle end to end: accept an invite on one controlled node, leave that family, then reject an invite on another controlled node, asserting the same post-step DOM transitions as the Storybook operator-lifecycle flow, including that the reject-node invite group ends empty.
|
||||
|
||||
#### Scenario: Operator accept-leave-reject completes
|
||||
|
||||
- **WHEN** the suite runs the operator lifecycle against the operator-persona mock build
|
||||
- **THEN** accepting the invite shows the current-family card with a leave action
|
||||
- **AND** after leaving and rejecting the other node's invite, that node's invite group is empty
|
||||
|
||||
### Requirement: Selector and journey parity across suites
|
||||
|
||||
All e2e suites (primary Playwright, and the optional native-webview leg) SHALL target the same `data-testid` selectors and assert the same observable outcomes as the existing Storybook flow stories, so they verify equivalent behavior across environments. Confirmation dialogs that portal outside the page canvas SHALL be located by their global test ids, mirroring the Storybook `screen`-scoped queries.
|
||||
|
||||
#### Scenario: Equivalent assertions across environments
|
||||
|
||||
- **WHEN** a journey step is asserted in more than one suite
|
||||
- **THEN** every suite queries the same `data-testid` and expects the same visible/absent outcome
|
||||
|
||||
### Requirement: Optional native-webview validation leg
|
||||
|
||||
The project SHALL provide an optional WebdriverIO + `tauri-driver` leg that launches the packaged Tauri binary and replays the owner and operator journeys against the platform native webview, following the Tauri WebDriver-in-CI flow (Ubuntu runner, `xvfb-run` headless display, `webkit2gtk-driver` + `tauri-driver`). The leg MUST run in CI on a supported platform (Linux) and MUST skip — not fail — on unsupported platforms (macOS) or when `tauri-driver`/`webkit2gtk-driver` is absent.
|
||||
|
||||
#### Scenario: Native leg validates the binary in CI
|
||||
|
||||
- **WHEN** the native leg runs on the Linux CI runner
|
||||
- **THEN** it installs `webkit2gtk-driver` and `tauri-driver`, builds the mock-wired binary, and replays the owner and operator journeys under `xvfb-run`
|
||||
- **AND** a journey failure is reported (non-blocking while the leg is stabilizing)
|
||||
|
||||
#### Scenario: Skip-not-fail off-platform
|
||||
|
||||
- **WHEN** the native leg is invoked on macOS or without the required drivers
|
||||
- **THEN** it is skipped with a clear message rather than reported as a failure
|
||||
|
||||
### Requirement: Optional sandbox real-IPC read smoke
|
||||
|
||||
The project MAY provide a read-only smoke that exercises the real `FamiliesContextProvider` + `src/requests/families.ts` against the node-families contract deployed to sandbox, validating the IPC wiring the mock stands in for. The smoke SHALL be read-only (no create/invite/kick/disband against shared sandbox) and SHALL assert rendered shape rather than exact contents (or pin a known family id), and MUST be separate from and non-blocking relative to the deterministic mock suites.
|
||||
|
||||
#### Scenario: Sandbox read smoke renders real data
|
||||
|
||||
- **WHEN** the read smoke runs against the sandbox-connected app
|
||||
- **THEN** the Family page renders the sandbox family/member via real IPC without performing any state-changing transaction
|
||||
- **AND** a failure does not block the primary mock suites
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Native-webview e2e harness
|
||||
|
||||
The project SHALL provide a WebdriverIO test harness that drives the mock-wired Tauri binary through `tauri-driver` against the platform native webview. The harness MUST launch the actual desktop application (not a browser pointed at a dev server) and MUST NOT require a live chain or Rust IPC handlers, relying instead on the build-time mock providers.
|
||||
|
||||
#### Scenario: Harness launches the mock-wired binary
|
||||
|
||||
- **WHEN** the WebdriverIO suite starts
|
||||
- **THEN** `tauri-driver` launches the mock-wired Tauri binary and a session attaches to its native webview
|
||||
- **AND** the Family page is reachable within that session
|
||||
|
||||
#### Scenario: Unsupported platform is skipped, not failed
|
||||
|
||||
- **WHEN** the suite is invoked on macOS (no WKWebView driver)
|
||||
- **THEN** the WebdriverIO/`tauri-driver` suite is skipped with a clear message rather than reported as a failure
|
||||
|
||||
### Requirement: Owner lifecycle journey
|
||||
|
||||
The harness SHALL replay the owner lifecycle journey end to end against the native webview: create a family, invite the self-controlled node, accept the invite from the operator tab, kick the member, and disband the family. The journey SHALL assert the same post-step DOM transitions verified by the Storybook owner-lifecycle flow.
|
||||
|
||||
#### Scenario: Owner create-to-disband completes
|
||||
|
||||
- **WHEN** the harness runs the owner lifecycle against the owner-persona mock build
|
||||
- **THEN** creating a family reveals the owner management page
|
||||
- **AND** the invited node appears as a pending invite, then as a joined member after acceptance
|
||||
- **AND** kicking the member removes it, and disbanding returns the create-family entry point
|
||||
|
||||
### Requirement: Operator lifecycle journey
|
||||
|
||||
The harness SHALL replay the operator lifecycle journey end to end: accept an invite on one controlled node, leave that family, then reject an invite on another controlled node. The journey SHALL assert the same post-step DOM transitions verified by the Storybook operator-lifecycle flow, including that the reject-node invite group ends empty.
|
||||
|
||||
#### Scenario: Operator accept-leave-reject completes
|
||||
|
||||
- **WHEN** the harness runs the operator lifecycle against the operator-persona mock build
|
||||
- **THEN** accepting the invite shows the current-family card with a leave action
|
||||
- **AND** after leaving and rejecting the other node's invite, that node's invite group is empty
|
||||
|
||||
### Requirement: Selector and journey parity with Storybook
|
||||
|
||||
The WebdriverIO journeys SHALL target the same `data-testid` selectors and assert the same observable outcomes as the existing Storybook flow stories and Playwright specs, so the two suites verify equivalent behavior across the browser and native-webview environments. Confirmation dialogs that portal outside the page canvas SHALL be located by their global test ids, mirroring the Storybook `screen`-scoped queries.
|
||||
|
||||
#### Scenario: Equivalent assertions across environments
|
||||
|
||||
- **WHEN** a journey step is asserted in both the Playwright/Storybook suite and the WebdriverIO suite
|
||||
- **THEN** both suites query the same `data-testid` and expect the same visible/absent outcome
|
||||
|
||||
### Requirement: CI execution on a supported platform
|
||||
|
||||
The WebdriverIO suite SHALL run in CI on a supported platform (Linux). The CI job MUST install the platform webdriver (`WebKitWebDriver`) and `tauri-driver`, build the mock-wired binary, and execute the suite; failures of any owner or operator journey MUST fail the job.
|
||||
|
||||
#### Scenario: CI runs the native-webview journeys
|
||||
|
||||
- **WHEN** CI runs on Linux for the change
|
||||
- **THEN** the job installs `WebKitWebDriver` and `tauri-driver`, builds the mock-wired binary, and runs the WebdriverIO journeys
|
||||
- **AND** the job fails if any owner or operator journey assertion fails
|
||||
@@ -1,43 +1,44 @@
|
||||
## 1. Build-time mock provider seam
|
||||
|
||||
- [ ] 1.1 Add a webpack `DefinePlugin` constant for the families mock flag (e.g. `process.env.WALLET_MOCK_FAMILIES`, tri-state `owner|operator|off`, default `off`) in the dev/mock webpack config.
|
||||
- [ ] 1.1 Add a webpack `DefinePlugin` constant for the families mock flag (`WALLET_MOCK_FAMILIES`, tri-state `owner|operator|off`, default `off`) in the dev/mock webpack config.
|
||||
- [ ] 1.2 Introduce a provider-selection module that exports either `FamiliesContextProvider` (real) or `MockFamiliesContextProvider` (mock) based on the compile-time flag, behind a `const` guard so the unused branch tree-shakes. Keep this seam in its **own** module — do NOT make `FamilyPage.tsx` depend on the flag or on Tauri, so the merged `FamilyPage.figma.tsx` Code Connect mapping (`example: () => <FamilyPage />`) still imports it in isolation.
|
||||
- [ ] 1.3 Have the Family route/entry consume the selection module instead of importing `FamiliesContextProvider` directly; in mock mode seed `buildOwnerFlowStore` / `buildOperatorFlowStore` per the persona flag (reuse `families.fixtures.ts`).
|
||||
- [ ] 1.4 Ensure the Family page is reachable via normal in-app navigation in the mock build and renders inside the app shell (not a Storybook iframe), keeping the existing `data-testid`s.
|
||||
- [ ] 1.3 Have the Family route/entry consume the selection module; in mock mode seed `buildOwnerFlowStore` / `buildOperatorFlowStore` per the persona flag (reuse `families.fixtures.ts`).
|
||||
- [ ] 1.4 Ensure the Family page is reachable via normal in-app navigation on the dev server (`:9000`) and renders inside the app shell + router (not a Storybook iframe), keeping the existing `data-testid`s.
|
||||
- [ ] 1.5 Verify a default (flag `off`) production build excludes families mock-engine code (inspect bundle / add a guard); confirm the real provider still wires unchanged.
|
||||
- [ ] 1.6 Add npm scripts to launch the mock-wired dev server per persona (e.g. `WALLET_MOCK_FAMILIES=owner pnpm webpack:dev`).
|
||||
|
||||
## 2. Mock-wired Tauri build
|
||||
## 2. Primary e2e — Playwright against the mock-wired dev server
|
||||
|
||||
- [ ] 2.1 Add a mock webpack build script (and, if needed, a `tauri.conf` profile / `devUrl` wiring) that produces the app bundle with the mock define set.
|
||||
- [ ] 2.2 Add a `tauri:dev`/`tauri:build` variant (npm scripts) that launches/produces the mock-wired binary for each persona configuration.
|
||||
- [ ] 2.3 Manually confirm (on Linux, or via `tauri dev`) the mock-wired app opens to the correct persona entry state: owner → create-family entry; operator → seeded node invites.
|
||||
- [ ] 2.1 Repoint `playwright.config.ts` from Storybook (:6006) to the mock-wired dev server (`baseURL http://localhost:9000`), with a `webServer` that launches the dev server with the mock flag (per persona) and `reuseExistingServer` locally.
|
||||
- [ ] 2.2 Port the owner lifecycle journey (create → invite → accept → kick → disband) to drive the app-shell route, reusing the existing `data-testid` selectors and the Storybook flow steps.
|
||||
- [ ] 2.3 Port the operator lifecycle journey (accept → leave, then reject), asserting the reject-node invite group ends empty.
|
||||
- [ ] 2.4 Port the multi-node operator invite-states assertion (`node-invite-group-201` present, `node-invite-group-203-empty`).
|
||||
- [ ] 2.5 Handle portalled confirmation dialogs via their global test ids (mirror the Storybook `screen`-scoped queries).
|
||||
- [ ] 2.6 Decide whether to retire the old Storybook-iframe specs or keep both; if repointing, update `e2e/families.spec.ts` accordingly and factor shared selector/step constants for parity.
|
||||
- [ ] 2.7 Confirm the suite runs green locally on macOS (Chromium; optionally WebKit project).
|
||||
|
||||
## 3. WebdriverIO + tauri-driver harness
|
||||
## 3. CI — wire the primary suite in
|
||||
|
||||
- [ ] 3.1 Add `webdriverio` (+ runner/test framework) to `nym-wallet` dev deps; document `cargo install tauri-driver --locked`.
|
||||
- [ ] 3.2 Create the WebdriverIO config that starts `tauri-driver`, points `tauri:options.application` at the mock-wired binary, and sets sensible CI step timeouts.
|
||||
- [ ] 3.3 Implement a skip-not-fail guard: detect macOS / missing `tauri-driver` / missing `WebKitWebDriver` and skip the suite with a clear message (design D5).
|
||||
- [ ] 3.4 Add `test:e2e:tauri` npm script wiring the WebdriverIO run.
|
||||
- [ ] 3.1 Add a Playwright e2e step to the existing `build` job in `.github/workflows/ci-nym-wallet-frontend.yml` (it is not in CI today — only unit tests + `build-storybook` run); install browsers (`npx playwright install --with-deps chromium`) and run `test:e2e`.
|
||||
- [ ] 3.2 Ensure the step launches the mock-wired dev server (per persona) and fails the job on any owner/operator journey failure.
|
||||
|
||||
## 4. Journey specs (parity with Storybook)
|
||||
## 4. Optional — native-webview validation leg (WebdriverIO + tauri-driver)
|
||||
|
||||
- [ ] 4.1 Port the owner lifecycle journey (create → invite → accept → kick → disband) from `FamilyFlows.stories.tsx` / `e2e/families.spec.ts` to WebdriverIO, reusing the same `data-testid` selectors.
|
||||
- [ ] 4.2 Port the operator lifecycle journey (accept → leave, then reject) to WebdriverIO, asserting the reject-node invite group ends empty.
|
||||
- [ ] 4.3 Port the multi-node operator invite-states assertion (`node-invite-group-201` present, `node-invite-group-203-empty`).
|
||||
- [ ] 4.4 Handle portalled confirmation dialogs by locating their global test ids (mirror the Storybook `screen`-scoped queries).
|
||||
- [ ] 4.5 Factor shared selector/step constants so the Playwright and WebdriverIO suites stay observably equivalent (parity requirement).
|
||||
- [ ] 4.1 Add `webdriverio` (+ runner) to `nym-wallet` dev deps; document `cargo install tauri-driver --locked`.
|
||||
- [ ] 4.2 Create the WebdriverIO config that starts `tauri-driver`, points `tauri:options.application` at the mock-wired binary, and sets CI step timeouts; add a `test:e2e:tauri` script.
|
||||
- [ ] 4.3 Implement the skip-not-fail guard: detect macOS / missing `tauri-driver` / missing `webkit2gtk-driver` and skip with a clear message (design D5).
|
||||
- [ ] 4.4 Reuse the journey steps/selectors from §2 (shared constants) so the native leg asserts identical outcomes.
|
||||
- [ ] 4.5 Add a **separate** CI job (ubuntu-22.04) following the Tauri WebDriver-in-CI flow: install `libwebkit2gtk-4.1-dev` + `webkit2gtk-driver` + `xvfb`, set up Rust + cache, `cargo install tauri-driver --locked`, build the mock-wired binary, run the suite under `xvfb-run`. Start it `continue-on-error` until stable.
|
||||
|
||||
## 5. CI integration
|
||||
## 5. Optional — sandbox real-IPC read smoke
|
||||
|
||||
- [ ] 5.1 Add a **separate** job to `.github/workflows/ci-nym-wallet-frontend.yml` (alongside the existing `build` job, not appended to it) running on `ubuntu-22.04`, installing `WebKitWebDriver` (WebKitGTK) and `tauri-driver` (`cargo install --locked`, cached) plus the Tauri/Rust build toolchain.
|
||||
- [ ] 5.2 In that job, build the mock-wired binary (per persona) and run `test:e2e:tauri`; fail the job on any owner/operator journey failure.
|
||||
- [ ] 5.3 Decide whether to also wire the existing Playwright→Storybook `test:e2e` into the `build` job as the cross-platform check (it is currently NOT in CI — only unit tests + `build-storybook` run); document the two-suite split in the e2e README/comment.
|
||||
- [ ] 5.4 Optionally gate the native-webview job as non-blocking (`continue-on-error`) initially if flaky, with a note to promote it to required once stable.
|
||||
- [ ] 5.1 Add a read-only smoke (Playwright or a small harness) that loads the app against the real `FamiliesContextProvider` connected to sandbox and asserts the Family page renders the sandbox family/member via real IPC.
|
||||
- [ ] 5.2 Keep it read-only (no create/invite/kick/disband) and assert shape (or pin the known family id) rather than exact contents; mark the job non-blocking and separate from the mock suites.
|
||||
|
||||
## 6. Verification & docs
|
||||
|
||||
- [ ] 6.1 Run the WebdriverIO suite in CI (Linux) and confirm both owner and operator journeys pass against the native webview.
|
||||
- [ ] 6.2 Confirm the macOS local invocation skips cleanly (no red failure).
|
||||
- [ ] 6.1 Run the primary Playwright suite in CI and locally (macOS) — confirm both owner and operator journeys pass against the app shell.
|
||||
- [ ] 6.2 If implemented, confirm the native leg passes in Linux CI and skips cleanly on macOS.
|
||||
- [ ] 6.3 Confirm `tsc` + eslint stay clean and the production build is unaffected (no mock code, no behavior change).
|
||||
- [ ] 6.4a Confirm the provider seam didn't break Code Connect (`FamilyPage.figma.tsx` still type-checks / `figma connect` parse is clean) and that the Nym 2.0 theme swap left all journey `data-testid`s intact (selectors unchanged).
|
||||
- [ ] 6.4 Document the two-suite setup (Playwright→Storybook for local/cross-platform; WebdriverIO→tauri-driver for native-webview CI) and the mock-flag usage in the wallet README / e2e comments.
|
||||
- [ ] 6.4 Confirm the provider seam didn't break Code Connect (`FamilyPage.figma.tsx` still type-checks) and the Nym 2.0 theme swap left all journey `data-testid`s intact.
|
||||
- [ ] 6.5 Document the tiered setup (primary Playwright→dev-server; optional WebdriverIO→tauri-driver CI; optional sandbox read smoke) and the mock-flag usage in the wallet README / e2e comments.
|
||||
|
||||
Reference in New Issue
Block a user