Skip to content

Commit 22a23ca

Browse files
committed
Restructure the README for first-contact impact
Researched what strong dev-tool READMEs do (auto-mobile, baguette, dsh-ios, agent-qa — including the ones that drew stars fast) and applied the pattern the old README lacked, keeping its honest prose intact: a centered hero with a feature sub-line and truthful static badges (license, platform, devices, Go, single-binary), a jump-nav row, a scannable "What you get", and a short "Why not Appium" that states the positioning outright. Renamed Install to Quick start, folded build-from-source into a <details>, and cross-linked the sections and examples. All links and anchors verified.
1 parent 7da8d77 commit 22a23ca

1 file changed

Lines changed: 69 additions & 20 deletions

File tree

‎README.md‎

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
1+
<div align="center">
2+
13
# DeviceDeck
24

3-
**Your simulators and emulators in a browser — and the session you drive by hand becomes a test
4-
your existing framework can run.**
5+
**Your simulators and emulators become a webpage — drive them with the test tools you already
6+
own, and turn a session you did by hand into a test that runs unchanged on real hardware.**
7+
8+
<sub>iOS&nbsp;+&nbsp;Android&nbsp;·&nbsp;native UI mirrored as **real DOM**&nbsp;·&nbsp;Flow&nbsp;Capture&nbsp;·&nbsp;MCP&nbsp;server&nbsp;for&nbsp;agents&nbsp;·&nbsp;Playwright&nbsp;/&nbsp;Cypress&nbsp;/&nbsp;Puppeteer&nbsp;with&nbsp;zero&nbsp;adapter</sub>
9+
10+
![License](https://img.shields.io/badge/license-Apache_2.0-blue.svg)
11+
![Platform](https://img.shields.io/badge/platform-macOS-lightgrey?logo=apple)
12+
![iOS + Android](https://img.shields.io/badge/devices-iOS_%2B_Android-success)
13+
![Go](https://img.shields.io/badge/Go-1.26-00ADD8?logo=go&logoColor=white)
14+
![Single binary](https://img.shields.io/badge/ships-single_binary-brightgreen)
15+
16+
[Quick start](#quick-start) · [Examples](examples/) · [Drive with an agent](examples/mcp/) · [How it works](#the-mirror-is-the-point) · [Known limits](#known-limits)
17+
18+
</div>
19+
20+
---
21+
22+
## The mirror is the point
523

624
A single Go binary. It streams an iOS Simulator or Android emulator to a browser, mirrors the
725
app's native UI tree as **real DOM**, and records what you do into a replayable Maestro flow.
826

9-
The mirror is the point. Because the device is an ordinary web page, the tools you already own
10-
drive it with no mobile-specific code and no Appium:
27+
Because the device is an ordinary web page, the tools you already own drive it with no
28+
mobile-specific code, no Appium, and no coordinates:
1129

1230
```ts
1331
await page.goto(`/device/${udid}?app=dev.devicelab.testhive`);
@@ -17,7 +35,34 @@ await page.getByRole('button', { name: 'Sign In' }).click();
1735
await expect(page.getByText('Hello, devicelab!')).toBeVisible();
1836
```
1937

20-
That is a real iOS Simulator being driven by stock Playwright.
38+
That is a real iOS Simulator being driven by stock Playwright. The same page, the same selectors,
39+
drive an Android emulator — and an AI agent drives it through an MCP server.
40+
41+
## What you get
42+
43+
- **A device that is a webpage.** The native accessibility tree is mirrored as real DOM —
44+
`data-testid` from identifiers, ARIA roles from element types — so `getByTestId`, `getByRole`,
45+
`click`, and `fill` all just work. No driver, no DSL, no coordinates.
46+
- **The tools you already know.** Playwright, Cypress, and Puppeteer each drive both platforms
47+
with zero adapter; any browser driver does. See runnable [`examples/`](examples/).
48+
- **Flow Capture.** Record a session by hand and it becomes a Maestro flow with **durable
49+
selectors** that replays unchanged on real hardware — the funnel from "I did this once" to
50+
"this is a test."
51+
- **Agents, first-class.** `devicedeck mcp` is a Model Context Protocol server, and a
52+
[Claude Code plugin](examples/mcp/) ships with it — an agent lists, boots, launches, inspects,
53+
taps, and screenshots devices through the one core.
54+
- **Typing that actually lands.** Every keystroke is verified against the device's own read-back
55+
and retyped if it drifted, so a loaded machine does not silently drop a character.
56+
- **One binary.** No relay, no agent split, no per-tool fork — the same core behind every surface.
57+
58+
## Why not Appium
59+
60+
Every mobile testing framework rebuilds the same four primitives — a driver, a selector language,
61+
a way to send input, a way to read the tree. DeviceDeck doesn't ship any of them. It makes the
62+
device a **web page**, so a web driver you already have *is* the driver, its selectors *are* the
63+
selectors, and its assertions *are* the assertions. The one thing it insists on is **durable
64+
selectors over coordinates**, because that is what lets a flow captured on a simulator run
65+
unchanged on a real device — which is the whole point.
2166

2267
## Status
2368

@@ -29,16 +74,16 @@ password fields. A captured flow replays unchanged under a stock `maestro-runner
2974
50/50 on Android with assertions, and a deliberate negative control confirming the gate can fail.
3075

3176
What that sentence does not cover: it has only ever run on the machine it was built on. Expect
32-
first-contact problems. See **Known limits** below.
77+
first-contact problems. See [**Known limits**](#known-limits) below.
3378

3479
## Requirements
3580

36-
- macOS (the video and input sidecars talk to CoreSimulator)
37-
- Xcode with at least one iOS Simulator runtime — **iOS 26.2 or newer is strongly recommended**;
38-
on 18.6 the simulator's render server crashes under repeated capture
39-
- For Android: the Android SDK with `adb` and `emulator` on `PATH`
81+
- **macOS** (the video and input sidecars talk to CoreSimulator)
82+
- **Xcode** with at least one iOS Simulator runtime — **iOS 26.2 or newer is strongly
83+
recommended**; on 18.6 the simulator's render server crashes under repeated capture
84+
- **For Android:** the Android SDK with `adb` and `emulator` on `PATH`
4085

41-
## Install
86+
## Quick start
4287

4388
Download a release and run it — the archive holds the binary and its two sidecars, which it
4489
expects to find beside itself:
@@ -49,15 +94,18 @@ cd devicedeck-<version>-darwin-arm64
4994
./devicedeck serve
5095
```
5196

52-
From source:
97+
Then open **<http://127.0.0.1:8787>**, pick a device, and it boots and starts streaming.
98+
99+
<details>
100+
<summary><strong>Build from source</strong></summary>
53101

54102
```bash
55103
make sidecar # builds the Swift sidecars (macOS, Xcode toolchain)
56104
make build
57105
./devicedeck serve
58106
```
59107

60-
Then open <http://127.0.0.1:8787>.
108+
</details>
61109

62110
## Using it
63111

@@ -98,9 +146,10 @@ field's contents live in its value, not its text, which is the one place the mir
98146
"every native node is a div".
99147

100148
**Your own tests** point at `/device/{udid}?app={bundleId}` and use ordinary selectors.
101-
`examples/` has runnable Playwright, Cypress and Puppeteer projects, and `examples/captured/`
102-
holds flows recorded through the console — including one from a Flutter app and one from React
103-
Navigation. Note that **a device serves one driver at a time**; run with a single worker.
149+
[`examples/`](examples/) has runnable Playwright, Cypress and Puppeteer projects — each with a
150+
login and a checkout journey — and [`examples/captured/`](examples/captured/) holds flows recorded
151+
through the console, including one from a Flutter app and one from React Navigation. Note that
152+
**a device serves one driver at a time**; run with a single worker.
104153

105154
**Agents drive it through an MCP server.** `devicedeck mcp` speaks the Model Context Protocol on
106155
stdin/stdout — a thin adapter over the same API — so an agent (Claude, Cursor, any MCP client)
@@ -141,14 +190,14 @@ on durable selectors.
141190

142191
## Licence
143192

144-
Apache License 2.0 — see `LICENSE`.
193+
Apache License 2.0 — see [`LICENSE`](LICENSE).
145194

146195
## Built on
147196

148197
[maestro-runner](https://github.com/devicelab-dev/maestro-runner) for device drivers and flow
149198
replay, both Apache-2.0. The Swift sidecars derive from
150199
[baguette](https://github.com/tddworks/baguette) (Apache-2.0) and
151-
[tapflow](https://github.com/jo-duchan/tapflow) (MIT); `ATTRIBUTION.md` records what was reused
152-
and where it lives.
200+
[tapflow](https://github.com/jo-duchan/tapflow) (MIT); [`ATTRIBUTION.md`](ATTRIBUTION.md) records
201+
what was reused and where it lives.
153202

154-
Built by [DeviceLab.dev](https://devicelab.dev)
203+
Built by [**DeviceLab.dev**](https://devicelab.dev)

0 commit comments

Comments
 (0)