Files
upstream-mirror feda81bf2f
Some checks failed
Audit / audit-js (push) Failing after 0s
bench / bench (map[os:ubuntu-latest runner:xvfb-run --auto-servernum target:x86_64-unknown-linux-gnu], nightly) (push) Failing after 0s
covector version or publish / run-integration-tests (ubuntu-latest) (push) Failing after 0s
Audit / audit-rust (push) Failing after 0s
test `@tauri-apps/cli` / test (ubuntu-latest) (push) Failing after 0s
test `tauri-cli` / test (map[os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
deploy schema worker / deploy (push) Failing after 0s
lint rust / clippy (map[cargo:cargo include: os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
lint rust / clippy (map[cargo:cross include:--package api os:ubuntu-latest target:aarch64-linux-android]) (push) Failing after 0s
supply chain health status / check rust dependencies with cargo vet (push) Failing after 0s
test `@tauri-apps/api` e2e (mobile) / android (push) Failing after 0s
test `@tauri-apps/api` e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
Udeps / changes (push) Failing after 0s
Udeps / setup (push) Has been skipped
Udeps / udeps (push) Has been skipped
bench / bench (map[os:macos-latest runner: target:aarch64-apple-darwin], nightly) (push) Has been cancelled
bench / bench (map[os:windows-latest runner: target:x86_64-pc-windows-msvc], nightly) (push) Has been cancelled
covector version or publish / run-integration-tests (macos-latest) (push) Has been cancelled
covector version or publish / run-integration-tests (windows-latest) (push) Has been cancelled
covector version or publish / version-or-publish (push) Has been cancelled
test `@tauri-apps/cli` / test (macos-latest) (push) Has been cancelled
test `tauri-cli` / test (map[args:--no-default-features --features native-tls-vendored os:windows-latest restore-only:true target:aarch64-pc-windows-msvc]) (push) Has been cancelled
test `tauri-cli` / test (map[os:macos-latest target:x86_64-apple-darwin]) (push) Has been cancelled
test `tauri-cli` / test (map[os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:macos-latest target:aarch64-apple-darwin]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include:--package api os:macos-latest target:aarch64-apple-ios]) (push) Has been cancelled
test `@tauri-apps/api` e2e (mobile) / ios (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
test `@tauri-apps/cli` / test (windows-latest) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
snapshot of https://github.com/tauri-apps/tauri.git (dev, 2026-09-23; blobs > 131072B omitted)
2026-09-23 17:00:42 +05:00

197 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `@tauri-apps/api` end-to-end tests
WebdriverIO suite that exercises every [`@tauri-apps/api`](../api) module against a
**real** Tauri app — the [`examples/api`](../../examples/api) validation app — rather than
a mocked backend, on desktop (Linux, macOS, Windows) and mobile (Android, iOS). Each module
has its own spec file, shared by every platform, and adding coverage for a new API is
normally just dropping in one more spec.
## How it works
- The example app is built with `withGlobalTauri: true`, so the entire API surface is
reachable on `window.__TAURI__` inside the webview.
- On desktop, WebdriverIO drives the app through [`@crabnebula/tauri-driver`](https://www.npmjs.com/package/@crabnebula/tauri-driver),
which bridges the WebDriver protocol to each platform's webview:
- **macOS** — the CrabNebula Webdriver, which needs [`tauri-plugin-automation`](https://crates.io/crates/tauri-plugin-automation)
(registered in `examples/api` behind its off-by-default `automation` Cargo feature, which
the suite's build enables) and a locally-running `@crabnebula/test-runner-backend`,
authenticated with `CN_API_KEY`.
- **Linux** — `webkit2gtk-driver` (`WebKitWebDriver` on `PATH`).
- **Windows** — `msedgedriver.exe` on `PATH`. It hands the app the `--remote-debugging-port`
it attaches to through `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS`, which WebView2 ignores in an
elevated process ([wry#1782](https://github.com/tauri-apps/wry/issues/1782)), so the suite has
to run unelevated.
- On mobile, WebdriverIO drives the app through [Appium](https://appium.io) (started by
`@wdio/appium-service`; the drivers are plain devDependencies of this package, which Appium
picks up on its own):
- **Android** — the UiAutomator2 driver. The suite switches to the app's `WEBVIEW_*`
context, which chromedriver reaches through the WebView's debugging socket. Debug builds
turn that on (`setWebContentsDebuggingEnabled`), so the suite builds a debug APK. A
chromedriver matching the device's WebView is downloaded on demand (see `E2E_CHROMEDRIVER`).
- **iOS** — the XCUITest driver on a simulator, attaching to the WKWebView through the
WebKit remote inspector. Debug builds mark the webview `isInspectable`, so the suite
builds an unsigned debug simulator app. The inspector identifies an app by the
`application-identifier` entitlement that Xcode embeds when it code signs a simulator
build; an unsigned one has none and is listed as `process-<executable name>` instead of
its bundle identifier, so the config has the driver match that name too
(`appium:additionalWebviewBundleIds`). The driver also starts with a script timeout of
0, which the config raises to the 30s the other drivers default to, or every
`executeAsync` would time out at once.
- Specs never `eval` in the page. They pass a function to the [`tauri()`](test/helpers/index.ts)
helper, which serializes it and runs it via the driver's own (CSP-exempt) script injection,
handing it `window.__TAURI__` as the first argument and returning its JSON result.
- Each spec file gets its own session — a fresh `tauri-driver` (and therefore a fresh app
instance) on desktop, a fresh Appium session (which relaunches the app) on mobile — so
each module's suite runs in isolation.
## Prerequisites
```sh
# from the repo root
pnpm install
pnpm build:api # examples/api resolves @tauri-apps/api from packages/api/dist
pnpm build:cli # examples/api's `tauri` script uses the local native CLI
```
Platform driver dependencies:
| Platform | Requirement |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| macOS | `CN_API_KEY` env var (CrabNebula Cloud). The automation plugin and test-runner-backend are wired up already. |
| Linux | `webkit2gtk-driver` package (provides `WebKitWebDriver`). |
| Windows | `msedgedriver.exe` matching your Edge version, on `PATH`. Run the suite unelevated. |
| Android | The usual Tauri Android setup (`ANDROID_HOME`, `NDK_HOME`, a JDK), plus a running emulator or a connected device with USB debugging. Network access the first time, for the chromedriver download. |
| iOS | macOS with Xcode and an iOS simulator runtime. `tauri ios init` installs [xcodegen](https://github.com/yonaskolb/XcodeGen) through Homebrew if missing. The first session compiles WebDriverAgent (a few minutes). |
## Running
```sh
# from the repo root
pnpm test:api-e2e
# or from this package
pnpm e2e
# iterate without rebuilding the app every run
E2E_SKIP_BUILD=1 pnpm e2e
# run a single module's spec
pnpm exec wdio run ./wdio.conf.ts --spec test/specs/window.spec.ts
# mobile (from the repo root; or `pnpm e2e:android` / `pnpm e2e:ios` from this package)
pnpm test:api-e2e:android
pnpm test:api-e2e:ios
```
The first run builds the app with [`tauri.e2e.conf.json`](tauri.e2e.conf.json) as a config
override, which enables the example's `automation` feature; afterwards use `E2E_SKIP_BUILD=1`
to reuse the existing binary. A binary supplied through `E2E_SKIP_BUILD` or `E2E_APP_PATH`
must have been built with that feature whenever the CrabNebula Webdriver is in use (always
on macOS).
The mobile configs ([`wdio.android.conf.ts`](wdio.android.conf.ts), [`wdio.ios.conf.ts`](wdio.ios.conf.ts),
sharing [`wdio.mobile.ts`](wdio.mobile.ts)) initialize the example's Android/Xcode project if
`src-tauri/gen` is missing, then run `tauri android build --debug --apk` /
`tauri ios build --debug --target aarch64-sim --no-sign`, compiling only the Rust target the
device runs (the Android one is read from the connected device through `adb`). The app must be
a **debug** build — release builds have webview debugging off, and Appium cannot see the page.
`E2E_SKIP_BUILD` and `E2E_APP_PATH` (an `.apk` / simulator `.app`) work as on desktop.
On iOS, the XCUITest driver compiles WebDriverAgent with xcodebuild in the first session, which
takes minutes on a CI runner. Appium's own release build of it for the simulator skips that:
`APPIUM_HOME=$PWD pnpm exec appium driver run xcuitest download-wda -- --outdir <dir> --platform iOS --kind sim`
(from this package), then `E2E_IOS_WDA=<dir>/WebDriverAgentRunner-Runner.app`, and the driver
installs and launches it as is (`appium:usePreinstalledWDA`).
The generated Gradle and Xcode projects call back into the CLI with `pnpm tauri …` from
`src-tauri` / `gen/apple`, which pnpm 12.0–12.3 could not resolve to the package's scripts
([pnpm/pnpm#14645](https://github.com/pnpm/pnpm/pull/14645)); the repo's `packageManager` pins a
fixed version, so run the suite through that pnpm (corepack) rather than an older global one.
## Environment variables
| Variable | Purpose |
| ------------------- | --------------------------------------------------------------------------------- |
| `CN_API_KEY` | CrabNebula Cloud key. Required on macOS (and whenever `E2E_CN_WEBDRIVER=1`). |
| `E2E_SKIP_BUILD` | Skip the `tauri build` step and reuse the existing binary. |
| `E2E_APP_PATH` | Absolute path to a prebuilt app/binary to test (also implies skip-build). |
| `E2E_SKIP` | Comma-separated module names to skip, e.g. `E2E_SKIP=tray,menu`. |
| `E2E_SKIP_WM` | Skip window-manager-dependent tests (minimize/maximize/fullscreen/position/hide). |
| `E2E_SPEC_RETRIES` | Retry count for flaky spec files (default `0`). |
| `E2E_CN_WEBDRIVER` | Use the CrabNebula Webdriver on Linux/Windows too (instead of the native driver). |
| `E2E_NATIVE_DRIVER` | Path passed to `tauri-driver --native-driver` (e.g. a specific chromedriver). |
| `CARGO_TARGET_DIR` | Override the target dir the app binary is looked up in. |
Mobile only:
| Variable | Purpose |
| -------------------- | ---------------------------------------------------------------------------------------------------- |
| `E2E_ANDROID_TARGET` | Rust target for the APK (`aarch64`, `armv7`, `i686`, `x86_64`); default: the connected device's ABI. |
| `E2E_ANDROID_DEVICE` | `adb` serial of the device/emulator to use (`appium:udid`); default: the first connected one. |
| `E2E_ANDROID_AVD` | Name of an AVD for Appium to boot (`appium:avd`) instead of using an already-running emulator. |
| `E2E_CHROMEDRIVER` | chromedriver binary matching the device's WebView, instead of letting Appium download one. |
| `E2E_IOS_TARGET` | Rust target for the simulator app (`aarch64-sim` or `x86_64`); default: the host architecture. |
| `E2E_IOS_DEVICE` | Simulator UDID or name (as in `xcrun simctl list`); default: a booted iPhone, else the newest one. |
| `E2E_IOS_WDA` | Prebuilt `WebDriverAgentRunner-Runner.app` for the simulator, used instead of compiling WDA. |
| `E2E_PLATFORM` | Set by the mobile configs for the spec workers (`android`/`ios`) — see `platform` in the helpers. |
Appium's own log is written to `logs/wdio-appium.log` in this package.
## Adding tests for a new API
1. **Add a spec.** Create `test/specs/<module>.spec.ts` and use `describeApi('<module>', …)`
with the `tauri()` helper. It is picked up automatically by the `test/specs/**/*.spec.ts`
glob. Minimal example:
```ts
import { expect } from '@wdio/globals'
import { tauri, describeApi } from '../helpers/index.js'
describeApi('app', () => {
it('reports the product name', async () => {
expect(await tauri((api) => api.app.getName())).toBe('Tauri API')
})
})
```
2. **Grant permissions if needed.** If the API calls a command that is not allowed by
default, add the permission to
[`examples/api/src-tauri/capabilities/run-app.json`](../../examples/api/src-tauri/capabilities/run-app.json).
The default permission sets are documented under
`crates/tauri/permissions/*/autogenerated/reference.md`.
3. **Add a Rust command if needed.** If the API needs a custom backend command, add it to
[`examples/api/src-tauri/src/cmd.rs`](../../examples/api/src-tauri/src/cmd.rs), register it
in the `generate_handler!` list in
[`src/lib.rs`](../../examples/api/src-tauri/src/lib.rs), add its permission, and grant it
in `run-app.json`.
4. **Handle environment-sensitive cases.** Use `itWm` (instead of `it`) for assertions that
depend on a real window manager, and `eventually()` to poll for state a WM applies
asynchronously. Branch on `platform` from the helpers (never `process.platform`, which is
the host running the emulator/simulator on mobile) for platform-specific behavior.
5. **Gate what mobile does not have.** The same specs run on Android and iOS. Wrap tests of
desktop-only commands (`#[cfg(desktop)]` in the core plugins, or no-ops on mobile such as
the window title and size) in `itDesktop`, use `itOn('android', …)` / `itOn('ios', …)` for
platform-specific APIs, and pass `{ desktopOnly: true }` to `describeApi` for modules whose
plugin is not registered on mobile at all (`menu`, `tray`). Skipped tests show up as pending
rather than silently disappearing.
### Rules for `tauri()` page functions
The function you pass to `tauri()` runs **inside the webview**, serialized as a string:
- It **cannot** close over anything from the spec module — pass every value it needs through
the trailing `tauri(fn, ...args)` arguments.
- It may only reference `api` (the `window.__TAURI__` object), those args, and browser
globals (`window`, `document`, `setTimeout`, `Promise`, …).
- Its return value must be JSON-serializable — return plain objects/primitives, not class
instances (call methods and return their results instead).
- Restore any app state you mutate (title, size, theme, …); tests within a spec file share
the same app instance.
- For in-page waiting, wrap logic in a `Promise` with an explicit `setTimeout` rejection so a
failure surfaces as a message rather than an opaque driver timeout.
Use `tauriError(fn, ...args)` to assert that a call rejects; it returns the rejection message.