@goliapkg/smix 1.0.27 → 2.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.
- package/README.md +53 -130
- package/dist/A11yNode.d.ts.map +1 -1
- package/dist/A11yNode.js +2 -3
- package/dist/A11yNode.js.map +1 -1
- package/dist/App.d.ts +37 -22
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +72 -66
- package/dist/App.js.map +1 -1
- package/dist/ExpectationFailure.d.ts +8 -1
- package/dist/ExpectationFailure.d.ts.map +1 -1
- package/dist/ExpectationFailure.js +7 -7
- package/dist/ExpectationFailure.js.map +1 -1
- package/dist/HttpRunner.d.ts +9 -31
- package/dist/HttpRunner.d.ts.map +1 -1
- package/dist/HttpRunner.js +14 -100
- package/dist/HttpRunner.js.map +1 -1
- package/dist/Locator.d.ts +7 -9
- package/dist/Locator.d.ts.map +1 -1
- package/dist/Locator.js +17 -18
- package/dist/Locator.js.map +1 -1
- package/dist/NodeDriver.d.ts +53 -0
- package/dist/NodeDriver.d.ts.map +1 -0
- package/dist/NodeDriver.js +70 -0
- package/dist/NodeDriver.js.map +1 -0
- package/dist/Pattern.js +2 -2
- package/dist/Pattern.js.map +1 -1
- package/dist/Selector.d.ts.map +1 -1
- package/dist/Selector.js +5 -8
- package/dist/Selector.js.map +1 -1
- package/dist/SelectorResolver.d.ts +4 -6
- package/dist/SelectorResolver.d.ts.map +1 -1
- package/dist/SelectorResolver.js +5 -8
- package/dist/SelectorResolver.js.map +1 -1
- package/dist/Smix.d.ts +12 -4
- package/dist/Smix.d.ts.map +1 -1
- package/dist/Smix.js +25 -23
- package/dist/Smix.js.map +1 -1
- package/dist/SystemPopup.d.ts +18 -0
- package/dist/SystemPopup.d.ts.map +1 -0
- package/dist/SystemPopup.js +7 -0
- package/dist/SystemPopup.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/loadNodeDriver.d.ts +21 -0
- package/dist/loadNodeDriver.d.ts.map +1 -0
- package/dist/loadNodeDriver.js +33 -0
- package/dist/loadNodeDriver.js.map +1 -0
- package/package.json +5 -2
- package/src/A11yNode.ts +2 -3
- package/src/App.ts +75 -78
- package/src/ExpectationFailure.ts +23 -13
- package/src/HttpRunner.ts +15 -126
- package/src/Locator.ts +17 -18
- package/src/NodeDriver.ts +109 -0
- package/src/Pattern.ts +2 -2
- package/src/Selector.ts +5 -8
- package/src/SelectorResolver.ts +7 -11
- package/src/Smix.ts +30 -26
- package/src/SystemPopup.ts +24 -0
- package/src/__tests__/AppDriving.test.ts +106 -0
- package/src/__tests__/MvpApiShape.test.ts +78 -46
- package/src/__tests__/ReadmeSnippets.test.ts +132 -0
- package/src/__tests__/RealAddonLoopback.test.ts +81 -0
- package/src/__tests__/SelectorFullSchema.test.ts +2 -4
- package/src/index.ts +19 -8
- package/src/loadNodeDriver.ts +41 -0
- package/src/SimRuntime.ts +0 -145
- package/src/__tests__/AppFullApi.test.ts +0 -168
- package/src/__tests__/AppTapMock.test.ts +0 -122
- package/src/__tests__/HttpRunner.test.ts +0 -121
- package/src/__tests__/Locator.test.ts +0 -128
- package/src/__tests__/LocatorToHave.test.ts +0 -118
package/README.md
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
# @goliapkg/smix
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
TypeScript types and host-side building blocks for
|
|
4
|
+
[smix](https://github.com/goliajp/smix) — AI-native UI automation for the
|
|
5
|
+
iOS Simulator and Android emulator.
|
|
6
|
+
|
|
7
|
+
**Live driving runs through the napi addon.** This package ships the same
|
|
8
|
+
typed API surface as the Swift / Kotlin / Rust SDKs; `Smix.launchApp`,
|
|
9
|
+
`App.tap`, `App.fill`, and the rest drive a device by calling into
|
|
10
|
+
`@goliapkg/smix-node`. `Smix.launchApp` loads the per-platform addon
|
|
11
|
+
automatically (via `loadNodeDriver`); the addon is built by a per-triple
|
|
12
|
+
prebuild matrix in CI, and its npm release follows in a later version — you
|
|
13
|
+
can also inject a `NodeDriver` explicitly for tests or a custom transport.
|
|
14
|
+
Three surfaces still throw `SmixNotImplementedError` pending their transport:
|
|
15
|
+
`App.screenshot` and `App.openUrl` need a runner wire route, and
|
|
16
|
+
`App.launchFresh` needs host-side state clearing. To drive a device without
|
|
17
|
+
the addon today, use the [`smix` CLI](https://crates.io/crates/smix-cli) or
|
|
18
|
+
one of the Swift / Kotlin / Rust SDKs.
|
|
19
|
+
|
|
20
|
+
## What works today
|
|
21
|
+
|
|
22
|
+
- **Selector DSL** — the full selector language, encoding to the exact
|
|
23
|
+
JSON wire shape the Rust core resolves.
|
|
24
|
+
- **Session lifecycle** — `Session.open` / `stillValid` / `relaunchApp` /
|
|
25
|
+
`renewActivation` / `close` against a running smix runner, over HTTP.
|
|
26
|
+
- **`ExpectationFailure`** — parse and produce smix's AI-readable
|
|
27
|
+
failure JSON.
|
|
28
|
+
- **`A11yNode`** — typed accessibility-tree model with `flatten` /
|
|
29
|
+
`findById` / `rectCenter` helpers for working with `smix tree --json`
|
|
30
|
+
output.
|
|
7
31
|
|
|
8
32
|
## Installation
|
|
9
33
|
|
|
@@ -13,165 +37,64 @@ bun add -D @goliapkg/smix
|
|
|
13
37
|
npm install -D @goliapkg/smix
|
|
14
38
|
```
|
|
15
39
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
via metro/expo tree-shaking.
|
|
40
|
+
Test-target only: keep it in `devDependencies`, never bundled into a
|
|
41
|
+
production RN / Expo app.
|
|
19
42
|
|
|
20
|
-
##
|
|
43
|
+
## Selector
|
|
21
44
|
|
|
22
45
|
```typescript
|
|
23
|
-
import {
|
|
46
|
+
import { Selector, literal, regex } from '@goliapkg/smix'
|
|
24
47
|
|
|
25
|
-
const runtime = new MockSimRuntime({ snapshotResult: /* A11yNode tree */ })
|
|
26
|
-
const resolver = new MockSelectorResolver()
|
|
27
|
-
resolver.registerHit('{"id":"btn-login"}', 'btn-login')
|
|
28
|
-
|
|
29
|
-
const app = await Smix.launchApp(bundleId('com.example.MyApp'), runtime, resolver.resolve)
|
|
30
|
-
await app.tap(Selector.id('btn-login'))
|
|
31
|
-
await app.fill(Selector.id('input-username'), 'alice')
|
|
32
|
-
|
|
33
|
-
const welcome = app.find(Selector.text(literal('Welcome back')))
|
|
34
|
-
await welcome.toBeVisible({ timeoutMs: 5_000 })
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Production: HTTP runtime
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { Smix, Selector, bundleId, HttpSimRuntime } from '@goliapkg/smix'
|
|
41
|
-
|
|
42
|
-
// Connects to smix-runner-server running on host (see swift-bridge/SmixRunnerCore)
|
|
43
|
-
const runtime = new HttpSimRuntime('http://localhost:14101')
|
|
44
|
-
|
|
45
|
-
const app = await Smix.launchApp(bundleId('com.example.MyApp'), runtime, runtime.resolver, runtime.labelsResolver)
|
|
46
|
-
await app.tap(Selector.id('btn-login').below(Selector.text(literal('Sign In'))).nth(0))
|
|
47
|
-
await app.find(Selector.role('button')).toHaveCount(3, { timeoutMs: 5_000 })
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
HTTP endpoints documented in [`src/HttpRunner.ts`](src/HttpRunner.ts) +
|
|
51
|
-
[Swift server route handler](../../swift-bridge/Sources/SmixRunnerCore/SmixRunnerServer.swift).
|
|
52
|
-
|
|
53
|
-
## API surface
|
|
54
|
-
|
|
55
|
-
### Selector
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
// Base discriminators (untagged JSON wire shape — matches Rust smix-selector)
|
|
59
48
|
Selector.id('btn-login')
|
|
60
49
|
Selector.text(literal('Sign In'))
|
|
61
50
|
Selector.text(regex('^Sub', 'i'))
|
|
62
51
|
Selector.label('Settings')
|
|
63
|
-
Selector.role('button')
|
|
64
52
|
Selector.role('button', literal('Submit'))
|
|
65
|
-
Selector.focused()
|
|
66
|
-
Selector.anchor({ below: Selector.text(literal('Total')) }, { nth: 0 })
|
|
67
53
|
Selector.localizedText({ en: 'Submit', ja: '送信' })
|
|
68
54
|
|
|
69
|
-
// Fluent modifier chaining (returns new Selector
|
|
70
|
-
Selector.id('btn').below(Selector.text(literal('Address')))
|
|
71
|
-
Selector.
|
|
72
|
-
Selector.text(literal('Item')).above(Selector.text(literal('Footer'))).first()
|
|
73
|
-
Selector.role('button').near(Selector.text(literal('Confirm'))).ancestor(Selector.role('dialog'))
|
|
55
|
+
// Fluent modifier chaining (returns a new Selector)
|
|
56
|
+
Selector.id('btn').below(Selector.text(literal('Address'))).nth(0)
|
|
57
|
+
Selector.role('button').near(Selector.text(literal('Confirm')))
|
|
74
58
|
```
|
|
75
59
|
|
|
76
|
-
Wire JSON (untagged +
|
|
60
|
+
Wire JSON (untagged + flattened — byte-identical to Rust `smix-selector`):
|
|
77
61
|
|
|
78
62
|
```json
|
|
79
63
|
{"id": "btn-login", "below": {"text": "Address"}, "nth": 0}
|
|
80
64
|
```
|
|
81
65
|
|
|
82
|
-
|
|
66
|
+
## Session lifecycle
|
|
83
67
|
|
|
84
68
|
```typescript
|
|
85
|
-
|
|
86
|
-
await app.fill(selector: Selector, text: string)
|
|
87
|
-
await app.pressKey(key: KeyName) // 'return' | 'delete' | 'space' | 'tab' | 'escape' | 'enter'
|
|
88
|
-
await app.swipe(direction: SwipeDirection) // 'up' | 'down' | 'left' | 'right'
|
|
89
|
-
await app.tapAtCoord(nx: number, ny: number) // 0..1, throws if out-of-range
|
|
90
|
-
await app.terminate()
|
|
91
|
-
await app.relaunch()
|
|
92
|
-
await app.launchFresh({ clearState?: boolean, clearKeychain?: boolean, appPath?: string })
|
|
93
|
-
```
|
|
69
|
+
import { HttpSimRuntime, Session } from '@goliapkg/smix'
|
|
94
70
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const tree: A11yNode = await app.tree()
|
|
100
|
-
const popups: A11yNode[] = await app.systemPopups()
|
|
101
|
-
await app.openUrl('myapp://deep/link')
|
|
71
|
+
const runtime = new HttpSimRuntime('http://127.0.0.1:22087')
|
|
72
|
+
const session = await Session.open(runtime, 'com.example.app')
|
|
73
|
+
await session.relaunchApp()
|
|
74
|
+
await session.close()
|
|
102
75
|
```
|
|
103
76
|
|
|
104
|
-
|
|
77
|
+
`HttpSimRuntime` also exposes `resolver` / `labelsResolver`, which
|
|
78
|
+
resolve a selector against a caller-supplied tree JSON via the runner's
|
|
79
|
+
`/select/resolve` routes.
|
|
105
80
|
|
|
106
|
-
|
|
107
|
-
const loc = app.find(Selector.text(literal('Welcome')))
|
|
108
|
-
await loc.toBeVisible({ timeoutMs: 5_000 }) // polls 250ms
|
|
109
|
-
await loc.toContainText('alice', { timeoutMs: 5_000 }) // substring
|
|
110
|
-
await loc.toHaveLabel('Sign In', { timeoutMs: 5_000 }) // strict equal
|
|
111
|
-
await loc.toHaveCount(3, { timeoutMs: 5_000 }) // exact match count
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### ExpectationFailure (AI-readable JSON)
|
|
81
|
+
## ExpectationFailure
|
|
115
82
|
|
|
116
83
|
```typescript
|
|
84
|
+
import { ExpectationFailure } from '@goliapkg/smix'
|
|
85
|
+
|
|
117
86
|
try {
|
|
118
|
-
|
|
87
|
+
// a driving call, once the native transport lands
|
|
119
88
|
} catch (e) {
|
|
120
89
|
if (e instanceof ExpectationFailure) {
|
|
121
|
-
e.code
|
|
122
|
-
e.
|
|
123
|
-
e.
|
|
124
|
-
e.
|
|
125
|
-
e.suggestions // string[] — ['check accessibilityIdentifier...', ...]
|
|
126
|
-
e.toJson() // single-line sorted-keys JSON for AI agent consumption
|
|
90
|
+
e.code // 'ELEMENT_NOT_FOUND' | 'AMBIGUOUS' | 'TIMEOUT' | ...
|
|
91
|
+
e.visibleElements // A11yNode[] — context for AI diagnosis
|
|
92
|
+
e.suggestions // string[]
|
|
93
|
+
e.toJson() // single-line JSON for agent consumption
|
|
127
94
|
}
|
|
128
95
|
}
|
|
129
96
|
```
|
|
130
97
|
|
|
131
|
-
## Demos
|
|
132
|
-
|
|
133
|
-
Run any of the realistic e2e flows in [`examples/demo-app/`](examples/demo-app):
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
cd examples/demo-app
|
|
137
|
-
bun login-flow.ts # login + welcome assertion
|
|
138
|
-
bun form-validation-flow.ts # 3-cycle form validation + inline errors
|
|
139
|
-
bun list-scroll-flow.ts # scrollable list + toHaveCount semantics
|
|
140
|
-
bun multi-screen-nav-flow.ts # nav + tapAtCoord + openUrl + relaunch
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
See [`examples/demo-app/COVERAGE.md`](examples/demo-app/COVERAGE.md) for the
|
|
144
|
-
SDK surface coverage matrix across demos.
|
|
145
|
-
|
|
146
|
-
## Conformance
|
|
147
|
-
|
|
148
|
-
Cross-binary harness verifies SDK output byte-identical to Rust:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
bash ../../scripts/sdk/run-cross-binary-harness.sh
|
|
152
|
-
# Summary: 24 / 24 fixtures byte-identical (Rust + Swift + TS)
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
## Test-target-only
|
|
156
|
-
|
|
157
|
-
`@goliapkg/smix` is intended for `devDependencies` only — never bundled
|
|
158
|
-
into a production RN/Expo app. `metro`/`expo`'s tree-shaking guarantees
|
|
159
|
-
the production bundle excludes it. The `HttpSimRuntime` connects to a
|
|
160
|
-
smix-runner-server running on the host (via `127.0.0.1:port` loopback),
|
|
161
|
-
not in-process.
|
|
162
|
-
|
|
163
|
-
## Architecture
|
|
164
|
-
|
|
165
|
-
- **Rust stone core** (`crates/smix-{selector,selector-resolver,screen,error}`):
|
|
166
|
-
selector resolution + AI-readable failure types, shared across all
|
|
167
|
-
language SDKs.
|
|
168
|
-
- **HTTP runtime** (`src/HttpRunner.ts`): wraps fetch to smix-runner-server
|
|
169
|
-
on the host. Production-default.
|
|
170
|
-
- **Mock runtime** (`src/SimRuntime.ts MockSimRuntime`): in-memory tree +
|
|
171
|
-
event log. Used by vitest unit tests + demo flows.
|
|
172
|
-
- **SDK facade** (`src/{Smix,App,Selector,Modifiers,Locator,ExpectationFailure}.ts`):
|
|
173
|
-
Playwright-style ergonomic surface, class-based fluent chaining.
|
|
174
|
-
|
|
175
98
|
## License
|
|
176
99
|
|
|
177
100
|
Apache-2.0 OR MIT (dual, at your option).
|
package/dist/A11yNode.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A11yNode.d.ts","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"A11yNode.d.ts","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAGA,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"}
|
package/dist/A11yNode.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Rust `#[serde(rename_all = "camelCase")]`.
|
|
1
|
+
// The wire form of `smix-screen`'s types. The camelCase keys are the
|
|
2
|
+
// contract with the runner, not a local style choice.
|
|
4
3
|
export function rectCenter(r) {
|
|
5
4
|
return { x: r.x + r.w / 2, y: r.y + r.h / 2 };
|
|
6
5
|
}
|
package/dist/A11yNode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A11yNode.js","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"A11yNode.js","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sDAAsD;AAStD,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
CHANGED
|
@@ -1,57 +1,72 @@
|
|
|
1
1
|
import { type A11yNode } from './A11yNode.js';
|
|
2
2
|
import { Locator, SmixNotImplementedError } from './Locator.js';
|
|
3
|
+
import type { NodeDriver, NodeSession } from './NodeDriver.js';
|
|
3
4
|
import { type Selector } from './Selector.js';
|
|
4
|
-
import { type KeyName, type SmixSimRuntime, type SwipeDirection } from './SimRuntime.js';
|
|
5
5
|
import type { LabelsResolver, SelectorResolver } from './SelectorResolver.js';
|
|
6
|
+
import type { SystemPopup } from './SystemPopup.js';
|
|
7
|
+
type KeyName = 'return' | 'delete' | 'space' | 'tab' | 'escape' | 'enter';
|
|
8
|
+
type SwipeDirection = 'up' | 'down' | 'left' | 'right';
|
|
6
9
|
/**
|
|
7
10
|
* A handle to a running app on the simulator / emulator.
|
|
8
11
|
*/
|
|
9
12
|
export declare class App {
|
|
10
13
|
readonly bundleId: string;
|
|
11
|
-
|
|
14
|
+
/** The napi driver (act + sense); a mock in tests, the real addon in prod. */
|
|
15
|
+
readonly driver: NodeDriver;
|
|
16
|
+
/** The napi session this app was launched in (lifecycle verbs). */
|
|
17
|
+
readonly session: NodeSession;
|
|
12
18
|
readonly resolver: SelectorResolver;
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* Wraps `resolve_selector_labels`, backing toHaveCount and
|
|
21
|
+
* toHaveLabel. The default throws rather than guessing: there is no
|
|
22
|
+
* sensible answer without a resolver, and a silent empty list would
|
|
23
|
+
* read as "no matches".
|
|
18
24
|
*/
|
|
19
25
|
readonly labelsResolver: LabelsResolver;
|
|
20
|
-
constructor(bundleId: string,
|
|
26
|
+
constructor(bundleId: string,
|
|
27
|
+
/** The napi driver (act + sense); a mock in tests, the real addon in prod. */
|
|
28
|
+
driver: NodeDriver,
|
|
29
|
+
/** The napi session this app was launched in (lifecycle verbs). */
|
|
30
|
+
session: NodeSession, resolver: SelectorResolver,
|
|
21
31
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
32
|
+
* Wraps `resolve_selector_labels`, backing toHaveCount and
|
|
33
|
+
* toHaveLabel. The default throws rather than guessing: there is no
|
|
34
|
+
* sensible answer without a resolver, and a silent empty list would
|
|
35
|
+
* read as "no matches".
|
|
26
36
|
*/
|
|
27
37
|
labelsResolver?: LabelsResolver);
|
|
38
|
+
/**
|
|
39
|
+
* Snapshot the tree, resolve `selector` against it, and return the first
|
|
40
|
+
* matching id. Selectors resolve to an id on this side; only the id crosses
|
|
41
|
+
* the napi boundary. No match throws ELEMENT_NOT_FOUND with the visible
|
|
42
|
+
* elements so the failure is AI-readable.
|
|
43
|
+
*/
|
|
44
|
+
private resolveFirstOrThrow;
|
|
28
45
|
tap(selector: Selector, _opts?: {
|
|
29
46
|
timeoutMs?: number;
|
|
30
47
|
}): Promise<void>;
|
|
31
|
-
/**
|
|
32
|
-
* Resolve [selector], tap to focus, then type [text]. Mirror
|
|
33
|
-
* Kotlin App.fill from v7.4 c3.
|
|
34
|
-
*/
|
|
35
48
|
fill(selector: Selector, text: string): Promise<void>;
|
|
36
49
|
pressKey(key: KeyName): Promise<void>;
|
|
37
50
|
swipe(direction: SwipeDirection): Promise<void>;
|
|
38
51
|
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
|
-
*/
|
|
52
|
+
/** Tap at a normalized coordinate (0..1) — escape hatch per §9 #3. */
|
|
43
53
|
tapAtCoord(nx: number, ny: number): Promise<void>;
|
|
44
54
|
terminate(): Promise<void>;
|
|
45
55
|
relaunch(): Promise<void>;
|
|
46
|
-
launchFresh(
|
|
56
|
+
launchFresh(_opts?: {
|
|
47
57
|
clearState?: boolean;
|
|
48
58
|
clearKeychain?: boolean;
|
|
49
59
|
appPath?: string;
|
|
50
60
|
}): Promise<void>;
|
|
51
61
|
find(selector: Selector): Locator;
|
|
62
|
+
/**
|
|
63
|
+
* The tree seam Locator polls. Pending napi: without an FFI path there is
|
|
64
|
+
* no live simulator to snapshot. Lands with the napi axis.
|
|
65
|
+
*/
|
|
66
|
+
snapshotTree(): Promise<A11yNode>;
|
|
52
67
|
tree(): Promise<A11yNode>;
|
|
53
|
-
systemPopups(): Promise<
|
|
54
|
-
openUrl(
|
|
68
|
+
systemPopups(): Promise<SystemPopup[]>;
|
|
69
|
+
openUrl(_url: string): Promise<void>;
|
|
55
70
|
}
|
|
56
71
|
export { SmixNotImplementedError };
|
|
57
72
|
//# sourceMappingURL=App.d.ts.map
|
package/dist/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,eAAe,CAAA;AAEtD,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,KAAK,QAAQ,EAAsB,MAAM,eAAe,CAAA;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,KAAK,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAA;AACzE,KAAK,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEtD;;GAEG;AACH,qBAAa,GAAG;aAEI,QAAQ,EAAE,MAAM;IAChC,8EAA8E;aAC9D,MAAM,EAAE,UAAU;IAClC,mEAAmE;aACnD,OAAO,EAAE,WAAW;aACpB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;aACa,cAAc,EAAE,cAAc;gBAZ9B,QAAQ,EAAE,MAAM;IAChC,8EAA8E;IAC9D,MAAM,EAAE,UAAU;IAClC,mEAAmE;IACnD,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;IACa,cAAc,GAAE,cAE/B;IAKH;;;;;OAKG;YACW,mBAAmB;IAiB3B,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1E,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,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;IAMvC,sEAAsE;IAChE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjD,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,WAAW,CAAC,KAAK,GAAE;QACvB,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,aAAa,CAAC,EAAE,OAAO,CAAA;QACvB,OAAO,CAAC,EAAE,MAAM,CAAA;KACZ,GAAG,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAIjC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIjC,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIzB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAItC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK3C;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
|
package/dist/App.js
CHANGED
|
@@ -1,122 +1,128 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
1
|
+
// A handle to a running app. Live driving (tap/fill/swipe/...) and live
|
|
2
|
+
// sense (snapshotTree) are pending the napi axis: the TS SDK has no FFI
|
|
3
|
+
// path, so with the fictional HTTP wire deleted there is no way to reach a
|
|
4
|
+
// simulator from here yet. These methods throw SmixNotImplementedError
|
|
5
|
+
// rather than posting to a route the runner never served. What does work is
|
|
6
|
+
// the resolver seam: given a treeJson, `resolver` / `labelsResolver` resolve
|
|
7
|
+
// selectors against the runner's served /select/resolve* routes.
|
|
8
|
+
import { flatten } from './A11yNode.js';
|
|
3
9
|
import { ExpectationFailure } from './ExpectationFailure.js';
|
|
4
10
|
import { Locator, SmixNotImplementedError } from './Locator.js';
|
|
5
|
-
import { encodeSelectorJson
|
|
11
|
+
import { encodeSelectorJson } from './Selector.js';
|
|
6
12
|
/**
|
|
7
13
|
* A handle to a running app on the simulator / emulator.
|
|
8
14
|
*/
|
|
9
15
|
export class App {
|
|
10
16
|
bundleId;
|
|
11
|
-
|
|
17
|
+
driver;
|
|
18
|
+
session;
|
|
12
19
|
resolver;
|
|
13
20
|
labelsResolver;
|
|
14
|
-
constructor(bundleId,
|
|
21
|
+
constructor(bundleId,
|
|
22
|
+
/** The napi driver (act + sense); a mock in tests, the real addon in prod. */
|
|
23
|
+
driver,
|
|
24
|
+
/** The napi session this app was launched in (lifecycle verbs). */
|
|
25
|
+
session, resolver,
|
|
15
26
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
27
|
+
* Wraps `resolve_selector_labels`, backing toHaveCount and
|
|
28
|
+
* toHaveLabel. The default throws rather than guessing: there is no
|
|
29
|
+
* sensible answer without a resolver, and a silent empty list would
|
|
30
|
+
* read as "no matches".
|
|
20
31
|
*/
|
|
21
32
|
labelsResolver = async () => {
|
|
22
33
|
throw new Error('labelsResolver not configured; pass one to Smix.launchApp or App constructor');
|
|
23
34
|
}) {
|
|
24
35
|
this.bundleId = bundleId;
|
|
25
|
-
this.
|
|
36
|
+
this.driver = driver;
|
|
37
|
+
this.session = session;
|
|
26
38
|
this.resolver = resolver;
|
|
27
39
|
this.labelsResolver = labelsResolver;
|
|
28
40
|
}
|
|
29
41
|
// ---- act methods --------------------------------------------------
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Snapshot the tree, resolve `selector` against it, and return the first
|
|
44
|
+
* matching id. Selectors resolve to an id on this side; only the id crosses
|
|
45
|
+
* the napi boundary. No match throws ELEMENT_NOT_FOUND with the visible
|
|
46
|
+
* elements so the failure is AI-readable.
|
|
47
|
+
*/
|
|
48
|
+
async resolveFirstOrThrow(selector) {
|
|
49
|
+
const treeJson = await this.driver.snapshotTree();
|
|
32
50
|
const selectorJson = encodeSelectorJson(selector);
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
51
|
+
const ids = await this.resolver(treeJson, selectorJson);
|
|
52
|
+
const first = ids[0];
|
|
53
|
+
if (first === undefined) {
|
|
54
|
+
const tree = JSON.parse(treeJson);
|
|
40
55
|
throw new ExpectationFailure({
|
|
41
|
-
code: '
|
|
42
|
-
message: `
|
|
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',
|
|
56
|
+
code: 'ELEMENT_NOT_FOUND',
|
|
57
|
+
message: `no element matched selector ${selectorJson}`,
|
|
52
58
|
selectorJson,
|
|
53
59
|
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
60
|
});
|
|
59
61
|
}
|
|
60
|
-
|
|
62
|
+
return first;
|
|
63
|
+
}
|
|
64
|
+
async tap(selector, _opts = {}) {
|
|
65
|
+
await this.driver.tapById(await this.resolveFirstOrThrow(selector));
|
|
61
66
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Resolve [selector], tap to focus, then type [text]. Mirror
|
|
64
|
-
* Kotlin App.fill from v7.4 c3.
|
|
65
|
-
*/
|
|
66
67
|
async fill(selector, text) {
|
|
67
|
-
await this.
|
|
68
|
-
await this.
|
|
68
|
+
const id = await this.resolveFirstOrThrow(selector);
|
|
69
|
+
await this.driver.tapById(id);
|
|
70
|
+
await this.driver.inputText(text);
|
|
69
71
|
}
|
|
70
72
|
async pressKey(key) {
|
|
71
|
-
await this.
|
|
73
|
+
await this.driver.pressKey(key === 'enter' ? 'return' : key);
|
|
72
74
|
}
|
|
73
75
|
async swipe(direction) {
|
|
74
|
-
await this.
|
|
76
|
+
await this.driver.swipe(direction);
|
|
75
77
|
}
|
|
76
78
|
async screenshot() {
|
|
77
|
-
|
|
79
|
+
// No runner wire route yet — singled out to a later checkpoint (v2.md
|
|
80
|
+
// decision, 2026-07-24). The blocker is the wire, not napi.
|
|
81
|
+
throw new SmixNotImplementedError('wire', 'App.screenshot');
|
|
78
82
|
}
|
|
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
|
+
/** Tap at a normalized coordinate (0..1) — escape hatch per §9 #3. */
|
|
83
84
|
async tapAtCoord(nx, ny) {
|
|
84
85
|
if (nx < 0 || nx > 1 || ny < 0 || ny > 1) {
|
|
85
86
|
throw new ExpectationFailure({
|
|
86
|
-
code: '
|
|
87
|
-
message: `tapAtCoord
|
|
88
|
-
suggestions: ['use normalized 0..1 — for raw points, divide by viewport size'],
|
|
87
|
+
code: 'ASSERTION_FAILED',
|
|
88
|
+
message: `tapAtCoord expects normalized 0..1 coordinates, got (${nx}, ${ny})`,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
await this.
|
|
91
|
+
await this.driver.tapAtCoord(nx, ny);
|
|
92
92
|
}
|
|
93
93
|
async terminate() {
|
|
94
|
-
await this.
|
|
94
|
+
await this.session.terminateApp();
|
|
95
95
|
}
|
|
96
96
|
async relaunch() {
|
|
97
|
-
await this.
|
|
98
|
-
await this.runtime.launch(this.bundleId);
|
|
97
|
+
await this.session.relaunchApp();
|
|
99
98
|
}
|
|
100
|
-
async launchFresh(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
appPath: opts.appPath,
|
|
106
|
-
});
|
|
99
|
+
async launchFresh(_opts = {}) {
|
|
100
|
+
// Clearing state / installing an appPath is host-side (simctl), with no
|
|
101
|
+
// runner wire — singled out to a later checkpoint. The blocker is the
|
|
102
|
+
// host, not napi.
|
|
103
|
+
throw new SmixNotImplementedError('host', 'App.launchFresh');
|
|
107
104
|
}
|
|
108
105
|
// ---- sense methods ------------------------------------------------
|
|
109
106
|
find(selector) {
|
|
110
107
|
return new Locator(this, selector);
|
|
111
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The tree seam Locator polls. Pending napi: without an FFI path there is
|
|
111
|
+
* no live simulator to snapshot. Lands with the napi axis.
|
|
112
|
+
*/
|
|
113
|
+
async snapshotTree() {
|
|
114
|
+
return JSON.parse(await this.driver.snapshotTree());
|
|
115
|
+
}
|
|
112
116
|
async tree() {
|
|
113
|
-
return this.
|
|
117
|
+
return this.snapshotTree();
|
|
114
118
|
}
|
|
115
119
|
async systemPopups() {
|
|
116
|
-
return this.
|
|
120
|
+
return JSON.parse(await this.driver.systemPopups());
|
|
117
121
|
}
|
|
118
|
-
async openUrl(
|
|
119
|
-
|
|
122
|
+
async openUrl(_url) {
|
|
123
|
+
// No runner wire route yet — singled out to a later checkpoint. The
|
|
124
|
+
// blocker is the wire, not napi.
|
|
125
|
+
throw new SmixNotImplementedError('wire', 'App.openUrl');
|
|
120
126
|
}
|
|
121
127
|
}
|
|
122
128
|
export { SmixNotImplementedError };
|
package/dist/App.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { A11yNode } from './A11yNode.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Rust `smix_error::FailureCode`'s wire strings verbatim —
|
|
4
|
+
* `crates/smix-error/tests/sdk_failure_code_parity.rs` reads this
|
|
5
|
+
* declaration and fails if the two sets ever diverge.
|
|
6
|
+
*/
|
|
7
|
+
export type FailureCode = 'ELEMENT_NOT_FOUND' | 'NOT_VISIBLE' | 'NOT_ENABLED' | 'AMBIGUOUS' | 'TIMEOUT' | 'ASSERTION_FAILED' | 'APP_NOT_RUNNING' | 'SIMULATOR_NOT_BOOTED'
|
|
8
|
+
/** The touch was synthesised, and it did not land inside the element the selector matched. Distinct from element-not-found: not-found means fix the selector, missed means the element was there and the touch went elsewhere. */
|
|
9
|
+
| 'TAP_MISSED' | 'DRIVER_ERROR';
|
|
3
10
|
export declare const FAILURE_CODES: readonly FailureCode[];
|
|
4
11
|
/**
|
|
5
12
|
* Structured failure thrown by SDK resolver paths (App.tap, Locator
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpectationFailure.d.ts","sourceRoot":"","sources":["../src/ExpectationFailure.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpectationFailure.d.ts","sourceRoot":"","sources":["../src/ExpectationFailure.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB,mBAAmB,GACnB,aAAa,GACb,aAAa,GACb,WAAW,GACX,SAAS,GACT,kBAAkB,GAClB,iBAAiB,GACjB,sBAAsB;AACxB,kOAAkO;GAChO,YAAY,GACZ,cAAc,CAAA;AAElB,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAItC,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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Kotlin v7.4 c1 + Rust smix-error.
|
|
1
|
+
// The wire form of `smix-error`'s failure surface.
|
|
3
2
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// toJson() emits sorted keys and an ISO-8601 timestamp on one line. Every
|
|
4
|
+
// SDK emits the same bytes for the same failure, so a reader — human or
|
|
5
|
+
// model — sees one format whichever language raised it.
|
|
7
6
|
export const FAILURE_CODES = [
|
|
8
|
-
'
|
|
9
|
-
'
|
|
7
|
+
'ELEMENT_NOT_FOUND', 'NOT_VISIBLE', 'NOT_ENABLED',
|
|
8
|
+
'AMBIGUOUS', 'TIMEOUT', 'ASSERTION_FAILED',
|
|
9
|
+
'APP_NOT_RUNNING', 'SIMULATOR_NOT_BOOTED', 'TAP_MISSED', 'DRIVER_ERROR',
|
|
10
10
|
];
|
|
11
11
|
/**
|
|
12
12
|
* Structured failure thrown by SDK resolver paths (App.tap, Locator
|