mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
WIP: docs: refresh after iOS + web support (#50)
* docs: README covers iOS + web, surface both example apps * docs(cli): document --ios-device and per-platform doctor * docs: tighten README, fold examples into Docs list * docs(runs): correct --clear-data lifecycle wording Default behavior no longer wipes app data between runs; --clear-data is now opt-in. * docs(getting-started): add iOS path, separate folio and folio-web Document just test-ios under examples/folio, and distinguish the KMP sample from the React + Vite folio-web sample. * docs(inspect): document the eight panels Lists Screenshot, ActionList, Timeline, ViolationsPanel, HierarchyPanel, SnapshotTable, MetricsChart, ExceptionsPanel. Cross-links HierarchyPanel to the spec language reference. * docs(writing-specs): document setup export, flag noLogcatErrors as android-only Mirrors pkg/spec/README.md so the manual covers the runner's setup-first fall-through. Marks noLogcatErrors as Android-only so iOS/web spec authors know it silently no-ops. * docs(folio): document web target and iOS sanderling test recipe After the KMP refactor folio also runs on wasmJs and the justfile exposes just web, just web-build, and just test-ios. Surface all three. * docs(folio-web): add README Covers prerequisites, demo credentials, just test recipe, and how the React + Vite host exposes state to the sanderling spec via stable ids and data-* attributes. * docs: scrub driver-implementation name from user docs Drop the implementation tool name from README, cli.md doctor table, and spec-language.md. These docs should describe behaviour, not the specific underlying tool the native sidecar wraps. * docs(development): scrub driver-implementation name from dev docs architecture, design-principles, decisions now describe the native sidecar by role (gRPC surface over OS UI-test pipeline) rather than by the specific tool it wraps.
This commit is contained in:
12 files changed
+178
-28
No files matched your search
@@ -14,7 +14,7 @@ flowchart TB
|
||||
R --> T["Trace writer\nJSONL + PNG"]
|
||||
end
|
||||
|
||||
SC["Maestro sidecar (JVM)"]
|
||||
SC["Native sidecar (JVM)"]
|
||||
DC["Device / Emulator"]
|
||||
CH["Chrome (CDP)"]
|
||||
RD[("runs/")]
|
||||
@@ -32,7 +32,7 @@ flowchart TB
|
||||
|
||||
**sanderling (Go).** The top-level binary. Bundles the spec with esbuild, evaluates it in goja, runs the main loop, dispatches actions through the `DeviceDriver` interface, writes the trace.
|
||||
|
||||
**Maestro sidecar (JVM).** A Kotlin process that wraps `maestro-client` and exposes a gRPC surface matching the `DeviceDriver` interface. Handles UI input, screenshots, the system accessibility tree, and OS-level alerts. Native platforms only.
|
||||
**Native sidecar (JVM).** A Kotlin process that exposes a gRPC surface matching the `DeviceDriver` interface. Handles UI input, screenshots, the system accessibility tree, and OS-level alerts. Native platforms only.
|
||||
|
||||
**Chrome (CDP).** For web targets, the Go binary drives Chrome directly over the Chrome DevTools Protocol. No sidecar is involved.
|
||||
|
||||
@@ -40,7 +40,7 @@ flowchart TB
|
||||
|
||||
| Channel | Platform | Transport | Purpose |
|
||||
|---|---|---|---|
|
||||
| Go to Maestro sidecar | Native | gRPC (localhost TCP) | UI input, screenshots, system alerts |
|
||||
| Go to native sidecar | Native | gRPC (localhost TCP) | UI input, screenshots, system alerts |
|
||||
| Go to Chrome | Web | Chrome DevTools Protocol | UI input, screenshots, DOM hierarchy, console logs |
|
||||
|
||||
On native, the transport split exists because only real UI events need to cross process and OS-API boundaries. Introspection is cheap, frequent, and lives on a fast local socket directly to the app. On web, CDP handles both.
|
||||
@@ -63,7 +63,7 @@ fetch state ─► evaluate properties ─► pick action ─► dispatch
|
||||
2. The runner fetches the UI hierarchy and logs from the sidecar.
|
||||
3. The runner feeds state into goja. Extractors re-read; properties re-evaluate; the action generator returns a weighted tree.
|
||||
4. The runner writes the trace entry for this step.
|
||||
5. The runner picks an action by weight and dispatches it through the driver (gRPC to sidecar -> Maestro -> UIAutomator or XCTest).
|
||||
5. The runner picks an action by weight and dispatches it through the driver (gRPC to sidecar -> UIAutomator or XCTest).
|
||||
6. Loop.
|
||||
|
||||
**Web (Chrome):**
|
||||
|
||||
@@ -20,7 +20,7 @@ Go's `internal/` directory restriction prevents any code outside this module fro
|
||||
|
||||
### `internal/driver/` is an interface + subdirectory implementations
|
||||
|
||||
The `driver.go` file defines the `DeviceDriver` interface. Concrete implementations live in subdirectories: `sidecar/` (Maestro gRPC), `chrome/` (CDP), `mock/` (tests). This pattern keeps the runner and verifier decoupled from any specific platform.
|
||||
The `driver.go` file defines the `DeviceDriver` interface. Concrete implementations live in subdirectories: `sidecar/` (gRPC to the native sidecar), `chrome/` (CDP), `mock/` (tests). This pattern keeps the runner and verifier decoupled from any specific platform.
|
||||
|
||||
### `internal/verifier/marshal.go` moves to `internal/inspect/`
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ title: Design principles
|
||||
|
||||
## 1. The app owns introspection; the driver owns input
|
||||
|
||||
On native platforms, the in-app SDK knows the state: view hierarchy, coverage, logs, exceptions, custom extractors. Maestro causes the state to change through taps, swipes, typed text, and deep links. The Go runner decides what to do.
|
||||
On native platforms, the in-app SDK knows the state: view hierarchy, coverage, logs, exceptions, custom extractors. The driver causes the state to change through taps, swipes, typed text, and deep links. The Go runner decides what to do.
|
||||
|
||||
Splitting these responsibilities is what makes the system work across iOS and Android with one spec surface. Neither Maestro nor the SDK alone is sufficient.
|
||||
Splitting these responsibilities is what makes the system work across iOS and Android with one spec surface. Neither the driver nor the SDK alone is sufficient.
|
||||
|
||||
- Maestro can read a coarse accessibility tree, but not the real `UIView` or `View` hierarchy, not coverage, not in-process logs.
|
||||
- The SDK can see everything inside the app, but cannot dispatch UI events the way the OS would. Touch injection through Maestro goes through XCTest or UIAutomator, which the OS treats as real input.
|
||||
- The driver can read a coarse accessibility tree, but not the real `UIView` or `View` hierarchy, not coverage, not in-process logs.
|
||||
- The SDK can see everything inside the app, but cannot dispatch UI events the way the OS would. Touch injection goes through the OS UI-test pipeline (XCTest on iOS, UIAutomator on Android), which the OS treats as real input.
|
||||
|
||||
On web, Chrome DevTools Protocol handles both input and introspection. No in-app SDK is needed.
|
||||
On web, the driver speaks the Chrome DevTools Protocol and handles both input and introspection. No in-app SDK is needed.
|
||||
|
||||
## 2. One TypeScript surface across platforms
|
||||
|
||||
@@ -23,13 +23,13 @@ Corollary: if a concept only exists on one platform, it does not belong in the s
|
||||
|
||||
## 3. The driver is an interface
|
||||
|
||||
`DeviceDriver` has two production implementations: `sidecar` (Maestro gRPC, for native) and `chrome` (CDP, for web), plus a `mock` for tests. The runner never knows which is wired in. Adding a new platform means adding a new implementation; nothing else changes.
|
||||
`DeviceDriver` has two production implementations: `sidecar` (gRPC to the native sidecar) and `chrome` (CDP, for web), plus a `mock` for tests. The runner never knows which is wired in. Adding a new platform means adding a new implementation; nothing else changes.
|
||||
|
||||
## 4. Hot loops bypass Maestro (native)
|
||||
## 4. Hot loops bypass the sidecar (native)
|
||||
|
||||
On native, per-step introspection (hierarchy dump, coverage read, pause and resume) goes over a local Unix socket directly to the SDK. Only physical UI events go through Maestro's gRPC.
|
||||
On native, per-step introspection (hierarchy dump, coverage read, pause and resume) goes over a local Unix socket directly to the SDK. Only physical UI events go through the sidecar's gRPC surface.
|
||||
|
||||
A 30-minute run is about 10,000 steps. Every step has at least one hierarchy dump and one coverage read. If those went through Maestro, the JVM sidecar would be the bottleneck. Instead the hot path is a 2 ms round-trip to an in-process Swift or Kotlin SDK.
|
||||
A 30-minute run is about 10,000 steps. Every step has at least one hierarchy dump and one coverage read. If those went through the JVM sidecar, the JVM hop would be the bottleneck. Instead the hot path is a 2 ms round-trip to an in-process Swift or Kotlin SDK.
|
||||
|
||||
On web, CDP is fast enough that a separate introspection channel is not needed.
|
||||
|
||||
|
||||
Reference in new issue
Block a user