Skip to content

Commit e6681bc

Browse files
committed
Improve SimDeck CLI waits and JS test defaults
1 parent 63f0bc7 commit e6681bc

9 files changed

Lines changed: 977 additions & 646 deletions

File tree

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ simdeck stream <udid> --frames 120 > stream.h264
132132
simdeck describe <udid>
133133
simdeck describe <udid> --format agent --max-depth 4
134134
simdeck describe <udid> --point 120,240
135+
simdeck wait-for <udid> --label "Welcome" --timeout-ms 5000
136+
simdeck assert <udid> --id login.button --source auto --max-depth 8
135137
simdeck tap <udid> 120 240
136138
simdeck tap <udid> --label "Continue" --wait-timeout-ms 5000
137139
simdeck swipe <udid> 200 700 200 200
@@ -161,6 +163,10 @@ simdeck chrome-profile <udid>
161163
simdeck logs <udid> --seconds 30 --limit 200
162164
```
163165

166+
`simdeck list` defaults to compact JSON for agent-friendly device selection.
167+
Use `simdeck list --format json` for the full inventory with paths and display
168+
metadata.
169+
164170
`boot` uses SimDeck's private CoreSimulator boot path so it can start devices
165171
without launching Simulator.app. If that private path is unavailable, the
166172
command returns the CoreSimulator error instead of falling back to
@@ -190,18 +196,20 @@ coordinates directly.
190196
```ts
191197
import { connect } from "simdeck/test";
192198

193-
const sim = await connect();
199+
const sim = await connect({ udid: "<udid>" });
194200
try {
195-
await sim.tap("<udid>", 0.5, 0.5);
196-
await sim.waitFor("<udid>", { label: "Continue" });
197-
await sim.screenshot("<udid>");
201+
await sim.tap(0.5, 0.5);
202+
await sim.waitFor({ label: "Continue" });
203+
await sim.screenshot();
198204
} finally {
199205
sim.close();
200206
}
201207
```
202208

203209
`connect()` starts the project daemon when needed, reuses it when it is already
204-
healthy, and only stops daemons it started itself.
210+
healthy, and only stops daemons it started itself. Pass `udid` to `connect()`
211+
to make it the default for session methods; each method still accepts an
212+
explicit UDID as the first argument when needed.
205213

206214
## NativeScript Inspector
207215

docs/cli/commands.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ simdeck daemon restart --video-codec software --stream-quality low
2929

3030
```sh
3131
simdeck list
32+
simdeck list --format json
3233
simdeck boot <udid>
3334
simdeck shutdown <udid>
3435
simdeck erase <udid>
3536
```
3637

3738
Android emulators appear as IDs such as `android:Pixel_8_API_36`.
39+
`list` defaults to compact JSON. Use `--format json` for the full simulator
40+
inventory, including paths and display metadata.
3841

3942
## Apps And URLs
4043

@@ -59,6 +62,8 @@ simdeck describe <udid> --source flutter
5962
simdeck describe <udid> --source uikit
6063
simdeck describe <udid> --source native-ax
6164
simdeck describe <udid> --point 120,240
65+
simdeck wait-for <udid> --label "Welcome" --timeout-ms 5000
66+
simdeck assert <udid> --id login.button --source auto --max-depth 8
6267
```
6368

6469
Default source selection prefers a connected framework inspector, then the Swift in-app agent, then native accessibility.

docs/guide/testing.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,20 @@ SimDeck supports two testing workflows:
1010
```ts
1111
import { connect } from "simdeck/test";
1212

13-
const sim = await connect();
13+
const udid = process.env.SIMDECK_UDID!;
14+
const sim = await connect({ udid });
1415

1516
try {
16-
const { simulators } = await sim.list();
17-
const udid = simulators.find((device) => device.isBooted)?.udid;
18-
19-
await sim.launch(udid, "com.example.App");
20-
await sim.tap(udid, 0.5, 0.5);
21-
await sim.waitFor(udid, { label: "Continue" });
22-
await sim.screenshot(udid);
17+
await sim.launch("com.example.App");
18+
await sim.tap(0.5, 0.5);
19+
await sim.waitFor({ label: "Continue" });
20+
await sim.screenshot();
2321
} finally {
2422
sim.close();
2523
}
2624
```
2725

