Skip to content

feat(drivers): Tauri driver adapter and a runnable sample, run on Windows and Ubuntu (Task 2.2, part 3b) - #11

Open
Andreas-Froyland wants to merge 3 commits into
mainfrom
task-2.2d-tauri
Open

Andreas-Froyland wants to merge 3 commits into
mainfrom
task-2.2d-tauri

Conversation

@Andreas-Froyland

@Andreas-Froyland Andreas-Froyland commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

Task 2.2 of the plan, part 3b: the Tauri driver adapter and a runnable sample consumer, run for real on Windows and Ubuntu/Xvfb through release-qa run. With #8–#10 this meets Task 2.2's exit criterion. A maintainer or agent can run the sample on either OS without the dashboard, and a fresh Ubuntu machine was taken to a passing run by following the written guide.

The adapter (packages/qa/src/drivers/tauri.ts)

TauriApp.start(ctx, { application, nativeDriver }) runs the chain Stage 0 proved: WebdriverIO remote(), then tauri-driver 2.0.6, then Edge WebDriver or WebKitWebDriver, then the installed app. It offers browser, restart() and close(), and close() waits until the app has really exited.

  • Ownership. tauri-driver is started through ctx.spawn, so the run owns it. The native driver (found by parent pid) and the app (found by exact executable path, never by name) are recorded with ctx.own, so the runner reaps them even if a hook doesn't.
  • Before starting anything, it refuses:
    • a native driver path that doesn't exist;
    • an app that is already running (that instance wouldn't be the run's);
    • ports that are already in use.
  • A tauri-driver that dies is reported at once with its exit code. It used to wait out the full 60 s timeout. The cause was a real race: spawning waits to record the process, so a driver that dies instantly has already emitted exit.
  • Kept out of the main entry point, so the core never loads WebdriverIO.

The sample consumer (examples/tauri-smoke/qa/)

project.json, the lifecycle, and the Stage 0 persistence scenario. The scenario saves a value, reads it independently from disk, restarts and sees it, clears it, restarts and sees it gone. It waits with the runner's ctx.waitFor, so a value that never shows is failed, not an infrastructure error.

Windows. The NSIS installer runs silently into the test root. I measured what a per-user install creates outside its directory, by installing into a throwaway directory and diffing. It adds an uninstall entry, Start Menu and Desktop shortcuts, and HKCU\Software\frogbyte\Release QA Smoke, which the uninstaller leaves behind. The lifecycle handles all of this:

  • It refuses to run if the sample is already installed for the user.
  • Cleanup runs the uninstaller in place (_?=), removes the leftover key, and removes the vendor key only if it's empty. The rule for "empty" is based on measured reg query output: an empty key prints nothing, and a key with a subkey prints one line.

Linux. The .deb is unpacked into the test root (dpkg-deb -x). There's no root and nothing system-wide. I checked the unpacked binary is byte for byte what apt-get install puts in /usr/bin, and it's also Stage 0's recorded installed binary.

Real runs (evidence)

