@goliapkg/smix 0.1.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.
Files changed (64) hide show
  1. package/README.md +193 -0
  2. package/dist/A11yNode.d.ts +36 -0
  3. package/dist/A11yNode.d.ts.map +1 -0
  4. package/dist/A11yNode.js +30 -0
  5. package/dist/A11yNode.js.map +1 -0
  6. package/dist/App.d.ts +57 -0
  7. package/dist/App.d.ts.map +1 -0
  8. package/dist/App.js +123 -0
  9. package/dist/App.js.map +1 -0
  10. package/dist/ExpectationFailure.d.ts +30 -0
  11. package/dist/ExpectationFailure.d.ts.map +1 -0
  12. package/dist/ExpectationFailure.js +47 -0
  13. package/dist/ExpectationFailure.js.map +1 -0
  14. package/dist/HttpRunner.d.ts +61 -0
  15. package/dist/HttpRunner.d.ts.map +1 -0
  16. package/dist/HttpRunner.js +148 -0
  17. package/dist/HttpRunner.js.map +1 -0
  18. package/dist/Locator.d.ts +60 -0
  19. package/dist/Locator.d.ts.map +1 -0
  20. package/dist/Locator.js +177 -0
  21. package/dist/Locator.js.map +1 -0
  22. package/dist/Pattern.d.ts +21 -0
  23. package/dist/Pattern.d.ts.map +1 -0
  24. package/dist/Pattern.js +44 -0
  25. package/dist/Pattern.js.map +1 -0
  26. package/dist/Selector.d.ts +96 -0
  27. package/dist/Selector.d.ts.map +1 -0
  28. package/dist/Selector.js +276 -0
  29. package/dist/Selector.js.map +1 -0
  30. package/dist/SelectorResolver.d.ts +41 -0
  31. package/dist/SelectorResolver.d.ts.map +1 -0
  32. package/dist/SelectorResolver.js +55 -0
  33. package/dist/SelectorResolver.js.map +1 -0
  34. package/dist/SimRuntime.d.ts +81 -0
  35. package/dist/SimRuntime.d.ts.map +1 -0
  36. package/dist/SimRuntime.js +91 -0
  37. package/dist/SimRuntime.js.map +1 -0
  38. package/dist/Smix.d.ts +22 -0
  39. package/dist/Smix.d.ts.map +1 -0
  40. package/dist/Smix.js +35 -0
  41. package/dist/Smix.js.map +1 -0
  42. package/dist/index.d.ts +11 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +16 -0
  45. package/dist/index.js.map +1 -0
  46. package/package.json +44 -0
  47. package/src/A11yNode.ts +62 -0
  48. package/src/App.ts +153 -0
  49. package/src/ExpectationFailure.ts +67 -0
  50. package/src/HttpRunner.ts +185 -0
  51. package/src/Locator.ts +190 -0
  52. package/src/Pattern.ts +51 -0
  53. package/src/Selector.ts +296 -0
  54. package/src/SelectorResolver.ts +76 -0
  55. package/src/SimRuntime.ts +145 -0
  56. package/src/Smix.ts +49 -0
  57. package/src/__tests__/AppFullApi.test.ts +168 -0
  58. package/src/__tests__/AppTapMock.test.ts +122 -0
  59. package/src/__tests__/HttpRunner.test.ts +121 -0
  60. package/src/__tests__/Locator.test.ts +128 -0
  61. package/src/__tests__/LocatorToHave.test.ts +118 -0
  62. package/src/__tests__/MvpApiShape.test.ts +117 -0
  63. package/src/__tests__/SelectorFullSchema.test.ts +161 -0
  64. package/src/index.ts +76 -0