28-
`connect()` starts the project daemon if needed, reuses a healthy daemon, and only stops daemons it started itself.
26+
`connect()` starts the project daemon if needed, reuses a healthy daemon, and only stops daemons it started itself. Pass `udid` to `connect()` to make it the default for session methods; methods still accept an explicit UDID as their first argument.
2927

3028
## Useful Test Methods
3129

packages/simdeck-test/dist/index.d.ts

Lines changed: 77 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type SimDeckLaunchOptions = {
55
isolated?: boolean;
66
port?: number;
77
videoCodec?: "auto" | "hardware" | "software" | "h264-software";
8+
udid?: string;
89
};
910
export type QueryOptions = {
1011
source?:
@@ -50,87 +51,89 @@ export type LogsOptions = {
5051
processes?: string[];
5152
q?: string;
5253
};
54+
type DeviceMethod<TArgs extends unknown[], TResult> = {
55+
(udid: string, ...args: TArgs): TResult;
56+
(...args: TArgs): TResult;
57+
};
5358
export type SimDeckSession = {
5459
endpoint: string;
5560
pid: number;
5661
projectRoot: string;
5762
list(): Promise<unknown>;
58-
boot(udid: string): Promise<unknown>;
59-
shutdown(udid: string): Promise<unknown>;
60-
erase(udid: string): Promise<unknown>;
61-
install(udid: string, appPath: string): Promise<void>;
62-
uninstall(udid: string, bundleId: string): Promise<void>;
63-
launch(udid: string, bundleId: string): Promise<void>;
64-
openUrl(udid: string, url: string): Promise<void>;
65-
tap(udid: string, x: number, y: number): Promise<void>;
66-
tapElement(
67-
udid: string,
68-
selector: ElementSelector,
69-
options?: TapOptions,
70-
): Promise<void>;
71-
touch(udid: string, x: number, y: number, phase: string): Promise<void>;
72-
swipe(
73-
udid: string,
74-
startX: number,
75-
startY: number,
76-
endX: number,
77-
endY: number,
78-
options?: SwipeOptions,
79-
): Promise<unknown>;
80-
gesture(
81-
udid: string,
82-
preset: string,
83-
options?: GestureOptions,
84-
): Promise<unknown>;
85-
typeText(
86-
udid: string,
87-
text: string,
88-
options?: TypeTextOptions,
89-
): Promise<unknown>;
90-
key(udid: string, keyCode: number, modifiers?: number): Promise<void>;
91-
keySequence(
92-
udid: string,
93-
keyCodes: number[],
94-
options?: KeySequenceOptions,
95-
): Promise<void>;
96-
button(udid: string, button: string, durationMs?: number): Promise<void>;
97-
home(udid: string): Promise<void>;
98-
dismissKeyboard(udid: string): Promise<void>;
99-
appSwitcher(udid: string): Promise<void>;
100-
rotateLeft(udid: string): Promise<void>;
101-
rotateRight(udid: string): Promise<void>;
102-
toggleAppearance(udid: string): Promise<void>;
103-
pasteboardSet(udid: string, text: string): Promise<void>;
104-
pasteboardGet(udid: string): Promise<string>;
105-
chromeProfile(udid: string): Promise<unknown>;
106-
logs(udid: string, options?: LogsOptions): Promise<unknown[]>;
107-
tree(udid: string, options?: QueryOptions): Promise<unknown>;
108-
query(
109-
udid: string,
110-
selector: ElementSelector,
111-
options?: QueryOptions,
112-
): Promise<unknown[]>;
113-
assert(
114-
udid: string,
115-
selector: ElementSelector,
116-
options?: QueryOptions,
117-
): Promise<unknown>;
118-
waitFor(
119-
udid: string,
120-
selector: ElementSelector,
121-
options?: QueryOptions & {
122-
timeoutMs?: number;
123-
pollMs?: number;
124-
},
125-
): Promise<unknown>;
126-
batch(
127-
udid: string,
128-
steps: unknown[],
129-
continueOnError?: boolean,
130-
): Promise<unknown>;
131-
screenshot(udid: string): Promise<Buffer>;
63+
boot: DeviceMethod<[], Promise<unknown>>;
64+
shutdown: DeviceMethod<[], Promise<unknown>>;
65+
erase: DeviceMethod<[], Promise<unknown>>;
66+
install: DeviceMethod<[appPath: string], Promise<void>>;
67+
uninstall: DeviceMethod<[bundleId: string], Promise<void>>;
68+
launch: DeviceMethod<[bundleId: string], Promise<void>>;
69+
openUrl: DeviceMethod<[url: string], Promise<void>>;
70+
tap: DeviceMethod<[x: number, y: number], Promise<void>>;
71+
tapElement: DeviceMethod<
72+
[selector: ElementSelector, options?: TapOptions],
73+
Promise<void>
74+
>;
75+
touch: DeviceMethod<[x: number, y: number, phase: string], Promise<void>>;
76+
swipe: DeviceMethod<
77+
[
78+
startX: number,
79+
startY: number,
80+
endX: number,
81+
endY: number,
82+
options?: SwipeOptions,
83+
],
84+
Promise<unknown>
85+
>;
86+
gesture: DeviceMethod<
87+
[preset: string, options?: GestureOptions],
88+
Promise<unknown>
89+
>;
90+
typeText: DeviceMethod<
91+
[text: string, options?: TypeTextOptions],
92+
Promise<unknown>
93+
>;
94+
key: DeviceMethod<[keyCode: number, modifiers?: number], Promise<void>>;
95+
keySequence: DeviceMethod<
96+
[keyCodes: number[], options?: KeySequenceOptions],
97+
Promise<void>
98+
>;
99+
button: DeviceMethod<[button: string, durationMs?: number], Promise<void>>;
100+
home: DeviceMethod<[], Promise<void>>;
101+
dismissKeyboard: DeviceMethod<[], Promise<void>>;
102+
appSwitcher: DeviceMethod<[], Promise<void>>;
103+
rotateLeft: DeviceMethod<[], Promise<void>>;
104+
rotateRight: DeviceMethod<[], Promise<void>>;
105+
toggleAppearance: DeviceMethod<[], Promise<void>>;
106+
pasteboardSet: DeviceMethod<[text: string], Promise<void>>;
107+
pasteboardGet: DeviceMethod<[], Promise<string>>;
108+
chromeProfile: DeviceMethod<[], Promise<unknown>>;
109+
logs: DeviceMethod<[options?: LogsOptions], Promise<unknown[]>>;
110+
tree: DeviceMethod<[options?: QueryOptions], Promise<unknown>>;
111+
query: DeviceMethod<
112+
[selector: ElementSelector, options?: QueryOptions],
113+
Promise<unknown[]>
114+
>;
115+
assert: DeviceMethod<
116+
[selector: ElementSelector, options?: QueryOptions],
117+
Promise<unknown>
118+
>;
119+
waitFor: DeviceMethod<
120+
[
121+
selector: ElementSelector,
122+
options?: QueryOptions & {
123+
timeoutMs?: number;
124+
pollMs?: number;
125+
},
126+
],
127+
Promise<unknown>
128+
>;
129+
batch: DeviceMethod<
130+
[steps: unknown[], continueOnError?: boolean],
131+
Promise<unknown>
132+
>;
133+
screenshot: DeviceMethod<[], Promise<Buffer>>;
132134
close(): void;
133135
};
134136
export declare function connect(
135137
options?: SimDeckLaunchOptions,
136138
): Promise<SimDeckSession>;
139+
export {};

0 commit comments

Comments
 (0)