@ahnseungchan/device-farm 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +213 -122
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
`@ahnseungchan/device-farm`의 바뀐 점을 적는다.
|
|
4
4
|
|
|
5
|
+
## [0.1.2] - 2026-11-11
|
|
6
|
+
|
|
7
|
+
### Docs
|
|
8
|
+
|
|
9
|
+
- README를 영어로 다시 쓰고 webview-kit README 형식(배지·한 줄 소개·데모 GIF·Live demo·Features·Install·FAQ·Contributing)으로 맞춤. Why 표는 빼고 기능·사용법만 둠. npm 패키지 설명을 영어로 바꿈. 한국어 문서는 `docs/README.ko.md`로 옮김
|
|
10
|
+
- `docs/assets/demo.gif` 추가: 데모 화면을 Playwright로 녹화해 ffmpeg로 만든다(`tools/record-demo-gif.ts`)
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- 데모 모드(`apps/web/demo/`): 서버·기기 없이 가상 앱 「Sample Shop」 가짜 데이터로 화면을 띄운다. 바꾸는 버튼은 「Demo mode: read-only」 토스트만 띄운다. `npm run demo:dev`·`demo:build`·`demo:preview`(127.0.0.1:5499). npm 패키지에는 들어가지 않는다
|
|
15
|
+
- GitHub Actions: CI(typecheck·test·build·데모 빌드), 데모를 GitHub Pages(https://ahnseungc.github.io/device-farm/)에 배포
|
|
16
|
+
|
|
17
|
+
## [0.1.1] - 2026-11-11
|
|
18
|
+
|
|
19
|
+
### Docs
|
|
20
|
+
|
|
21
|
+
- README를 WebViewKit 형식으로 개편: 가운데 정렬 로고·배지·바로가기, 구조 다이어그램(SVG), Why 비교표, 긴 내용은 접기(`<details>`), Contributing
|
|
22
|
+
- 로고·구조 다이어그램을 `docs/assets/`에 추가(npm에서도 보이도록 GitHub 절대 URL로 링크, 패키지에는 넣지 않음)
|
|
23
|
+
|
|
5
24
|
## [0.1.0] - 2026-11-11
|
|
6
25
|
|
|
7
26
|
### Added
|
package/README.md
CHANGED
|
@@ -1,72 +1,70 @@
|
|
|
1
1
|
# device-farm
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ahnseungchan/device-farm)
|
|
4
|
+
[](https://github.com/Ahnseungc/device-farm/actions/workflows/ci.yml)
|
|
4
5
|
[](https://github.com/Ahnseungc/device-farm/blob/main/LICENSE)
|
|
5
|
-
[](https://nodejs.org/)
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**A device farm that runs QA on real phones for mobile apps built with WebViews. Install it in your project and describe your app in one `farm.config.ts`.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://ahnseungc.github.io/device-farm/"><img src="https://raw.githubusercontent.com/Ahnseungc/device-farm/main/docs/assets/demo.gif" width="800" alt="device-farm demo: device list, a failed screen compared with its baseline, the screen map, chaos candidate weights, and extracted policy rules" /></a>
|
|
11
|
+
</p>
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
<p align="center">
|
|
14
|
+
<b><a href="https://ahnseungc.github.io/device-farm/">Live demo →</a></b> (fixture data for a fictional "Sample Shop" app, no server or device needed, read-only)
|
|
15
|
+
</p>
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
| 새 빌드가 나오면 기기마다 깔고 화면을 하나씩 열어 본다 | Firebase App Distribution·TestFlight 새 빌드를 받아 연결된 기기 전부에 깔고, 고정 주소 화면을 딥링크로 열어 기준 화면과 픽셀 비교 |
|
|
16
|
-
| 버튼을 다 눌러 봤는지 기억에 의존한다 | 화면의 버튼을 하나씩 눌러 반응(이동·팝업·반응 없음·오류)을 남기고, 결과가 **화면 지도**가 된다 |
|
|
17
|
-
| 시나리오 테스트를 코드로 짜고 고친다 | 사람이 기기를 움직이면 녹화해 플로우 후보로 올리고, 확정한 것만 돈다 |
|
|
18
|
-
| 뒤로·연타·백그라운드 같은 엣지 케이스는 운에 맡긴다 | 시드 고정 카오스 탐색으로 재현 가능한 무작위 조작 |
|
|
19
|
-
| 정책 문서·피그마 문구와 화면이 맞는지 눈으로 대조한다 | Confluence 정책·피그마 문구를 화면 글자와 맞춰 본다 |
|
|
20
|
-
| 탐색 자동화가 실제 결제·탈퇴를 누를까 무섭다 | 쓰기 요청 가드(허용 목록 밖 POST/PUT/PATCH/DELETE 차단)와 금지 버튼 목록. 가드를 못 걸면 시작하지 않는다 |
|
|
17
|
+
The app under test is not in the code. Package ids, web origins, bottom tabs, forbidden words, and scenarios are all **injected** through `farm.config.ts`.
|
|
18
|
+
Android is driven with adb + Chrome DevTools Protocol (Playwright), iOS with Appium (XCUITest, WebDriverAgent), both inside the app's WebView.
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
[한국어 문서](https://github.com/Ahnseungc/device-farm/blob/main/docs/README.ko.md)
|
|
23
21
|
|
|
24
22
|
## Features
|
|
25
23
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- **Claude Code MCP**:
|
|
24
|
+
- **Build checks**: watch for new builds → install → check fixed screens. Fails on app error text, login bounce, blank screens, or a pixel diff against the baseline.
|
|
25
|
+
- **Button check and screen map**: records how every button reacts and stitches screens together into a map. Build flows by chaining map blocks.
|
|
26
|
+
- **Flows**: scenarios that start at home and move only by tapping buttons and links. Record them, assemble them from the map, or generate candidates from a PRD with an LLM (optional).
|
|
27
|
+
- **Data QA**: re-calls the GET APIs a screen uses and checks totals and displayed values with plain rules (no LLM).
|
|
28
|
+
- **Exploration and chaos**: breadth-first exploration, plus seeded chaos (back, double tap, back while loading, background, restart).
|
|
29
|
+
- **Policy and Figma checks**: detects Confluence policy changes, extracts rules, and compares Figma copy.
|
|
30
|
+
- **Remote control**: watch the device screen in the browser (Android H.264, iOS MJPEG) and tap it yourself. A device you control is locked for others.
|
|
31
|
+
- **Claude Code (MCP)**: Claude explores the device through MCP tools and turns the paths it finds into flow candidates.
|
|
34
32
|
|
|
35
|
-
##
|
|
33
|
+
## Install
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
| `adb`(Android platform-tools), USB 디버깅을 켠 기기 | Android |
|
|
41
|
-
| WebView 디버깅을 켠 dev 빌드: `WebView.setWebContentsDebuggingEnabled(true)` | Android |
|
|
42
|
-
| Xcode, Appium + XCUITest 드라이버(WebDriverAgent), `isInspectable = true`인 빌드 | iOS |
|
|
43
|
-
| Firebase 서비스 계정, App Store Connect API 키 | 새 빌드 자동 받기(선택) |
|
|
44
|
-
| Confluence API 토큰, Figma 토큰 | 정책·피그마 대조(선택) |
|
|
35
|
+
```bash
|
|
36
|
+
npm install -D @ahnseungchan/device-farm
|
|
37
|
+
```
|
|
45
38
|
|
|
46
|
-
|
|
39
|
+
| Requirement | When |
|
|
40
|
+
| --------------------------------------------------------------------------------- | ---------------------------- |
|
|
41
|
+
| macOS, Node.js 22+ | Always |
|
|
42
|
+
| `adb` (Android platform-tools) and a device with USB debugging on | Android |
|
|
43
|
+
| A dev build with WebView debugging: `WebView.setWebContentsDebuggingEnabled(true)` | Android |
|
|
44
|
+
| Xcode, Appium + XCUITest driver (WebDriverAgent), a build with `isInspectable = true` | iOS |
|
|
45
|
+
| Firebase service account, App Store Connect API key | Auto-installing new builds (optional) |
|
|
46
|
+
| Confluence API token, Figma token | Policy and Figma checks (optional) |
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
2. **경로형 딥링크**: `<scheme>://<웹 경로>`를 받으면 그 웹 경로를 WebView로 연다.
|
|
50
|
-
3. **하단 탭이 웹 링크**(`a[href]` 또는 `role=link`)이고, 화면 상단 뒤로 버튼에 이름(`aria-label`)이 있다.
|
|
51
|
-
4. 점검용 계정으로 로그인해 둔다. 로그인 화면으로 튕기면 「로그인 필요」로 남는다.
|
|
48
|
+
Host Mac and device setup: [docs/operations.md](https://github.com/Ahnseungc/device-farm/blob/main/docs/operations.md) (Korean)
|
|
52
49
|
|
|
53
|
-
|
|
50
|
+
<details>
|
|
51
|
+
<summary><b>What the app under test needs</b></summary>
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
1. A build with **WebView debugging** on. device-farm connects through that debugging socket.
|
|
54
|
+
2. **Path deep links**: `<scheme>://<web path>` opens that web path in the WebView.
|
|
55
|
+
3. **Bottom tabs are web links** (`a[href]` or `role=link`), and the in-page back button has a name (`aria-label`).
|
|
56
|
+
4. A test account that is already signed in. If a screen bounces to login, it is reported as "login required".
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
npm install -D @ahnseungchan/device-farm
|
|
59
|
-
```
|
|
58
|
+
</details>
|
|
60
59
|
|
|
61
60
|
## Quick start
|
|
62
61
|
|
|
63
62
|
```bash
|
|
64
|
-
npx device-farm init
|
|
65
|
-
|
|
66
|
-
npx device-farm dev # http://127.0.0.1:5173 (--port 로 변경)
|
|
63
|
+
npx device-farm init # creates farm.config.ts and .env.example (skips files that exist)
|
|
64
|
+
npx device-farm dev # http://127.0.0.1:5173 (change with --port)
|
|
67
65
|
```
|
|
68
66
|
|
|
69
|
-
`
|
|
67
|
+
Edit the `farm.config.ts` that `init` created. With only the required fields it looks like this:
|
|
70
68
|
|
|
71
69
|
```ts
|
|
72
70
|
// farm.config.ts
|
|
@@ -77,119 +75,212 @@ export default defineAppConfig({
|
|
|
77
75
|
appIds: { dev: "com.example.app.dev", prod: "com.example.app" },
|
|
78
76
|
web: { dev: "https://dev.app.example.com", prod: "https://app.example.com" },
|
|
79
77
|
homePath: "/home",
|
|
80
|
-
homeTabName: "
|
|
81
|
-
tabPaths: {
|
|
78
|
+
homeTabName: "Home",
|
|
79
|
+
tabPaths: { Search: "/search", My: "/my" },
|
|
82
80
|
deepLink: { scheme: "myapp", homeHost: "home" },
|
|
83
81
|
});
|
|
84
82
|
```
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
84
|
+
If you need tokens, copy `.env.example` to `.env` and fill it in. Add `.env` and `test-results/` to `.gitignore`.
|
|
85
|
+
Everything the farm produces (run history, screenshots, baselines, the screen map) goes to `test-results/` in your working directory.
|
|
86
|
+
|
|
87
|
+
<details>
|
|
88
|
+
<summary><b>Data QA rule example</b></summary>
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { defineAppConfig, formatWon, numberAt, textHas } from "@ahnseungchan/device-farm";
|
|
92
|
+
|
|
93
|
+
export default defineAppConfig({
|
|
94
|
+
// ...required fields
|
|
95
|
+
apiOrigin: "https://api-dev.app.example.com",
|
|
96
|
+
qa: {
|
|
97
|
+
sessionPath: "/api/auth/session",
|
|
98
|
+
screens: [
|
|
99
|
+
{
|
|
100
|
+
path: "/cart",
|
|
101
|
+
name: "Cart",
|
|
102
|
+
apis: { cart: "/v1/users/{userId}/cart" },
|
|
103
|
+
rules: [
|
|
104
|
+
{
|
|
105
|
+
id: "total-shown",
|
|
106
|
+
title: "The total from the API is shown on screen",
|
|
107
|
+
check: (context) => {
|
|
108
|
+
const shown = `${formatWon(numberAt(context, "cart", "totalWon"))}원`;
|
|
109
|
+
return textHas(context.text, shown) ? null : `Not on screen: total "${shown}"`;
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Run it with `npx device-farm qa --device <id> [--path /cart]`. The full example is in the `farm.config.ts` that `init` creates.
|
|
120
|
+
|
|
121
|
+
</details>
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
`defineAppConfig()` fills in defaults for anything you leave out. The `AppConfigInput` and `AppConfig` types are exported from the package.
|
|
126
|
+
The config file is resolved as `FARM_CONFIG` → `farm.config.ts` in the working directory → `farm.config.example.ts`. Secrets are read from `.env` in the working directory.
|
|
127
|
+
|
|
128
|
+
| Field | Required | What it is |
|
|
129
|
+
| ----------------------------------- | -------- | ---------------------------------------------------------------------------------- |
|
|
130
|
+
| `name` | ✓ | App name, used in the UI title and prompts |
|
|
131
|
+
| `appIds` | ✓ | `{ dev, prod }` Android package = iOS bundle id. Use `iosBundleIds` if they differ |
|
|
132
|
+
| `homePath`, `homeTabName`, `tabPaths` | ✓ | Home path, home tab name, and bottom tab name → path |
|
|
133
|
+
| `web` | | `{ dev, prod }` WebView origins. Falls back to `FARM_WEB_ORIGIN_DEV` / `FARM_WEB_ORIGIN_PROD` |
|
|
134
|
+
| `apiOrigin` | | API host when it differs from the web origin. The guard treats it as internal, and data QA calls it |
|
|
135
|
+
| `deepLink` | | `{ scheme, homeHost }` for path deep links |
|
|
136
|
+
| `guard` | | Words and API patterns **added** to the built-in deny lists, and `pushTokenRequests` that always pass |
|
|
137
|
+
| `scenarios`, `excludedScenarioPaths` | | Fixed screens to check, and paths to skip |
|
|
138
|
+
|
|
139
|
+
<details>
|
|
140
|
+
<summary><b>All fields</b></summary>
|
|
141
|
+
|
|
142
|
+
| Field | What it is |
|
|
143
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
144
|
+
| `inPageBackNames` | Names of the in-page back button. Default `["뒤로가기", "Back"]` |
|
|
145
|
+
| `loginPaths`, `appErrorText` | Login screen paths and the app's error text. Default `["/login"]`, `"Application error"` |
|
|
146
|
+
| `android.mainActivity` | The WebView activity. If another native screen is on top, it is closed with the back key |
|
|
147
|
+
| `webview` | `stackResetScript` (bridge call that closes the WebView stack), `sheetCloseName` (bottom sheet close button) |
|
|
148
|
+
| `guard` | `extraForbiddenButtonWords`, `extraForbiddenClickNames`, `extraForbiddenApiPatterns`, `pushTokenRequests`, and `avoidedNamePattern` / `noPressPathPattern` to never press |
|
|
149
|
+
| `qa` | `{ screens, sessionPath }`: data QA screens and rules, and the signed-in session path |
|
|
150
|
+
| `policy.confluence` | `{ cloudId, spaceKey, titleQuery, siteUrl }`: where the policy docs live |
|
|
151
|
+
| `figma` | `{ frontRepo, ref, appDir }`: the front-end checkout used to map screens to Figma nodes |
|
|
152
|
+
| `install` | `sources` (`firebase` / `testflight` per platform and build), `firebase` (app ids), `testflightDevAppName` |
|
|
153
|
+
| `ios` | `{ teamId, wdaBundleId }` for WDA signing. Falls back to `IOS_TEAM_ID` / `IOS_WDA_BUNDLE_ID` |
|
|
154
|
+
| `access.allowedDomains` | Tailscale account domains allowed to open the UI. Empty means this Mac only |
|
|
155
|
+
|
|
156
|
+
The data QA helpers `equalWon`, `formatWon`, `numberAt`, `textHas`, and `pick` are exported from the package root. Environment variables are listed in `.env.example`.
|
|
157
|
+
|
|
158
|
+
</details>
|
|
116
159
|
|
|
117
160
|
## CLI
|
|
118
161
|
|
|
119
|
-
|
|
162
|
+
Every command reads config and `.env` from the working directory (your project) and writes results there. Run `npx device-farm <command> --help` for options.
|
|
163
|
+
|
|
164
|
+
| Command | What it does |
|
|
165
|
+
| ---------------------------------------------------- | -------------------------------------------------------------------- |
|
|
166
|
+
| `device-farm init` | Create `farm.config.ts` and `.env.example` |
|
|
167
|
+
| `device-farm dev [--port 5173]` | UI + API server on 127.0.0.1. New-build and policy watchers run with it |
|
|
168
|
+
| `device-farm check --build dev\|prod --version 2.1.5` | Ask the running server for a build check and print progress until it ends |
|
|
169
|
+
| `device-farm explore` | Exploration check (needs the server) |
|
|
170
|
+
| `device-farm chaos --device <serial>` | Chaos exploration (Android dev) |
|
|
171
|
+
| `device-farm record --device <serial>` | Record a person's actions → screen map and flow candidates |
|
|
172
|
+
| `device-farm qa --device <id>` | Data QA only |
|
|
173
|
+
| `device-farm policy-watch` | Detect policy changes, extract rules, cache Figma copy |
|
|
174
|
+
| `device-farm policy-check --device <id>` | Policy and Figma comparison on one device |
|
|
175
|
+
| `device-farm explore-allowlist` | Add write APIs from OpenAPI (`FARM_OPENAPI_URL`) to the allowlist as deny |
|
|
176
|
+
| `device-farm mcp` | MCP server for Claude Code (stdio) |
|
|
177
|
+
|
|
178
|
+
<details>
|
|
179
|
+
<summary><b>Options per command</b></summary>
|
|
120
180
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
| `device-farm policy-check --device <id> [--path /my/orders]` | 정책·피그마 대조를 기기 한 대에서 |
|
|
132
|
-
| `device-farm explore-allowlist` | OpenAPI(`FARM_OPENAPI_URL`)의 쓰기 API를 `explore/allowlist.json`에 deny로 추가 |
|
|
133
|
-
| `device-farm mcp` | Claude Code용 MCP 서버(stdio) |
|
|
181
|
+
```bash
|
|
182
|
+
device-farm dev [--port 5173]
|
|
183
|
+
device-farm check --build dev|prod --version 2.1.5 [--devices all|id,id] [--port 5173]
|
|
184
|
+
device-farm explore [--devices all|id,id] [--build dev] [--port 5173]
|
|
185
|
+
device-farm chaos --device <Android serial> [--seed 42] [--episodes 3] [--steps 50] [--minutes 60] [--no-guard]
|
|
186
|
+
device-farm record --device <Android serial> [--seconds 300] [--name "flow name"]
|
|
187
|
+
device-farm qa --device <id> [--path /cart]
|
|
188
|
+
device-farm policy-watch [--extract <pageId>] [--figma]
|
|
189
|
+
device-farm policy-check --device <id> [--path /my/orders]
|
|
190
|
+
```
|
|
134
191
|
|
|
135
|
-
|
|
192
|
+
`check` and `explore` send work to a running `dev` server. If you changed the port, pass the same `--port`.
|
|
193
|
+
Keep the exploration allowlist `explore/allowlist.json`, input test values `explore/form-values.json`, and policy sources `policy/sources.json` in your working directory and commit them. Without an allowlist, every write request is blocked.
|
|
136
194
|
|
|
137
|
-
|
|
195
|
+
</details>
|
|
138
196
|
|
|
139
|
-
|
|
197
|
+
## Claude Code (MCP)
|
|
140
198
|
|
|
141
199
|
```bash
|
|
200
|
+
# in your project folder
|
|
142
201
|
claude mcp add --scope project device-farm -- npx device-farm mcp
|
|
143
202
|
```
|
|
144
203
|
|
|
145
|
-
|
|
204
|
+
- Tool flow: `device_start` → `device_look` / `device_press` / `device_go_home` / `device_report_issue` → `device_finish`.
|
|
205
|
+
- When it finishes, results are merged into the screen map, and paths that can be replayed from home become flow candidates. The report is written to `test-results/agent/<time>/report.json`.
|
|
206
|
+
- Exploration runs only on Android dev builds with the write-request guard on. It runs on your Claude Code account; no API key is needed.
|
|
146
207
|
|
|
147
|
-
|
|
208
|
+
This repository also works as a Claude Code plugin (`.claude-plugin`, skills `run-check`, `chaos-explore`, `policy-figma-check`). Clone it, run `npm install`, and add it as a plugin.
|
|
148
209
|
|
|
149
|
-
##
|
|
210
|
+
## Safety
|
|
150
211
|
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
- `chaos --no-guard
|
|
155
|
-
-
|
|
212
|
+
- Exploration, button checks, chaos, and MCP exploration run on **Android dev builds** with the write-request guard on. If the guard cannot attach, they do not start (fail closed).
|
|
213
|
+
- Guard order: reads (GET, HEAD, OPTIONS) pass → `pushTokenRequests` pass → forbidden paths are blocked → allowlist `allow` passes → `deny` and unknown are blocked.
|
|
214
|
+
- Built-in forbidden buttons (buy, pay, sell, transfer, withdraw, delete account, order, refund, cancel, delete, log out, and more) are removed from the candidate list and refused if pressed. App config can only add forbidden words, never remove them. Password and PIN screens are never pressed, only left.
|
|
215
|
+
- `chaos --no-guard` does not block write requests. Use it **only against a dev server**. Forbidden buttons are still never pressed.
|
|
216
|
+
- The UI server listens on 127.0.0.1 only. Remote access goes through `tailscale serve`, limited to `access.allowedDomains`.
|
|
156
217
|
|
|
157
218
|
## FAQ
|
|
158
219
|
|
|
159
|
-
|
|
160
|
-
|
|
220
|
+
<details>
|
|
221
|
+
<summary><b>Can I open the UI without a device?</b></summary>
|
|
222
|
+
|
|
223
|
+
Yes. `npx device-farm init && npx device-farm dev` opens the UI with the example config. The device list is empty and no checks run.
|
|
224
|
+
To see the UI filled with data, open the [live demo](https://ahnseungc.github.io/device-farm/).
|
|
225
|
+
|
|
226
|
+
</details>
|
|
227
|
+
|
|
228
|
+
<details>
|
|
229
|
+
<summary><b>Does iOS support exploration and chaos?</b></summary>
|
|
230
|
+
|
|
231
|
+
Build checks and remote control support iOS (Appium). Exploration, chaos, recording, and MCP exploration support Android dev builds only for now.
|
|
161
232
|
|
|
162
|
-
|
|
163
|
-
빌드 점검·원격 조작은 iOS(Appium)를 지원합니다. 탐색·카오스·녹화·MCP 탐색은 지금 Android dev 빌드만 지원합니다.
|
|
233
|
+
</details>
|
|
164
234
|
|
|
165
|
-
|
|
166
|
-
|
|
235
|
+
<details>
|
|
236
|
+
<summary><b>Why is the UI in Korean?</b></summary>
|
|
167
237
|
|
|
168
|
-
|
|
169
|
-
아니요. 점검·탐색·카오스·데이터 QA는 규칙으로만 판정합니다. PRD로 플로우 후보를 만들 때(`FLOW_LLM_*`)와 정책 판정에만 씁니다. MCP는 사용자의 Claude Code 계정으로 돕니다.
|
|
238
|
+
It was built for a Korean team first. The CLI help and UI text are Korean for now; an English UI PR is welcome.
|
|
170
239
|
|
|
171
|
-
|
|
172
|
-
작업 폴더의 `test-results/`. 커밋하지 마세요.
|
|
240
|
+
</details>
|
|
173
241
|
|
|
174
|
-
|
|
242
|
+
<details>
|
|
243
|
+
<summary><b><code>check</code> says it cannot reach the server.</b></summary>
|
|
175
244
|
|
|
176
|
-
|
|
177
|
-
- [설계: 빌드 점검·기준 화면·플로우·화면 지도](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-device-farm.md)
|
|
178
|
-
- [설계: 탐색 점검과 쓰기 요청 가드](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-explore.md)
|
|
179
|
-
- [설계: 카오스 탐색](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-chaos.md)
|
|
180
|
-
- [설계: 정책·피그마 대조](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-policy-figma.md)
|
|
245
|
+
`check` and `explore` send work to a running `device-farm dev` server. Start `dev` in another terminal first, and pass the same `--port` if you changed it.
|
|
181
246
|
|
|
182
|
-
|
|
247
|
+
</details>
|
|
248
|
+
|
|
249
|
+
<details>
|
|
250
|
+
<summary><b>Do I need an LLM API key?</b></summary>
|
|
251
|
+
|
|
252
|
+
No. Checks, exploration, chaos, and data QA are judged by rules only. An LLM is used only to generate flow candidates from a PRD (`FLOW_LLM_*`) and to judge policy behavior rules.
|
|
253
|
+
|
|
254
|
+
</details>
|
|
255
|
+
|
|
256
|
+
## Docs
|
|
257
|
+
|
|
258
|
+
Design notes are in Korean for now.
|
|
259
|
+
|
|
260
|
+
- [Operations: host Mac and device setup](https://github.com/Ahnseungc/device-farm/blob/main/docs/operations.md)
|
|
261
|
+
- [Design: build checks, baselines, flows, screen map](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-device-farm.md)
|
|
262
|
+
- [Design: exploration and the write-request guard](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-explore.md)
|
|
263
|
+
- [Design: chaos exploration](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-chaos.md)
|
|
264
|
+
- [Design: policy and Figma checks](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-policy-figma.md)
|
|
265
|
+
|
|
266
|
+
## Contributing
|
|
267
|
+
|
|
268
|
+
Issues and PRs are welcome. Bug reports with a repro, docs and translations, and demo improvements all help.
|
|
183
269
|
|
|
184
270
|
```bash
|
|
185
271
|
git clone https://github.com/Ahnseungc/device-farm.git && cd device-farm
|
|
186
272
|
npm install
|
|
187
|
-
npm run
|
|
188
|
-
npm run
|
|
189
|
-
npm run
|
|
273
|
+
npm run typecheck && npm test && npm run build
|
|
274
|
+
npm run dev # UI development (vite dev), config: farm.config.ts → farm.config.example.ts
|
|
275
|
+
npm run demo:dev # demo UI with fixture data at http://127.0.0.1:5499
|
|
190
276
|
```
|
|
191
277
|
|
|
192
|
-
npm workspaces
|
|
278
|
+
It is an npm workspaces monorepo. Dependencies point one way, `core ← devices ← engine ← server ← apps`, enforced by `tools/boundaries.test.ts`. The published package bundles the workspaces into `dist/` (tsup + vite build).
|
|
279
|
+
|
|
280
|
+
The demo (`apps/web/demo/`) swaps only `./api-client` and `./live-view` for fixture modules and reuses the real screens; it is not part of the package build.
|
|
281
|
+
`npm run demo:build && npx tsx tools/record-demo-gif.ts` regenerates `docs/assets/demo.gif` (needs ffmpeg). Pushing to `main` deploys the demo to GitHub Pages via `.github/workflows/pages.yml`.
|
|
282
|
+
|
|
283
|
+
Releases: bump `package.json` and `CHANGELOG.md`, run `npm run publish:npm` (`prepublishOnly` runs typecheck, test, and build), then push a `v<version>` tag.
|
|
193
284
|
|
|
194
285
|
## License
|
|
195
286
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahnseungchan/device-farm",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "A device farm that runs QA on real phones for WebView-based mobile apps: baseline screen diffs, button crawl and screen map, flow recording, seeded chaos exploration, policy/Figma copy checks, and a Claude Code MCP server. Configure your app in one farm.config.ts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"device-farm": "dist/bin.js"
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
23
|
"dev": "vite --config apps/web/vite.config.ts --configLoader runner",
|
|
24
|
+
"demo:dev": "vite --config apps/web/demo/vite.config.ts --configLoader runner",
|
|
25
|
+
"demo:build": "vite build --config apps/web/demo/vite.config.ts --configLoader runner",
|
|
26
|
+
"demo:preview": "vite preview --config apps/web/demo/vite.config.ts --configLoader runner",
|
|
24
27
|
"build": "rm -rf dist && tsup && vite build --config apps/web/vite.config.ts --configLoader runner --outDir ../../dist/web --emptyOutDir && tsx tools/build-templates.ts",
|
|
25
28
|
"test": "for f in $(find packages apps tools -name '*.test.ts' -not -path '*/node_modules/*'); do npx tsx \"$f\" || exit 1; done",
|
|
26
29
|
"typecheck": "tsc --noEmit",
|