@taqwright/taqwright 0.0.24

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 (132) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +108 -0
  3. package/dist/auto-appium.d.ts +12 -0
  4. package/dist/auto-appium.js +77 -0
  5. package/dist/bin/branding.d.ts +6 -0
  6. package/dist/bin/branding.js +22 -0
  7. package/dist/bin/index.d.ts +2 -0
  8. package/dist/bin/index.js +321 -0
  9. package/dist/bin/init.d.ts +26 -0
  10. package/dist/bin/init.js +902 -0
  11. package/dist/bin/inspect.d.ts +9 -0
  12. package/dist/bin/inspect.js +91 -0
  13. package/dist/bin/report-branding.d.ts +2 -0
  14. package/dist/bin/report-branding.js +42 -0
  15. package/dist/branding-assets.d.ts +1 -0
  16. package/dist/branding-assets.js +1 -0
  17. package/dist/capabilities-helpers.d.ts +7 -0
  18. package/dist/capabilities-helpers.js +14 -0
  19. package/dist/capabilities.d.ts +6 -0
  20. package/dist/capabilities.js +86 -0
  21. package/dist/config.d.ts +17 -0
  22. package/dist/config.js +235 -0
  23. package/dist/discovery-setup.d.ts +1 -0
  24. package/dist/discovery-setup.js +61 -0
  25. package/dist/discovery.d.ts +17 -0
  26. package/dist/discovery.js +55 -0
  27. package/dist/docs/configuration.html +376 -0
  28. package/dist/docs/custom-reporters.html +265 -0
  29. package/dist/docs/docker.html +339 -0
  30. package/dist/docs/docs.js +173 -0
  31. package/dist/docs/generating-tests.html +161 -0
  32. package/dist/docs/images/taqwright-html-report.png +0 -0
  33. package/dist/docs/index.html +13 -0
  34. package/dist/docs/installation.html +686 -0
  35. package/dist/docs/parallel.html +271 -0
  36. package/dist/docs/running-tests.html +385 -0
  37. package/dist/docs/styles.css +460 -0
  38. package/dist/docs/writing-tests.html +565 -0
  39. package/dist/doctor.d.ts +33 -0
  40. package/dist/doctor.js +508 -0
  41. package/dist/expect.d.ts +38 -0
  42. package/dist/expect.js +96 -0
  43. package/dist/fixture/artifact-mode.d.ts +2 -0
  44. package/dist/fixture/artifact-mode.js +7 -0
  45. package/dist/fixture/index.d.ts +15 -0
  46. package/dist/fixture/index.js +324 -0
  47. package/dist/images/taqwright-html-report.png +0 -0
  48. package/dist/images/taqwright_favicon.png +0 -0
  49. package/dist/images/taqwright_logo.png +0 -0
  50. package/dist/index.d.ts +9 -0
  51. package/dist/index.js +7 -0
  52. package/dist/inspector/codegen-appium.d.ts +3 -0
  53. package/dist/inspector/codegen-appium.js +228 -0
  54. package/dist/inspector/devices.d.ts +41 -0
  55. package/dist/inspector/devices.js +422 -0
  56. package/dist/inspector/locator-suggester.d.ts +23 -0
  57. package/dist/inspector/locator-suggester.js +539 -0
  58. package/dist/inspector/recorder.d.ts +128 -0
  59. package/dist/inspector/recorder.js +162 -0
  60. package/dist/inspector/server.d.ts +39 -0
  61. package/dist/inspector/server.js +1210 -0
  62. package/dist/inspector/session.d.ts +84 -0
  63. package/dist/inspector/session.js +262 -0
  64. package/dist/inspector/ui.d.ts +1 -0
  65. package/dist/inspector/ui.js +5508 -0
  66. package/dist/keys.d.ts +3 -0
  67. package/dist/keys.js +28 -0
  68. package/dist/locator/index.d.ts +206 -0
  69. package/dist/locator/index.js +1506 -0
  70. package/dist/logger.d.ts +5 -0
  71. package/dist/logger.js +5 -0
  72. package/dist/mobile/index.d.ts +130 -0
  73. package/dist/mobile/index.js +762 -0
  74. package/dist/network/android.d.ts +5 -0
  75. package/dist/network/android.js +87 -0
  76. package/dist/network/ca.d.ts +10 -0
  77. package/dist/network/ca.js +136 -0
  78. package/dist/network/har.d.ts +90 -0
  79. package/dist/network/har.js +101 -0
  80. package/dist/network/host-proxy.d.ts +16 -0
  81. package/dist/network/host-proxy.js +134 -0
  82. package/dist/network/index.d.ts +26 -0
  83. package/dist/network/index.js +105 -0
  84. package/dist/network/ios-sim.d.ts +3 -0
  85. package/dist/network/ios-sim.js +29 -0
  86. package/dist/network/proxy.d.ts +13 -0
  87. package/dist/network/proxy.js +310 -0
  88. package/dist/providers/appium.d.ts +23 -0
  89. package/dist/providers/appium.js +288 -0
  90. package/dist/providers/browserstack/index.d.ts +5 -0
  91. package/dist/providers/browserstack/index.js +77 -0
  92. package/dist/providers/browserstack/utils.d.ts +1 -0
  93. package/dist/providers/browserstack/utils.js +6 -0
  94. package/dist/providers/cloud.d.ts +53 -0
  95. package/dist/providers/cloud.js +117 -0
  96. package/dist/providers/emulator/index.d.ts +8 -0
  97. package/dist/providers/emulator/index.js +47 -0
  98. package/dist/providers/index.d.ts +10 -0
  99. package/dist/providers/index.js +33 -0
  100. package/dist/providers/lambdatest/index.d.ts +28 -0
  101. package/dist/providers/lambdatest/index.js +99 -0
  102. package/dist/providers/lambdatest/utils.d.ts +1 -0
  103. package/dist/providers/lambdatest/utils.js +6 -0
  104. package/dist/providers/local/index.d.ts +9 -0
  105. package/dist/providers/local/index.js +53 -0
  106. package/dist/providers/local-session.d.ts +16 -0
  107. package/dist/providers/local-session.js +55 -0
  108. package/dist/setup/archive.d.ts +2 -0
  109. package/dist/setup/archive.js +43 -0
  110. package/dist/setup/avd.d.ts +12 -0
  111. package/dist/setup/avd.js +103 -0
  112. package/dist/setup/index.d.ts +6 -0
  113. package/dist/setup/index.js +55 -0
  114. package/dist/setup/install-android.d.ts +2 -0
  115. package/dist/setup/install-android.js +70 -0
  116. package/dist/setup/install-appium.d.ts +1 -0
  117. package/dist/setup/install-appium.js +64 -0
  118. package/dist/setup/install-jdk.d.ts +1 -0
  119. package/dist/setup/install-jdk.js +58 -0
  120. package/dist/setup/paths.d.ts +16 -0
  121. package/dist/setup/paths.js +88 -0
  122. package/dist/setup/spawn-tool.d.ts +3 -0
  123. package/dist/setup/spawn-tool.js +11 -0
  124. package/dist/tracer/index.d.ts +34 -0
  125. package/dist/tracer/index.js +687 -0
  126. package/dist/tracer/proxy.d.ts +3 -0
  127. package/dist/tracer/proxy.js +60 -0
  128. package/dist/types/index.d.ts +189 -0
  129. package/dist/types/index.js +6 -0
  130. package/dist/utils.d.ts +2 -0
  131. package/dist/utils.js +37 -0
  132. package/package.json +79 -0