Windows 11 (this laptop's desktop) Ubuntu 24.04.5 (fresh WSL2 distro, then removed)
Setup Stage 0's drivers, re-downloaded and verified against Stage 0's hashes followed the guide literally, as root, from a clone of this branch
Passing runs 2 3
Negative checks disk assertion broken on purpose: failed, exit 1, cleanup ok; missing native driver: interrupted, exit 3 (now in 2 s, with the real cause), cleanup ok —
Left behind nothing (registry, shortcuts, app data, install dir, processes checked after every run) nothing (app data, install dir, processes checked)

doctor on Linux reports virtual (Xvfb serving :99). On WSL2, WSLg sets WAYLAND_DISPLAY, which would make GTK draw through WSLg instead of Xvfb. The guide says to unset it.

Decisions to review

  • Real runs happen on designated machines, not in CI. I first considered a CI job. tool-layout.md records that "packaged-app GUI runs need a designated machine and are not in CI", and hosted Windows runners are Windows Server rather than the Windows 11 desktop baseline. So I followed that decision and did it as in Stage 0: this laptop, plus a throwaway WSL2 distro. Say if you'd rather add a CI job.
  • WebdriverIO has an npm audit advisory. webdriverio 9.31.9 (Stage 0's version, pinned exactly) brings one advisory: extract-zip ≤2.0.1, a symlink path traversal when extracting an archive. It's reached through @puppeteer/browsers, which WebdriverIO uses to download browsers. The adapter never takes that path: it connects to a running tauri-driver, and drivers are fetched and hash-checked separately. No patched extract-zip exists, and npm's only fix is WebdriverIO below 8.15. This is recorded in tool-layout.md.
  • Linux unpacks instead of installing, which trades exercising the package manager for full ownership and no root.
  • The sample imports the package by relative path (../../../packages/qa/src/...) because nothing is published. Its qa/ files are type-checked as part of the package's typecheck.

Testing

  • Unit tests for the OS helpers (processes by exact executable, children, ports) and for the adapter's pre-session refusals, driven with Node standing in for a driver that can't start.

  • Mutation check: 9 rules, all killed, files restored.

  • The real behaviour is proven by the runs above, not by the unit suite.

  • Clean npm ci, npm run typecheck (now including the sample's qa/ files), npm test: 648 passed, 3 skipped. No leaked processes, temp directories or sample install traces.

  • CI on ubuntu-24.04 and windows-2025

Not verified

  • Evidence files. Attempts record evidence: []: the scenario takes no screenshots yet. The DOM checks and the independent disk read prove state, as in Stage 0.
  • Reliability at Stage 0's scale. These runs show the scenario passing through the runner (2 + 3 runs). They aren't Stage 0's 10×3 reliability study.
  • Installing the Linux package. The .deb is unpacked, so the package manager's steps and desktop integration aren't exercised.

🤖 Generated with Claude Code


Summary by cubic

Adds a Tauri driver adapter and a runnable sample consumer so the packaged sample app now passes release-qa run on an interactive Windows desktop and Ubuntu with Xvfb, meeting Task 2.2's exit criterion. Includes a setup guide and recorded evidence of real runs on both.

  • The adapter starts tauri-driver and opens a WebdriverIO session on the installed app, and owns the native driver and app processes so the runner reaps them.
  • It refuses to start when the native driver path is missing, the app is already running, or the driver ports are in use, and reports a tauri-driver that dies immediately with its exit code.
  • The sample's persistence scenario saves a value, verifies it on disk, restarts, clears, and restarts again; it waits via ctx.waitFor so a missing value is a failed scenario rather than an infrastructure error.
  • Windows installs silently via NSIS into the test root and cleanup removes the uninstaller's leftover registry key; Linux unpacks the .deb so no root access is needed.
  • Adds webdriverio 9.31.9 exactly pinned, kept out of the main entry point; the related npm audit advisory (extract-zip path traversal via @puppeteer/browsers) is documented and never reached by the adapter.

Written for commit 414e2da. Summary will update on new commits.

Review in cubic

Andreas-Froyland and others added 3 commits September 23, 2026 22:25
…sumer (Task 2.2, part 3b, in progress)

- packages/qa/src/drivers/tauri.ts: TauriApp starts tauri-driver as an owned process, opens a WebdriverIO session on
  the installed app, restarts and closes it (waiting for the app to exit), and owns the native driver and the app
  (found by parent pid and exact executable path) so the runner reaps them. Configuration mistakes are reported
  before anything starts; a tauri-driver that dies is reported at once with its exit code.
- packages/qa/src/drivers/os.ts: processes by exact executable, children of a process, port checks.
- examples/tauri-smoke/qa: project.json, the lifecycle (NSIS into the test root on Windows with the installer's
  outside effects undone; the .deb unpacked into the test root on Linux), and the Stage 0 persistence scenario.
- docs/guides/run-the-sample.md: the setup guide for a fresh Windows or Ubuntu machine.
- webdriverio 9.31.9 (the Stage 0 version), kept out of the package's main entry point.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…al sample runs

- Evidence: one passing run per platform as the CLI wrote it (Windows 11 desktop; a fresh Ubuntu 24.04 WSL2 machine
  set up by following the guide), with the versions and hashes behind each.
- tool-layout: the Stage 0 harness is promoted; the mapping says what the tool checks and what it does not (driver
  version match is a setup step, the installed binary was hash-checked by hand); the webdriverio dependency and its
  npm audit advisory (extract-zip via @puppeteer/browsers, a path the adapter never takes) are recorded.
- README and local-runs: the adapter and runnable sample exist, with links to the guide and evidence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18 issues found across 25 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="examples/tauri-smoke/qa/lifecycle.ts">

<violation number="1" location="examples/tauri-smoke/qa/lifecycle.ts:39">
P2: This code claims `installDir` as owned without checking that it was absent. Refuse an existing install directory before recording ownership; otherwise untracked files in a designated root can be overwritten and recursively deleted by cleanup.</violation>

<violation number="2" location="examples/tauri-smoke/qa/lifecycle.ts:40">
P1: The Windows installer creates artifacts outside the runner ledger, but this lifecycle has no durable cleanup record for them. If the Node process dies after the installer finishes, `reset` removes only `smoke-app`, leaves the registry and shortcuts behind, and the next run refuses the existing `UNINSTALL_KEY`; persist cleanup metadata for these side effects or make reset invoke the lifecycle-specific cleanup before clearing the dirty state.</violation>
</file>

<file name="examples/tauri-smoke/qa/app.ts">

<violation number="1" location="examples/tauri-smoke/qa/app.ts:26">
P2: An empty `XDG_DATA_HOME` is treated as configured even though the XDG fallback should apply; filtering it out makes the cache directory become `dataDirs()[0]`. Reset then leaves the real app data behind and `settingFile()` checks the wrong file. Use truthy fallback semantics for the XDG variables.</violation>

<violation number="2" location="examples/tauri-smoke/qa/app.ts:75">
P3: On Linux without `/usr/bin/WebKitWebDriver`, this error tells the user to configure a Windows Edge driver. Report the Linux-specific `WebKitWebDriver`/`webkit2gtk-driver` fix instead.</violation>
</file>

<file name="packages/qa/src/drivers/os.ts">

<violation number="1" location="packages/qa/src/drivers/os.ts:28">
P1: Process discovery fails open on Linux: any `/proc` access error becomes “no processes,” bypassing the already-running refusal and ownership tracking. Propagate enumeration errors and only ignore an individual PID that disappeared between the directory scan and the read.</violation>

<violation number="2" location="packages/qa/src/drivers/os.ts:55">
P2: The `timeoutMs` deadline is not enforced while `portInUse` awaits `connect`. A stalled loopback connection can remain pending beyond the deadline; add a socket timeout and destroy it on timeout.</violation>
</file>

<file name="packages/qa/src/drivers/tauri.ts">

<violation number="1" location="packages/qa/src/drivers/tauri.ts:75">
P2: Driver-death handling leaves `waitForPort` running after the exit race wins, so a failed start still keeps timers and socket probes alive for up to the 60-second timeout. Make the port wait abortable and cancel it when tauri-driver exits.</violation>

<violation number="2" location="packages/qa/src/drivers/tauri.ts:121">
P1: `#open()` records the application only after `remote()` resolves. If the launch phase times out or WebDriver rejects after launching the app, `#own` is never reached or is refused, so startup fails without a cleanup handle and the app can remain running outside the ledger. Make session startup transactional and retain cleanup ownership for any app created before rethrowing.</violation>

<violation number="3" location="packages/qa/src/drivers/tauri.ts:121">
P1: The post-session scan owns every process with the application path, including an instance started by someone else after preflight. Track the process launched by the native-driver chain or retain and exclude baseline identities before recording cleanup ownership.</violation>

<violation number="4" location="packages/qa/src/drivers/tauri.ts:128">
P1: An unidentifiable live process is silently omitted from the ownership ledger, so cleanup has nothing to reap. Retry and verify that the PID exited, or fail the start as infrastructure error instead of skipping it.</violation>
</file>

<file name="examples/tauri-smoke/scripts/write-candidate.mjs">

<violation number="1" location="examples/tauri-smoke/scripts/write-candidate.mjs:14">
P3: This fallback treats every non-Windows host as Linux. Reject unsupported platforms explicitly instead of labeling a macOS or other host's bundle as a Linux candidate.</violation>

<violation number="2" location="examples/tauri-smoke/scripts/write-candidate.mjs:23">
P2: The optional id is written verbatim, so values such as `stage 0` produce a manifest that `release-qa run` rejects as malformed. Validate the id before writing and fail with the accepted grammar.</violation>
</file>

<file name="packages/qa/test/drivers/tauri.test.ts">

<violation number="1" location="packages/qa/test/drivers/tauri.test.ts:14">
P3: `began` is captured before `await arrange(...)`, so the measured `ms` includes arrange's `acquireTestRoot` warm-up — the PowerShell identity lock that the fixture's own comment says "can take seconds on a busy machine" and was deliberately moved inside arrange to stay "outside any deadline". On a slow or loaded Windows machine, that noise counts against the `ms < 15_000` assertion and can make these tests flake. Start the clock after arrange so the bound measures only `executeScenario`.</violation>

<violation number="2" location="packages/qa/test/drivers/tauri.test.ts:35">
P3: This test is titled "reported with its exit code", but the assertion only matches /tauri-driver exited/. If the adapter stopped including the exit code in its message (`tauri-driver exited (${code ?? signal})...` in src/drivers/tauri.ts), this test would still pass, so the behavior the test name promises is not pinned. Assert the code explicitly.</violation>
</file>

<file name="examples/tauri-smoke/qa/persistence.spec.ts">

<violation number="1" location="examples/tauri-smoke/qa/persistence.spec.ts:29">
P2: A failed Save that never creates `setting.txt` throws `ENOENT` before `assert.equal`, so the runner reports infrastructure-error instead of candidate failure. Assert file existence first.</violation>

<violation number="2" location="examples/tauri-smoke/qa/persistence.spec.ts:39">
P2: After the final restart, the cleared state is verified only through the readout, and `#saved-value` starts empty in the static HTML (`examples/tauri-smoke/web/index.html`). `restart()` returns as soon as the WebDriver session is up, so `shows(ctx, '')` — which polls immediately at 200 ms — can pass during the window before the app's first `load_value`/`refresh` settles the DOM, even if a misbehaving candidate re-created `setting.txt` at startup. Every other state transition checks disk (`readFileSync`/`existsSync`); add the same disk assertion here so a startup re-write cannot slip through as a false pass.</violation>
</file>

<file name="examples/tauri-smoke/qa/evidence/linux/candidate.json">

<violation number="1" location="examples/tauri-smoke/qa/evidence/linux/candidate.json:8">
P2: This committed candidate manifest references a `.deb` that is not present, so `release-qa run --candidate` fails before it can reproduce the recorded Linux run. Include the artifact with the manifest, or store this as explicitly non-runnable metadata instead of a candidate manifest.</violation>
</file>

<file name="docs/guides/run-the-sample.md">

<violation number="1" location="docs/guides/run-the-sample.md:29">
P2: The Windows setup fails for the per-user WebView2 installation this section documents: it only queries HKLM, so `$v` is unavailable and the driver download URL cannot be formed. Query the HKCU location as a fallback before downloading.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

}
const dir = installDir(ctx);
await ctx.own({ kind: 'path', path: dir, label: 'installed app' });
if (windows) await runToEnd(ctx, 'installer', ctx.artifact.path, ['/S', `/D=${dir}`]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The Windows installer creates artifacts outside the runner ledger, but this lifecycle has no durable cleanup record for them. If the Node process dies after the installer finishes, reset removes only smoke-app, leaves the registry and shortcuts behind, and the next run refuses the existing UNINSTALL_KEY; persist cleanup metadata for these side effects or make reset invoke the lifecycle-specific cleanup before clearing the dirty state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/tauri-smoke/qa/lifecycle.ts, line 40:

<comment>The Windows installer creates artifacts outside the runner ledger, but this lifecycle has no durable cleanup record for them. If the Node process dies after the installer finishes, `reset` removes only `smoke-app`, leaves the registry and shortcuts behind, and the next run refuses the existing `UNINSTALL_KEY`; persist cleanup metadata for these side effects or make reset invoke the lifecycle-specific cleanup before clearing the dirty state.</comment>

<file context>
@@ -0,0 +1,103 @@
+  }
+  const dir = installDir(ctx);
+  await ctx.own({ kind: 'path', path: dir, label: 'installed app' });
+  if (windows) await runToEnd(ctx, 'installer', ctx.artifact.path, ['/S', `/D=${dir}`]);
+  else await runToEnd(ctx, 'unpack', 'dpkg-deb', ['-x', ctx.artifact.path, dir]);
+  if (!existsSync(executable(ctx))) throw new Error(`the installer finished but ${executable(ctx)} does not exist`);
</file context>

connectionRetryTimeout: this.#options.startTimeoutMs,
capabilities: { 'tauri:options': { application: this.#options.application } } as WebdriverIO.Capabilities,
});
await this.#own(await processesRunning(this.#options.application), 'application');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: #open() records the application only after remote() resolves. If the launch phase times out or WebDriver rejects after launching the app, #own is never reached or is refused, so startup fails without a cleanup handle and the app can remain running outside the ledger. Make session startup transactional and retain cleanup ownership for any app created before rethrowing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/src/drivers/tauri.ts, line 121:

<comment>`#open()` records the application only after `remote()` resolves. If the launch phase times out or WebDriver rejects after launching the app, `#own` is never reached or is refused, so startup fails without a cleanup handle and the app can remain running outside the ledger. Make session startup transactional and retain cleanup ownership for any app created before rethrowing.</comment>

<file context>
@@ -0,0 +1,131 @@
+      connectionRetryTimeout: this.#options.startTimeoutMs,
+      capabilities: { 'tauri:options': { application: this.#options.application } } as WebdriverIO.Capabilities,
+    });
+    await this.#own(await processesRunning(this.#options.application), 'application');
+  }
+
</file context>

return pidsIn(await output('powershell', ['-NoProfile', '-NonInteractive', '-Command', script]));
}
const pids: number[] = [];
for (const entry of await readdir('/proc').catch(() => [] as string[])) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Process discovery fails open on Linux: any /proc access error becomes “no processes,” bypassing the already-running refusal and ownership tracking. Propagate enumeration errors and only ignore an individual PID that disappeared between the directory scan and the read.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/src/drivers/os.ts, line 28:

<comment>Process discovery fails open on Linux: any `/proc` access error becomes “no processes,” bypassing the already-running refusal and ownership tracking. Propagate enumeration errors and only ignore an individual PID that disappeared between the directory scan and the read.</comment>

<file context>
@@ -0,0 +1,69 @@
+    return pidsIn(await output('powershell', ['-NoProfile', '-NonInteractive', '-Command', script]));
+  }
+  const pids: number[] = [];
+  for (const entry of await readdir('/proc').catch(() => [] as string[])) {
+    if (!/^\d+$/.test(entry)) continue;
+    const exe = await readlink(`/proc/${entry}/exe`).catch(() => undefined);
</file context>

connectionRetryTimeout: this.#options.startTimeoutMs,
capabilities: { 'tauri:options': { application: this.#options.application } } as WebdriverIO.Capabilities,
});
await this.#own(await processesRunning(this.#options.application), 'application');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The post-session scan owns every process with the application path, including an instance started by someone else after preflight. Track the process launched by the native-driver chain or retain and exclude baseline identities before recording cleanup ownership.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/src/drivers/tauri.ts, line 121:

<comment>The post-session scan owns every process with the application path, including an instance started by someone else after preflight. Track the process launched by the native-driver chain or retain and exclude baseline identities before recording cleanup ownership.</comment>

<file context>
@@ -0,0 +1,131 @@
+      connectionRetryTimeout: this.#options.startTimeoutMs,
+      capabilities: { 'tauri:options': { application: this.#options.application } } as WebdriverIO.Capabilities,
+    });
+    await this.#own(await processesRunning(this.#options.application), 'application');
+  }
+
</file context>