package/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # @smix/rn
2
+
3
+ > **Status (2026-06-30)**: v7 initiative ✅ closed — TS SDK API surface complete,
4
+ > 61 vitest unit tests + 4 demo flows running end-to-end PASS + 24-fixture
5
+ > cross-binary harness × 3 backend byte-identical (Rust + Swift + TS).
6
+ >
7
+ > Currently `0.1.0-pre.1` (not yet published to npm). **Public ship gated by
8
+ > user actions** per [docs/ai-guide/v7-release-gpg-infra.md](../../docs/ai-guide/v7-release-gpg-infra.md):
9
+ > `@smix` npm scope ownership + smix-runner-server Swift handler injection
10
+ > (v8 backlog).
11
+ >
12
+ > See also [v7 full initiative wrap](../../docs/plan-history/v7-full-initiative-wrap.md)
13
+ > for the 10-cycle outcome summary.
14
+
15
+ Playwright-style iOS Simulator + Android emulator UI automation SDK for
16
+ TypeScript / React Native / Expo / Detox-compatible test runners. v7
17
+ initiative brings smix's Rust-core selector resolver to TS via HTTP
18
+ client(production) or in-process mock(unit tests),packaged as ESM Node
19
+ 20+ module。
20
+
21
+ ## Installation(post v0.1.0 ship)
22
+
23
+ ```bash
24
+ # Once v0.1.0 published:
25
+ bun add -D @smix/rn
26
+ # OR
27
+ npm install -D @smix/rn
28
+ ```
29
+
30
+ For test target / Detox-equiv consumer. **Iron rule (§9 #13)**: this
31
+ package ships only to `devDependencies` — never bundled into a
32
+ production RN/Expo app via metro/expo tree-shaking。
33
+
34
+ ## Quick start(MockSimRuntime — unit tests)
35
+
36
+ ```typescript
37
+ import { Smix, Selector, literal, MockSimRuntime, MockSelectorResolver, bundleId } from '@smix/rn'
38
+
39
+ const runtime = new MockSimRuntime({ snapshotResult: /* A11yNode tree */ })
40
+ const resolver = new MockSelectorResolver()
41
+ resolver.registerHit('{"id":"btn-login"}', 'btn-login')
42
+
43
+ const app = await Smix.launchApp(bundleId('com.example.MyApp'), runtime, resolver.resolve)
44
+ await app.tap(Selector.id('btn-login'))
45
+ await app.fill(Selector.id('input-username'), 'alice')
46
+
47
+ const welcome = app.find(Selector.text(literal('Welcome back')))
48
+ await welcome.toBeVisible({ timeoutMs: 5_000 })
49
+ ```
50
+
51
+ ## Production: HTTP runtime
52
+
53
+ ```typescript
54
+ import { Smix, Selector, bundleId, HttpSimRuntime } from '@smix/rn'
55
+
56
+ // Connects to smix-runner-server running on host(see swift-bridge/SmixRunnerCore)
57
+ const runtime = new HttpSimRuntime('http://localhost:14101')
58
+
59
+ const app = await Smix.launchApp(bundleId('com.example.MyApp'), runtime, runtime.resolver, runtime.labelsResolver)
60
+ await app.tap(Selector.id('btn-login').below(Selector.text(literal('Sign In'))).nth(0))
61
+ await app.find(Selector.role('button')).toHaveCount(3, { timeoutMs: 5_000 })
62
+ ```
63
+
64
+ HTTP endpoints documented in [`src/HttpRunner.ts`](src/HttpRunner.ts) +
65
+ [Swift server route handler](../../swift-bridge/Sources/SmixRunnerCore/SmixRunnerServer.swift).
66
+
67
+ ## API surface(v7 complete)
68
+
69
+ ### Selector
70
+
71
+ ```typescript
72
+ // Base discriminators(untagged JSON wire shape — matches Rust smix-selector)
73
+ Selector.id('btn-login')
74
+ Selector.text(literal('Sign In'))
75
+ Selector.text(regex('^Sub', 'i'))
76
+ Selector.label('Settings')
77
+ Selector.role('button')
78
+ Selector.role('button', literal('Submit'))
79
+ Selector.focused()
80
+ Selector.anchor({ below: Selector.text(literal('Total')) }, { nth: 0 })
81
+ Selector.localizedText({ en: 'Submit', ja: '送信' })
82
+
83
+ // Fluent modifier chaining(returns new Selector with Modifiers updated)
84
+ Selector.id('btn').below(Selector.text(literal('Address')))
85
+ Selector.label('Edit').nth(0)
86
+ Selector.text(literal('Item')).above(Selector.text(literal('Footer'))).first()
87
+ Selector.role('button').near(Selector.text(literal('Confirm'))).ancestor(Selector.role('dialog'))
88
+ ```
89
+
90
+ Wire JSON(untagged + flatten — byte-identical to Rust smix-selector):
91
+
92
+ ```json
93
+ {"id": "btn-login", "below": {"text": "Address"}, "nth": 0}
94
+ ```
95
+
96
+ ### App(act)
97
+
98
+ ```typescript
99
+ await app.tap(selector: Selector, opts?: { timeoutMs?: number })
100
+ await app.fill(selector: Selector, text: string)
101
+ await app.pressKey(key: KeyName) // 'return' | 'delete' | 'space' | 'tab' | 'escape' | 'enter'
102
+ await app.swipe(direction: SwipeDirection) // 'up' | 'down' | 'left' | 'right'
103
+ await app.tapAtCoord(nx: number, ny: number) // 0..1, throws if out-of-range
104
+ await app.terminate()
105
+ await app.relaunch()
106
+ await app.launchFresh({ clearState?: boolean, clearKeychain?: boolean, appPath?: string })
107
+ ```
108
+
109
+ ### App(sense)
110
+
111
+ ```typescript
112
+ const png: Uint8Array = await app.screenshot()
113
+ const tree: A11yNode = await app.tree()
114
+ const popups: A11yNode[] = await app.systemPopups()
115
+ await app.openUrl('myapp://deep/link')
116
+ ```
117
+
118
+ ### Locator(assertions)
119
+
120
+ ```typescript
121
+ const loc = app.find(Selector.text(literal('Welcome')))
122
+ await loc.toBeVisible({ timeoutMs: 5_000 }) // polls 250ms
123
+ await loc.toContainText('alice', { timeoutMs: 5_000 }) // substring
124
+ await loc.toHaveLabel('Sign In', { timeoutMs: 5_000 }) // strict equal
125
+ await loc.toHaveCount(3, { timeoutMs: 5_000 }) // exact match count
126
+ ```
127
+
128
+ ### ExpectationFailure(AI-readable JSON)
129
+
130
+ ```typescript
131
+ try {
132
+ await app.tap(Selector.id('btn-missing'))
133
+ } catch (e) {
134
+ if (e instanceof ExpectationFailure) {
135
+ e.code // 'notFound' | 'ambiguous' | 'notInteractable' | 'timeout' | 'wrongState' | 'unknown'
136
+ e.message // human-readable
137
+ e.selectorJson // original Selector encoded as JSON
138
+ e.visibleElements // A11yNode[] — first 20 nodes from current tree
139
+ e.suggestions // string[] — ['check accessibilityIdentifier...', ...]
140
+ e.toJson() // single-line sorted-keys JSON for AI agent consumption
141
+ }
142
+ }
143
+ ```
144
+
145
+ ## Demos
146
+
147
+ Run any of the 4 realistic e2e flows in [`examples/demo-app/`](examples/demo-app):
148
+
149
+ ```bash
150
+ cd examples/demo-app
151
+ bun login-flow.ts # login + welcome assertion
152
+ bun form-validation-flow.ts # 3-cycle form validation + inline errors
153
+ bun list-scroll-flow.ts # scrollable list + toHaveCount semantics
154
+ bun multi-screen-nav-flow.ts # nav + tapAtCoord + openUrl + relaunch
155
+ ```
156
+
157
+ See [`examples/demo-app/COVERAGE.md`](examples/demo-app/COVERAGE.md) for the
158
+ SDK surface coverage matrix across demos.
159
+
160
+ ## Conformance
161
+
162
+ Cross-binary harness verifies SDK output byte-identical to Rust:
163
+
164
+ ```bash
165
+ bash ../../scripts/sdk/run-cross-binary-harness.sh
166
+ # Summary: 24 / 24 fixtures byte-identical (Rust + Swift + TS)
167
+ ```
168
+
169
+ ## Iron rule §9 #13
170
+
171
+ `@smix/rn` is **never** bundled into a production RN/Expo app. It lives
172
+ in `devDependencies` only — metro/expo's tree-shaking guarantees the
173
+ production bundle excludes it. The `HttpSimRuntime` connects to a
174
+ smix-runner-server running on the host(via 127.0.0.1:port loopback),
175
+ not in-process。
176
+
177
+ ## Architecture
178
+
179
+ Per [docs/plan-cold/v7-sdk-design.md](../../docs/plan-cold/v7-sdk-design.md):
180
+
181
+ - **Rust stone core**(crates/smix-{selector,selector-resolver,screen,error}):
182
+ selector resolution + AI-readable failure types,shared across all
183
+ 4 language SDKs。
184
+ - **HTTP runtime**(`src/HttpRunner.ts`): wraps fetch to smix-runner-server
185
+ on the host。Production-default。
186
+ - **Mock runtime**(`src/SimRuntime.ts MockSimRuntime`): in-memory tree +
187
+ event log。Used by vitest unit tests + 4 demo flows。
188
+ - **SDK facade**(`src/{Smix,App,Selector,Modifiers,Locator,ExpectationFailure}.ts`):
189
+ Playwright-style ergonomic surface,class-based fluent chaining。
190
+
191
+ ## License
192
+
193
+ Apache-2.0 OR MIT(dual,at your option)。
@@ -0,0 +1,36 @@
1
+ export interface Rect {
2
+ x: number;
3
+ y: number;
4
+ w: number;
5
+ h: number;
6
+ }
7
+ export declare function rectCenter(r: Rect): {
8
+ x: number;
9
+ y: number;
10
+ };
11
+ export type A11yRole = 'button' | 'link' | 'textField' | 'secureTextField' | 'searchField' | 'switch' | 'toggle' | 'checkBox' | 'radio' | 'image' | 'staticText' | 'tab' | 'tabBar' | 'navigationBar' | 'cell' | 'alert' | 'dialog' | 'slider' | 'progressBar' | 'picker' | 'menu' | 'menuItem' | 'scrollView' | 'segmentedControl' | 'table' | 'collectionView' | 'webView' | 'keyboard';
12
+ export interface A11yNode {
13
+ rawType: string;
14
+ role?: A11yRole | undefined;
15
+ identifier?: string | undefined;
16
+ label?: string | undefined;
17
+ title?: string | undefined;
18
+ placeholderValue?: string | undefined;
19
+ value?: string | undefined;
20
+ text?: string | undefined;
21
+ bounds: Rect;
22
+ enabled?: boolean | undefined;
23
+ selected?: boolean | undefined;
24
+ hasFocus?: boolean | undefined;
25
+ visible?: boolean | undefined;
26
+ children?: A11yNode[] | undefined;
27
+ }
28
+ /**
29
+ * Recursively find the first node whose [identifier] matches [id].
30
+ */
31
+ export declare function findById(node: A11yNode, id: string): A11yNode | null;
32
+ /**
33
+ * DFS pre-order flatten — self + all descendants in a single list.
34
+ */
35
+ export declare function flatten(node: A11yNode): A11yNode[];
36
+ //# sourceMappingURL=A11yNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"A11yNode.d.ts","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,MAAM,MAAM,QAAQ,GAChB,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,iBAAiB,GAAG,aAAa,GACnE,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,GACnE,KAAK,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAChE,QAAQ,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GACzD,YAAY,GAAG,kBAAkB,GAAG,OAAO,GAAG,gBAAgB,GAC9D,SAAS,GAAG,UAAU,CAAA;AAE1B,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,MAAM,EAAE,IAAI,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;CAClC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAOpE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAMlD"}
@@ -0,0 +1,30 @@
1
+ // v7.5 c1 — A11yNode + Rect mirror Rust smix-screen A11yNode +
2
+ // Swift v7.2 + Kotlin v7.4 A11yNode. CamelCase wire keys match
3
+ // Rust `#[serde(rename_all = "camelCase")]`.
4
+ export function rectCenter(r) {
5
+ return { x: r.x + r.w / 2, y: r.y + r.h / 2 };
6
+ }
7
+ /**
8
+ * Recursively find the first node whose [identifier] matches [id].
9
+ */
10
+ export function findById(node, id) {
11
+ if (node.identifier === id)
12
+ return node;
13
+ for (const child of node.children ?? []) {
14
+ const hit = findById(child, id);
15
+ if (hit !== null)
16
+ return hit;
17
+ }
18
+ return null;
19
+ }
20
+ /**
21
+ * DFS pre-order flatten — self + all descendants in a single list.
22
+ */
23
+ export function flatten(node) {
24
+ const out = [node];
25
+ for (const child of node.children ?? []) {
26
+ out.push(...flatten(child));
27
+ }
28
+ return out;
29
+ }
30
+ //# sourceMappingURL=A11yNode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"A11yNode.js","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,6CAA6C;AAS7C,MAAM,UAAU,UAAU,CAAC,CAAO;IAChC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA;AAC/C,CAAC;AA2BD;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAc,EAAE,EAAU;IACjD,IAAI,IAAI,CAAC,UAAU,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACvC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC/B,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,GAAG,CAAA;IAC9B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,MAAM,GAAG,GAAe,CAAC,IAAI,CAAC,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
package/dist/App.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { type A11yNode } from './A11yNode.js';
2
+ import { Locator, SmixNotImplementedError } from './Locator.js';
3
+ import { type Selector } from './Selector.js';
4
+ import { type KeyName, type SmixSimRuntime, type SwipeDirection } from './SimRuntime.js';
5
+ import type { LabelsResolver, SelectorResolver } from './SelectorResolver.js';
6
+ /**
7
+ * A handle to a running app on the simulator / emulator.
8
+ */
9
+ export declare class App {
10
+ readonly bundleId: string;
11
+ readonly runtime: SmixSimRuntime;
12
+ readonly resolver: SelectorResolver;
13
+ /**
14
+ * v7.6 c1 — wraps Rust `resolve_selector_labels` (each match's
15
+ * `.label`). Used by Locator.toHaveCount + toHaveLabel. Defaults
16
+ * to a stub that throws — tests + production should pass a real
17
+ * impl (HttpSimRuntime.labelsResolver or MockLabelsResolver.resolve).
18
+ */
19
+ readonly labelsResolver: LabelsResolver;
20
+ constructor(bundleId: string, runtime: SmixSimRuntime, resolver: SelectorResolver,
21
+ /**
22
+ * v7.6 c1 — wraps Rust `resolve_selector_labels` (each match's
23
+ * `.label`). Used by Locator.toHaveCount + toHaveLabel. Defaults
24
+ * to a stub that throws — tests + production should pass a real
25
+ * impl (HttpSimRuntime.labelsResolver or MockLabelsResolver.resolve).
26
+ */
27
+ labelsResolver?: LabelsResolver);
28
+ tap(selector: Selector, _opts?: {
29
+ timeoutMs?: number;
30
+ }): Promise<void>;
31
+ /**
32
+ * Resolve [selector], tap to focus, then type [text]. Mirror
33
+ * Kotlin App.fill from v7.4 c3.
34
+ */
35
+ fill(selector: Selector, text: string): Promise<void>;
36
+ pressKey(key: KeyName): Promise<void>;
37
+ swipe(direction: SwipeDirection): Promise<void>;
38
+ screenshot(): Promise<Uint8Array>;
39
+ /**
40
+ * Tap at a normalized coordinate (0..1) — escape hatch per §9 #3.
41
+ * Out-of-range values throw ExpectationFailure code=wrongState.
42
+ */
43
+ tapAtCoord(nx: number, ny: number): Promise<void>;
44
+ terminate(): Promise<void>;
45
+ relaunch(): Promise<void>;
46
+ launchFresh(opts?: {
47
+ clearState?: boolean;
48
+ clearKeychain?: boolean;
49
+ appPath?: string;
50
+ }): Promise<void>;
51
+ find(selector: Selector): Locator;
52
+ tree(): Promise<A11yNode>;
53
+ systemPopups(): Promise<A11yNode[]>;
54
+ openUrl(url: string): Promise<void>;
55
+ }
56
+ export { SmixNotImplementedError };
57
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAAqB,MAAM,eAAe,CAAA;AAEhE,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7E;;GAEG;AACH,qBAAa,GAAG;aAEI,QAAQ,EAAE,MAAM;aAChB,OAAO,EAAE,cAAc;aACvB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;aACa,cAAc,EAAE,cAAc;gBAT9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;IACa,cAAc,GAAE,cAE/B;IAKG,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsChF;;;OAGG;IACG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,KAAK,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAIvC;;;OAGG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjD,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKzB,WAAW,CAAC,IAAI,GAAE;QACtB,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,aAAa,CAAC,EAAE,OAAO,CAAA;QACvB,OAAO,CAAC,EAAE,MAAM,CAAA;KACZ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtB,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAI3B,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIzB,YAAY,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAInC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1C;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
package/dist/App.js ADDED
@@ -0,0 +1,123 @@
1
+ // v7.5 c3 — App class full wire mirrors Kotlin v7.4 c5 / Swift v7.2.
2
+ import { findById, flatten } from './A11yNode.js';
3
+ import { ExpectationFailure } from './ExpectationFailure.js';
4
+ import { Locator, SmixNotImplementedError } from './Locator.js';
5
+ import { encodeSelectorJson, } from './Selector.js';
6
+ /**
7
+ * A handle to a running app on the simulator / emulator.
8
+ */
9
+ export class App {
10
+ bundleId;
11
+ runtime;
12
+ resolver;
13
+ labelsResolver;
14
+ constructor(bundleId, runtime, resolver,
15
+ /**
16
+ * v7.6 c1 — wraps Rust `resolve_selector_labels` (each match's
17
+ * `.label`). Used by Locator.toHaveCount + toHaveLabel. Defaults
18
+ * to a stub that throws — tests + production should pass a real
19
+ * impl (HttpSimRuntime.labelsResolver or MockLabelsResolver.resolve).
20
+ */
21
+ labelsResolver = async () => {
22
+ throw new Error('labelsResolver not configured; pass one to Smix.launchApp or App constructor');
23
+ }) {
24
+ this.bundleId = bundleId;
25
+ this.runtime = runtime;
26
+ this.resolver = resolver;
27
+ this.labelsResolver = labelsResolver;
28
+ }
29
+ // ---- act methods --------------------------------------------------
30
+ async tap(selector, _opts = {}) {
31
+ const tree = await this.runtime.snapshotTree();
32
+ const selectorJson = encodeSelectorJson(selector);
33
+ const treeJson = JSON.stringify(tree);
34
+ let matches;
35
+ try {
36
+ matches = await this.resolver(treeJson, selectorJson);
37
+ }
38
+ catch (e) {
39
+ const msg = e instanceof Error ? e.message : String(e);
40
+ throw new ExpectationFailure({
41
+ code: 'unknown',
42
+ message: `FFI resolveSelector raised: ${msg}`,
43
+ selectorJson,
44
+ });
45
+ }
46
+ const firstId = matches[0];
47
+ const node = firstId !== undefined ? findById(tree, firstId) : null;
48
+ if (node === null) {
49
+ throw new ExpectationFailure({
50
+ code: 'notFound',
51
+ message: 'no candidates after spatial + index filters',
52
+ selectorJson,
53
+ visibleElements: flatten(tree).slice(0, 20),
54
+ suggestions: [
55
+ 'check `accessibilityIdentifier` is set on the target view',
56
+ 'consider relaxing to text(...) if the label is more stable than the id',
57
+ ],
58
+ });
59
+ }
60
+ await this.runtime.synthesizeTap(node.bounds.x + node.bounds.w / 2, node.bounds.y + node.bounds.h / 2);
61
+ }
62
+ /**
63
+ * Resolve [selector], tap to focus, then type [text]. Mirror
64
+ * Kotlin App.fill from v7.4 c3.
65
+ */
66
+ async fill(selector, text) {
67
+ await this.tap(selector);
68
+ await this.runtime.sendString(text);
69
+ }
70
+ async pressKey(key) {
71
+ await this.runtime.pressKey(key);
72
+ }
73
+ async swipe(direction) {
74
+ await this.runtime.swipe(direction);
75
+ }
76
+ async screenshot() {
77
+ return this.runtime.screenshot();
78
+ }
79
+ /**
80
+ * Tap at a normalized coordinate (0..1) — escape hatch per §9 #3.
81
+ * Out-of-range values throw ExpectationFailure code=wrongState.
82
+ */
83
+ async tapAtCoord(nx, ny) {
84
+ if (nx < 0 || nx > 1 || ny < 0 || ny > 1) {
85
+ throw new ExpectationFailure({
86
+ code: 'wrongState',
87
+ message: `tapAtCoord arguments out of [0,1] range: nx=${nx}, ny=${ny}`,
88
+ suggestions: ['use normalized 0..1 — for raw points, divide by viewport size'],
89
+ });
90
+ }
91
+ await this.runtime.synthesizeTapAtNormalized(nx, ny);
92
+ }
93
+ async terminate() {
94
+ await this.runtime.terminate(this.bundleId);
95
+ }
96
+ async relaunch() {
97
+ await this.runtime.terminate(this.bundleId);
98
+ await this.runtime.launch(this.bundleId);
99
+ }
100
+ async launchFresh(opts = {}) {
101
+ await this.runtime.launchFresh({
102
+ bundleId: this.bundleId,
103
+ clearState: opts.clearState ?? false,
104
+ clearKeychain: opts.clearKeychain ?? false,
105
+ appPath: opts.appPath,
106
+ });
107
+ }
108
+ // ---- sense methods ------------------------------------------------
109
+ find(selector) {
110
+ return new Locator(this, selector);
111
+ }
112
+ async tree() {
113
+ return this.runtime.snapshotTree();
114
+ }
115
+ async systemPopups() {
116
+ return this.runtime.systemPopups();
117
+ }
118
+ async openUrl(url) {
119
+ await this.runtime.openUrl(url);
120
+ }
121
+ }
122
+ export { SmixNotImplementedError };
123
+ //# sourceMappingURL=App.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAErE,OAAO,EAAiB,QAAQ,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EACL,kBAAkB,GAEnB,MAAM,eAAe,CAAA;AAQtB;;GAEG;AACH,MAAM,OAAO,GAAG;IAEI;IACA;IACA;IAOA;IAVlB,YACkB,QAAgB,EAChB,OAAuB,EACvB,QAA0B;IAC1C;;;;;OAKG;IACa,iBAAiC,KAAK,IAAI,EAAE;QAC1D,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;IACjG,CAAC;QAXe,aAAQ,GAAR,QAAQ,CAAQ;QAChB,YAAO,GAAP,OAAO,CAAgB;QACvB,aAAQ,GAAR,QAAQ,CAAkB;QAO1B,mBAAc,GAAd,cAAc,CAE7B;IACA,CAAC;IAEJ,sEAAsE;IAEtE,KAAK,CAAC,GAAG,CAAC,QAAkB,EAAE,QAAgC,EAAE;QAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;QAC9C,MAAM,YAAY,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAErC,IAAI,OAA0B,CAAA;QAC9B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;QACvD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YACtD,MAAM,IAAI,kBAAkB,CAAC;gBAC3B,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,+BAA+B,GAAG,EAAE;gBAC7C,YAAY;aACb,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACnE,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,kBAAkB,CAAC;gBAC3B,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,6CAA6C;gBACtD,YAAY;gBACZ,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC3C,WAAW,EAAE;oBACX,2DAA2D;oBAC3D,wEAAwE;iBACzE;aACF,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAC9B,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EACjC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAClC,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,QAAkB,EAAE,IAAY;QACzC,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACxB,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAY;QACzB,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAyB;QACnC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACrC,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAA;IAClC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,EAAU;QACrC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,kBAAkB,CAAC;gBAC3B,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,+CAA+C,EAAE,QAAQ,EAAE,EAAE;gBACtE,WAAW,EAAE,CAAC,+DAA+D,CAAC;aAC/E,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,yBAAyB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC3C,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAId,EAAE;QACJ,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,KAAK;YACpC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,KAAK;YAC1C,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAA;IACJ,CAAC;IAED,sEAAsE;IAEtE,IAAI,CAAC,QAAkB;QACrB,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,CAAC;CACF;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
@@ -0,0 +1,30 @@
1
+ import type { A11yNode } from './A11yNode.js';
2
+ export type FailureCode = 'notFound' | 'ambiguous' | 'notInteractable' | 'timeout' | 'wrongState' | 'unknown';
3
+ export declare const FAILURE_CODES: readonly FailureCode[];
4
+ /**
5
+ * Structured failure thrown by SDK resolver paths (App.tap, Locator
6
+ * assertions) when matching fails. Wire format matches Swift +
7
+ * Kotlin + Rust 1:1.
8
+ */
9
+ export declare class ExpectationFailure extends Error {
10
+ readonly code: FailureCode;
11
+ readonly selectorJson: string | null;
12
+ readonly visibleElements: readonly A11yNode[];
13
+ readonly suggestions: readonly string[];
14
+ readonly timestamp: number;
15
+ constructor(opts: {
16
+ code: FailureCode;
17
+ message: string;
18
+ selectorJson?: string | undefined;
19
+ visibleElements?: readonly A11yNode[] | undefined;
20
+ suggestions?: readonly string[] | undefined;
21
+ timestamp?: number | undefined;
22
+ });
23
+ /**
24
+ * AI-readable JSON dump — what Claude Code sees when the test fails.
25
+ * Sorted keys, single line. Mirror Swift errorDescription / Kotlin
26
+ * errorJson() byte-shape.
27
+ */
28
+ toJson(): string;
29
+ }
30
+ //# sourceMappingURL=ExpectationFailure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpectationFailure.d.ts","sourceRoot":"","sources":["../src/ExpectationFailure.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,SAAS,GACT,YAAY,GACZ,SAAS,CAAA;AAEb,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAGtC,CAAA;AAEV;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,eAAe,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC7C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;gBAEd,IAAI,EAAE;QAChB,IAAI,EAAE,WAAW,CAAA;QACjB,OAAO,EAAE,MAAM,CAAA;QACf,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACjC,eAAe,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,CAAA;QACjD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;QAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC/B;IAUD;;;;OAIG;IACH,MAAM,IAAI,MAAM;CAUjB"}
@@ -0,0 +1,47 @@
1
+ // v7.5 c1 — ExpectationFailure + FailureCode mirror Swift v7.2 c4 +
2
+ // Kotlin v7.4 c1 + Rust smix-error.
3
+ //
4
+ // AI-readable JSON contract: toJson() emits sorted-keys, ISO-8601
5
+ // timestamp single-line JSON matching Swift errorDescription contract
6
+ // (per design.md §9 #11).
7
+ export const FAILURE_CODES = [
8
+ 'notFound', 'ambiguous', 'notInteractable',
9
+ 'timeout', 'wrongState', 'unknown',
10
+ ];
11
+ /**
12
+ * Structured failure thrown by SDK resolver paths (App.tap, Locator
13
+ * assertions) when matching fails. Wire format matches Swift +
14
+ * Kotlin + Rust 1:1.
15
+ */
16
+ export class ExpectationFailure extends Error {
17
+ code;
18
+ selectorJson;
19
+ visibleElements;
20
+ suggestions;
21
+ timestamp;
22
+ constructor(opts) {
23
+ super(opts.message);
24
+ this.name = 'ExpectationFailure';
25
+ this.code = opts.code;
26
+ this.selectorJson = opts.selectorJson ?? null;
27
+ this.visibleElements = opts.visibleElements ?? [];
28
+ this.suggestions = opts.suggestions ?? [];
29
+ this.timestamp = opts.timestamp ?? Date.now();
30
+ }
31
+ /**
32
+ * AI-readable JSON dump — what Claude Code sees when the test fails.
33
+ * Sorted keys, single line. Mirror Swift errorDescription / Kotlin
34
+ * errorJson() byte-shape.
35
+ */
36
+ toJson() {
37
+ return JSON.stringify({
38
+ code: this.code,
39
+ message: this.message,
40
+ selector: this.selectorJson,
41
+ suggestions: this.suggestions,
42
+ timestamp: this.timestamp,
43
+ visibleElements: this.visibleElements,
44
+ });
45
+ }
46
+ }
47
+ //# sourceMappingURL=ExpectationFailure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpectationFailure.js","sourceRoot":"","sources":["../src/ExpectationFailure.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,oCAAoC;AACpC,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,0BAA0B;AAY1B,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,UAAU,EAAE,WAAW,EAAE,iBAAiB;IAC1C,SAAS,EAAE,YAAY,EAAE,SAAS;CAC1B,CAAA;AAEV;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAClC,IAAI,CAAa;IACjB,YAAY,CAAe;IAC3B,eAAe,CAAqB;IACpC,WAAW,CAAmB;IAC9B,SAAS,CAAQ;IAE1B,YAAY,IAOX;QACC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACnB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAA;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACrB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAA;QAC7C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,EAAE,CAAA;QACjD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAA;QACzC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAA;IAC/C,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,IAAI,CAAC,YAAY;YAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,eAAe,EAAE,IAAI,CAAC,eAAe;SACtC,CAAC,CAAA;IACJ,CAAC;CACF"}
@@ -0,0 +1,61 @@
1
+ import { type A11yNode } from './A11yNode.js';
2
+ import type { KeyName, SmixSimRuntime, SwipeDirection } from './SimRuntime.js';
3
+ import type { LabelsResolver, SelectorResolver } from './SelectorResolver.js';
4
+ /**
5
+ * Minimal HTTP client surface — overridable for testing. fetch-compatible
6
+ * signature so consumers can pass globalThis.fetch directly.
7
+ */
8
+ export type HttpFetch = (input: string, init: {
9
+ method: 'POST';
10
+ headers: Record<string, string>;
11
+ body: string;
12
+ }) => Promise<{
13
+ ok: boolean;
14
+ status: number;
15
+ json(): Promise<unknown>;
16
+ text(): Promise<string>;
17
+ }>;
18
+ /**
19
+ * SmixSimRuntime + SelectorResolver wrapper around the smix-runner HTTP
20
+ * server. Used in production RN/Expo test targets via fetch.
21
+ */
22
+ export declare class HttpSimRuntime implements SmixSimRuntime {
23
+ readonly baseUrl: string;
24
+ readonly fetchImpl: HttpFetch;
25
+ readonly resolver: SelectorResolver;
26
+ /**
27
+ * v7.7 c1 — labels resolver wrapping Rust `resolve_selector_labels`
28
+ * via smix-runner-server `/select/resolve-labels` endpoint. Used by
29
+ * `Locator.toHaveCount` (count = labels.length) + Locator.toHaveLabel.
30
+ * Pass directly to App constructor or Smix.launchApp.
31
+ */
32
+ readonly labelsResolver: LabelsResolver;
33
+ constructor(baseUrl: string, fetchImpl?: HttpFetch);
34
+ /**
35
+ * v7.7 c1 — direct match-count via dedicated FFI fn (avoid allocating
36
+ * the full ids list when callers only need count). Mirrors Rust
37
+ * `resolve_selector_count`. Locator.toHaveCount can prefer this over
38
+ * `labelsResolver` if it needs only count, not the labels themselves.
39
+ */
40
+ resolveCount(treeJson: string, selectorJson: string): Promise<number>;
41
+ launch(bundleId: string): Promise<void>;
42
+ terminate(bundleId: string): Promise<void>;
43
+ snapshotTree(): Promise<A11yNode>;
44
+ synthesizeTap(x: number, y: number): Promise<void>;
45
+ sendString(text: string): Promise<void>;
46
+ pressKey(key: KeyName): Promise<void>;
47
+ swipe(direction: SwipeDirection): Promise<void>;
48
+ screenshot(): Promise<Uint8Array>;
49
+ systemPopups(): Promise<A11yNode[]>;
50
+ openUrl(url: string): Promise<void>;
51
+ launchFresh(opts: {
52
+ bundleId: string;
53
+ clearState: boolean;
54
+ clearKeychain: boolean;
55
+ appPath?: string | undefined;
56
+ }): Promise<void>;
57
+ launchFromPath(appPath: string): Promise<void>;
58
+ synthesizeTapAtNormalized(nx: number, ny: number): Promise<void>;
59
+ private post;
60
+ }
61
+ //# sourceMappingURL=HttpRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpRunner.d.ts","sourceRoot":"","sources":["../src/HttpRunner.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EACV,OAAO,EAEP,cAAc,EACd,cAAc,EACf,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7E;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACpE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAA;AAEhG;;;GAGG;AACH,qBAAa,cAAe,YAAW,cAAc;aAWjC,OAAO,EAAE,MAAM;aACf,SAAS,EAAE,SAAS;IAXtC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAA;gBAGrB,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,SAAoD;IAYjF;;;;;OAKG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKrE,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC;IAKjC,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,KAAK,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAKjC,YAAY,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAKnC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,WAAW,CAAC,IAAI,EAAE;QACtB,QAAQ,EAAE,MAAM,CAAA;QAChB,UAAU,EAAE,OAAO,CAAA;QACnB,aAAa,EAAE,OAAO,CAAA;QACtB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC;IAUX,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIxD,IAAI;CAkBnB"}