@@ -0,0 +1,422 @@
1
+ import { execFile, spawn } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { existsSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { androidEnvForAvd, resolveAvdSdk } from '../setup/avd.js';
6
+ const execFileP = promisify(execFile);
7
+ const exe = (name) => (process.platform === 'win32' ? `${name}.exe` : name);
8
+ export async function listDevices() {
9
+ const [androidAll, ios, toolsMissing] = await Promise.all([
10
+ listAndroid().catch(() => []),
11
+ process.platform === 'darwin' ? listIos().catch(() => []) : Promise.resolve([]),
12
+ detectMissingTools(),
13
+ ]);
14
+ const android = await annotateAndroidBootability(androidAll);
15
+ return { android, ios, toolsMissing };
16
+ }
17
+ export async function annotateAndroidBootability(devices) {
18
+ const out = [];
19
+ for (const dev of devices) {
20
+ if (dev.state !== 'shutdown' || dev.type !== 'android' || !dev.avdName) {
21
+ out.push(dev);
22
+ continue;
23
+ }
24
+ const { image, sdkRoot } = await resolveAvdSdk(dev.avdName);
25
+ if (image && !sdkRoot) {
26
+ out.push({
27
+ ...dev,
28
+ bootable: false,
29
+ bootHint: `system image "${image}" is not installed in any Android SDK`,
30
+ });
31
+ }
32
+ else {
33
+ out.push(dev);
34
+ }
35
+ }
36
+ return out;
37
+ }
38
+ async function detectMissingTools() {
39
+ const out = {};
40
+ if (!(await commandExists('adb')))
41
+ out.adb = true;
42
+ if (!(await commandExists('emulator')))
43
+ out.emulator = true;
44
+ if (process.platform === 'darwin' && !(await commandExists('xcrun')))
45
+ out.xcrun = true;
46
+ return out;
47
+ }
48
+ async function listAndroid() {
49
+ const avds = (await commandExists('emulator')) ? await runningAvds() : [];
50
+ const online = (await commandExists('adb'))
51
+ ? await onlineAdbDevices()
52
+ : new Map();
53
+ const out = [];
54
+ const claimedSerials = new Set();
55
+ for (const avd of avds) {
56
+ let serial;
57
+ let osVersion;
58
+ for (const [s, info] of online) {
59
+ if (info.avdName === avd) {
60
+ serial = s;
61
+ osVersion = info.osVersion;
62
+ claimedSerials.add(s);
63
+ break;
64
+ }
65
+ }
66
+ out.push({
67
+ type: 'android',
68
+ udid: serial ?? `avd:${avd}`,
69
+ name: avd.replaceAll('_', ' '),
70
+ osVersion,
71
+ state: serial ? 'booted' : 'shutdown',
72
+ avdName: avd,
73
+ });
74
+ }
75
+ for (const [serial, info] of online) {
76
+ if (claimedSerials.has(serial))
77
+ continue;
78
+ out.push({
79
+ type: 'android',
80
+ udid: serial,
81
+ name: info.avdName ?? serial,
82
+ osVersion: info.osVersion,
83
+ state: 'booted',
84
+ avdName: info.avdName,
85
+ });
86
+ }
87
+ return out;
88
+ }
89
+ async function runningAvds() {
90
+ try {
91
+ const { stdout } = await execFileP('emulator', ['-list-avds']);
92
+ return stdout
93
+ .split('\n')
94
+ .map((l) => l.trim())
95
+ .filter(Boolean);
96
+ }
97
+ catch {
98
+ return [];
99
+ }
100
+ }
101
+ async function onlineAdbDevices() {
102
+ const out = new Map();
103
+ let stdout;
104
+ try {
105
+ ({ stdout } = await execFileP('adb', ['devices', '-l']));
106
+ }
107
+ catch {
108
+ return out;
109
+ }
110
+ const lines = stdout.split('\n').slice(1);
111
+ const serials = [];
112
+ for (const line of lines) {
113
+ const m = line.match(/^(\S+)\s+device\b/);
114
+ if (m)
115
+ serials.push(m[1]);
116
+ }
117
+ await Promise.all(serials.map(async (serial) => {
118
+ const info = {};
119
+ try {
120
+ const { stdout: name } = await execFileP('adb', [
121
+ '-s',
122
+ serial,
123
+ 'shell',
124
+ 'getprop',
125
+ 'ro.boot.qemu.avd_name',
126
+ ]);
127
+ const trimmed = name.trim();
128
+ if (trimmed)
129
+ info.avdName = trimmed;
130
+ }
131
+ catch {
132
+ return;
133
+ }
134
+ if (!info.avdName) {
135
+ try {
136
+ const { stdout: name } = await execFileP('adb', ['-s', serial, 'emu', 'avd', 'name']);
137
+ info.avdName = name.split('\n')[0]?.trim();
138
+ }
139
+ catch {
140
+ }
141
+ }
142
+ try {
143
+ const { stdout: ver } = await execFileP('adb', [
144
+ '-s',
145
+ serial,
146
+ 'shell',
147
+ 'getprop',
148
+ 'ro.build.version.release',
149
+ ]);
150
+ info.osVersion = ver.trim();
151
+ }
152
+ catch {
153
+ }
154
+ out.set(serial, info);
155
+ }));
156
+ return out;
157
+ }
158
+ export async function startAndroidEmulator(avdName) {
159
+ const { image, sdkRoot } = await resolveAvdSdk(avdName);
160
+ if (image && !sdkRoot) {
161
+ throw new Error(`Cannot boot "${avdName}": its system image "${image}" is not installed in any Android ` +
162
+ `SDK (checked the managed SDK, ANDROID_HOME, and your system SDK). Install it with ` +
163
+ `\`sdkmanager "${image.replace(/\//g, ';')}"\`, or recreate the AVD against an installed image.`);
164
+ }
165
+ const env = { ...process.env, ...((await androidEnvForAvd(avdName)) ?? {}) };
166
+ if (sdkRoot) {
167
+ env.ANDROID_HOME = sdkRoot;
168
+ env.ANDROID_SDK_ROOT = sdkRoot;
169
+ }
170
+ else if (env.ANDROID_HOME && !env.ANDROID_SDK_ROOT) {
171
+ env.ANDROID_SDK_ROOT = env.ANDROID_HOME;
172
+ }
173
+ const resolved = env.ANDROID_HOME
174
+ ? path.join(env.ANDROID_HOME, 'emulator', exe('emulator'))
175
+ : undefined;
176
+ const cmd = resolved && existsSync(resolved) ? resolved : 'emulator';
177
+ if (cmd === 'emulator' && !(await commandExists('emulator'))) {
178
+ throw new Error('No Android SDK found to boot the emulator — set ANDROID_HOME (or run `taqwright install`).');
179
+ }
180
+ const child = spawn(cmd, ['-avd', avdName], {
181
+ detached: process.platform !== 'win32',
182
+ stdio: ['ignore', 'pipe', 'pipe'],
183
+ env,
184
+ });
185
+ let out = '';
186
+ const accumulate = (d) => {
187
+ out += d.toString();
188
+ };
189
+ child.stdout?.on('data', accumulate);
190
+ child.stderr?.on('data', accumulate);
191
+ await new Promise((resolve, reject) => {
192
+ const done = () => {
193
+ clearTimeout(timer);
194
+ child.off('exit', onExit);
195
+ child.off('error', onError);
196
+ };
197
+ const onExit = (code) => {
198
+ done();
199
+ const tail = out.trim().split('\n').slice(-12).join('\n');
200
+ reject(new Error(`Emulator "${avdName}" exited (code ${code ?? 'null'}) during startup.` +
201
+ (tail ? `\n${tail}` : '')));
202
+ };
203
+ const onError = (err) => {
204
+ done();
205
+ reject(err);
206
+ };
207
+ const timer = setTimeout(() => {
208
+ done();
209
+ child.stdout?.off('data', accumulate);
210
+ child.stderr?.off('data', accumulate);
211
+ child.stdout?.resume();
212
+ child.stderr?.resume();
213
+ child.unref();
214
+ resolve();
215
+ }, 4000);
216
+ child.once('exit', onExit);
217
+ child.once('error', onError);
218
+ });
219
+ if (process.platform === 'win32')
220
+ maximizeEmulatorWindowWindows();
221
+ }
222
+ function maximizeEmulatorWindowWindows() {
223
+ const ps = [
224
+ 'Add-Type @"',
225
+ 'using System;using System.Runtime.InteropServices;',
226
+ 'public class TwWin{',
227
+ '[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h,int n);',
228
+ '[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);',
229
+ '[DllImport("user32.dll")] public static extern bool IsZoomed(IntPtr h);}',
230
+ '"@',
231
+ '$deadline=(Get-Date).AddSeconds(60)',
232
+ '$stable=0',
233
+ 'while((Get-Date) -lt $deadline){',
234
+ ' $p=Get-Process | Where-Object {$_.MainWindowTitle -like "*Android Emulator*" -and $_.MainWindowHandle -ne 0} | Select-Object -First 1',
235
+ ' if($p){',
236
+ ' $h=$p.MainWindowHandle',
237
+ ' if([TwWin]::IsZoomed($h)){ $stable++; if($stable -ge 3){ break } }',
238
+ ' else { [TwWin]::ShowWindow($h,3) | Out-Null; [TwWin]::SetForegroundWindow($h) | Out-Null; $stable=0 }',
239
+ ' }',
240
+ ' Start-Sleep -Milliseconds 1000',
241
+ '}',
242
+ ].join('\n');
243
+ try {
244
+ const encoded = Buffer.from(ps, 'utf16le').toString('base64');
245
+ const child = spawn('powershell', ['-NoProfile', '-NonInteractive', '-EncodedCommand', encoded], { detached: true, stdio: 'ignore', windowsHide: true });
246
+ child.on('error', () => { });
247
+ child.unref();
248
+ }
249
+ catch {
250
+ }
251
+ }
252
+ export async function stopAndroidEmulator(serial) {
253
+ if (!serial.startsWith('emulator-')) {
254
+ throw new Error(`Cannot stop a device that isn't an emulator (got: ${serial}).`);
255
+ }
256
+ await execFileP('adb', ['-s', serial, 'emu', 'kill']);
257
+ }
258
+ export function findSerialForAvd(online, avdName) {
259
+ for (const [serial, info] of online) {
260
+ if (info.avdName === avdName)
261
+ return serial;
262
+ }
263
+ return undefined;
264
+ }
265
+ async function isAndroidDeviceReady(serial) {
266
+ try {
267
+ const { stdout: booted } = await execFileP('adb', [
268
+ '-s',
269
+ serial,
270
+ 'shell',
271
+ 'getprop',
272
+ 'sys.boot_completed',
273
+ ]);
274
+ if (booted.trim() !== '1')
275
+ return false;
276
+ const { stdout: pmPath } = await execFileP('adb', [
277
+ '-s',
278
+ serial,
279
+ 'shell',
280
+ 'pm',
281
+ 'path',
282
+ 'android',
283
+ ]);
284
+ return pmPath.includes('package:');
285
+ }
286
+ catch {
287
+ return false;
288
+ }
289
+ }
290
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
291
+ export async function ensureAndroidAvdReady(avdName, opts = {}) {
292
+ const timeoutMs = opts.timeoutMs ?? 180_000;
293
+ const deadline = Date.now() + timeoutMs;
294
+ let serial = findSerialForAvd(await onlineAdbDevices(), avdName);
295
+ if (!serial) {
296
+ await startAndroidEmulator(avdName);
297
+ while (!serial && Date.now() < deadline) {
298
+ await sleep(2000);
299
+ serial = findSerialForAvd(await onlineAdbDevices(), avdName);
300
+ }
301
+ if (!serial) {
302
+ throw new Error(`taqwright: AVD "${avdName}" did not come online within ${Math.round(timeoutMs / 1000)}s.`);
303
+ }
304
+ }
305
+ await execFileP('adb', ['-s', serial, 'wait-for-device']).catch(() => {
306
+ });
307
+ while (Date.now() < deadline) {
308
+ if (await isAndroidDeviceReady(serial))
309
+ return serial;
310
+ await sleep(2000);
311
+ }
312
+ throw new Error(`taqwright: emulator ${serial} (AVD "${avdName}") was not ready (boot_completed + ` +
313
+ `PackageManager) within ${Math.round(timeoutMs / 1000)}s.`);
314
+ }
315
+ export async function waitForAndroidDeviceReady(serial, opts = {}) {
316
+ const timeoutMs = opts.timeoutMs ?? 60_000;
317
+ const deadline = Date.now() + timeoutMs;
318
+ while (Date.now() < deadline) {
319
+ if ((await onlineAdbDevices()).has(serial) && (await isAndroidDeviceReady(serial))) {
320
+ return true;
321
+ }
322
+ await sleep(1500);
323
+ }
324
+ return false;
325
+ }
326
+ export async function resolveAndroidSerial(opts) {
327
+ const { udid, avdName } = opts;
328
+ if (udid && !udid.startsWith('avd:'))
329
+ return udid;
330
+ if (avdName)
331
+ return findSerialForAvd(await onlineAdbDevices(), avdName);
332
+ return undefined;
333
+ }
334
+ export function isTransientDeviceError(message) {
335
+ return [
336
+ /device offline/i,
337
+ /was not in the list of connected devices/i,
338
+ /io\.appium\.settings/i,
339
+ /error executing adbexec/i,
340
+ /cannot start the .* application/i,
341
+ /device unauthorized/i,
342
+ ].some((re) => re.test(message));
343
+ }
344
+ async function listIos() {
345
+ if (!(await commandExists('xcrun')))
346
+ return [];
347
+ let stdout;
348
+ try {
349
+ ({ stdout } = await execFileP('xcrun', ['simctl', 'list', 'devices', 'available', '--json']));
350
+ }
351
+ catch {
352
+ return [];
353
+ }
354
+ let parsed;
355
+ try {
356
+ parsed = JSON.parse(stdout);
357
+ }
358
+ catch {
359
+ return [];
360
+ }
361
+ const out = [];
362
+ for (const [runtimeKey, entries] of Object.entries(parsed.devices ?? {})) {
363
+ const osVersion = parseRuntimeVersion(runtimeKey);
364
+ for (const entry of entries) {
365
+ if (entry.isAvailable === false)
366
+ continue;
367
+ out.push({
368
+ type: 'ios',
369
+ udid: entry.udid,
370
+ name: entry.name,
371
+ osVersion,
372
+ state: stateFromSimctl(entry.state),
373
+ runtime: runtimeKey,
374
+ });
375
+ }
376
+ }
377
+ out.sort((a, b) => {
378
+ if (a.state !== b.state)
379
+ return a.state === 'booted' ? -1 : 1;
380
+ return a.name.localeCompare(b.name);
381
+ });
382
+ return out;
383
+ }
384
+ function parseRuntimeVersion(runtimeKey) {
385
+ const m = runtimeKey.match(/iOS-(\d+)-(\d+)$/);
386
+ return m ? `${m[1]}.${m[2]}` : undefined;
387
+ }
388
+ function stateFromSimctl(s) {
389
+ switch (s) {
390
+ case 'Booted':
391
+ return 'booted';
392
+ case 'Shutdown':
393
+ return 'shutdown';
394
+ case 'Booting':
395
+ return 'booting';
396
+ default:
397
+ return 'unknown';
398
+ }
399
+ }
400
+ export async function startIosSimulator(udid) {
401
+ if (!(await commandExists('xcrun'))) {
402
+ throw new Error('`xcrun` is not on PATH (Xcode command-line tools missing).');
403
+ }
404
+ await execFileP('xcrun', ['simctl', 'boot', udid]).catch((err) => {
405
+ if (!/already booted|state: Booted/i.test(String(err.stderr ?? err.message ?? '')))
406
+ throw err;
407
+ });
408
+ await execFileP('open', ['-a', 'Simulator']).catch(() => {
409
+ });
410
+ }
411
+ export async function stopIosSimulator(udid) {
412
+ await execFileP('xcrun', ['simctl', 'shutdown', udid]);
413
+ }
414
+ async function commandExists(name) {
415
+ return new Promise((resolve) => {
416
+ const child = spawn(process.platform === 'win32' ? 'where' : 'which', [name], {
417
+ stdio: 'ignore',
418
+ });
419
+ child.on('exit', (code) => resolve(code === 0));
420
+ child.on('error', () => resolve(false));
421
+ });
422
+ }
@@ -0,0 +1,23 @@
1
+ import { Platform, type LocatorDescriptor } from '../types/index.js';
2
+ export type LocatorCategory = 'id' | 'uiautomator' | 'predicate' | 'classChain' | 'xpath' | 'css';
3
+ export interface LocatorCandidate {
4
+ category: LocatorCategory;
5
+ subLabel: string;
6
+ priority: number;
7
+ code: string;
8
+ using: string;
9
+ value: string;
10
+ descriptor?: LocatorDescriptor;
11
+ }
12
+ export interface LocatorSuggestion extends LocatorCandidate {
13
+ count: number;
14
+ unique: boolean;
15
+ }
16
+ export type ElementAttrs = Record<string, string | undefined>;
17
+ export declare const CATEGORY_ORDER: Record<Platform, LocatorCategory[]>;
18
+ export declare const WEB_CATEGORY_ORDER: LocatorCategory[];
19
+ export declare function generateCandidates(platform: Platform, attrs: ElementAttrs, _xpath: string, isWeb?: boolean): LocatorCandidate[];
20
+ export declare function selectBestPerCategory(platform: Platform, suggestions: LocatorSuggestion[], isWeb?: boolean): LocatorSuggestion[];
21
+ export declare function isPositional(s: LocatorCandidate): boolean;
22
+ export declare function pickRecommended(platform: Platform, verified: LocatorSuggestion[], isWeb?: boolean): LocatorSuggestion | undefined;
23
+ export declare function makeNthSuggestion(base: LocatorCandidate, idx: number): LocatorSuggestion;