@appetize/cli 0.17.0 → 0.19.0

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.
@@ -10,7 +10,7 @@ description: >-
10
10
  or answering what is currently on screen — even if Appetize is not mentioned
11
11
  by name.
12
12
  metadata:
13
- cli-version: '0.17.0'
13
+ cli-version: '0.19.0'
14
14
  ---
15
15
 
16
16
  # Appetize
@@ -31,6 +31,9 @@ Confirm the CLI is installed and note the version:
31
31
  appetize --version
32
32
  ```
33
33
 
34
+ End every command with `--json`, so its result is always JSON. If your tool runs
35
+ commands in a terminal, add `--quiet` too.
36
+
34
37
  `APPETIZE_API_TOKEN` must be set for private APIs and session targets. A target
35
38
  is an uploaded build's public key, e.g. `app_a1b2c3`. Discover one with
36
39
  `appetize build list`, filtering with `--app <appId>` when you know the bundle
@@ -53,9 +56,9 @@ configured once rather than flagged on every session.
53
56
  Start a session, naming a device and a target:
54
57
 
55
58
  ```bash
56
- appetize session start iphone15pro app_a1b2c3
57
- appetize session start pixel7 app_a1b2c3 --device-os-version 14
58
- appetize session start iphone15pro app_a1b2c3 --open myapp://orders/42
59
+ appetize session start iphone15pro app_a1b2c3 --json
60
+ appetize session start pixel7 app_a1b2c3 --device-os-version 14 --json
61
+ appetize session start iphone15pro app_a1b2c3 --open myapp://orders/42 --json
59
62
  ```
60
63
 
61
64
  This blocks until the device is ready, then prints the session:
@@ -108,7 +111,7 @@ jq 'select(.type == "response") | "\(.response.status) \(.request.method) \(.req
108
111
  Stop the session when you are done, so the device is released:
109
112
 
110
113
  ```bash
111
- appetize session stop
114
+ appetize session stop --json
112
115
  ```
113
116
 
114
117
  Session state lives in `~/.appetize/cli/sessions/<session-id>/state.json` (or under
@@ -122,8 +125,8 @@ Video is not captured unless you ask for it — nothing streams from the device
122
125
  until you start:
123
126
 
124
127
  ```bash
125
- appetize recording start login-flow
126
- appetize recording stop
128
+ appetize recording start login-flow --json
129
+ appetize recording stop --json
127
130
  ```
128
131
 
129
132
  `recording start` has the daemon open the video stream and write it to the file
@@ -144,7 +147,7 @@ Playback is a constant 30fps, so it does not track real time.
144
147
  ### 1. Inspect first
145
148
 
146
149
  ```bash
147
- appetize inspect --select-test-id login-form
150
+ appetize inspect --select-test-id login-form --json
148
151
  ```
149
152
 
150
153
  `inspect` writes the view hierarchy as JSON to `inspect.json` by default (name
@@ -164,12 +167,12 @@ not "does not exist".
164
167
  Prefer matching what an element _is_ over where it happens to sit:
165
168
 
166
169
  ```bash
167
- appetize tap --select-test-id save
168
- appetize tap --select-text 'Log in'
169
- appetize tap --select-text '/^item \d+/i' --select-index 2
170
- appetize type 'user@example.com{Tab}hunter2{Enter}'
171
- appetize swipe --direction up
172
- appetize swipe --from-test-id card-3 --to-test-id archive-bin
170
+ appetize tap --select-test-id save --json
171
+ appetize tap --select-text 'Log in' --json
172
+ appetize tap --select-text '/^item \d+/i' --select-index 2 --json
173
+ appetize type 'user@example.com{Tab}hunter2{Enter}' --json
174
+ appetize swipe --direction up --json
175
+ appetize swipe --from-test-id card-3 --to-test-id archive-bin --json
173
176
  ```
174
177
 
175
178
  `swipe` comes in two shapes. A `--direction` scrolls the screen (add
@@ -179,8 +182,8 @@ a target. Both ends take the same selectors as `tap`, so a drag can name its
179
182
  endpoints instead of guessing coordinates:
180
183
 
181
184
  ```bash
182
- appetize swipe --direction left --from-test-id card --from-index 1
183
- appetize swipe --from-text 'Drag me' --from-y 0.9 --to-test-id drop-zone
185
+ appetize swipe --direction left --from-test-id card --from-index 1 --json
186
+ appetize swipe --from-text 'Drag me' --from-y 0.9 --to-test-id drop-zone --json
184
187
  ```
185
188
 
186
189
  `--select-test-id` is the most durable, then `--select-text` (an exact string,
@@ -195,7 +198,7 @@ In `type`, `{...}` names a key: `{Enter}`, `{Tab}`, `{Backspace}`, the arrows.
195
198
  ### 3. Verify
196
199
 
197
200
  ```bash
