@mobilewright/core 0.0.1
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/dist/device.d.ts +34 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +73 -0
- package/dist/device.js.map +1 -0
- package/dist/expect.d.ts +30 -0
- package/dist/expect.d.ts.map +1 -0
- package/dist/expect.js +74 -0
- package/dist/expect.js.map +1 -0
- package/dist/expect.test.d.ts +2 -0
- package/dist/expect.test.d.ts.map +1 -0
- package/dist/expect.test.js +212 -0
- package/dist/expect.test.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/locator.d.ts +69 -0
- package/dist/locator.d.ts.map +1 -0
- package/dist/locator.js +204 -0
- package/dist/locator.js.map +1 -0
- package/dist/locator.test.d.ts +2 -0
- package/dist/locator.test.d.ts.map +1 -0
- package/dist/locator.test.js +240 -0
- package/dist/locator.test.js.map +1 -0
- package/dist/query-engine.d.ts +32 -0
- package/dist/query-engine.d.ts.map +1 -0
- package/dist/query-engine.js +115 -0
- package/dist/query-engine.js.map +1 -0
- package/dist/query-engine.test.d.ts +2 -0
- package/dist/query-engine.test.d.ts.map +1 -0
- package/dist/query-engine.test.js +244 -0
- package/dist/query-engine.test.js.map +1 -0
- package/dist/screen.d.ts +23 -0
- package/dist/screen.d.ts.map +1 -0
- package/dist/screen.js +37 -0
- package/dist/screen.js.map +1 -0
- package/dist/sleep.d.ts +2 -0
- package/dist/sleep.d.ts.map +1 -0
- package/dist/sleep.js +4 -0
- package/dist/sleep.js.map +1 -0
- package/package.json +25 -0
package/dist/locator.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
import { queryAll } from './query-engine.js';
|
|
3
|
+
import { sleep } from './sleep.js';
|
|
4
|
+
const DEFAULT_TIMEOUT = 5_000;
|
|
5
|
+
const DEFAULT_POLL_INTERVAL = 100;
|
|
6
|
+
const DEFAULT_STABILITY_DELAY = 50;
|
|
7
|
+
export class Locator {
|
|
8
|
+
/** Create a root locator that searches the entire view hierarchy. */
|
|
9
|
+
static root(driver, options = {}) {
|
|
10
|
+
return new Locator(driver, { kind: 'root' }, options);
|
|
11
|
+
}
|
|
12
|
+
constructor(driver, strategy, options = {}) {
|
|
13
|
+
this.driver = driver;
|
|
14
|
+
this.strategy = strategy;
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
// ─── Chaining ────────────────────────────────────────────────
|
|
18
|
+
getByLabel(label, opts) {
|
|
19
|
+
return this.child({ kind: 'label', value: label, exact: opts?.exact });
|
|
20
|
+
}
|
|
21
|
+
getByTestId(testId) {
|
|
22
|
+
return this.child({ kind: 'testId', value: testId });
|
|
23
|
+
}
|
|
24
|
+
getByText(text, opts) {
|
|
25
|
+
return this.child({ kind: 'text', value: text, exact: opts?.exact });
|
|
26
|
+
}
|
|
27
|
+
getByType(type) {
|
|
28
|
+
return this.child({ kind: 'type', value: type });
|
|
29
|
+
}
|
|
30
|
+
getByRole(role, opts) {
|
|
31
|
+
return this.child({ kind: 'role', value: role, name: opts?.name });
|
|
32
|
+
}
|
|
33
|
+
child(childStrategy) {
|
|
34
|
+
return new Locator(this.driver, { kind: 'chain', parent: this.strategy, child: childStrategy }, this.options);
|
|
35
|
+
}
|
|
36
|
+
// ─── Actions ─────────────────────────────────────────────────
|
|
37
|
+
async tap(opts) {
|
|
38
|
+
const node = await this.resolveActionable(opts?.timeout);
|
|
39
|
+
const { x, y } = centerOf(node.bounds);
|
|
40
|
+
await this.driver.tap(x, y);
|
|
41
|
+
}
|
|
42
|
+
async doubleTap(opts) {
|
|
43
|
+
const node = await this.resolveActionable(opts?.timeout);
|
|
44
|
+
const { x, y } = centerOf(node.bounds);
|
|
45
|
+
await this.driver.doubleTap(x, y);
|
|
46
|
+
}
|
|
47
|
+
async longPress(opts) {
|
|
48
|
+
const node = await this.resolveActionable(opts?.timeout);
|
|
49
|
+
const { x, y } = centerOf(node.bounds);
|
|
50
|
+
await this.driver.longPress(x, y, opts?.duration);
|
|
51
|
+
}
|
|
52
|
+
async fill(text, opts) {
|
|
53
|
+
const node = await this.resolveActionable(opts?.timeout);
|
|
54
|
+
const { x, y } = centerOf(node.bounds);
|
|
55
|
+
// Tap to focus, then type
|
|
56
|
+
await this.driver.tap(x, y);
|
|
57
|
+
await this.driver.typeText(text);
|
|
58
|
+
}
|
|
59
|
+
async screenshot(opts) {
|
|
60
|
+
const node = await this.resolveVisible(opts?.timeout);
|
|
61
|
+
const fullScreenshot = await this.driver.screenshot();
|
|
62
|
+
return cropToElement(fullScreenshot, node.bounds, await this.driver.getScreenSize());
|
|
63
|
+
}
|
|
64
|
+
// ─── Queries (with auto-wait for visibility) ─────────────────
|
|
65
|
+
async isVisible(opts) {
|
|
66
|
+
try {
|
|
67
|
+
await this.waitFor({ state: 'visible', timeout: opts?.timeout ?? 0 });
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async isEnabled(opts) {
|
|
75
|
+
const node = await this.resolve(opts?.timeout ?? 0);
|
|
76
|
+
return node !== null && node.isEnabled;
|
|
77
|
+
}
|
|
78
|
+
async getText(opts) {
|
|
79
|
+
const node = await this.resolveVisible(opts?.timeout);
|
|
80
|
+
return node.text ?? node.label ?? node.value ?? '';
|
|
81
|
+
}
|
|
82
|
+
async waitFor(opts) {
|
|
83
|
+
await this.pollUntilState(opts?.state ?? 'visible', opts?.timeout);
|
|
84
|
+
}
|
|
85
|
+
// ─── Internal resolution ─────────────────────────────────────
|
|
86
|
+
/** Wait for a visible node and return it. Used by getText, screenshot. */
|
|
87
|
+
async resolveVisible(timeout) {
|
|
88
|
+
const node = await this.pollUntilState('visible', timeout);
|
|
89
|
+
return node;
|
|
90
|
+
}
|
|
91
|
+
/** Poll until the given state is satisfied. Returns the matched node (or null for hidden/disabled). */
|
|
92
|
+
async pollUntilState(state, timeout) {
|
|
93
|
+
const effectiveTimeout = timeout ?? this.options.timeout ?? DEFAULT_TIMEOUT;
|
|
94
|
+
const pollInterval = this.options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
95
|
+
const deadline = Date.now() + effectiveTimeout;
|
|
96
|
+
while (true) {
|
|
97
|
+
const roots = await this.driver.getViewHierarchy();
|
|
98
|
+
const node = queryAll(roots, this.strategy)[0] ?? null;
|
|
99
|
+
if (checkState(node, state)) {
|
|
100
|
+
return node;
|
|
101
|
+
}
|
|
102
|
+
if (Date.now() >= deadline) {
|
|
103
|
+
throw new LocatorError(`Locator timed out waiting for state "${state}" after ${effectiveTimeout}ms`, this.strategy);
|
|
104
|
+
}
|
|
105
|
+
await sleep(pollInterval);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Resolve to a single actionable node (visible, enabled, stable bounds) */
|
|
109
|
+
async resolveActionable(timeout) {
|
|
110
|
+
const effectiveTimeout = timeout ?? this.options.timeout ?? DEFAULT_TIMEOUT;
|
|
111
|
+
const pollInterval = this.options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
112
|
+
const stabilityDelay = this.options.stabilityDelay ?? DEFAULT_STABILITY_DELAY;
|
|
113
|
+
const deadline = Date.now() + effectiveTimeout;
|
|
114
|
+
let previousBounds = null;
|
|
115
|
+
let lastReason = 'no matching element found';
|
|
116
|
+
while (true) {
|
|
117
|
+
const roots = await this.driver.getViewHierarchy();
|
|
118
|
+
const node = queryAll(roots, this.strategy)[0];
|
|
119
|
+
if (!node) {
|
|
120
|
+
lastReason = 'no matching element found';
|
|
121
|
+
}
|
|
122
|
+
else if (!node.isVisible) {
|
|
123
|
+
lastReason = 'element found but not visible';
|
|
124
|
+
}
|
|
125
|
+
else if (!node.isEnabled) {
|
|
126
|
+
lastReason = 'element found but not enabled';
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Stability check: bounds haven't changed since last poll
|
|
130
|
+
if (previousBounds && boundsEqual(previousBounds, node.bounds)) {
|
|
131
|
+
return node;
|
|
132
|
+
}
|
|
133
|
+
previousBounds = { ...node.bounds };
|
|
134
|
+
if (Date.now() >= deadline)
|
|
135
|
+
return node; // accept without stability
|
|
136
|
+
await sleep(stabilityDelay);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (Date.now() >= deadline) {
|
|
140
|
+
throw new LocatorError(`Locator: ${lastReason} after ${effectiveTimeout}ms`, this.strategy);
|
|
141
|
+
}
|
|
142
|
+
await sleep(pollInterval);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** Resolve without waiting — returns null if not found */
|
|
146
|
+
async resolve(timeout) {
|
|
147
|
+
const deadline = Date.now() + timeout;
|
|
148
|
+
const pollInterval = this.options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
149
|
+
do {
|
|
150
|
+
const roots = await this.driver.getViewHierarchy();
|
|
151
|
+
const matches = queryAll(roots, this.strategy);
|
|
152
|
+
if (matches.length > 0)
|
|
153
|
+
return matches[0];
|
|
154
|
+
if (timeout <= 0)
|
|
155
|
+
return null;
|
|
156
|
+
await sleep(pollInterval);
|
|
157
|
+
} while (Date.now() < deadline);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function cropToElement(screenshot, bounds, screenSize) {
|
|
162
|
+
const metadata = await sharp(screenshot).metadata();
|
|
163
|
+
const scale = (metadata.width ?? 1) / screenSize.width;
|
|
164
|
+
return sharp(screenshot)
|
|
165
|
+
.extract({
|
|
166
|
+
left: Math.round(bounds.x * scale),
|
|
167
|
+
top: Math.round(bounds.y * scale),
|
|
168
|
+
width: Math.round(bounds.width * scale),
|
|
169
|
+
height: Math.round(bounds.height * scale),
|
|
170
|
+
})
|
|
171
|
+
.toBuffer();
|
|
172
|
+
}
|
|
173
|
+
function centerOf(bounds) {
|
|
174
|
+
return {
|
|
175
|
+
x: Math.round(bounds.x + bounds.width / 2),
|
|
176
|
+
y: Math.round(bounds.y + bounds.height / 2),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function boundsEqual(a, b) {
|
|
180
|
+
return (a.x === b.x &&
|
|
181
|
+
a.y === b.y &&
|
|
182
|
+
a.width === b.width &&
|
|
183
|
+
a.height === b.height);
|
|
184
|
+
}
|
|
185
|
+
function checkState(node, state) {
|
|
186
|
+
switch (state) {
|
|
187
|
+
case 'visible':
|
|
188
|
+
return node !== null && node.isVisible;
|
|
189
|
+
case 'hidden':
|
|
190
|
+
return node === null || !node.isVisible;
|
|
191
|
+
case 'enabled':
|
|
192
|
+
return node !== null && node.isEnabled;
|
|
193
|
+
case 'disabled':
|
|
194
|
+
return node !== null && !node.isEnabled;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export class LocatorError extends Error {
|
|
198
|
+
constructor(message, strategy) {
|
|
199
|
+
super(message);
|
|
200
|
+
this.strategy = strategy;
|
|
201
|
+
this.name = 'LocatorError';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# 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 @@
|
|
|
1
|
+
{"version":3,"file":"locator.test.d.ts","sourceRoot":"","sources":["../src/locator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
import { Locator, LocatorError } from './locator.js';
|
|
3
|
+
function node(overrides) {
|
|
4
|
+
return {
|
|
5
|
+
isVisible: true,
|
|
6
|
+
isEnabled: true,
|
|
7
|
+
bounds: { x: 0, y: 0, width: 100, height: 44 },
|
|
8
|
+
children: [],
|
|
9
|
+
...overrides,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function createMockDriver(hierarchy) {
|
|
13
|
+
let currentHierarchy = hierarchy;
|
|
14
|
+
const tracker = {
|
|
15
|
+
tapCalls: [],
|
|
16
|
+
doubleTapCalls: [],
|
|
17
|
+
longPressCalls: [],
|
|
18
|
+
typeTextCalls: [],
|
|
19
|
+
swipeCalls: [],
|
|
20
|
+
gestureCalls: [],
|
|
21
|
+
pressButtonCalls: [],
|
|
22
|
+
setOrientationCalls: [],
|
|
23
|
+
launchAppCalls: [],
|
|
24
|
+
terminateAppCalls: [],
|
|
25
|
+
installAppCalls: [],
|
|
26
|
+
uninstallAppCalls: [],
|
|
27
|
+
openUrlCalls: [],
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
_tracker: tracker,
|
|
31
|
+
_setHierarchy: (h) => { currentHierarchy = h; },
|
|
32
|
+
connect: async () => ({ deviceId: 'device1', platform: 'ios' }),
|
|
33
|
+
disconnect: async () => { },
|
|
34
|
+
getViewHierarchy: async () => currentHierarchy,
|
|
35
|
+
tap: async (...args) => { tracker.tapCalls.push(args); },
|
|
36
|
+
doubleTap: async (...args) => { tracker.doubleTapCalls.push(args); },
|
|
37
|
+
longPress: async (...args) => { tracker.longPressCalls.push(args); },
|
|
38
|
+
typeText: async (...args) => { tracker.typeTextCalls.push(args); },
|
|
39
|
+
swipe: async (...args) => { tracker.swipeCalls.push(args); },
|
|
40
|
+
gesture: async (...args) => { tracker.gestureCalls.push(args); },
|
|
41
|
+
pressButton: async (...args) => { tracker.pressButtonCalls.push(args); },
|
|
42
|
+
screenshot: async () => Buffer.from(''),
|
|
43
|
+
getScreenSize: async () => ({ width: 390, height: 844 }),
|
|
44
|
+
getOrientation: async () => 'portrait',
|
|
45
|
+
setOrientation: async (...args) => { tracker.setOrientationCalls.push(args); },
|
|
46
|
+
launchApp: async (...args) => { tracker.launchAppCalls.push(args); },
|
|
47
|
+
terminateApp: async (...args) => { tracker.terminateAppCalls.push(args); },
|
|
48
|
+
listApps: async () => [],
|
|
49
|
+
getForegroundApp: async () => ({ bundleId: 'com.test' }),
|
|
50
|
+
installApp: async (...args) => { tracker.installAppCalls.push(args); },
|
|
51
|
+
uninstallApp: async (...args) => { tracker.uninstallAppCalls.push(args); },
|
|
52
|
+
listDevices: async () => [],
|
|
53
|
+
openUrl: async (...args) => { tracker.openUrlCalls.push(args); },
|
|
54
|
+
startRecording: async () => { },
|
|
55
|
+
stopRecording: async () => ({ output: '' }),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
test.describe('Locator', () => {
|
|
59
|
+
const hierarchy = [
|
|
60
|
+
node({
|
|
61
|
+
type: 'Window',
|
|
62
|
+
children: [
|
|
63
|
+
node({
|
|
64
|
+
type: 'Button',
|
|
65
|
+
label: 'Submit',
|
|
66
|
+
identifier: 'submitBtn',
|
|
67
|
+
bounds: { x: 20, y: 100, width: 200, height: 50 },
|
|
68
|
+
}),
|
|
69
|
+
node({
|
|
70
|
+
type: 'TextField',
|
|
71
|
+
label: 'Email',
|
|
72
|
+
identifier: 'emailField',
|
|
73
|
+
bounds: { x: 20, y: 200, width: 350, height: 44 },
|
|
74
|
+
}),
|
|
75
|
+
node({
|
|
76
|
+
type: 'Button',
|
|
77
|
+
label: 'Cancel',
|
|
78
|
+
identifier: 'cancelBtn',
|
|
79
|
+
isEnabled: false,
|
|
80
|
+
bounds: { x: 20, y: 300, width: 200, height: 50 },
|
|
81
|
+
}),
|
|
82
|
+
],
|
|
83
|
+
}),
|
|
84
|
+
];
|
|
85
|
+
test.describe('tap', () => {
|
|
86
|
+
test('taps at center of matched element', async () => {
|
|
87
|
+
const driver = createMockDriver(hierarchy);
|
|
88
|
+
const locator = new Locator(driver, {
|
|
89
|
+
kind: 'label',
|
|
90
|
+
value: 'Submit',
|
|
91
|
+
});
|
|
92
|
+
await locator.tap();
|
|
93
|
+
expect(driver._tracker.tapCalls).toEqual([[120, 125]]);
|
|
94
|
+
});
|
|
95
|
+
test('throws LocatorError when element not found', async () => {
|
|
96
|
+
const driver = createMockDriver(hierarchy);
|
|
97
|
+
const locator = new Locator(driver, {
|
|
98
|
+
kind: 'label',
|
|
99
|
+
value: 'Nonexistent',
|
|
100
|
+
}, { timeout: 200 });
|
|
101
|
+
await expect(locator.tap()).rejects.toThrow(LocatorError);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
test.describe('fill', () => {
|
|
105
|
+
test('taps to focus then types text', async () => {
|
|
106
|
+
const driver = createMockDriver(hierarchy);
|
|
107
|
+
const locator = new Locator(driver, {
|
|
108
|
+
kind: 'testId',
|
|
109
|
+
value: 'emailField',
|
|
110
|
+
});
|
|
111
|
+
await locator.fill('test@example.com');
|
|
112
|
+
expect(driver._tracker.tapCalls).toEqual([[195, 222]]);
|
|
113
|
+
expect(driver._tracker.typeTextCalls).toEqual([['test@example.com']]);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
test.describe('auto-waiting', () => {
|
|
117
|
+
test('waits for element to become visible', async () => {
|
|
118
|
+
const hiddenNode = node({
|
|
119
|
+
type: 'Button',
|
|
120
|
+
label: 'Delayed',
|
|
121
|
+
identifier: 'delayedBtn',
|
|
122
|
+
isVisible: false,
|
|
123
|
+
bounds: { x: 10, y: 10, width: 100, height: 40 },
|
|
124
|
+
});
|
|
125
|
+
const visibleNode = { ...hiddenNode, isVisible: true };
|
|
126
|
+
const tree = [node({ type: 'Window', children: [hiddenNode] })];
|
|
127
|
+
const treeVisible = [
|
|
128
|
+
node({ type: 'Window', children: [visibleNode] }),
|
|
129
|
+
];
|
|
130
|
+
const driver = createMockDriver(tree);
|
|
131
|
+
let callCount = 0;
|
|
132
|
+
driver.getViewHierarchy = async () => {
|
|
133
|
+
callCount++;
|
|
134
|
+
return callCount >= 3 ? treeVisible : tree;
|
|
135
|
+
};
|
|
136
|
+
const locator = new Locator(driver, {
|
|
137
|
+
kind: 'testId',
|
|
138
|
+
value: 'delayedBtn',
|
|
139
|
+
}, { timeout: 2000, pollInterval: 10, stabilityDelay: 10 });
|
|
140
|
+
await locator.tap();
|
|
141
|
+
expect(driver._tracker.tapCalls.length).toBeGreaterThan(0);
|
|
142
|
+
});
|
|
143
|
+
test('rejects action on disabled element after timeout', async () => {
|
|
144
|
+
const driver = createMockDriver(hierarchy);
|
|
145
|
+
const locator = new Locator(driver, {
|
|
146
|
+
kind: 'testId',
|
|
147
|
+
value: 'cancelBtn',
|
|
148
|
+
}, { timeout: 200 });
|
|
149
|
+
await expect(locator.tap()).rejects.toThrow(/not enabled/);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
test.describe('waitFor', () => {
|
|
153
|
+
test('resolves immediately when element is already visible', async () => {
|
|
154
|
+
const driver = createMockDriver(hierarchy);
|
|
155
|
+
const locator = new Locator(driver, {
|
|
156
|
+
kind: 'label',
|
|
157
|
+
value: 'Submit',
|
|
158
|
+
});
|
|
159
|
+
await locator.waitFor({ state: 'visible' });
|
|
160
|
+
const text = await locator.getText({ timeout: 0 });
|
|
161
|
+
expect(text).toBe('Submit');
|
|
162
|
+
});
|
|
163
|
+
test('waits for hidden state', async () => {
|
|
164
|
+
const emptyTree = [
|
|
165
|
+
node({ type: 'Window', children: [] }),
|
|
166
|
+
];
|
|
167
|
+
const driver = createMockDriver(hierarchy);
|
|
168
|
+
let callCount = 0;
|
|
169
|
+
driver.getViewHierarchy = async () => {
|
|
170
|
+
callCount++;
|
|
171
|
+
return callCount >= 2 ? emptyTree : hierarchy;
|
|
172
|
+
};
|
|
173
|
+
const locator = new Locator(driver, {
|
|
174
|
+
kind: 'label',
|
|
175
|
+
value: 'Submit',
|
|
176
|
+
}, { timeout: 2000, pollInterval: 10 });
|
|
177
|
+
// waitFor hidden should resolve once the element disappears
|
|
178
|
+
await locator.waitFor({ state: 'hidden' });
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
test.describe('getText', () => {
|
|
182
|
+
test('returns text/label/value of matched element', async () => {
|
|
183
|
+
const treeWithText = [
|
|
184
|
+
node({
|
|
185
|
+
type: 'Window',
|
|
186
|
+
children: [
|
|
187
|
+
node({
|
|
188
|
+
type: 'StaticText',
|
|
189
|
+
label: 'Welcome',
|
|
190
|
+
text: 'Welcome back!',
|
|
191
|
+
bounds: { x: 10, y: 10, width: 200, height: 30 },
|
|
192
|
+
}),
|
|
193
|
+
],
|
|
194
|
+
}),
|
|
195
|
+
];
|
|
196
|
+
const driver = createMockDriver(treeWithText);
|
|
197
|
+
const locator = new Locator(driver, {
|
|
198
|
+
kind: 'label',
|
|
199
|
+
value: 'Welcome',
|
|
200
|
+
});
|
|
201
|
+
const text = await locator.getText();
|
|
202
|
+
expect(text).toBe('Welcome back!');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
test.describe('chaining', () => {
|
|
206
|
+
test('supports chained locators', async () => {
|
|
207
|
+
const treeWithList = [
|
|
208
|
+
node({
|
|
209
|
+
type: 'Window',
|
|
210
|
+
children: [
|
|
211
|
+
node({
|
|
212
|
+
type: 'Table',
|
|
213
|
+
identifier: 'list1',
|
|
214
|
+
children: [
|
|
215
|
+
node({
|
|
216
|
+
type: 'Cell',
|
|
217
|
+
label: 'Item 1',
|
|
218
|
+
bounds: { x: 0, y: 0, width: 390, height: 44 },
|
|
219
|
+
}),
|
|
220
|
+
node({
|
|
221
|
+
type: 'Cell',
|
|
222
|
+
label: 'Item 2',
|
|
223
|
+
bounds: { x: 0, y: 44, width: 390, height: 44 },
|
|
224
|
+
}),
|
|
225
|
+
],
|
|
226
|
+
}),
|
|
227
|
+
],
|
|
228
|
+
}),
|
|
229
|
+
];
|
|
230
|
+
const driver = createMockDriver(treeWithList);
|
|
231
|
+
const locator = new Locator(driver, {
|
|
232
|
+
kind: 'testId',
|
|
233
|
+
value: 'list1',
|
|
234
|
+
}).getByLabel('Item 2');
|
|
235
|
+
await locator.tap();
|
|
236
|
+
expect(driver._tracker.tapCalls).toEqual([[195, 66]]);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
//# sourceMappingURL=locator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.test.js","sourceRoot":"","sources":["../src/locator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ViewNode } from '@mobilewright/protocol';
|
|
2
|
+
export type LocatorStrategy = {
|
|
3
|
+
kind: 'root';
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'label';
|
|
6
|
+
value: string;
|
|
7
|
+
exact?: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'testId';
|
|
10
|
+
value: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'text';
|
|
13
|
+
value: string | RegExp;
|
|
14
|
+
exact?: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'type';
|
|
17
|
+
value: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'role';
|
|
20
|
+
value: string;
|
|
21
|
+
name?: string | RegExp;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'chain';
|
|
24
|
+
parent: LocatorStrategy;
|
|
25
|
+
child: LocatorStrategy;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Query all ViewNodes in a tree that match a locator strategy.
|
|
29
|
+
* Returns matching nodes in document order.
|
|
30
|
+
*/
|
|
31
|
+
export declare function queryAll(roots: ViewNode[], strategy: LocatorStrategy): ViewNode[];
|
|
32
|
+
//# sourceMappingURL=query-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-engine.d.ts","sourceRoot":"","sources":["../src/query-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,QAAQ,EAAE,EACjB,QAAQ,EAAE,eAAe,GACxB,QAAQ,EAAE,CA+BZ"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query all ViewNodes in a tree that match a locator strategy.
|
|
3
|
+
* Returns matching nodes in document order.
|
|
4
|
+
*/
|
|
5
|
+
export function queryAll(roots, strategy) {
|
|
6
|
+
if (strategy.kind === 'chain') {
|
|
7
|
+
// Root parent means "search the whole tree" — skip chaining
|
|
8
|
+
if (strategy.parent.kind === 'root') {
|
|
9
|
+
return queryAll(roots, strategy.child);
|
|
10
|
+
}
|
|
11
|
+
const parents = queryAll(roots, strategy.parent);
|
|
12
|
+
const results = [];
|
|
13
|
+
for (const parent of parents) {
|
|
14
|
+
// Try tree-based first (works when hierarchy has real children)
|
|
15
|
+
const treeResults = queryAll(parent.children, strategy.child);
|
|
16
|
+
if (treeResults.length > 0) {
|
|
17
|
+
results.push(...treeResults);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
// Bounds fallback: find nodes contained within parent's bounds (flat lists)
|
|
21
|
+
const contained = flattenNodes(roots).filter((n) => n !== parent && isContainedWithin(n.bounds, parent.bounds));
|
|
22
|
+
results.push(...queryAll(contained, strategy.child));
|
|
23
|
+
}
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
26
|
+
const results = [];
|
|
27
|
+
walkTree(roots, (node) => {
|
|
28
|
+
if (matchesStrategy(node, strategy)) {
|
|
29
|
+
results.push(node);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
function walkTree(nodes, visitor) {
|
|
35
|
+
for (const node of nodes) {
|
|
36
|
+
visitor(node);
|
|
37
|
+
walkTree(node.children, visitor);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function matchesStrategy(node, strategy) {
|
|
41
|
+
switch (strategy.kind) {
|
|
42
|
+
case 'root':
|
|
43
|
+
return true;
|
|
44
|
+
case 'label':
|
|
45
|
+
if (!node.label)
|
|
46
|
+
return false;
|
|
47
|
+
return strategy.exact === false
|
|
48
|
+
? node.label.toLowerCase().includes(strategy.value.toLowerCase())
|
|
49
|
+
: node.label === strategy.value;
|
|
50
|
+
case 'testId':
|
|
51
|
+
return node.identifier === strategy.value;
|
|
52
|
+
case 'text': {
|
|
53
|
+
const nodeText = node.text ?? node.label ?? node.value ?? '';
|
|
54
|
+
if (strategy.value instanceof RegExp) {
|
|
55
|
+
return strategy.value.test(nodeText);
|
|
56
|
+
}
|
|
57
|
+
return strategy.exact === false
|
|
58
|
+
? nodeText.toLowerCase().includes(strategy.value.toLowerCase())
|
|
59
|
+
: nodeText === strategy.value;
|
|
60
|
+
}
|
|
61
|
+
case 'type':
|
|
62
|
+
return (node.type.toLowerCase() === strategy.value.toLowerCase());
|
|
63
|
+
case 'role':
|
|
64
|
+
if (!matchesRole(node, strategy.value))
|
|
65
|
+
return false;
|
|
66
|
+
if (strategy.name !== undefined) {
|
|
67
|
+
const nodeLabel = node.label ?? node.text ?? '';
|
|
68
|
+
if (strategy.name instanceof RegExp) {
|
|
69
|
+
return strategy.name.test(nodeLabel);
|
|
70
|
+
}
|
|
71
|
+
return nodeLabel === strategy.name;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
case 'chain':
|
|
75
|
+
// Handled above in queryAll
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const ROLE_TYPE_MAP = {
|
|
80
|
+
button: ['button', 'imagebutton'],
|
|
81
|
+
textfield: ['textfield', 'securetextfield', 'edittext', 'searchfield'],
|
|
82
|
+
text: ['statictext', 'textview', 'text'],
|
|
83
|
+
image: ['image', 'imageview'],
|
|
84
|
+
switch: ['switch', 'toggle'],
|
|
85
|
+
checkbox: ['checkbox'],
|
|
86
|
+
slider: ['slider', 'seekbar'],
|
|
87
|
+
list: ['table', 'collectionview', 'listview', 'recyclerview', 'scrollview'],
|
|
88
|
+
listitem: ['cell', 'linearlayout', 'relativelayout', 'other'],
|
|
89
|
+
tab: ['tab', 'tabbar'],
|
|
90
|
+
link: ['link'],
|
|
91
|
+
header: ['navigationbar', 'toolbar', 'header'],
|
|
92
|
+
};
|
|
93
|
+
function matchesRole(node, role) {
|
|
94
|
+
const normalizedType = node.type.toLowerCase();
|
|
95
|
+
const roleTypes = ROLE_TYPE_MAP[role.toLowerCase()];
|
|
96
|
+
if (roleTypes) {
|
|
97
|
+
return roleTypes.includes(normalizedType);
|
|
98
|
+
}
|
|
99
|
+
// Fallback: direct type match
|
|
100
|
+
return normalizedType === role.toLowerCase();
|
|
101
|
+
}
|
|
102
|
+
/** Flatten a ViewNode tree into a single array. */
|
|
103
|
+
function flattenNodes(roots) {
|
|
104
|
+
const result = [];
|
|
105
|
+
walkTree(roots, (node) => result.push(node));
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
/** Check if inner bounds are fully contained within outer bounds. */
|
|
109
|
+
function isContainedWithin(inner, outer) {
|
|
110
|
+
return (inner.x >= outer.x &&
|
|
111
|
+
inner.y >= outer.y &&
|
|
112
|
+
inner.x + inner.width <= outer.x + outer.width &&
|
|
113
|
+
inner.y + inner.height <= outer.y + outer.height);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=query-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-engine.js","sourceRoot":"","sources":["../src/query-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-engine.test.d.ts","sourceRoot":"","sources":["../src/query-engine.test.ts"],"names":[],"mappings":""}
|