Skip to content

feat(e2e): add @appduct/e2e for the e2e test runner - #265

Merged
V3RON merged 9 commits into
mainfrom
feat/e2e-appduct-integration
Oct 21, 2026
Merged

V3RON merged 9 commits into
mainfrom
feat/e2e-appduct-integration

Conversation

@V3RON

@V3RON V3RON commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

No issue: this came out of a design conversation about binding Appduct to the e2e test runner.

What changed

New package @appduct/e2e. withAppduct(test) adds an appduct fixture that calls the app's tools from an e2e test, and appductTools() gives e2e's agent appduct_tools and appduct_call. The fixture connects on first use, reuses a session while the app stays connected across tests (serial groups, tests that don't relaunch) and revokes a session the app has left before pairing again. Devices get the link through e2e's device.openLink; on iOS the fixture accepts the simulator's one-time "Open in …?" question when the claim doesn't arrive within 2 s. Two appduct/client changes support it: AppClient.state() and revoke(), and a fix for waitForSession() missing a claim that landed between its first look and its subscription (web pages claim fast enough to hit it).

Acceptance criteria

# Criterion Test Tier
1 A test calls the app's tools through the appduct fixture on web, iOS and Android packages/e2e/e2e/tests/tools.e2e.ts device
2 e2e's agent lists and calls the app's tools tools.e2e.ts "the agent calls the app's tools" (scripted model) device
3 Tests in a serial group share one session packages/e2e/e2e/tests/sessions.e2e.ts device
4 A session the app left is revoked and the next use pairs again; reuse while active; one re-pair per call; concurrent calls re-pair once packages/e2e/src/__tests__/session.test.ts unit
5 Calls wait up to 10 s for a tool the app registers late session.test.ts unit
6 AppClient.state() reports active/suspended; revoke() ends the session and disconnects the app packages/appduct/src/__tests__/e2e/client.e2e.test.ts integration
7 waitForSession() resolves for a claim that lands before its subscription takes effect, and rejects at once when subscribing fails packages/appduct/src/__tests__/rpc-client.test.ts "waitForSession" unit
8 Device links open through e2e; iOS's question is accepted only when the claim is late; a web page that doesn't connect fails in 10 s with a hint packages/e2e/src/__tests__/pairing.test.ts unit
9 The agent tools call the running test's session and refuse after it ends packages/e2e/src/__tests__/agent-tools.test.ts unit
10 The release checks, packs and publishes @appduct/e2e in lockstep packages/appduct/src/__tests__/release-version.e2e.test.ts unit

E2E evidence

The e2e-device suite was not run. The package's own suite (packages/e2e/e2e, run through the e2e CLI) passed:

Target Tests Notes
web (playground-web, Chromium) 6/6
iOS 26.4 simulator, newly created (playground-native) 6/6 one device.alert accept (3.7 s) on the first link, none after
iOS 26.4 simulator, reused 6/6 no prompt
Android 15 emulator (playground-native) 6/6

Last run after the review fixes, all three targets together: 18/18, 15 pairings, 12 revoked, one session left per target.

Checklist

  • CHANGELOG.md has an entry under Unreleased (writing-changelog skill), or the change is not user-visible
  • User-facing docs updated for every surface the change touches (writing-user-docs skill), or the change is not user-visible
  • No new import past a module's index.ts; no new direct node:* I/O outside an adapter
  • Simplification checklist from the architecture skill applied, exceptions explained above
  • docs/ARCHITECTURE.md updated if a surface it describes changed

packages/e2e/src/index.ts casts the fixture definition: e2e's mapped FixtureDefinitions type can't prove appduct is absent from a generic base. It is also the package's composition root: it builds the real clock and the appductDaemon adapter; memory-daemon.ts is the in-memory fake beside it.

Before the next release: npm trusted publishing has to be set up for @appduct/e2e, like the other packages, or the new publish-e2e job fails.

Out of scope

  • One session per worker stays until the daemon's grace period: e2e gives tests no hook at worker or run end. A reporter (onRunFinished) could clean up later.
  • The website has no page for @appduct/e2e yet; the package README covers it.

Status

Implement: done Review: done (10 findings fixed) E2E: package suite done, device suite pending Ready: no

@V3RON

V3RON commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Code review (/code-review) found 10 issues; all fixed in 3018ae9, 0c5685c and c68af17.

# Finding Fix
1 call()'s 10 s wait for a late tool started before pairing, so a slow device launch used it up The wait starts at the app's first tool_not_found
2 Same for tools() and an empty list The wait starts at the first answer
3 @appduct/e2e was not versioned, packed or published by the release Added to release-version.mjs, deploy.yaml (publish-e2e, after the CLI) and the cut-release skill
4 revoke() and close() ran together, so the revoke could be dropped close() waits for the revoke
5 The fixture withdrew the agent's session without try/finally Wrapped in try/finally
6 waitForSession() ignored unknown_session from events.subscribe too, then hung until its timeout The subscribe and the look have their own handlers; a failed subscribe rejects at once
7 A failed pairing left its pending link in the daemon Links last only as long as the claim wait (60 s device, 10 s web)
8 A web page without @appduct/web hung each use for 60 s Web claims wait 10 s and the error says what to check
9 Agent tools found the session through module state, which two copies of the module wouldn't share The session lives on globalThis under a Symbol.for key
10 Pairing reached the daemon without a port, so the iOS prompt logic had no tests Daemon port with appductDaemon and an in-memory fake; 8 pairing tests

Every new test was red before its fix, and a mutation of each fixed line fails a test. The package suite passes on web, an iOS 26.4 simulator and an Android 15 emulator (18/18).

@V3RON
V3RON merged commit d8f4d43 into main Oct 21, 2026
12 checks passed
@V3RON
V3RON deleted the feat/e2e-appduct-integration branch October 21, 2026 18:37
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