198
- appetize screenshot after-login
201
+ appetize screenshot after-login --json
199
202
  ```
200
203
 
201
204
  Screenshot after each meaningful step. A tap that "succeeded" only means the
@@ -206,7 +209,7 @@ for you.
206
209
  ### 4. Wait by inspecting, not by sleeping
207
210
 
208
211
  ```bash
209
- appetize inspect --select-test-id home-feed --timeout 5000
212
+ appetize inspect --select-test-id home-feed --timeout 5000 --json
210
213
  ```
211
214
 
212
215
  `--timeout <ms>` makes the command wait for the element and fail with a non-zero
@@ -236,8 +239,8 @@ workflow, push your change so it runs, then restart the session against the
236
239
  build id it produced:
237
240
 
238
241
  ```bash
239
- appetize session stop
240
- appetize session start iphone15pro <new-id>
242
+ appetize session stop --json
243
+ appetize session start iphone15pro <new-id> --json
241
244
  ```
242
245
 
243
246
  If you cannot tell from the repository's CI configuration how an iOS build
@@ -257,9 +260,9 @@ When a turn ends with a visual change or a final state that deserves a demo,
257
260
  record the flow and surface the video the same way:
258
261
 
259
262
  ```bash
260
- appetize recording start demo
263
+ appetize recording start demo --json
261
264
  # drive the flow
262
- appetize recording stop
265
+ appetize recording stop --json
263
266
  ```
264
267
 
265
268
  Where your environment lets you attach media to a pull request, include the
@@ -276,10 +279,8 @@ transcript is the record — never commit screenshots or recordings to the repo.
276
279
  | Element not found | Re-inspect. It is off screen, covered, or the screen changed. |
277
280
  | App fails only on network calls | If you started with `--proxy`, retry without it. |
278
281
 
279
- A session moves through phases while starting: `requesting`, `queued`,
280
- `starting`, `downloadingApp`, `installingApp`, `launchingApp`, `ready`. If
281
- `session start` is taking a while, that sequence tells you whether you are
282
- queued for capacity or waiting on an install.
282
+ If `session start` is taking a while, `state.phase` in its `state.json` tells
283
+ you whether you are queued for capacity or waiting on an install.
283
284
 
284
285
  ## Dismissing the soft keyboard
285
286
 
@@ -290,14 +291,14 @@ if the app wired that up, and many apps do not.
290
291
  **iOS** — press the return key:
291
292
 
292
293
  ```bash
293
- appetize type '{Enter}'
294
+ appetize type '{Enter}' --json
294
295
  ```
295
296
 
296
297
  **Android** — press the back button; while the keyboard is up it dismisses
297
298
  the keyboard instead of navigating back:
298
299
 
299
300
  ```bash
300
- appetize press back
301
+ appetize press back --json
301
302
  ```
302
303
 
303
304
  Both leave you on the same screen with the field still focused. Confirm from
@@ -315,15 +316,20 @@ test id or a dismiss button changes the thing you are supposed to be verifying.
315
316
  Prefer `open` over navigating screen by screen when a deep link exists:
316
317
 
317
318
  ```bash
318
- appetize open myapp://orders/42
319
- appetize open https://myapp.com/orders/42
320
- appetize inspect --select-test-id order-42 --timeout 5000
319
+ appetize open myapp://orders/42 --json
320
+ appetize open https://myapp.com/orders/42 --json
321
+ appetize inspect --select-test-id order-42 --timeout 5000 --json
321
322
  ```
322
323
 
323
324
  `open` returning only means the URL was delivered, so verify the destination
324
325
  screen with `inspect --timeout`. A custom scheme needs its app installed on the
325
326
  device. iOS caps URLs at 2048 characters.
326
327
 
328
+ ## Logged-out state
329
+
330
+ On iOS, `appetize keychain clear` clears the entire keychain without restarting
331
+ the session. It has no effect on Android.
332
+
327
333
  ## Full command reference
328
334
 
329
335
  `references/commands.md` has every command, flag, output shape and exit
@@ -16,6 +16,7 @@ same flags at the terminal.
16
16
  - [press](#press)
17
17
  - [rotate](#rotate)
18
18
  - [open](#open)
19
+ - [keychain clear](#keychain-clear)
19
20
  - [screenshot](#screenshot)
20
21
  - [recording start](#recording-start)
21
22
  - [recording stop](#recording-stop)
@@ -26,6 +27,11 @@ same flags at the terminal.
26
27
 
27
28
  ## Global
28
29
 
30
+ `--json` prints the result as JSON. End every command with it. `--no-json`
31
+ prints text.
32
+
33
+ `--quiet` hides the progress spinner shown on an interactive terminal.
34
+
29
35
  `--session-id <id>` is accepted by every command except those marked "Takes
30
36
  no session". On
31
37
  `session start` it names the session being created; everywhere else it picks
@@ -55,9 +61,7 @@ once the device is ready; `--no-wait` prints `{ logs, sessionId }` immediately.
55
61
  available to it: `adb connect <adbSerial>` attaches to the device until the
56
62
  session ends. `viewerUrl` opens the local browser viewer. `logs`
57
63
  carries a `device` and a `daemon` path, plus a `network` path — the JSONL capture
58
- of the app's traffic — when started with `--proxy` (or `--proxy intercept`). Progress phases are logged to stderr:
59
- `requesting`, `queued`, `starting`, `downloadingApp`, `installingApp`,
60
- `launchingApp`, `ready`.
64
+ of the app's traffic — when started with `--proxy` (or `--proxy intercept`).
61
65
 
62
66
  Network traffic can be proxied or intercepted for debugging. `--proxy` (or
63
67
  `--proxy intercept`) provisions the intercepting proxy so traffic can be
@@ -75,7 +79,7 @@ and starting a session with an id already in use fails.
75
79
  appetize session stop
76
80
  ```
