163 lines
6.5 KiB
Markdown
163 lines
6.5 KiB
Markdown
# Testing
|
|
|
|
Readest uses three test tiers, all powered by [Vitest](https://vitest.dev/).
|
|
|
|
## Unit Tests (`pnpm test`)
|
|
|
|
Runs tests in a **jsdom** environment. No browser or Tauri runtime required.
|
|
|
|
```bash
|
|
pnpm test # Run all unit tests
|
|
pnpm test -- src/__tests__/utils/misc.test.ts # Run a single file
|
|
pnpm test -- --watch # Watch mode
|
|
```
|
|
|
|
- **Config:** `vitest.config.mts`
|
|
- **Pattern:** `src/**/*.test.ts` (excludes `*.browser.test.ts` and `*.tauri.test.ts`)
|
|
- **Environment:** jsdom
|
|
- **Use for:** Pure logic, utilities, services that don't need real browser APIs or Tauri IPC.
|
|
|
|
## Browser Tests (`pnpm test:browser`)
|
|
|
|
Runs tests in a **real Chromium** browser via Playwright. Required for code that depends on Web Workers, SharedArrayBuffer, OPFS, or other browser-only APIs.
|
|
|
|
```bash
|
|
pnpm test:browser
|
|
```
|
|
|
|
- **Config:** `vitest.browser.config.mts`
|
|
- **Pattern:** `src/**/*.browser.test.ts`
|
|
- **Browser:** Chromium (headless, via `@vitest/browser-playwright`)
|
|
- **Use for:** WASM modules (e.g. `@tursodatabase/database-wasm`), Web Worker integration, browser-specific storage APIs.
|
|
|
|
## Tauri Integration Tests (`pnpm test:tauri`)
|
|
|
|
Runs Vitest tests **inside the Tauri WebView**, with access to Tauri IPC and native plugin commands. Tests execute in the actual app environment.
|
|
|
|
### Step 1: Start the Tauri App
|
|
|
|
In one terminal, start the app with the `webdriver` feature enabled:
|
|
|
|
```bash
|
|
pnpm tauri:dev:test # Dev mode (uses tauri dev server, faster iteration)
|
|
pnpm tauri:build:test # Debug release build (closer to production)
|
|
```
|
|
|
|
These commands compile the Rust backend with `--features webdriver`, which:
|
|
|
|
- Includes `tauri-plugin-webdriver` (embeds a W3C WebDriver server on port 4445)
|
|
- Adds a runtime capability granting plugin permissions to remote URLs (`http://127.0.0.1:*`), so Vitest's browser-mode iframe can call Tauri IPC
|
|
|
|
Keep this running while you run tests.
|
|
|
|
### Step 2: Run Tests
|
|
|
|
In another terminal:
|
|
|
|
```bash
|
|
pnpm test:tauri
|
|
```
|
|
|
|
Vitest connects directly to the embedded WebDriver server (port 4445) in the running Tauri app and executes tests inside its WebView.
|
|
|
|
- **Config:** `vitest.tauri.config.mts`
|
|
- **Pattern:** `src/**/*.tauri.test.ts`
|
|
- **Browser provider:** `@vitest/browser-webdriverio` (connects to port 4445)
|
|
- **Use for:** Tauri plugin commands (turso, native-tts, etc.), native filesystem, Tauri IPC.
|
|
|
|
### Writing Tauri Tests
|
|
|
|
Tests access Tauri IPC via a shared helper:
|
|
|
|
```typescript
|
|
import { invoke } from '../tauri/tauri-invoke';
|
|
|
|
it('calls a plugin command', async () => {
|
|
const result = await invoke('plugin:turso|load', { options: { path: 'sqlite::memory:' } });
|
|
expect(result).toBeDefined();
|
|
});
|
|
```
|
|
|
|
The `invoke()` helper accesses `window.top.__TAURI_INTERNALS__` (Vitest runs in an iframe, Tauri injects IPC into the main frame).
|
|
|
|
**Limitations:** Only custom invoke commands and plugin commands listed in the webdriver capability work. Standard Tauri JS APIs (e.g. `@tauri-apps/api`) that rely on `URL: local` may not work from the Vitest iframe.
|
|
|
|
## Android Device E2E (`pnpm test:android`)
|
|
|
|
Drives the **installed Readest app** on an adb-connected Android device or
|
|
emulator: gestures are injected with `adb shell input`, and the app's state is
|
|
probed through the WebView's **Chrome DevTools Protocol** (forwarded from the
|
|
`webview_devtools_remote_<pid>` abstract socket). This is the only lane that
|
|
exercises real Android touch selection, native handle behavior, and page-turn
|
|
gestures (e.g. the issue #1553 hyphen-selection fixes).
|
|
|
|
```bash
|
|
# One-time: install a dev build on the device/emulator
|
|
pnpm dev-android
|
|
|
|
# Start an emulator if no device is attached (see `emulator -list-avds`)
|
|
emulator -avd Pixel_9_Pro &
|
|
|
|
# Run the lane (soft-skips when no adb/device/app is available)
|
|
pnpm test:android
|
|
|
|
# With several devices attached, pick one:
|
|
ANDROID_SERIAL=emulator-5554 pnpm test:android
|
|
```
|
|
|
|
- **Config:** `vitest.android.config.mts` (node environment, serial execution, `retry: 1`)
|
|
- **Pattern:** `src/**/*.android.test.ts`
|
|
- **Helpers:** `src/__tests__/android/helpers/` — `adb.ts` (gestures), `cdp.ts` (DevTools client), `reader.ts` (app-level probes)
|
|
- **Fixtures:** plain EPUBs from `src/__tests__/fixtures/data/` (e.g. `sample-alice.epub`), opened transiently via a `VIEW` intent so the device library is never modified
|
|
- **Use for:** native text selection, touch gestures, selection handles, anything that only reproduces in the Android WebView compositor.
|
|
|
|
### Conventions
|
|
|
|
- **Probe, don't hardcode:** locate words/handles at runtime via CDP and derive
|
|
device pixels from `devicePixelRatio` — never bake in coordinates.
|
|
- **Poll, don't sleep:** use `waitFor()` on an observable condition (selection
|
|
state, handle count, frame position); reserve fixed pauses for gesture
|
|
pacing (long-press hold, corner dwell).
|
|
- **Discover, don't assume:** the harness finds a hyphenated on-screen
|
|
paragraph at runtime and derives every gesture target from live layout, so
|
|
any English fixture works regardless of fonts or screen size (hyphenation
|
|
is on by default in the app).
|
|
- **Serial only:** one device, one app — the config disables parallelism.
|
|
|
|
### CI
|
|
|
|
`.github/workflows/android-e2e.yml` runs the lane on an x86_64 emulator
|
|
(ubuntu runner with KVM): it builds a **debug** APK for `x86_64` (no signing
|
|
secrets needed), boots a cached AVD via `reactivecircus/android-emulator-runner`,
|
|
installs the APK, and runs `pnpm test:android`. It is intentionally not
|
|
PR-blocking — it runs nightly, on `workflow_dispatch`, or when a PR gets the
|
|
`e2e-android` label.
|
|
|
|
## E2E Tests (WDIO)
|
|
|
|
Full end-to-end tests using WebDriverIO, for UI-level testing against the running Tauri app. Same two-step workflow as Tauri integration tests.
|
|
|
|
```bash
|
|
# Terminal 1: start the app (same as for Tauri integration tests)
|
|
pnpm tauri:dev:test
|
|
|
|
# Terminal 2: run E2E tests
|
|
pnpm test:e2e
|
|
```
|
|
|
|
- **Config:** `wdio.conf.ts`
|
|
- **Pattern:** `e2e/**/*.e2e.ts`
|
|
- **Framework:** Mocha (via `@wdio/mocha-framework`)
|
|
- **Connects to:** port 4445 (embedded WebDriver server)
|
|
- **Use for:** UI interaction tests, window management, navigation flows.
|
|
|
|
## Test File Naming
|
|
|
|
| Suffix | Runner | Environment |
|
|
| ------------------- | ------------------- | --------------------- |
|
|
| `*.test.ts` | `pnpm test` | jsdom |
|
|
| `*.browser.test.ts` | `pnpm test:browser` | Chromium (Playwright) |
|
|
| `*.tauri.test.ts` | `pnpm test:tauri` | Tauri WebView |
|
|
| `*.e2e.ts` | `pnpm test:e2e` | Tauri app (WDIO) |
|
|
| `*.android.test.ts` | `pnpm test:android` | Android device (CDP) |
|