@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.
- package/README.md +193 -0
- package/dist/A11yNode.d.ts +36 -0
- package/dist/A11yNode.d.ts.map +1 -0
- package/dist/A11yNode.js +30 -0
- package/dist/A11yNode.js.map +1 -0
- package/dist/App.d.ts +57 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +123 -0
- package/dist/App.js.map +1 -0
- package/dist/ExpectationFailure.d.ts +30 -0
- package/dist/ExpectationFailure.d.ts.map +1 -0
- package/dist/ExpectationFailure.js +47 -0
- package/dist/ExpectationFailure.js.map +1 -0
- package/dist/HttpRunner.d.ts +61 -0
- package/dist/HttpRunner.d.ts.map +1 -0
- package/dist/HttpRunner.js +148 -0
- package/dist/HttpRunner.js.map +1 -0
- package/dist/Locator.d.ts +60 -0
- package/dist/Locator.d.ts.map +1 -0
- package/dist/Locator.js +177 -0
- package/dist/Locator.js.map +1 -0
- package/dist/Pattern.d.ts +21 -0
- package/dist/Pattern.d.ts.map +1 -0
- package/dist/Pattern.js +44 -0
- package/dist/Pattern.js.map +1 -0
- package/dist/Selector.d.ts +96 -0
- package/dist/Selector.d.ts.map +1 -0
- package/dist/Selector.js +276 -0
- package/dist/Selector.js.map +1 -0
- package/dist/SelectorResolver.d.ts +41 -0
- package/dist/SelectorResolver.d.ts.map +1 -0
- package/dist/SelectorResolver.js +55 -0
- package/dist/SelectorResolver.js.map +1 -0
- package/dist/SimRuntime.d.ts +81 -0
- package/dist/SimRuntime.d.ts.map +1 -0
- package/dist/SimRuntime.js +91 -0
- package/dist/SimRuntime.js.map +1 -0
- package/dist/Smix.d.ts +22 -0
- package/dist/Smix.d.ts.map +1 -0
- package/dist/Smix.js +35 -0
- package/dist/Smix.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
- package/src/A11yNode.ts +62 -0
- package/src/App.ts +153 -0
- package/src/ExpectationFailure.ts +67 -0
- package/src/HttpRunner.ts +185 -0
- package/src/Locator.ts +190 -0
- package/src/Pattern.ts +51 -0
- package/src/Selector.ts +296 -0
- package/src/SelectorResolver.ts +76 -0
- package/src/SimRuntime.ts +145 -0
- package/src/Smix.ts +49 -0
- package/src/__tests__/AppFullApi.test.ts +168 -0
- package/src/__tests__/AppTapMock.test.ts +122 -0
- package/src/__tests__/HttpRunner.test.ts +121 -0
- package/src/__tests__/Locator.test.ts +128 -0
- package/src/__tests__/LocatorToHave.test.ts +118 -0
- package/src/__tests__/MvpApiShape.test.ts +117 -0
- package/src/__tests__/SelectorFullSchema.test.ts +161 -0
- package/src/index.ts +76 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// v7.5 c3 — HTTP-based SmixSimRuntime + SelectorResolver implementations.
|
|
2
|
+
//
|
|
3
|
+
// Per R-7.5-B HTTP-runtime decision: TS SDK communicates with smix-runner
|
|
4
|
+
// via fetch JSON-RPC-ish API (one POST per call). Default endpoint
|
|
5
|
+
// matches existing smix-runner-client conventions; consumers override
|
|
6
|
+
// via base URL constructor param.
|
|
7
|
+
//
|
|
8
|
+
// Wire contract (mirrors smix-runner-client v3+):
|
|
9
|
+
// POST /sim/launch { bundleId } → 200
|
|
10
|
+
// POST /sim/terminate { bundleId } → 200
|
|
11
|
+
// POST /sim/launch-fresh { bundleId, clearState, clearKeychain, appPath? } → 200
|
|
12
|
+
// POST /sim/launch-from-path { appPath } → 200
|
|
13
|
+
// POST /sim/screenshot {} → 200 { png: base64 }
|
|
14
|
+
// POST /sim/system-popups {} → 200 { nodes: A11yNode[] }
|
|
15
|
+
// POST /sim/open-url { url } → 200
|
|
16
|
+
// POST /a11y/snapshot {} → 200 { tree: A11yNode }
|
|
17
|
+
// POST /input/tap { x, y } → 200
|
|
18
|
+
// POST /input/tap-normalized { nx, ny } → 200
|
|
19
|
+
// POST /input/swipe { direction } → 200
|
|
20
|
+
// POST /input/send-string { text } → 200
|
|
21
|
+
// POST /input/press-key { key } → 200
|
|
22
|
+
// POST /select/resolve { treeJson, selectorJson } → 200 { ids: string[] }
|
|
23
|
+
//
|
|
24
|
+
// v7.7 c1 contract additions (server impl deferred):
|
|
25
|
+
// POST /select/resolve-count { treeJson, selectorJson } → 200 { count: u32 }
|
|
26
|
+
// POST /select/resolve-labels { treeJson, selectorJson } → 200 { labels: string[] }
|
|
27
|
+
//
|
|
28
|
+
// The exact endpoint shapes are negotiated with smix-runner-server in
|
|
29
|
+
// follow-up Swift work; for v7.5-v7.7 c1 we wire the TS-side fetch +
|
|
30
|
+
// decode and a MockHttpRunnerClient stub that lets vitest exercise the
|
|
31
|
+
// runtime without booting a real server.
|
|
32
|
+
/**
|
|
33
|
+
* SmixSimRuntime + SelectorResolver wrapper around the smix-runner HTTP
|
|
34
|
+
* server. Used in production RN/Expo test targets via fetch.
|
|
35
|
+
*/
|
|
36
|
+
export class HttpSimRuntime {
|
|
37
|
+
baseUrl;
|
|
38
|
+
fetchImpl;
|
|
39
|
+
resolver;
|
|
40
|
+
/**
|
|
41
|
+
* v7.7 c1 — labels resolver wrapping Rust `resolve_selector_labels`
|
|
42
|
+
* via smix-runner-server `/select/resolve-labels` endpoint. Used by
|
|
43
|
+
* `Locator.toHaveCount` (count = labels.length) + Locator.toHaveLabel.
|
|
44
|
+
* Pass directly to App constructor or Smix.launchApp.
|
|
45
|
+
*/
|
|
46
|
+
labelsResolver;
|
|
47
|
+
constructor(baseUrl, fetchImpl = globalThis.fetch) {
|
|
48
|
+
this.baseUrl = baseUrl;
|
|
49
|
+
this.fetchImpl = fetchImpl;
|
|
50
|
+
this.resolver = async (treeJson, selectorJson) => {
|
|
51
|
+
const r = await this.post('/select/resolve', { treeJson, selectorJson });
|
|
52
|
+
return r.ids;
|
|
53
|
+
};
|
|
54
|
+
this.labelsResolver = async (treeJson, selectorJson) => {
|
|
55
|
+
const r = await this.post('/select/resolve-labels', { treeJson, selectorJson });
|
|
56
|
+
return r.labels;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* v7.7 c1 — direct match-count via dedicated FFI fn (avoid allocating
|
|
61
|
+
* the full ids list when callers only need count). Mirrors Rust
|
|
62
|
+
* `resolve_selector_count`. Locator.toHaveCount can prefer this over
|
|
63
|
+
* `labelsResolver` if it needs only count, not the labels themselves.
|
|
64
|
+
*/
|
|
65
|
+
async resolveCount(treeJson, selectorJson) {
|
|
66
|
+
const r = await this.post('/select/resolve-count', { treeJson, selectorJson });
|
|
67
|
+
return r.count;
|
|
68
|
+
}
|
|
69
|
+
async launch(bundleId) {
|
|
70
|
+
await this.post('/sim/launch', { bundleId });
|
|
71
|
+
}
|
|
72
|
+
async terminate(bundleId) {
|
|
73
|
+
await this.post('/sim/terminate', { bundleId });
|
|
74
|
+
}
|
|
75
|
+
async snapshotTree() {
|
|
76
|
+
const r = await this.post('/a11y/snapshot', {});
|
|
77
|
+
return r.tree;
|
|
78
|
+
}
|
|
79
|
+
async synthesizeTap(x, y) {
|
|
80
|
+
await this.post('/input/tap', { x, y });
|
|
81
|
+
}
|
|
82
|
+
async sendString(text) {
|
|
83
|
+
await this.post('/input/send-string', { text });
|
|
84
|
+
}
|
|
85
|
+
async pressKey(key) {
|
|
86
|
+
await this.post('/input/press-key', { key });
|
|
87
|
+
}
|
|
88
|
+
async swipe(direction) {
|
|
89
|
+
await this.post('/input/swipe', { direction });
|
|
90
|
+
}
|
|
91
|
+
async screenshot() {
|
|
92
|
+
const r = await this.post('/sim/screenshot', {});
|
|
93
|
+
return base64ToBytes(r.png);
|
|
94
|
+
}
|
|
95
|
+
async systemPopups() {
|
|
96
|
+
const r = await this.post('/sim/system-popups', {});
|
|
97
|
+
return r.nodes;
|
|
98
|
+
}
|
|
99
|
+
async openUrl(url) {
|
|
100
|
+
await this.post('/sim/open-url', { url });
|
|
101
|
+
}
|
|
102
|
+
async launchFresh(opts) {
|
|
103
|
+
const payload = {
|
|
104
|
+
bundleId: opts.bundleId,
|
|
105
|
+
clearState: opts.clearState,
|
|
106
|
+
clearKeychain: opts.clearKeychain,
|
|
107
|
+
appPath: opts.appPath,
|
|
108
|
+
};
|
|
109
|
+
await this.post('/sim/launch-fresh', payload);
|
|
110
|
+
}
|
|
111
|
+
async launchFromPath(appPath) {
|
|
112
|
+
await this.post('/sim/launch-from-path', { appPath });
|
|
113
|
+
}
|
|
114
|
+
async synthesizeTapAtNormalized(nx, ny) {
|
|
115
|
+
await this.post('/input/tap-normalized', { nx, ny });
|
|
116
|
+
}
|
|
117
|
+
async post(path, body) {
|
|
118
|
+
const url = `${this.baseUrl}${path}`;
|
|
119
|
+
const resp = await this.fetchImpl(url, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: { 'content-type': 'application/json' },
|
|
122
|
+
body: JSON.stringify(body),
|
|
123
|
+
});
|
|
124
|
+
if (!resp.ok) {
|
|
125
|
+
const text = await resp.text();
|
|
126
|
+
throw new Error(`smix-runner ${path} → HTTP ${resp.status}: ${text}`);
|
|
127
|
+
}
|
|
128
|
+
// 200 with no body returns undefined; tolerate empty response
|
|
129
|
+
try {
|
|
130
|
+
return await resp.json();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function base64ToBytes(b64) {
|
|
138
|
+
if (typeof globalThis.atob === 'function') {
|
|
139
|
+
const bin = globalThis.atob(b64);
|
|
140
|
+
const out = new Uint8Array(bin.length);
|
|
141
|
+
for (let i = 0; i < bin.length; i++)
|
|
142
|
+
out[i] = bin.charCodeAt(i);
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
// Node fallback
|
|
146
|
+
return Uint8Array.from(Buffer.from(b64, 'base64'));
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=HttpRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpRunner.js","sourceRoot":"","sources":["../src/HttpRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { App } from './App.js';
|
|
2
|
+
import { type Selector } from './Selector.js';
|
|
3
|
+
export declare class Locator {
|
|
4
|
+
readonly app: App;
|
|
5
|
+
readonly selector: Selector;
|
|
6
|
+
constructor(app: App, selector: Selector);
|
|
7
|
+
/**
|
|
8
|
+
* Resolve [selector] until at least one match exists AND that match
|
|
9
|
+
* is `visible=true`. Polls every 250ms until [timeoutMs] expires.
|
|
10
|
+
*/
|
|
11
|
+
toBeVisible(opts?: {
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve [selector] until a match exists AND `text.includes(needle)`
|
|
16
|
+
* on label/title/text fields.
|
|
17
|
+
*/
|
|
18
|
+
toContainText(text: string, opts?: {
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* v7.6 c1 — Resolve [selector] until at least one match exists AND
|
|
23
|
+
* that match's label equals [label]. Polls via standard resolver +
|
|
24
|
+
* findById path (label is on the snapshot node, not on FFI's labels
|
|
25
|
+
* resolver which is more useful for count-based assertions).
|
|
26
|
+
*/
|
|
27
|
+
toHaveLabel(label: string, opts?: {
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* v7.6 c1 — Resolve [selector] until the labels resolver returns
|
|
32
|
+
* exactly [n] matches. Uses dedicated `app.labelsResolver` (wraps
|
|
33
|
+
* Rust `resolve_selector_labels`) so count semantics skip index
|
|
34
|
+
* modifier per Playwright "match all" behavior.
|
|
35
|
+
*/
|
|
36
|
+
toHaveCount(n: number, opts?: {
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Generic 250ms poll loop. On each tick, resolve [selector] via the
|
|
41
|
+
* app's runtime + resolver, then check predicates.
|
|
42
|
+
*
|
|
43
|
+
* - [successPredicate] returns true → return immediately
|
|
44
|
+
* - [wrongStatePredicate] returns true → throw wrongState
|
|
45
|
+
* - Otherwise, continue polling.
|
|
46
|
+
*
|
|
47
|
+
* On timeout, throws timeout with the last tree's first-20 nodes.
|
|
48
|
+
*/
|
|
49
|
+
private pollUntil;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Compile-time-known unimplemented surface (distinct from
|
|
53
|
+
* ExpectationFailure which is runtime resolution failure).
|
|
54
|
+
*/
|
|
55
|
+
export declare class SmixNotImplementedError extends Error {
|
|
56
|
+
readonly stage: string;
|
|
57
|
+
readonly api: string;
|
|
58
|
+
constructor(stage: string, api: string);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=Locator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Locator.d.ts","sourceRoot":"","sources":["../src/Locator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,eAAe,CAAA;AAKtB,qBAAa,OAAO;aAEA,GAAG,EAAE,GAAG;aACR,QAAQ,EAAE,QAAQ;gBADlB,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,QAAQ;IAGpC;;;OAGG;IACG,WAAW,CAAC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASnE;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnF;;;;;OAKG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASlF;;;;;OAKG;IACG,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC9E;;;;;;;;;OASG;YACW,SAAS;CAoDxB;AAMD;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aACpB,KAAK,EAAE,MAAM;aAAkB,GAAG,EAAE,MAAM;gBAA1C,KAAK,EAAE,MAAM,EAAkB,GAAG,EAAE,MAAM;CAIvE"}
|
package/dist/Locator.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// v7.5 c3 — Locator poll loop, mirror Kotlin v7.4 c3 + Swift v7.1 c3.
|
|
2
|
+
//
|
|
3
|
+
// 250ms tick over a millisecond deadline. On timeout, throws
|
|
4
|
+
// ExpectationFailure with the last tree's first-20 visible elements
|
|
5
|
+
// populated for AI agent diagnosis.
|
|
6
|
+
import { findById, flatten } from './A11yNode.js';
|
|
7
|
+
import { ExpectationFailure } from './ExpectationFailure.js';
|
|
8
|
+
import { encodeSelectorJson, } from './Selector.js';
|
|
9
|
+
const POLL_INTERVAL_MS = 250;
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
11
|
+
export class Locator {
|
|
12
|
+
app;
|
|
13
|
+
selector;
|
|
14
|
+
constructor(app, selector) {
|
|
15
|
+
this.app = app;
|
|
16
|
+
this.selector = selector;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve [selector] until at least one match exists AND that match
|
|
20
|
+
* is `visible=true`. Polls every 250ms until [timeoutMs] expires.
|
|
21
|
+
*/
|
|
22
|
+
async toBeVisible(opts = {}) {
|
|
23
|
+
return this.pollUntil({
|
|
24
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
25
|
+
wrongStatePredicate: node => node !== null && node.visible !== true,
|
|
26
|
+
successPredicate: node => node !== null && node.visible === true,
|
|
27
|
+
timeoutMessage: 'element never became visible',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve [selector] until a match exists AND `text.includes(needle)`
|
|
32
|
+
* on label/title/text fields.
|
|
33
|
+
*/
|
|
34
|
+
async toContainText(text, opts = {}) {
|
|
35
|
+
return this.pollUntil({
|
|
36
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
37
|
+
wrongStatePredicate: () => false,
|
|
38
|
+
successPredicate: node => node !== null && (node.label?.includes(text) === true ||
|
|
39
|
+
node.title?.includes(text) === true ||
|
|
40
|
+
node.text?.includes(text) === true),
|
|
41
|
+
timeoutMessage: `element text never contained "${text}"`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* v7.6 c1 — Resolve [selector] until at least one match exists AND
|
|
46
|
+
* that match's label equals [label]. Polls via standard resolver +
|
|
47
|
+
* findById path (label is on the snapshot node, not on FFI's labels
|
|
48
|
+
* resolver which is more useful for count-based assertions).
|
|
49
|
+
*/
|
|
50
|
+
async toHaveLabel(label, opts = {}) {
|
|
51
|
+
return this.pollUntil({
|
|
52
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
53
|
+
wrongStatePredicate: () => false,
|
|
54
|
+
successPredicate: node => node !== null && node.label === label,
|
|
55
|
+
timeoutMessage: `element label never equaled "${label}"`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* v7.6 c1 — Resolve [selector] until the labels resolver returns
|
|
60
|
+
* exactly [n] matches. Uses dedicated `app.labelsResolver` (wraps
|
|
61
|
+
* Rust `resolve_selector_labels`) so count semantics skip index
|
|
62
|
+
* modifier per Playwright "match all" behavior.
|
|
63
|
+
*/
|
|
64
|
+
async toHaveCount(n, opts = {}) {
|
|
65
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
66
|
+
const deadline = Date.now() + timeoutMs;
|
|
67
|
+
let lastCount = 0;
|
|
68
|
+
let lastTree = null;
|
|
69
|
+
const selectorJson = encodeSelectorJson(this.selector);
|
|
70
|
+
while (true) {
|
|
71
|
+
const tree = await this.app.runtime.snapshotTree();
|
|
72
|
+
lastTree = tree;
|
|
73
|
+
let labels;
|
|
74
|
+
try {
|
|
75
|
+
labels = await this.app.labelsResolver(JSON.stringify(tree), selectorJson);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
79
|
+
throw new ExpectationFailure({
|
|
80
|
+
code: 'unknown',
|
|
81
|
+
message: `FFI resolveSelectorLabels raised during poll: ${msg}`,
|
|
82
|
+
selectorJson,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
lastCount = labels.length;
|
|
86
|
+
if (lastCount === n)
|
|
87
|
+
return;
|
|
88
|
+
if (Date.now() >= deadline)
|
|
89
|
+
break;
|
|
90
|
+
await delay(POLL_INTERVAL_MS);
|
|
91
|
+
}
|
|
92
|
+
throw new ExpectationFailure({
|
|
93
|
+
code: 'wrongState',
|
|
94
|
+
message: `expected ${n} matches, last poll saw ${lastCount}`,
|
|
95
|
+
selectorJson,
|
|
96
|
+
visibleElements: lastTree !== null ? flatten(lastTree).slice(0, 20) : [],
|
|
97
|
+
suggestions: [
|
|
98
|
+
'verify the selector matches the expected count in the current tree',
|
|
99
|
+
'consider extending the timeout if render is slow',
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Generic 250ms poll loop. On each tick, resolve [selector] via the
|
|
105
|
+
* app's runtime + resolver, then check predicates.
|
|
106
|
+
*
|
|
107
|
+
* - [successPredicate] returns true → return immediately
|
|
108
|
+
* - [wrongStatePredicate] returns true → throw wrongState
|
|
109
|
+
* - Otherwise, continue polling.
|
|
110
|
+
*
|
|
111
|
+
* On timeout, throws timeout with the last tree's first-20 nodes.
|
|
112
|
+
*/
|
|
113
|
+
async pollUntil(opts) {
|
|
114
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
115
|
+
let lastTree = null;
|
|
116
|
+
while (true) {
|
|
117
|
+
const tree = await this.app.runtime.snapshotTree();
|
|
118
|
+
lastTree = tree;
|
|
119
|
+
const selectorJson = encodeSelectorJson(this.selector);
|
|
120
|
+
let matches;
|
|
121
|
+
try {
|
|
122
|
+
matches = await this.app.resolver(JSON.stringify(tree), selectorJson);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
126
|
+
throw new ExpectationFailure({
|
|
127
|
+
code: 'unknown',
|
|
128
|
+
message: `FFI resolveSelector raised during poll: ${msg}`,
|
|
129
|
+
selectorJson,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const firstId = matches[0];
|
|
133
|
+
const node = firstId !== undefined ? findById(tree, firstId) : null;
|
|
134
|
+
if (opts.wrongStatePredicate(node)) {
|
|
135
|
+
throw new ExpectationFailure({
|
|
136
|
+
code: 'wrongState',
|
|
137
|
+
message: 'element matched but predicate failed (e.g. not visible)',
|
|
138
|
+
selectorJson,
|
|
139
|
+
visibleElements: node !== null ? [node, ...flatten(tree).slice(0, 19)] : flatten(tree).slice(0, 20),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (opts.successPredicate(node))
|
|
143
|
+
return;
|
|
144
|
+
if (Date.now() >= deadline)
|
|
145
|
+
break;
|
|
146
|
+
await delay(POLL_INTERVAL_MS);
|
|
147
|
+
}
|
|
148
|
+
throw new ExpectationFailure({
|
|
149
|
+
code: 'timeout',
|
|
150
|
+
message: opts.timeoutMessage,
|
|
151
|
+
selectorJson: encodeSelectorJson(this.selector),
|
|
152
|
+
visibleElements: lastTree !== null ? flatten(lastTree).slice(0, 20) : [],
|
|
153
|
+
suggestions: [
|
|
154
|
+
'consider extending the timeout if the UI takes longer to settle',
|
|
155
|
+
'verify the selector matches when manually inspected (use app.tree())',
|
|
156
|
+
],
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function delay(ms) {
|
|
161
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Compile-time-known unimplemented surface (distinct from
|
|
165
|
+
* ExpectationFailure which is runtime resolution failure).
|
|
166
|
+
*/
|
|
167
|
+
export class SmixNotImplementedError extends Error {
|
|
168
|
+
stage;
|
|
169
|
+
api;
|
|
170
|
+
constructor(stage, api) {
|
|
171
|
+
super(`${api} not implemented yet (lands ${stage})`);
|
|
172
|
+
this.stage = stage;
|
|
173
|
+
this.api = api;
|
|
174
|
+
this.name = 'SmixNotImplementedError';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=Locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Locator.js","sourceRoot":"","sources":["../src/Locator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type Pattern = {
|
|
2
|
+
kind: 'literal';
|
|
3
|
+
value: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'regex';
|
|
6
|
+
regex: string;
|
|
7
|
+
flags: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function literal(value: string): Pattern;
|
|
10
|
+
export declare function regex(pat: string, flags?: string): Pattern;
|
|
11
|
+
/**
|
|
12
|
+
* Encode a Pattern to its Rust-compatible untagged JSON form
|
|
13
|
+
* (bare string for literal / `{regex, flags}` object for regex).
|
|
14
|
+
* Returns a JSON-encodable value (not a JSON string).
|
|
15
|
+
*/
|
|
16
|
+
export declare function patternToJson(p: Pattern): unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Decode a JSON value into a Pattern. Throws on unrecognized shape.
|
|
19
|
+
*/
|
|
20
|
+
export declare function patternFromJson(value: unknown): Pattern;
|
|
21
|
+
//# sourceMappingURL=Pattern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pattern.d.ts","sourceRoot":"","sources":["../src/Pattern.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAOjD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAcvD"}
|
package/dist/Pattern.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// v7.5 c1 — Pattern (literal vs regex) mirror Swift v7.2 c3 + Kotlin v7.4 c4
|
|
2
|
+
// + Rust smix-selector `#[serde(untagged)] enum Pattern`.
|
|
3
|
+
//
|
|
4
|
+
// Wire JSON forms (untagged):
|
|
5
|
+
// Literal: bare string e.g. "hello"
|
|
6
|
+
// Regex: {"regex":"...","flags":"i"} (flags default "i")
|
|
7
|
+
export function literal(value) {
|
|
8
|
+
return { kind: 'literal', value };
|
|
9
|
+
}
|
|
10
|
+
export function regex(pat, flags = 'i') {
|
|
11
|
+
return { kind: 'regex', regex: pat, flags };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Encode a Pattern to its Rust-compatible untagged JSON form
|
|
15
|
+
* (bare string for literal / `{regex, flags}` object for regex).
|
|
16
|
+
* Returns a JSON-encodable value (not a JSON string).
|
|
17
|
+
*/
|
|
18
|
+
export function patternToJson(p) {
|
|
19
|
+
switch (p.kind) {
|
|
20
|
+
case 'literal':
|
|
21
|
+
return p.value;
|
|
22
|
+
case 'regex':
|
|
23
|
+
return { regex: p.regex, flags: p.flags };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decode a JSON value into a Pattern. Throws on unrecognized shape.
|
|
28
|
+
*/
|
|
29
|
+
export function patternFromJson(value) {
|
|
30
|
+
if (typeof value === 'string') {
|
|
31
|
+
return { kind: 'literal', value };
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === 'object' && value !== null && 'regex' in value) {
|
|
34
|
+
const obj = value;
|
|
35
|
+
const rx = obj.regex;
|
|
36
|
+
if (typeof rx !== 'string') {
|
|
37
|
+
throw new Error(`Pattern.regex must be string, got: ${typeof rx}`);
|
|
38
|
+
}
|
|
39
|
+
const flags = typeof obj.flags === 'string' ? obj.flags : 'i';
|
|
40
|
+
return { kind: 'regex', regex: rx, flags };
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`Pattern must be string or {regex, flags?} object, got: ${JSON.stringify(value)}`);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=Pattern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pattern.js","sourceRoot":"","sources":["../src/Pattern.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,0DAA0D;AAC1D,EAAE;AACF,8BAA8B;AAC9B,wDAAwD;AACxD,gEAAgE;AAMhE,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW,EAAE,KAAK,GAAG,GAAG;IAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC,KAAK,CAAA;QAChB,KAAK,OAAO;YACV,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;IAC7C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IACnC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;QACpE,MAAM,GAAG,GAAG,KAAgC,CAAA;QAC5C,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAA;QACpB,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,OAAO,EAAE,EAAE,CAAC,CAAA;QACpE,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAA;QAC7D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;IAC5C,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,0DAA0D,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;AACpG,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type Pattern } from './Pattern.js';
|
|
2
|
+
/** All-optional modifier set stacked onto a base Selector. */
|
|
3
|
+
export interface Modifiers {
|
|
4
|
+
near?: Selector | undefined;
|
|
5
|
+
below?: Selector | undefined;
|
|
6
|
+
above?: Selector | undefined;
|
|
7
|
+
leftOf?: Selector | undefined;
|
|
8
|
+
rightOf?: Selector | undefined;
|
|
9
|
+
inside?: Selector | undefined;
|
|
10
|
+
ancestor?: Selector | undefined;
|
|
11
|
+
nth?: number | undefined;
|
|
12
|
+
first?: boolean | undefined;
|
|
13
|
+
last?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** Spatial-only anchor box (no index, no base). Used by `anchor()`. */
|
|
16
|
+
export interface AnchorBox {
|
|
17
|
+
near?: Selector | undefined;
|
|
18
|
+
below?: Selector | undefined;
|
|
19
|
+
above?: Selector | undefined;
|
|
20
|
+
leftOf?: Selector | undefined;
|
|
21
|
+
rightOf?: Selector | undefined;
|
|
22
|
+
inside?: Selector | undefined;
|
|
23
|
+
ancestor?: Selector | undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Index-only modifier subset used by `anchor()`. */
|
|
26
|
+
export interface IndexModifiers {
|
|
27
|
+
nth?: number | undefined;
|
|
28
|
+
first?: boolean | undefined;
|
|
29
|
+
last?: boolean | undefined;
|
|
30
|
+
}
|
|
31
|
+
export type SelectorKind = 'id' | 'text' | 'label' | 'role' | 'focused' | 'anchor' | 'localizedText';
|
|
32
|
+
/** Discriminated union (kind tag for narrowing; wire-shape is untagged). */
|
|
33
|
+
export type SelectorData = {
|
|
34
|
+
kind: 'id';
|
|
35
|
+
id: string;
|
|
36
|
+
modifiers: Modifiers;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'text';
|
|
39
|
+
text: Pattern;
|
|
40
|
+
modifiers: Modifiers;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'label';
|
|
43
|
+
label: string;
|
|
44
|
+
modifiers: Modifiers;
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'role';
|
|
47
|
+
role: string;
|
|
48
|
+
name?: Pattern | undefined;
|
|
49
|
+
modifiers: Modifiers;
|
|
50
|
+
} | {
|
|
51
|
+
kind: 'focused';
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'anchor';
|
|
54
|
+
box: AnchorBox;
|
|
55
|
+
index: IndexModifiers;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'localizedText';
|
|
58
|
+
map: Readonly<Record<string, string>>;
|
|
59
|
+
modifiers: Modifiers;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* A11y selector — identifies a node in the tree.
|
|
63
|
+
*
|
|
64
|
+
* Wire JSON shape matches Rust smix-selector `#[serde(untagged)]`:
|
|
65
|
+
* discriminator is which key is present. Modifiers are flattened
|
|
66
|
+
* into the same JSON body. Implemented via [selectorToJson].
|
|
67
|
+
*/
|
|
68
|
+
export declare class Selector {
|
|
69
|
+
readonly data: SelectorData;
|
|
70
|
+
constructor(data: SelectorData);
|
|
71
|
+
static id(value: string): Selector;
|
|
72
|
+
static text(p: Pattern): Selector;
|
|
73
|
+
static label(value: string): Selector;
|
|
74
|
+
static role(value: string, name?: Pattern): Selector;
|
|
75
|
+
static focused(): Selector;
|
|
76
|
+
static anchor(box: AnchorBox, index?: IndexModifiers): Selector;
|
|
77
|
+
static localizedText(map: Record<string, string>): Selector;
|
|
78
|
+
below(anchor: Selector): Selector;
|
|
79
|
+
above(anchor: Selector): Selector;
|
|
80
|
+
leftOf(anchor: Selector): Selector;
|
|
81
|
+
rightOf(anchor: Selector): Selector;
|
|
82
|
+
near(anchor: Selector): Selector;
|
|
83
|
+
inside(anchor: Selector): Selector;
|
|
84
|
+
ancestor(anchor: Selector): Selector;
|
|
85
|
+
nth(i: number): Selector;
|
|
86
|
+
first(): Selector;
|
|
87
|
+
last(): Selector;
|
|
88
|
+
private withMods;
|
|
89
|
+
}
|
|
90
|
+
/** Encode a Selector to its Rust-compatible untagged JSON value. */
|
|
91
|
+
export declare function selectorToJsonValue(s: Selector): unknown;
|
|
92
|
+
/** Encode a Selector to its Rust-compatible untagged JSON string. */
|
|
93
|
+
export declare function encodeSelectorJson(s: Selector): string;
|
|
94
|
+
/** Decode a Rust-compatible untagged JSON value into a Selector. */
|
|
95
|
+
export declare function selectorFromJsonValue(value: unknown): Selector;
|
|
96
|
+
//# sourceMappingURL=Selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../src/Selector.ts"],"names":[],"mappings":"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"}
|