77
81
 
78
- Signals the daemon to shut down and prints `{ ended: true, sessionId }`. Reports
82
+ Signals the daemon to shut down and prints `{ sessionId }`. Reports
79
83
  the id even if the daemon had already exited, so it is safe to call twice.
80
84
 
81
85
  ## build list
@@ -113,7 +117,7 @@ appetize build upload <file> [options]
113
117
 
114
118
  Uploads a `.apk`, `.apks`, `.zip` or `.tar.gz` and prints
115
119
  `{ created, id, platform }` on stdout, where `id` is a target `session start`
116
- takes. The id is also logged to stderr as soon as the upload lands.
120
+ takes.
117
121
 
118
122
  The extension decides the platform — `.apk` and `.apks` are Android, `.zip`,
119
123
  `.tar.gz` and `.tgz` are iOS — and any other file is refused before uploading.
@@ -172,7 +176,7 @@ appetize tap [options]
172
176
  | `--select-*` | Element or position to tap — see [Selectors](#selectors) |
173
177
  | `--timeout` | Milliseconds to wait for the element |
174
178
 
175
- Prints `{ tapped: true }`. Combining an element selector with `--select-x` /
179
+ Prints `{}`. Combining an element selector with `--select-x` /
176
180
  `--select-y` taps a point within that element, and values outside `0-1` are
177
181
  allowed to reach just beyond its edge (e.g. `--select-x=-0.1`).
178
182
 
@@ -191,7 +195,7 @@ appetize swipe [options]
191
195
  | `--duration` | Milliseconds the gesture travels for |
192
196
  | `--timeout` | Milliseconds to wait for any `--from`/`--to` elements |
193
197
 
194
- Prints `{ swiped: true }`. Two shapes are valid: a `--direction` with an
198
+ Prints `{}`. Two shapes are valid: a `--direction` with an
195
199
  optional `--from-*` starting point, or both a `--from-*` and a `--to-*` point to
196
200
  form a path. Mixing `--direction` with `--to-*` is an error, as is `--distance`
197
201
  without `--direction`.
@@ -206,7 +210,7 @@ appetize type <text> [options]
206
210
  | --------- | ---------------------------------- |
207
211
  | `--delay` | Milliseconds to wait between steps |
208
212
 
209
- Prints `{ typed: true }`. Bare text is delivered as a string; `{...}` names a
213
+ Prints `{}`. Bare text is delivered as a string; `{...}` names a
210
214
  key delivered as a keystroke. Supported keys: `ArrowDown`, `ArrowLeft`,
211
215
  `ArrowRight`, `ArrowUp`, `Backspace`, `Enter`, `Tab`. A token may carry a repeat
212
216
  count as in `{Backspace>16/}`, and `{{` types a literal `{`. An unclosed token
@@ -220,7 +224,7 @@ Typing does not focus a field — tap it first.
220
224
  appetize press <button>
221
225
  ```
222
226
 
223
- Prints `{ pressed: true }`. Buttons: `home` and `lock` on both platforms;
227
+ Prints `{}`. Buttons: `home` and `lock` on both platforms;
224
228
  `back`, `menu`, `unlock`, `volumeUp` and `volumeDown` on Android only. Calling
225
229
  an Android-only button on iOS fails.
226
230
 
@@ -233,7 +237,7 @@ main page.
233
237
  appetize rotate <orientation>
234
238
  ```
235
239
 
236
- Prints the resulting orientation. Orientations: `portrait`, `landscapeLeft`,
240
+ Prints `{}`. Orientations: `portrait`, `landscapeLeft`,
237
241
  `landscapeRight`, `upsideDown`.
238
242
 
239
243
  ## open
@@ -242,9 +246,18 @@ Prints the resulting orientation. Orientations: `portrait`, `landscapeLeft`,
242
246
  appetize open <url>
243
247
  ```
244
248
 
245
- Prints `{ opened: true }`. `<url>` is a deep link (`myapp://orders/42`) or an
249
+ Prints `{}`. `<url>` is a deep link (`myapp://orders/42`) or an
246
250
  http(s) URL. A malformed or scheme-less value is refused.
247
251
 
252
+ ## keychain clear
253
+
254
+ ```
255
+ appetize keychain clear
256
+ ```
257
+
258
+ Prints `{}`. iOS only: clears the entire keychain. No effect on
259
+ Android.
260
+
248
261
  ## screenshot
249
262
 
250
263
  ```
@@ -252,7 +265,7 @@ appetize screenshot [output]
252
265
  ```
253
266
 
254
267
  `output` defaults to `screenshot`, and an extension matching the image is added
255
- if you omit one. Writes a PNG and prints `{ bytes, mimeType, output }`.
268
+ if you omit one. Writes a PNG and prints `{ bytes, output }`.
256
269
 
257
270
  ## recording start
258
271
 
@@ -265,7 +278,7 @@ appetize recording start <output> [options]
265
278
  | `--force` | Overwrite the file if it already exists |
266
279
 
267
280
  Asks the daemon to open the device video stream and capture it to `output`,
268
- printing `{ path, recording: true }`. Capture is opt-in — video is lazy
281
+ printing `{ path }`. Capture is opt-in — video is lazy
269
282
  device-side, so nothing streams until this runs.
270
283
 
271
284
  `output` is required. `.mp4` is added if you omit an extension, any other
@@ -284,7 +297,7 @@ capture is running.
284
297
  appetize recording stop
285
298
  ```
286
299
 
287
- Closes the stream and the file, printing `{ path, recording: false }`. Fails
300
+ Closes the stream and the file, printing `{ path }`. Fails
288
301
  with `No video recording in progress` when nothing is recording. Ending the
289
302
  session while recording closes the file too, noting it in `daemon.log`.
290
303
 
@@ -355,6 +368,7 @@ slider track.
355
368
  | `APPETIZE_API_TOKEN` | Authorizes private APIs and session targets |
356
369
  | `APPETIZE_ENDPOINT` | Appetize URL used when `--endpoint` is omitted |
357
370
  | `APPETIZE_CLI_HOME` | Overrides the session state directory |
371
+ | `APPETIZE_OUTPUT` | `json` or `text` when neither flag is passed |
358
372
 
359
373
  Session state is written to `~/.appetize/cli/sessions/<session-id>/state.json`.
360
374
  Device logs stream as JSONL to `device.log.jsonl` beside it, and the daemon's
@@ -362,12 +376,16 @@ own output goes to `daemon.log`.
362
376
 
363
377
  ## Exit codes and output streams
364
378
 
365
- Commands exit `0` on success and non-zero on failure, printing the message to
366
- stderr. A selector `--timeout` that expires is a failure, which is what makes it
367
- usable as a wait.
379
+ Commands exit `0` on success and non-zero on failure, printing
380
+ `{ "error": { "message": "…" } }` to stderr (text on a terminal, unless
381
+ `--json`), plus `sessionDir` (the session's
382
+ logs and `state.json`) once a session is found, unless the command was misused.
383
+ A selector `--timeout` that expires is a failure, which is what makes it usable
384
+ as a wait.
368
385
 
369
- Machine-readable results go to stdout as JSON. Progress and errors are always
370
- stderr.
386
+ Results go to stdout as JSON (text on a terminal, unless `--json`) and hold
387
+ only what you could not know beforehand; `{}` means the command ran. stderr
388
+ carries only errors, plus a progress spinner on an interactive terminal.
371
389
 
372
390
  Common failures:
373
391
 
@@ -375,4 +393,4 @@ Common failures:
375
393
  | --------------------------- | --------------------------------------------------------------------------------- |
376
394
  | `No active sessions` | `session start` first; if one should be live, its `daemon.log` says why it exited |
377
395
  | `No session found for id X` | `endedReason` in the session's `state.json` |
378
- | `Session is not ready` | `state.phase` in `state.json` says how far it got |
396
+ | `Session is not ready` | `state.phase` in `state.json` under `error.sessionDir` says how far it got |