for (const pid of pids) {
const identity = await processIdentity(pid);
// A process that already exited needs no owning; one that is alive but unidentifiable cannot be owned safely.
if (identity !== undefined) await this.#ctx.own({ kind: 'process', pid, identity, label });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: An unidentifiable live process is silently omitted from the ownership ledger, so cleanup has nothing to reap. Retry and verify that the PID exited, or fail the start as infrastructure error instead of skipping it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/src/drivers/tauri.ts, line 128:

<comment>An unidentifiable live process is silently omitted from the ownership ledger, so cleanup has nothing to reap. Retry and verify that the PID exited, or fail the start as infrastructure error instead of skipping it.</comment>

<file context>
@@ -0,0 +1,131 @@
+    for (const pid of pids) {
+      const identity = await processIdentity(pid);
+      // A process that already exited needs no owning; one that is alive but unidentifiable cannot be owned safely.
+      if (identity !== undefined) await this.#ctx.own({ kind: 'process', pid, identity, label });
+    }
+  }
</file context>

else driver.once('exit', fail);
});
exited.catch(() => undefined); // it also exits normally later, when the run's cleanup stops it
await Promise.race([waitForPort(port, app.#options.startTimeoutMs), exited]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Driver-death handling leaves waitForPort running after the exit race wins, so a failed start still keeps timers and socket probes alive for up to the 60-second timeout. Make the port wait abortable and cancel it when tauri-driver exits.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/src/drivers/tauri.ts, line 75:

<comment>Driver-death handling leaves `waitForPort` running after the exit race wins, so a failed start still keeps timers and socket probes alive for up to the 60-second timeout. Make the port wait abortable and cancel it when tauri-driver exits.</comment>

<file context>
@@ -0,0 +1,131 @@
+      else driver.once('exit', fail);
+    });
+    exited.catch(() => undefined); // it also exits normally later, when the run's cleanup stops it
+    await Promise.race([waitForPort(port, app.#options.startTimeoutMs), exited]);
+    // The native driver is tauri-driver's child, not the run's; own it so it cannot outlive the run.
+    await app.#own(await childrenOf(driver.pid as number), 'native driver');
</file context>

import { fileURLToPath } from 'node:url';

const bundle = join(dirname(fileURLToPath(import.meta.url)), '..', 'src-tauri', 'target', 'release', 'bundle');
const [profile, kind, extension] = process.platform === 'win32' ? ['windows', 'nsis', '.exe'] : ['linux', 'deb', '.deb'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This fallback treats every non-Windows host as Linux. Reject unsupported platforms explicitly instead of labeling a macOS or other host's bundle as a Linux candidate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/tauri-smoke/scripts/write-candidate.mjs, line 14:

<comment>This fallback treats every non-Windows host as Linux. Reject unsupported platforms explicitly instead of labeling a macOS or other host's bundle as a Linux candidate.</comment>

<file context>
@@ -0,0 +1,26 @@
+import { fileURLToPath } from 'node:url';
+
+const bundle = join(dirname(fileURLToPath(import.meta.url)), '..', 'src-tauri', 'target', 'release', 'bundle');
+const [profile, kind, extension] = process.platform === 'win32' ? ['windows', 'nsis', '.exe'] : ['linux', 'deb', '.deb'];
+
+const files = readdirSync(join(bundle, kind)).filter((name) => name.endsWith(extension));
</file context>

afterEach(cleanUpProcessesAndRoots);

async function launchWith(options: Parameters<typeof TauriApp.start>[1]) {
const began = Date.now();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: began is captured before await arrange(...), so the measured ms includes arrange's acquireTestRoot warm-up — the PowerShell identity lock that the fixture's own comment says "can take seconds on a busy machine" and was deliberately moved inside arrange to stay "outside any deadline". On a slow or loaded Windows machine, that noise counts against the ms < 15_000 assertion and can make these tests flake. Start the clock after arrange so the bound measures only executeScenario.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/test/drivers/tauri.test.ts, line 14:

<comment>`began` is captured before `await arrange(...)`, so the measured `ms` includes arrange's `acquireTestRoot` warm-up — the PowerShell identity lock that the fixture's own comment says "can take seconds on a busy machine" and was deliberately moved inside arrange to stay "outside any deadline". On a slow or loaded Windows machine, that noise counts against the `ms < 15_000` assertion and can make these tests flake. Start the clock after arrange so the bound measures only `executeScenario`.</comment>

<file context>
@@ -0,0 +1,56 @@
+afterEach(cleanUpProcessesAndRoots);
+
+async function launchWith(options: Parameters<typeof TauriApp.start>[1]) {
+  const began = Date.now();
+  const { context } = await arrange({
+    lifecycle: lifecycleOf([], { launch: async (ctx) => { await TauriApp.start(ctx, options); } }),
</file context>
Suggested change
const began = Date.now();
const { context } = await arrange({
lifecycle: lifecycleOf([], { launch: async (ctx) => { await TauriApp.start(ctx, options); } }),
timeouts: { phaseMs: 30_000, stepsMs: 2000, cleanupMs: 20_000 },
});
const began = Date.now();

// Node rejects tauri-driver's arguments and exits at once: a driver that cannot start.
const { result, ms } = await launchWith({ application: `${process.execPath}.not-running`, nativeDriver: process.execPath, tauriDriver: process.execPath, startTimeoutMs: 20_000 });
expect(result).toMatchObject({ outcome: 'interrupted', reason: 'infrastructure-error' });
expect(result.detail).toMatch(/tauri-driver exited/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This test is titled "reported with its exit code", but the assertion only matches /tauri-driver exited/. If the adapter stopped including the exit code in its message (tauri-driver exited (${code ?? signal})... in src/drivers/tauri.ts), this test would still pass, so the behavior the test name promises is not pinned. Assert the code explicitly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/qa/test/drivers/tauri.test.ts, line 35:

<comment>This test is titled "reported with its exit code", but the assertion only matches /tauri-driver exited/. If the adapter stopped including the exit code in its message (`tauri-driver exited (${code ?? signal})...` in src/drivers/tauri.ts), this test would still pass, so the behavior the test name promises is not pinned. Assert the code explicitly.</comment>

<file context>
@@ -0,0 +1,56 @@
+    // Node rejects tauri-driver's arguments and exits at once: a driver that cannot start.
+    const { result, ms } = await launchWith({ application: `${process.execPath}.not-running`, nativeDriver: process.execPath, tauriDriver: process.execPath, startTimeoutMs: 20_000 });
+    expect(result).toMatchObject({ outcome: 'interrupted', reason: 'infrastructure-error' });
+    expect(result.detail).toMatch(/tauri-driver exited/);
+    expect(ms).toBeLessThan(15_000);
+  });
</file context>
Suggested change
expect(result.detail).toMatch(/tauri-driver exited/);
expect(result.detail).toMatch(/tauri-driver exited \(\d+\) before it listened on port/);

const configured = process.env.RELEASE_QA_NATIVE_DRIVER;
if (configured !== undefined && configured !== '') return configured;
if (!windows && existsSync('/usr/bin/WebKitWebDriver')) return '/usr/bin/WebKitWebDriver';
throw new Error('set RELEASE_QA_NATIVE_DRIVER to the msedgedriver.exe matching the installed WebView2 runtime (see the setup guide)');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: On Linux without /usr/bin/WebKitWebDriver, this error tells the user to configure a Windows Edge driver. Report the Linux-specific WebKitWebDriver/webkit2gtk-driver fix instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/tauri-smoke/qa/app.ts, line 75:

<comment>On Linux without `/usr/bin/WebKitWebDriver`, this error tells the user to configure a Windows Edge driver. Report the Linux-specific `WebKitWebDriver`/`webkit2gtk-driver` fix instead.</comment>

<file context>
@@ -0,0 +1,84 @@
+  const configured = process.env.RELEASE_QA_NATIVE_DRIVER;
+  if (configured !== undefined && configured !== '') return configured;
+  if (!windows && existsSync('/usr/bin/WebKitWebDriver')) return '/usr/bin/WebKitWebDriver';
+  throw new Error('set RELEASE_QA_NATIVE_DRIVER to the msedgedriver.exe matching the installed WebView2 runtime (see the setup guide)');
+}
+
</file context>
Suggested change
throw new Error('set RELEASE_QA_NATIVE_DRIVER to the msedgedriver.exe matching the installed WebView2 runtime (see the setup guide)');
throw new Error(windows ? 'set RELEASE_QA_NATIVE_DRIVER to the msedgedriver.exe matching the installed WebView2 runtime (see the setup guide)' : 'install webkit2gtk-driver or set RELEASE_QA_NATIVE_DRIVER to WebKitWebDriver (see the setup guide)');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant