NYM-1199: Finalize native webview E2E wiring / unblock mock binary build
This commit completes the setup for `tauri-driver` E2E tests by correctly wiring the mock binary and resolving a blocking build issue. Key changes include: - Implementing the `tauri.mock.conf.json` to boot the mock binary into `main.mock.html` and configuring persona-specific in-webview navigation within the E2E spec. - Resolving a long-standing `webpack:prod` failure by adjusting `tsconfig.json` with `outDir: ./.tsbuild` and excluding test files from the main program, alongside adding a `declare module '*.css'` type declaration. - Updating `wdio.conf.ts` and documentation to reflect the completed native leg wiring.
This commit is contained in:
@@ -1 +1,2 @@
|
||||
test-results/
|
||||
.tsbuild/
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
import { FAMILY_NODES, TID } from '../e2e/shared/families';
|
||||
import { FAMILY_IDS, FAMILY_NODES, TID } from '../e2e/shared/families';
|
||||
|
||||
/**
|
||||
* Native-webview replay of the owner + operator journeys (design D4), sharing selectors +
|
||||
* fixtures with the primary Playwright suite via `e2e/shared/families` (parity requirement).
|
||||
*
|
||||
* SCAFFOLD: runnable once the native-leg wiring lands (see wdio.conf.ts) — the mock-wired
|
||||
* binary must launch the Family page per persona. Each `describe` below assumes the binary
|
||||
* was started in the matching persona (owner / operator); per-persona launch is the TODO.
|
||||
* The mock binary boots into `main.mock.html` (owner persona) — so the owner journey runs on
|
||||
* launch with no navigation. The operator journey navigates the webview to the operator persona
|
||||
* first. On Linux/WebKitGTK the Tauri asset scheme is `tauri://localhost/` (the mock config sets
|
||||
* `useHttpsScheme: false`); adjust `appUrl` if a platform serves a different scheme.
|
||||
*/
|
||||
|
||||
const appUrl = (persona: 'owner' | 'operator' | 'operator-seeded') =>
|
||||
`tauri://localhost/main.mock.html?persona=${persona}`;
|
||||
|
||||
const byId = (id: string) => $(`[data-testid="${id}"]`);
|
||||
const inGroup = (node: number, id: string) => byId(TID.inviteGroup(node)).$(`[data-testid="${id}"]`);
|
||||
const inSection = (node: number, id: string) => byId(TID.operatorNodeSection(node)).$(`[data-testid="${id}"]`);
|
||||
|
||||
const { ownerFlow, operatorAccept, operatorReject } = FAMILY_NODES;
|
||||
|
||||
describe('Families flows — native webview (owner persona)', () => {
|
||||
describe('Families flows — native webview', () => {
|
||||
it('owner lifecycle: create → invite → accept → kick → disband', async () => {
|
||||
const fid = FAMILY_IDS.ownerFlow;
|
||||
|
||||
await byId(TID.createFamilyName).waitForDisplayed({ timeout: 30_000 });
|
||||
await byId(TID.createFamilyName).setValue('Flow Family');
|
||||
await byId(TID.createFamilyDescription).setValue('A family created in a flow test.');
|
||||
@@ -28,11 +34,11 @@ describe('Families flows — native webview (owner persona)', () => {
|
||||
await byId(TID.pendingInvite(ownerFlow)).waitForDisplayed();
|
||||
|
||||
await byId(TID.tabOperator).click();
|
||||
await inGroup(ownerFlow, TID.acceptCard).click();
|
||||
await byId(TID.acceptConfirm).click();
|
||||
await byId(TID.operatorNodeFamily(ownerFlow)).waitForDisplayed();
|
||||
await inSection(ownerFlow, TID.acceptCard(fid)).click();
|
||||
await byId(TID.acceptConfirm(fid)).click();
|
||||
|
||||
await byId(TID.tabOwner).click();
|
||||
await byId(TID.memberJoined(ownerFlow)).waitForDisplayed();
|
||||
await byId(TID.memberJoinedKick(ownerFlow)).click();
|
||||
await byId(TID.memberJoinedKickConfirm(ownerFlow)).click();
|
||||
await byId(TID.memberJoined(ownerFlow)).waitForExist({ reverse: true });
|
||||
@@ -41,20 +47,21 @@ describe('Families flows — native webview (owner persona)', () => {
|
||||
await byId(TID.deleteConfirm).click();
|
||||
await byId(TID.createFamilyName).waitForDisplayed();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Families flows — native webview (operator persona)', () => {
|
||||
it('operator lifecycle: accept → leave, then reject', async () => {
|
||||
await byId(TID.tabOperator).click();
|
||||
await inGroup(operatorAccept, TID.acceptCard).click();
|
||||
await byId(TID.acceptConfirm).click();
|
||||
await byId(TID.operatorNodeFamily(operatorAccept)).waitForDisplayed();
|
||||
const fid = FAMILY_IDS.operatorFlow;
|
||||
await browser.url(appUrl('operator'));
|
||||
|
||||
await byId(TID.leaveButton).click();
|
||||
await byId(TID.tabOperator).click();
|
||||
await inSection(operatorAccept, TID.acceptCard(fid)).click();
|
||||
await byId(TID.acceptConfirm(fid)).click();
|
||||
await inSection(operatorAccept, TID.leaveButton).waitForDisplayed();
|
||||
|
||||
await inSection(operatorAccept, TID.leaveButton).click();
|
||||
await byId(TID.leaveConfirm).click();
|
||||
|
||||
await inGroup(operatorReject, TID.rejectCard).click();
|
||||
await byId(TID.rejectConfirm).click();
|
||||
await inSection(operatorReject, TID.rejectCard(fid)).click();
|
||||
await byId(TID.rejectConfirm(fid)).click();
|
||||
await byId(TID.inviteGroupEmpty(operatorReject)).waitForDisplayed();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,8 +36,17 @@ non-blocking `e2e-tauri` CI job under `xvfb`.
|
||||
|
||||
- Config: [`../wdio.conf.ts`](../wdio.conf.ts) · Spec: [`../e2e-tauri/families.tauri.ts`](../e2e-tauri/families.tauri.ts)
|
||||
- Prereqs (Linux): `webkit2gtk-driver`, `cargo install tauri-driver --locked`.
|
||||
- **TODO (native-leg wiring):** point the mock binary's window at `main.mock.html?persona=…`
|
||||
and confirm the release binary path in `wdio.conf.ts`. Until then this tier is a scaffold.
|
||||
- Binary wiring: `pnpm tauri:build:mock` builds the frontend with `WALLET_MOCK_FAMILIES=on`
|
||||
and a Tauri binary whose window boots `main.mock.html` (owner persona) via
|
||||
[`../src-tauri/tauri.mock.conf.json`](../src-tauri/tauri.mock.conf.json); the spec navigates to
|
||||
other personas in-webview. `wdio.conf.ts` points at `src-tauri/target/release/NymWallet`.
|
||||
- The run itself is Linux-CI-only (`tauri-driver`; macOS skips via `e2e-tauri/run.mjs`).
|
||||
|
||||
Note: `tauri:build:mock` runs `webpack:prod`, which was previously broken repo-wide — the
|
||||
shared `ForkTsCheckerWebpackPlugin` (`write-references` emit mode) + `allowJs` emitted `.js`
|
||||
into `src` (polluting it / breaking Jest) and type-checked test files. Fixed in the wallet via
|
||||
`outDir: ./.tsbuild` (redirects the emit), `declare module '*.css'` (`src/typings/css.d.ts`),
|
||||
and excluding `**/*.test.*` from the type-check program.
|
||||
|
||||
## Tier 3 — Sandbox real-IPC read smoke (optional, manual)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
## 4. Optional — native-webview validation leg (WebdriverIO + tauri-driver)
|
||||
|
||||
- [x] 4.1 Add `webdriverio` + `@wdio/{cli,local-runner,mocha-framework,spec-reporter}` + `tsx` to dev deps; documented `cargo install tauri-driver --locked` (README + CI).
|
||||
- [x] 4.2 Create `wdio.conf.ts` (starts `tauri-driver`, `tauri:options.application` → built binary, mocha timeouts) + `test:e2e:tauri` script. **TODO in-file:** point the mock binary's window at `main.mock.html?persona=…` + confirm the release binary path (native-leg wiring).
|
||||
- [x] 4.2 Create `wdio.conf.ts` (starts `tauri-driver`, `tauri:options.application` → release binary, mocha timeouts) + `test:e2e:tauri` script. **Binary wiring DONE:** `src-tauri/tauri.mock.conf.json` overrides the window to boot `main.mock.html` (owner persona); `tauri:build:mock` = `WALLET_MOCK_FAMILIES=on webpack:prod` + `tauri build --no-bundle --config tauri.mock.conf.json`. Operator persona reached via in-webview `browser.url('tauri://localhost/main.mock.html?persona=operator')` in the spec.
|
||||
- [x] 4.3 Implement the skip-not-fail guard (`e2e-tauri/run.mjs`): macOS / missing `tauri-driver` / missing `webkit2gtk-driver` → exit 0 with a clear message (design D5).
|
||||
- [x] 4.4 Reuse the journey selectors from §2 via `e2e/shared/families.ts` (`e2e-tauri/families.tauri.ts`), so the native leg asserts identical outcomes.
|
||||
- [x] 4.5 Add a **separate** `e2e-tauri` CI job (ubuntu-22.04, `continue-on-error`) following the Tauri WebDriver-in-CI flow: `libwebkit2gtk-4.1-dev` + `webkit2gtk-driver` + `xvfb`, Rust + cache, `cargo install tauri-driver --locked`, build mock binary, run under `xvfb-run`.
|
||||
@@ -39,7 +39,8 @@
|
||||
## 6. Verification & docs
|
||||
|
||||
- [x] 6.1 Run the primary Playwright suite locally (macOS) — **DONE: 3/3 green** against the mock-wired app shell. (CI execution still pending the first push.) Fixes needed to get a clean browser render: skip React Refresh/HMR + the dev-server live-reload client in the mock build (`webpack.dev.js`, avoids missing `core-js-pure`/`ansi-html-community`); add the relative `node_modules` walk for the mock build (pnpm strict + absolute `resolve.modules` dropped `object-assign`); make `src/utils/common.ts` resolve `getCurrentWebviewWindow()` lazily (was crashing at import outside Tauri).
|
||||
- [ ] 6.2 Confirm the native leg passes in Linux CI and skips cleanly on macOS — **pending** the native-leg wiring (4.2 TODO) + a Linux CI run.
|
||||
- [x] 6.2a **Fixed the pre-existing `webpack:prod` failure** that blocked the mock binary (and `pnpm build` generally). Root cause: the shared `ForkTsCheckerWebpackPlugin` runs in `mode: 'write-references'` (emit) and, with the wallet's `allowJs: true` + no `outDir`, emitted `.js` next to sources — polluting `src` (broke Jest), erroring "would overwrite input file" on `.test.js`/`.test.ts` pairs, and type-checking test files (jest globals). Contained wallet fix: add `declare module '*.css'` (`src/typings/css.d.ts`), set `outDir: ./.tsbuild` (redirects the emit out of `src`; `tsc --noEmit`/ts-loader/ts-jest ignore it), and exclude `**/*.test.*` from the type-check program (Jest still type-checks tests via ts-jest). Result: `webpack:prod` exits 0, emits `dist/main.mock.html`, no `src` pollution.
|
||||
- [ ] 6.2b Confirm the native leg passes in Linux CI and skips cleanly on macOS — wiring done (4.2), prod build fixed (6.2a); remaining: build the mock binary (`tauri:build:mock`, in progress locally) and run the WebdriverIO suite, which is **Linux/Windows-only** (`tauri-driver`), so it executes in the `e2e-tauri` CI job.
|
||||
- [x] 6.3 Confirm `tsc` + eslint stay clean and the production build is unaffected — verified: after `pnpm install`, `tsc` is fully clean (exit 0); `main.mock.tsx` + `utils/common.ts` lint clean; webpack prod-safe (no `mainMock` entry with flag off).
|
||||
- [x] 6.4 Confirm the provider seam didn't break Code Connect (seam is a separate entry; `FamilyPage.tsx` + `FamilyPageRoute.tsx` untouched; `FamilyPage.figma.tsx` now type-checks once `@figma/code-connect` is installed) and the Nym 2.0 theme swap left journey `data-testid`s intact (color-only).
|
||||
- [x] 6.5 Document the tiered setup + mock-flag usage (`e2e/README.md`).
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"tauri:build:adhoc": "APPLE_SIGNING_IDENTITY=- tauri build -b app",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:buildx86": "tauri build --target x86_64-apple-darwin",
|
||||
"tauri:build:mock": "WALLET_MOCK_FAMILIES=on run-s webpack:prod tauri:build:no-sign",
|
||||
"tauri:build:mock": "WALLET_MOCK_FAMILIES=on run-s webpack:prod tauri:build:mock:bin",
|
||||
"tauri:build:mock:bin": "tauri build --no-bundle --config src-tauri/tauri.mock.conf.json",
|
||||
"test": "jest --config jest.config.cjs",
|
||||
"tsc": "tsc --noEmit true",
|
||||
"tsc:watch": "tsc --noEmit true --watch",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"label": "main",
|
||||
"title": "Nym Wallet (mock e2e)",
|
||||
"url": "main.mock.html",
|
||||
"width": 1268,
|
||||
"height": 768,
|
||||
"minWidth": 1024,
|
||||
"minHeight": 640,
|
||||
"resizable": true,
|
||||
"useHttpsScheme": false,
|
||||
"backgroundColor": "#242b2d"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
// Side-effect CSS imports (e.g. `import '@assets/fonts/.../fonts.css'`). Webpack handles the
|
||||
// actual loading; this just gives the type-checker an ambient module so it doesn't error (TS2882).
|
||||
declare module '*.css';
|
||||
@@ -14,6 +14,11 @@
|
||||
"isolatedModules": false,
|
||||
"jsx": "react-jsx",
|
||||
"sourceMap": true,
|
||||
// The shared ForkTsCheckerWebpackPlugin runs in `write-references` (emit) mode; with
|
||||
// `allowJs` this would emit `.js` next to sources, polluting `src` (breaks Jest) and
|
||||
// erroring "would overwrite input file". Redirect that emit to a throwaway dir.
|
||||
// (`tsc --noEmit` and ts-loader/ts-jest ignore this; webpack controls its own output.)
|
||||
"outDir": "./.tsbuild",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@assets/*": ["../assets/*"],
|
||||
@@ -34,6 +39,10 @@
|
||||
"e2e",
|
||||
"e2e-tauri",
|
||||
"playwright.config.ts",
|
||||
"wdio.conf.ts"
|
||||
"wdio.conf.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.test.js",
|
||||
".tsbuild"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ import path from 'node:path';
|
||||
* Optional native-webview e2e config (design D4): drives the packaged Tauri binary through
|
||||
* tauri-driver + WebdriverIO. Launched via `e2e-tauri/run.mjs` (skip-not-fail on macOS).
|
||||
*
|
||||
* TODO (native-leg wiring — the remaining work to make this runnable):
|
||||
* 1. `application` below must point at the MOCK-WIRED binary (built with
|
||||
* WALLET_MOCK_FAMILIES=on, `tauri:build:mock`). Confirm the release binary path/name.
|
||||
* 2. The mock binary's window must open `main.mock.html?persona=...` (Tauri window URL),
|
||||
* so the journeys land on the Family page per persona. Until then this is a scaffold.
|
||||
* The mock binary (`pnpm tauri:build:mock`, built with WALLET_MOCK_FAMILIES=on and the
|
||||
* `tauri.mock.conf.json` override) boots its window directly into `main.mock.html` — the mock
|
||||
* app shell, no Tauri auth/login — so the owner journey runs on launch. Other personas are
|
||||
* reached by in-webview navigation (see `appUrl` in the spec); on Linux/WebKitGTK the asset
|
||||
* scheme is `tauri://localhost/` (the mock config sets `useHttpsScheme: false`).
|
||||
*/
|
||||
|
||||
let tauriDriver: ChildProcess | undefined;
|
||||
|
||||
// Linux release output; mainBinaryName is "NymWallet" (see tauri.conf.json). Adjust if the
|
||||
// mock build emits a distinct artifact.
|
||||
const APPLICATION = path.resolve(__dirname, 'src-tauri/target/release/NymWallet');
|
||||
// `--no-bundle` release binary. The Cargo workspace target dir is at the wallet root
|
||||
// (`<wallet>/target/release`), not `src-tauri/target`. mainBinaryName is "NymWallet".
|
||||
const APPLICATION = path.resolve(__dirname, 'target/release/NymWallet');
|
||||
|
||||
export const config: WebdriverIO.Config = {
|
||||
runner: 'local',
|
||||
|
||||
Reference in New Issue
Block a user