@wdio/mcp 2.0.0 → 2.2.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.
@@ -0,0 +1,1178 @@
1
+ // src/scripts/get-browser-accessibility-tree.ts
2
+ function flattenAccessibilityTree(node, result = []) {
3
+ if (!node) return result;
4
+ if (node.role !== "WebArea" || node.name) {
5
+ const entry = {
6
+ role: node.role || "",
7
+ name: node.name || "",
8
+ value: node.value ?? "",
9
+ description: node.description || "",
10
+ disabled: node.disabled ? "true" : "",
11
+ focused: node.focused ? "true" : "",
12
+ selected: node.selected ? "true" : "",
13
+ checked: node.checked === true ? "true" : node.checked === false ? "false" : node.checked === "mixed" ? "mixed" : "",
14
+ expanded: node.expanded === true ? "true" : node.expanded === false ? "false" : "",
15
+ pressed: node.pressed === true ? "true" : node.pressed === false ? "false" : node.pressed === "mixed" ? "mixed" : "",
16
+ readonly: node.readonly ? "true" : "",
17
+ required: node.required ? "true" : "",
18
+ level: node.level ?? "",
19
+ valuemin: node.valuemin ?? "",
20
+ valuemax: node.valuemax ?? "",
21
+ autocomplete: node.autocomplete || "",
22
+ haspopup: node.haspopup || "",
23
+ invalid: node.invalid ? "true" : "",
24
+ modal: node.modal ? "true" : "",
25
+ multiline: node.multiline ? "true" : "",
26
+ multiselectable: node.multiselectable ? "true" : "",
27
+ orientation: node.orientation || "",
28
+ keyshortcuts: node.keyshortcuts || "",
29
+ roledescription: node.roledescription || "",
30
+ valuetext: node.valuetext || ""
31
+ };
32
+ result.push(entry);
33
+ }
34
+ if (node.children && Array.isArray(node.children)) {
35
+ for (const child of node.children) {
36
+ flattenAccessibilityTree(child, result);
37
+ }
38
+ }
39
+ return result;
40
+ }
41
+ async function getBrowserAccessibilityTree(browser) {
42
+ const puppeteer = await browser.getPuppeteer();
43
+ const pages = await puppeteer.pages();
44
+ if (pages.length === 0) {
45
+ return [];
46
+ }
47
+ const page = pages[0];
48
+ const snapshot = await page.accessibility.snapshot({
49
+ interestingOnly: true
50
+ });
51
+ if (!snapshot) {
52
+ return [];
53
+ }
54
+ return flattenAccessibilityTree(snapshot);
55
+ }
56
+
57
+ // src/scripts/get-interactable-browser-elements.ts
58
+ var elementsScript = (elementType = "interactable") => (function() {
59
+ const interactableSelectors = [
60
+ "a[href]",
61
+ // Links with href
62
+ "button",
63
+ // Buttons
64
+ 'input:not([type="hidden"])',
65
+ // Input fields (except hidden)
66
+ "select",
67
+ // Select dropdowns
68
+ "textarea",
69
+ // Text areas
70
+ '[role="button"]',
71
+ // Elements with button role
72
+ '[role="link"]',
73
+ // Elements with link role
74
+ '[role="checkbox"]',
75
+ // Elements with checkbox role
76
+ '[role="radio"]',
77
+ // Elements with radio role
78
+ '[role="tab"]',
79
+ // Elements with tab role
80
+ '[role="menuitem"]',
81
+ // Elements with menuitem role
82
+ '[role="combobox"]',
83
+ // Elements with combobox role
84
+ '[role="option"]',
85
+ // Elements with option role
86
+ '[role="switch"]',
87
+ // Elements with switch role
88
+ '[role="slider"]',
89
+ // Elements with slider role
90
+ '[role="textbox"]',
91
+ // Elements with textbox role
92
+ '[role="searchbox"]',
93
+ // Elements with searchbox role
94
+ '[contenteditable="true"]',
95
+ // Editable content
96
+ '[tabindex]:not([tabindex="-1"])'
97
+ // Elements with tabindex
98
+ ];
99
+ const visualSelectors = [
100
+ "img",
101
+ // Images
102
+ "picture",
103
+ // Picture elements
104
+ "svg",
105
+ // SVG graphics
106
+ "video",
107
+ // Video elements
108
+ "canvas",
109
+ // Canvas elements
110
+ '[style*="background-image"]'
111
+ // Elements with background images
112
+ ];
113
+ function isVisible(element) {
114
+ if (typeof element.checkVisibility === "function") {
115
+ return element.checkVisibility({
116
+ opacityProperty: true,
117
+ visibilityProperty: true,
118
+ contentVisibilityAuto: true
119
+ });
120
+ }
121
+ const style = window.getComputedStyle(element);
122
+ return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0" && element.offsetWidth > 0 && element.offsetHeight > 0;
123
+ }
124
+ function getCssSelector(element) {
125
+ if (element.id) {
126
+ return `#${CSS.escape(element.id)}`;
127
+ }
128
+ if (element.className && typeof element.className === "string") {
129
+ const classes = element.className.trim().split(/\s+/).filter(Boolean);
130
+ if (classes.length > 0) {
131
+ const classSelector = classes.slice(0, 2).map((c) => `.${CSS.escape(c)}`).join("");
132
+ const tagWithClass = `${element.tagName.toLowerCase()}${classSelector}`;
133
+ if (document.querySelectorAll(tagWithClass).length === 1) {
134
+ return tagWithClass;
135
+ }
136
+ }
137
+ }
138
+ let current = element;
139
+ const path = [];
140
+ while (current && current !== document.documentElement) {
141
+ let selector = current.tagName.toLowerCase();
142
+ if (current.id) {
143
+ selector = `#${CSS.escape(current.id)}`;
144
+ path.unshift(selector);
145
+ break;
146
+ }
147
+ const parent = current.parentElement;
148
+ if (parent) {
149
+ const siblings = Array.from(parent.children).filter(
150
+ (child) => child.tagName === current.tagName
151
+ );
152
+ if (siblings.length > 1) {
153
+ const index = siblings.indexOf(current) + 1;
154
+ selector += `:nth-child(${index})`;
155
+ }
156
+ }
157
+ path.unshift(selector);
158
+ current = current.parentElement;
159
+ if (path.length >= 4) {
160
+ break;
161
+ }
162
+ }
163
+ return path.join(" > ");
164
+ }
165
+ function getElements() {
166
+ const selectors = [];
167
+ if (elementType === "interactable" || elementType === "all") {
168
+ selectors.push(...interactableSelectors);
169
+ }
170
+ if (elementType === "visual" || elementType === "all") {
171
+ selectors.push(...visualSelectors);
172
+ }
173
+ const allElements = [];
174
+ selectors.forEach((selector) => {
175
+ const elements = document.querySelectorAll(selector);
176
+ elements.forEach((element) => {
177
+ if (!allElements.includes(element)) {
178
+ allElements.push(element);
179
+ }
180
+ });
181
+ });
182
+ const elementInfos = allElements.filter((element) => isVisible(element) && !element.disabled).map((element) => {
183
+ const el = element;
184
+ const inputEl = element;
185
+ const rect = el.getBoundingClientRect();
186
+ const isInViewport = rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
187
+ const info = {
188
+ tagName: el.tagName.toLowerCase(),
189
+ type: el.getAttribute("type") || "",
190
+ id: el.id || "",
191
+ className: (typeof el.className === "string" ? el.className : "") || "",
192
+ textContent: el.textContent?.trim() || "",
193
+ value: inputEl.value || "",
194
+ placeholder: inputEl.placeholder || "",
195
+ href: el.getAttribute("href") || "",
196
+ ariaLabel: el.getAttribute("aria-label") || "",
197
+ role: el.getAttribute("role") || "",
198
+ src: el.getAttribute("src") || "",
199
+ alt: el.getAttribute("alt") || "",
200
+ cssSelector: getCssSelector(el),
201
+ isInViewport
202
+ };
203
+ return info;
204
+ });
205
+ return elementInfos;
206
+ }
207
+ return getElements();
208
+ })();
209
+ async function getBrowserInteractableElements(browser, options = {}) {
210
+ const { elementType = "interactable" } = options;
211
+ return browser.execute(elementsScript, elementType);
212
+ }
213
+
214
+ // src/locators/constants.ts
215
+ var ANDROID_INTERACTABLE_TAGS = [
216
+ // Input elements
217
+ "android.widget.EditText",
218
+ "android.widget.AutoCompleteTextView",
219
+ "android.widget.MultiAutoCompleteTextView",
220
+ "android.widget.SearchView",
221
+ // Button-like elements
222
+ "android.widget.Button",
223
+ "android.widget.ImageButton",
224
+ "android.widget.ToggleButton",
225
+ "android.widget.CompoundButton",
226
+ "android.widget.RadioButton",
227
+ "android.widget.CheckBox",
228
+ "android.widget.Switch",
229
+ "android.widget.FloatingActionButton",
230
+ "com.google.android.material.button.MaterialButton",
231
+ "com.google.android.material.floatingactionbutton.FloatingActionButton",
232
+ // Text elements (often tappable)
233
+ "android.widget.TextView",
234
+ "android.widget.CheckedTextView",
235
+ // Image elements (often tappable)
236
+ "android.widget.ImageView",
237
+ "android.widget.QuickContactBadge",
238
+ // Selection elements
239
+ "android.widget.Spinner",
240
+ "android.widget.SeekBar",
241
+ "android.widget.RatingBar",
242
+ "android.widget.ProgressBar",
243
+ "android.widget.DatePicker",
244
+ "android.widget.TimePicker",
245
+ "android.widget.NumberPicker",
246
+ // List/grid items
247
+ "android.widget.AdapterView"
248
+ ];
249
+ var ANDROID_LAYOUT_CONTAINERS = [
250
+ // Core ViewGroup classes
251
+ "android.view.ViewGroup",
252
+ "android.view.View",
253
+ "android.widget.FrameLayout",
254
+ "android.widget.LinearLayout",
255
+ "android.widget.RelativeLayout",
256
+ "android.widget.GridLayout",
257
+ "android.widget.TableLayout",
258
+ "android.widget.TableRow",
259
+ "android.widget.AbsoluteLayout",
260
+ // AndroidX layout classes
261
+ "androidx.constraintlayout.widget.ConstraintLayout",
262
+ "androidx.coordinatorlayout.widget.CoordinatorLayout",
263
+ "androidx.appcompat.widget.LinearLayoutCompat",
264
+ "androidx.cardview.widget.CardView",
265
+ "androidx.appcompat.widget.ContentFrameLayout",
266
+ "androidx.appcompat.widget.FitWindowsFrameLayout",
267
+ // Scrolling containers
268
+ "android.widget.ScrollView",
269
+ "android.widget.HorizontalScrollView",
270
+ "android.widget.NestedScrollView",
271
+ "androidx.core.widget.NestedScrollView",
272
+ "androidx.recyclerview.widget.RecyclerView",
273
+ "android.widget.ListView",
274
+ "android.widget.GridView",
275
+ "android.widget.AbsListView",
276
+ // App chrome / system elements
277
+ "android.widget.ActionBarContainer",
278
+ "android.widget.ActionBarOverlayLayout",
279
+ "android.view.ViewStub",
280
+ "androidx.appcompat.widget.ActionBarContainer",
281
+ "androidx.appcompat.widget.ActionBarContextView",
282
+ "androidx.appcompat.widget.ActionBarOverlayLayout",
283
+ // Decor views
284
+ "com.android.internal.policy.DecorView",
285
+ "android.widget.DecorView"
286
+ ];
287
+ var IOS_INTERACTABLE_TAGS = [
288
+ // Input elements
289
+ "XCUIElementTypeTextField",
290
+ "XCUIElementTypeSecureTextField",
291
+ "XCUIElementTypeTextView",
292
+ "XCUIElementTypeSearchField",
293
+ // Button-like elements
294
+ "XCUIElementTypeButton",
295
+ "XCUIElementTypeLink",
296
+ // Text elements (often tappable)
297
+ "XCUIElementTypeStaticText",
298
+ // Image elements
299
+ "XCUIElementTypeImage",
300
+ "XCUIElementTypeIcon",
301
+ // Selection elements
302
+ "XCUIElementTypeSwitch",
303
+ "XCUIElementTypeSlider",
304
+ "XCUIElementTypeStepper",
305
+ "XCUIElementTypeSegmentedControl",
306
+ "XCUIElementTypePicker",
307
+ "XCUIElementTypePickerWheel",
308
+ "XCUIElementTypeDatePicker",
309
+ "XCUIElementTypePageIndicator",
310
+ // Table/list items
311
+ "XCUIElementTypeCell",
312
+ "XCUIElementTypeMenuItem",
313
+ "XCUIElementTypeMenuBarItem",
314
+ // Toggle elements
315
+ "XCUIElementTypeCheckBox",
316
+ "XCUIElementTypeRadioButton",
317
+ "XCUIElementTypeToggle",
318
+ // Other interactive
319
+ "XCUIElementTypeKey",
320
+ "XCUIElementTypeKeyboard",
321
+ "XCUIElementTypeAlert",
322
+ "XCUIElementTypeSheet"
323
+ ];
324
+ var IOS_LAYOUT_CONTAINERS = [
325
+ // Generic containers
326
+ "XCUIElementTypeOther",
327
+ "XCUIElementTypeGroup",
328
+ "XCUIElementTypeLayoutItem",
329
+ // Scroll containers
330
+ "XCUIElementTypeScrollView",
331
+ "XCUIElementTypeTable",
332
+ "XCUIElementTypeCollectionView",
333
+ "XCUIElementTypeScrollBar",
334
+ // Navigation chrome
335
+ "XCUIElementTypeNavigationBar",
336
+ "XCUIElementTypeTabBar",
337
+ "XCUIElementTypeToolbar",
338
+ "XCUIElementTypeStatusBar",
339
+ "XCUIElementTypeMenuBar",
340
+ // Windows and views
341
+ "XCUIElementTypeWindow",
342
+ "XCUIElementTypeSheet",
343
+ "XCUIElementTypeDrawer",
344
+ "XCUIElementTypeDialog",
345
+ "XCUIElementTypePopover",
346
+ "XCUIElementTypePopUpButton",
347
+ // Outline elements
348
+ "XCUIElementTypeOutline",
349
+ "XCUIElementTypeOutlineRow",
350
+ "XCUIElementTypeBrowser",
351
+ "XCUIElementTypeSplitGroup",
352
+ "XCUIElementTypeSplitter",
353
+ // Application root
354
+ "XCUIElementTypeApplication"
355
+ ];
356
+
357
+ // src/locators/xml-parsing.ts
358
+ import { DOMParser } from "@xmldom/xmldom";
359
+ import xpath from "xpath";
360
+ function childNodesOf(node) {
361
+ const children = [];
362
+ if (node.childNodes) {
363
+ for (let i = 0; i < node.childNodes.length; i++) {
364
+ const child = node.childNodes.item(i);
365
+ if (child?.nodeType === 1) {
366
+ children.push(child);
367
+ }
368
+ }
369
+ }
370
+ return children;
371
+ }
372
+ function translateRecursively(domNode, parentPath = "", index = null) {
373
+ const attributes = {};
374
+ const element = domNode;
375
+ if (element.attributes) {
376
+ for (let attrIdx = 0; attrIdx < element.attributes.length; attrIdx++) {
377
+ const attr = element.attributes.item(attrIdx);
378
+ if (attr) {
379
+ attributes[attr.name] = attr.value.replace(/(\n)/gm, "\\n");
380
+ }
381
+ }
382
+ }
383
+ const path = index === null ? "" : `${parentPath ? parentPath + "." : ""}${index}`;
384
+ return {
385
+ children: childNodesOf(domNode).map(
386
+ (childNode, childIndex) => translateRecursively(childNode, path, childIndex)
387
+ ),
388
+ tagName: domNode.nodeName,
389
+ attributes,
390
+ path
391
+ };
392
+ }
393
+ function isSameElement(node1, node2) {
394
+ if (node1.nodeType !== 1 || node2.nodeType !== 1) return false;
395
+ const el1 = node1;
396
+ const el2 = node2;
397
+ if (el1.nodeName !== el2.nodeName) return false;
398
+ const bounds1 = el1.getAttribute("bounds");
399
+ const bounds2 = el2.getAttribute("bounds");
400
+ if (bounds1 && bounds2) {
401
+ return bounds1 === bounds2;
402
+ }
403
+ const x1 = el1.getAttribute("x");
404
+ const y1 = el1.getAttribute("y");
405
+ const x2 = el2.getAttribute("x");
406
+ const y2 = el2.getAttribute("y");
407
+ if (x1 && y1 && x2 && y2) {
408
+ return x1 === x2 && y1 === y2 && el1.getAttribute("width") === el2.getAttribute("width") && el1.getAttribute("height") === el2.getAttribute("height");
409
+ }
410
+ return false;
411
+ }
412
+ function xmlToJSON(sourceXML) {
413
+ try {
414
+ const parser = new DOMParser();
415
+ const sourceDoc = parser.parseFromString(sourceXML, "text/xml");
416
+ const parseErrors = sourceDoc.getElementsByTagName("parsererror");
417
+ if (parseErrors.length > 0) {
418
+ console.error("[xmlToJSON] XML parsing error:", parseErrors[0].textContent);
419
+ return null;
420
+ }
421
+ const children = childNodesOf(sourceDoc);
422
+ const firstChild = children[0] || (sourceDoc.documentElement ? childNodesOf(sourceDoc.documentElement)[0] : null);
423
+ return firstChild ? translateRecursively(firstChild) : { children: [], tagName: "", attributes: {}, path: "" };
424
+ } catch (e) {
425
+ console.error("[xmlToJSON] Failed to parse XML:", e);
426
+ return null;
427
+ }
428
+ }
429
+ function xmlToDOM(sourceXML) {
430
+ try {
431
+ const parser = new DOMParser();
432
+ const doc = parser.parseFromString(sourceXML, "text/xml");
433
+ const parseErrors = doc.getElementsByTagName("parsererror");
434
+ if (parseErrors.length > 0) {
435
+ console.error("[xmlToDOM] XML parsing error:", parseErrors[0].textContent);
436
+ return null;
437
+ }
438
+ return doc;
439
+ } catch (e) {
440
+ console.error("[xmlToDOM] Failed to parse XML:", e);
441
+ return null;
442
+ }
443
+ }
444
+ function evaluateXPath(doc, xpathExpr) {
445
+ try {
446
+ const nodes = xpath.select(xpathExpr, doc);
447
+ if (Array.isArray(nodes)) {
448
+ return nodes;
449
+ }
450
+ return [];
451
+ } catch (e) {
452
+ console.error(`[evaluateXPath] Failed to evaluate "${xpathExpr}":`, e);
453
+ return [];
454
+ }
455
+ }
456
+ function checkXPathUniqueness(doc, xpathExpr, targetNode) {
457
+ try {
458
+ const nodes = evaluateXPath(doc, xpathExpr);
459
+ const totalMatches = nodes.length;
460
+ if (totalMatches === 0) {
461
+ return { isUnique: false };
462
+ }
463
+ if (totalMatches === 1) {
464
+ return { isUnique: true };
465
+ }
466
+ if (targetNode) {
467
+ for (let i = 0; i < nodes.length; i++) {
468
+ if (nodes[i].isSameNode(targetNode) || isSameElement(nodes[i], targetNode)) {
469
+ return {
470
+ isUnique: false,
471
+ index: i + 1,
472
+ // 1-based index for XPath
473
+ totalMatches
474
+ };
475
+ }
476
+ }
477
+ }
478
+ return { isUnique: false, totalMatches };
479
+ } catch (e) {
480
+ console.error(`[checkXPathUniqueness] Error checking "${xpathExpr}":`, e);
481
+ return { isUnique: false };
482
+ }
483
+ }
484
+ function findDOMNodeByPath(doc, path) {
485
+ if (!path) return doc.documentElement;
486
+ const indices = path.split(".").map(Number);
487
+ let current = doc.documentElement;
488
+ for (const index of indices) {
489
+ if (!current) return null;
490
+ const children = [];
491
+ if (current.childNodes) {
492
+ for (let i = 0; i < current.childNodes.length; i++) {
493
+ const child = current.childNodes.item(i);
494
+ if (child?.nodeType === 1) {
495
+ children.push(child);
496
+ }
497
+ }
498
+ }
499
+ current = children[index] || null;
500
+ }
501
+ return current;
502
+ }
503
+ function parseAndroidBounds(bounds) {
504
+ const match = bounds.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
505
+ if (!match) {
506
+ return { x: 0, y: 0, width: 0, height: 0 };
507
+ }
508
+ const x1 = parseInt(match[1], 10);
509
+ const y1 = parseInt(match[2], 10);
510
+ const x2 = parseInt(match[3], 10);
511
+ const y2 = parseInt(match[4], 10);
512
+ return {
513
+ x: x1,
514
+ y: y1,
515
+ width: x2 - x1,
516
+ height: y2 - y1
517
+ };
518
+ }
519
+ function parseIOSBounds(attributes) {
520
+ return {
521
+ x: parseInt(attributes.x || "0", 10),
522
+ y: parseInt(attributes.y || "0", 10),
523
+ width: parseInt(attributes.width || "0", 10),
524
+ height: parseInt(attributes.height || "0", 10)
525
+ };
526
+ }
527
+ function countAttributeOccurrences(sourceXML, attribute, value) {
528
+ const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
529
+ const pattern = new RegExp(`${attribute}=["']${escapedValue}["']`, "g");
530
+ const matches = sourceXML.match(pattern);
531
+ return matches ? matches.length : 0;
532
+ }
533
+ function isAttributeUnique(sourceXML, attribute, value) {
534
+ return countAttributeOccurrences(sourceXML, attribute, value) === 1;
535
+ }
536
+
537
+ // src/locators/element-filter.ts
538
+ function matchesTagList(tagName, tagList) {
539
+ if (tagList.includes(tagName)) {
540
+ return true;
541
+ }
542
+ for (const tag of tagList) {
543
+ if (tagName.endsWith(tag) || tagName.includes(tag)) {
544
+ return true;
545
+ }
546
+ }
547
+ return false;
548
+ }
549
+ function matchesTagFilters(element, includeTagNames, excludeTagNames) {
550
+ if (includeTagNames.length > 0 && !matchesTagList(element.tagName, includeTagNames)) {
551
+ return false;
552
+ }
553
+ if (matchesTagList(element.tagName, excludeTagNames)) {
554
+ return false;
555
+ }
556
+ return true;
557
+ }
558
+ function matchesAttributeFilters(element, requireAttributes, minAttributeCount) {
559
+ if (requireAttributes.length > 0) {
560
+ const hasRequiredAttr = requireAttributes.some((attr) => element.attributes?.[attr]);
561
+ if (!hasRequiredAttr) return false;
562
+ }
563
+ if (element.attributes && minAttributeCount > 0) {
564
+ const attrCount = Object.values(element.attributes).filter(
565
+ (v) => v !== void 0 && v !== null && v !== ""
566
+ ).length;
567
+ if (attrCount < minAttributeCount) {
568
+ return false;
569
+ }
570
+ }
571
+ return true;
572
+ }
573
+ function isInteractableElement(element, isNative, automationName) {
574
+ const isAndroid = automationName.toLowerCase().includes("uiautomator");
575
+ const interactableTags = isAndroid ? ANDROID_INTERACTABLE_TAGS : IOS_INTERACTABLE_TAGS;
576
+ if (matchesTagList(element.tagName, interactableTags)) {
577
+ return true;
578
+ }
579
+ if (isAndroid) {
580
+ if (element.attributes?.clickable === "true" || element.attributes?.focusable === "true" || element.attributes?.checkable === "true" || element.attributes?.["long-clickable"] === "true") {
581
+ return true;
582
+ }
583
+ }
584
+ if (!isAndroid) {
585
+ if (element.attributes?.accessible === "true") {
586
+ return true;
587
+ }
588
+ }
589
+ return false;
590
+ }
591
+ function isLayoutContainer(element, platform) {
592
+ const containerList = platform === "android" ? ANDROID_LAYOUT_CONTAINERS : IOS_LAYOUT_CONTAINERS;
593
+ return matchesTagList(element.tagName, containerList);
594
+ }
595
+ function hasMeaningfulContent(element, platform) {
596
+ const attrs = element.attributes;
597
+ if (attrs.text && attrs.text.trim() !== "" && attrs.text !== "null") {
598
+ return true;
599
+ }
600
+ if (platform === "android") {
601
+ if (attrs["content-desc"] && attrs["content-desc"].trim() !== "" && attrs["content-desc"] !== "null") {
602
+ return true;
603
+ }
604
+ } else {
605
+ if (attrs.label && attrs.label.trim() !== "" && attrs.label !== "null") {
606
+ return true;
607
+ }
608
+ if (attrs.name && attrs.name.trim() !== "" && attrs.name !== "null") {
609
+ return true;
610
+ }
611
+ }
612
+ return false;
613
+ }
614
+ function shouldIncludeElement(element, filters, isNative, automationName) {
615
+ const {
616
+ includeTagNames = [],
617
+ excludeTagNames = ["hierarchy"],
618
+ requireAttributes = [],
619
+ minAttributeCount = 0,
620
+ fetchableOnly = false,
621
+ clickableOnly = false,
622
+ visibleOnly = true
623
+ } = filters;
624
+ if (!matchesTagFilters(element, includeTagNames, excludeTagNames)) {
625
+ return false;
626
+ }
627
+ if (!matchesAttributeFilters(element, requireAttributes, minAttributeCount)) {
628
+ return false;
629
+ }
630
+ if (clickableOnly && element.attributes?.clickable !== "true") {
631
+ return false;
632
+ }
633
+ if (visibleOnly) {
634
+ const isAndroid = automationName.toLowerCase().includes("uiautomator");
635
+ if (isAndroid && element.attributes?.displayed === "false") {
636
+ return false;
637
+ }
638
+ if (!isAndroid && element.attributes?.visible === "false") {
639
+ return false;
640
+ }
641
+ }
642
+ if (fetchableOnly && !isInteractableElement(element, isNative, automationName)) {
643
+ return false;
644
+ }
645
+ return true;
646
+ }
647
+ function getDefaultFilters(platform, includeContainers = false) {
648
+ const layoutContainers = platform === "android" ? ANDROID_LAYOUT_CONTAINERS : IOS_LAYOUT_CONTAINERS;
649
+ return {
650
+ excludeTagNames: includeContainers ? ["hierarchy"] : ["hierarchy", ...layoutContainers],
651
+ fetchableOnly: !includeContainers,
652
+ visibleOnly: true,
653
+ clickableOnly: false
654
+ };
655
+ }
656
+
657
+ // src/locators/locator-generation.ts
658
+ function isValidValue(value) {
659
+ return value !== void 0 && value !== null && value !== "null" && value.trim() !== "";
660
+ }
661
+ function escapeText(text) {
662
+ return text.replace(/"/g, '\\"').replace(/\n/g, "\\n");
663
+ }
664
+ function escapeXPathValue(value) {
665
+ if (!value.includes("'")) {
666
+ return `'${value}'`;
667
+ }
668
+ if (!value.includes('"')) {
669
+ return `"${value}"`;
670
+ }
671
+ const parts = [];
672
+ let current = "";
673
+ for (const char of value) {
674
+ if (char === "'") {
675
+ if (current) parts.push(`'${current}'`);
676
+ parts.push(`"'"`);
677
+ current = "";
678
+ } else {
679
+ current += char;
680
+ }
681
+ }
682
+ if (current) parts.push(`'${current}'`);
683
+ return `concat(${parts.join(",")})`;
684
+ }
685
+ function generateIndexedXPath(baseXPath, index) {
686
+ return `(${baseXPath})[${index}]`;
687
+ }
688
+ function generateIndexedUiAutomator(baseSelector, index) {
689
+ return `${baseSelector}.instance(${index - 1})`;
690
+ }
691
+ function checkUniqueness(ctx, xpath2, targetNode) {
692
+ if (ctx.parsedDOM) {
693
+ return checkXPathUniqueness(ctx.parsedDOM, xpath2, targetNode);
694
+ }
695
+ const match = xpath2.match(/\/\/\*\[@([^=]+)="([^"]+)"\]/);
696
+ if (match) {
697
+ const [, attr, value] = match;
698
+ return { isUnique: isAttributeUnique(ctx.sourceXML, attr, value) };
699
+ }
700
+ return { isUnique: false };
701
+ }
702
+ function getSiblingIndex(element) {
703
+ const parent = element.parentNode;
704
+ if (!parent) return 1;
705
+ const tagName = element.nodeName;
706
+ let index = 0;
707
+ for (let i = 0; i < parent.childNodes.length; i++) {
708
+ const child = parent.childNodes.item(i);
709
+ if (child?.nodeType === 1 && child.nodeName === tagName) {
710
+ index++;
711
+ if (child === element) return index;
712
+ }
713
+ }
714
+ return 1;
715
+ }
716
+ function countSiblings(element) {
717
+ const parent = element.parentNode;
718
+ if (!parent) return 1;
719
+ const tagName = element.nodeName;
720
+ let count = 0;
721
+ for (let i = 0; i < parent.childNodes.length; i++) {
722
+ const child = parent.childNodes.item(i);
723
+ if (child?.nodeType === 1 && child.nodeName === tagName) {
724
+ count++;
725
+ }
726
+ }
727
+ return count;
728
+ }
729
+ function findUniqueAttribute(element, ctx) {
730
+ const attrs = ctx.isAndroid ? ["resource-id", "content-desc", "text"] : ["name", "label", "value"];
731
+ for (const attr of attrs) {
732
+ const value = element.getAttribute(attr);
733
+ if (value && value.trim()) {
734
+ const xpath2 = `//*[@${attr}=${escapeXPathValue(value)}]`;
735
+ const result = ctx.parsedDOM ? checkXPathUniqueness(ctx.parsedDOM, xpath2) : { isUnique: isAttributeUnique(ctx.sourceXML, attr, value) };
736
+ if (result.isUnique) {
737
+ return `@${attr}=${escapeXPathValue(value)}`;
738
+ }
739
+ }
740
+ }
741
+ return null;
742
+ }
743
+ function buildHierarchicalXPath(ctx, element, maxDepth = 3) {
744
+ if (!ctx.parsedDOM) return null;
745
+ const pathParts = [];
746
+ let current = element;
747
+ let depth = 0;
748
+ while (current && depth < maxDepth) {
749
+ const tagName = current.nodeName;
750
+ const uniqueAttr = findUniqueAttribute(current, ctx);
751
+ if (uniqueAttr) {
752
+ pathParts.unshift(`//${tagName}[${uniqueAttr}]`);
753
+ break;
754
+ } else {
755
+ const siblingIndex = getSiblingIndex(current);
756
+ const siblingCount = countSiblings(current);
757
+ if (siblingCount > 1) {
758
+ pathParts.unshift(`${tagName}[${siblingIndex}]`);
759
+ } else {
760
+ pathParts.unshift(tagName);
761
+ }
762
+ }
763
+ const parent = current.parentNode;
764
+ current = parent && parent.nodeType === 1 ? parent : null;
765
+ depth++;
766
+ }
767
+ if (pathParts.length === 0) return null;
768
+ let result = pathParts[0];
769
+ for (let i = 1; i < pathParts.length; i++) {
770
+ result += "/" + pathParts[i];
771
+ }
772
+ if (!result.startsWith("//")) {
773
+ result = "//" + result;
774
+ }
775
+ return result;
776
+ }
777
+ function addXPathLocator(results, xpath2, ctx, targetNode) {
778
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
779
+ if (uniqueness.isUnique) {
780
+ results.push(["xpath", xpath2]);
781
+ } else if (uniqueness.index) {
782
+ results.push(["xpath", generateIndexedXPath(xpath2, uniqueness.index)]);
783
+ } else {
784
+ if (targetNode && ctx.parsedDOM) {
785
+ const hierarchical = buildHierarchicalXPath(ctx, targetNode);
786
+ if (hierarchical) {
787
+ results.push(["xpath", hierarchical]);
788
+ }
789
+ }
790
+ results.push(["xpath", xpath2]);
791
+ }
792
+ }
793
+ function isInUiAutomatorScope(element, doc) {
794
+ if (!doc) return true;
795
+ const hierarchyNodes = evaluateXPath(doc, "/hierarchy/*");
796
+ if (hierarchyNodes.length === 0) return true;
797
+ const lastIndex = hierarchyNodes.length;
798
+ const pathParts = element.path.split(".");
799
+ if (pathParts.length === 0 || pathParts[0] === "") return true;
800
+ const firstIndex = parseInt(pathParts[0], 10);
801
+ return firstIndex === lastIndex - 1;
802
+ }
803
+ function buildUiAutomatorSelector(element) {
804
+ const attrs = element.attributes;
805
+ const parts = [];
806
+ if (isValidValue(attrs["resource-id"])) {
807
+ parts.push(`resourceId("${attrs["resource-id"]}")`);
808
+ }
809
+ if (isValidValue(attrs.text) && attrs.text.length < 100) {
810
+ parts.push(`text("${escapeText(attrs.text)}")`);
811
+ }
812
+ if (isValidValue(attrs["content-desc"])) {
813
+ parts.push(`description("${attrs["content-desc"]}")`);
814
+ }
815
+ if (isValidValue(attrs.class)) {
816
+ parts.push(`className("${attrs.class}")`);
817
+ }
818
+ if (parts.length === 0) return null;
819
+ return `android=new UiSelector().${parts.join(".")}`;
820
+ }
821
+ function buildPredicateString(element) {
822
+ const attrs = element.attributes;
823
+ const conditions = [];
824
+ if (isValidValue(attrs.name)) {
825
+ conditions.push(`name == "${escapeText(attrs.name)}"`);
826
+ }
827
+ if (isValidValue(attrs.label)) {
828
+ conditions.push(`label == "${escapeText(attrs.label)}"`);
829
+ }
830
+ if (isValidValue(attrs.value)) {
831
+ conditions.push(`value == "${escapeText(attrs.value)}"`);
832
+ }
833
+ if (attrs.visible === "true") {
834
+ conditions.push("visible == 1");
835
+ }
836
+ if (attrs.enabled === "true") {
837
+ conditions.push("enabled == 1");
838
+ }
839
+ if (conditions.length === 0) return null;
840
+ return `-ios predicate string:${conditions.join(" AND ")}`;
841
+ }
842
+ function buildClassChain(element) {
843
+ const attrs = element.attributes;
844
+ const tagName = element.tagName;
845
+ if (!tagName.startsWith("XCUI")) return null;
846
+ let selector = `**/${tagName}`;
847
+ if (isValidValue(attrs.label)) {
848
+ selector += `[\`label == "${escapeText(attrs.label)}"\`]`;
849
+ } else if (isValidValue(attrs.name)) {
850
+ selector += `[\`name == "${escapeText(attrs.name)}"\`]`;
851
+ }
852
+ return `-ios class chain:${selector}`;
853
+ }
854
+ function buildXPath(element, sourceXML, isAndroid) {
855
+ const attrs = element.attributes;
856
+ const tagName = element.tagName;
857
+ const conditions = [];
858
+ if (isAndroid) {
859
+ if (isValidValue(attrs["resource-id"])) {
860
+ conditions.push(`@resource-id="${attrs["resource-id"]}"`);
861
+ }
862
+ if (isValidValue(attrs["content-desc"])) {
863
+ conditions.push(`@content-desc="${attrs["content-desc"]}"`);
864
+ }
865
+ if (isValidValue(attrs.text) && attrs.text.length < 100) {
866
+ conditions.push(`@text="${escapeText(attrs.text)}"`);
867
+ }
868
+ } else {
869
+ if (isValidValue(attrs.name)) {
870
+ conditions.push(`@name="${attrs.name}"`);
871
+ }
872
+ if (isValidValue(attrs.label)) {
873
+ conditions.push(`@label="${attrs.label}"`);
874
+ }
875
+ if (isValidValue(attrs.value)) {
876
+ conditions.push(`@value="${attrs.value}"`);
877
+ }
878
+ }
879
+ if (conditions.length === 0) {
880
+ return `//${tagName}`;
881
+ }
882
+ return `//${tagName}[${conditions.join(" and ")}]`;
883
+ }
884
+ function getSimpleSuggestedLocators(element, ctx, automationName, targetNode) {
885
+ const results = [];
886
+ const isAndroid = automationName.toLowerCase().includes("uiautomator");
887
+ const attrs = element.attributes;
888
+ const inUiAutomatorScope = isAndroid ? isInUiAutomatorScope(element, ctx.parsedDOM) : true;
889
+ if (isAndroid) {
890
+ const resourceId = attrs["resource-id"];
891
+ if (isValidValue(resourceId)) {
892
+ const xpath2 = `//*[@resource-id="${resourceId}"]`;
893
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
894
+ if (uniqueness.isUnique && inUiAutomatorScope) {
895
+ results.push(["id", `android=new UiSelector().resourceId("${resourceId}")`]);
896
+ } else if (uniqueness.index && inUiAutomatorScope) {
897
+ const base = `android=new UiSelector().resourceId("${resourceId}")`;
898
+ results.push(["id", generateIndexedUiAutomator(base, uniqueness.index)]);
899
+ }
900
+ }
901
+ const contentDesc = attrs["content-desc"];
902
+ if (isValidValue(contentDesc)) {
903
+ const xpath2 = `//*[@content-desc="${contentDesc}"]`;
904
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
905
+ if (uniqueness.isUnique) {
906
+ results.push(["accessibility-id", `~${contentDesc}`]);
907
+ }
908
+ }
909
+ const text = attrs.text;
910
+ if (isValidValue(text) && text.length < 100) {
911
+ const xpath2 = `//*[@text="${escapeText(text)}"]`;
912
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
913
+ if (uniqueness.isUnique && inUiAutomatorScope) {
914
+ results.push(["text", `android=new UiSelector().text("${escapeText(text)}")`]);
915
+ } else if (uniqueness.index && inUiAutomatorScope) {
916
+ const base = `android=new UiSelector().text("${escapeText(text)}")`;
917
+ results.push(["text", generateIndexedUiAutomator(base, uniqueness.index)]);
918
+ }
919
+ }
920
+ } else {
921
+ const name = attrs.name;
922
+ if (isValidValue(name)) {
923
+ const xpath2 = `//*[@name="${name}"]`;
924
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
925
+ if (uniqueness.isUnique) {
926
+ results.push(["accessibility-id", `~${name}`]);
927
+ }
928
+ }
929
+ const label = attrs.label;
930
+ if (isValidValue(label) && label !== attrs.name) {
931
+ const xpath2 = `//*[@label="${escapeText(label)}"]`;
932
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
933
+ if (uniqueness.isUnique) {
934
+ results.push(["predicate-string", `-ios predicate string:label == "${escapeText(label)}"`]);
935
+ }
936
+ }
937
+ const value = attrs.value;
938
+ if (isValidValue(value)) {
939
+ const xpath2 = `//*[@value="${escapeText(value)}"]`;
940
+ const uniqueness = checkUniqueness(ctx, xpath2, targetNode);
941
+ if (uniqueness.isUnique) {
942
+ results.push(["predicate-string", `-ios predicate string:value == "${escapeText(value)}"`]);
943
+ }
944
+ }
945
+ }
946
+ return results;
947
+ }
948
+ function getComplexSuggestedLocators(element, ctx, automationName, targetNode) {
949
+ const results = [];
950
+ const isAndroid = automationName.toLowerCase().includes("uiautomator");
951
+ const inUiAutomatorScope = isAndroid ? isInUiAutomatorScope(element, ctx.parsedDOM) : true;
952
+ if (isAndroid) {
953
+ if (inUiAutomatorScope) {
954
+ const uiAutomator = buildUiAutomatorSelector(element);
955
+ if (uiAutomator) {
956
+ results.push(["uiautomator", uiAutomator]);
957
+ }
958
+ }
959
+ const xpath2 = buildXPath(element, ctx.sourceXML, true);
960
+ if (xpath2) {
961
+ addXPathLocator(results, xpath2, ctx, targetNode);
962
+ }
963
+ if (inUiAutomatorScope && isValidValue(element.attributes.class)) {
964
+ results.push([
965
+ "class-name",
966
+ `android=new UiSelector().className("${element.attributes.class}")`
967
+ ]);
968
+ }
969
+ } else {
970
+ const predicate = buildPredicateString(element);
971
+ if (predicate) {
972
+ results.push(["predicate-string", predicate]);
973
+ }
974
+ const classChain = buildClassChain(element);
975
+ if (classChain) {
976
+ results.push(["class-chain", classChain]);
977
+ }
978
+ const xpath2 = buildXPath(element, ctx.sourceXML, false);
979
+ if (xpath2) {
980
+ addXPathLocator(results, xpath2, ctx, targetNode);
981
+ }
982
+ const type = element.tagName;
983
+ if (type.startsWith("XCUIElementType")) {
984
+ results.push(["class-name", `-ios class chain:**/${type}`]);
985
+ }
986
+ }
987
+ return results;
988
+ }
989
+ function getSuggestedLocators(element, sourceXML, automationName, ctx, targetNode) {
990
+ const locatorCtx = ctx ?? {
991
+ sourceXML,
992
+ parsedDOM: null,
993
+ isAndroid: automationName.toLowerCase().includes("uiautomator")
994
+ };
995
+ const simpleLocators = getSimpleSuggestedLocators(element, locatorCtx, automationName, targetNode);
996
+ const complexLocators = getComplexSuggestedLocators(element, locatorCtx, automationName, targetNode);
997
+ const seen = /* @__PURE__ */ new Set();
998
+ const results = [];
999
+ for (const locator of [...simpleLocators, ...complexLocators]) {
1000
+ if (!seen.has(locator[1])) {
1001
+ seen.add(locator[1]);
1002
+ results.push(locator);
1003
+ }
1004
+ }
1005
+ return results;
1006
+ }
1007
+ function locatorsToObject(locators) {
1008
+ const result = {};
1009
+ for (const [strategy, value] of locators) {
1010
+ if (!result[strategy]) {
1011
+ result[strategy] = value;
1012
+ }
1013
+ }
1014
+ return result;
1015
+ }
1016
+
1017
+ // src/locators/index.ts
1018
+ function parseBounds(element, platform) {
1019
+ return platform === "android" ? parseAndroidBounds(element.attributes.bounds || "") : parseIOSBounds(element.attributes);
1020
+ }
1021
+ function isWithinViewport(bounds, viewport) {
1022
+ return bounds.x >= 0 && bounds.y >= 0 && bounds.width > 0 && bounds.height > 0 && bounds.x + bounds.width <= viewport.width && bounds.y + bounds.height <= viewport.height;
1023
+ }
1024
+ function transformElement(element, locators, ctx) {
1025
+ const attrs = element.attributes;
1026
+ const bounds = parseBounds(element, ctx.platform);
1027
+ return {
1028
+ tagName: element.tagName,
1029
+ locators: locatorsToObject(locators),
1030
+ text: attrs.text || attrs.label || "",
1031
+ contentDesc: attrs["content-desc"] || "",
1032
+ resourceId: attrs["resource-id"] || "",
1033
+ accessibilityId: attrs.name || attrs["content-desc"] || "",
1034
+ label: attrs.label || "",
1035
+ value: attrs.value || "",
1036
+ className: attrs.class || element.tagName,
1037
+ clickable: attrs.clickable === "true" || attrs.accessible === "true" || attrs["long-clickable"] === "true",
1038
+ enabled: attrs.enabled !== "false",
1039
+ displayed: ctx.platform === "android" ? attrs.displayed !== "false" : attrs.visible !== "false",
1040
+ bounds,
1041
+ isInViewport: isWithinViewport(bounds, ctx.viewportSize)
1042
+ };
1043
+ }
1044
+ function shouldProcess(element, ctx) {
1045
+ if (shouldIncludeElement(element, ctx.filters, ctx.isNative, ctx.automationName)) {
1046
+ return true;
1047
+ }
1048
+ return isLayoutContainer(element, ctx.platform) && hasMeaningfulContent(element, ctx.platform);
1049
+ }
1050
+ function processElement(element, ctx) {
1051
+ if (!shouldProcess(element, ctx)) return;
1052
+ try {
1053
+ const targetNode = ctx.parsedDOM ? findDOMNodeByPath(ctx.parsedDOM, element.path) : void 0;
1054
+ const locators = getSuggestedLocators(
1055
+ element,
1056
+ ctx.sourceXML,
1057
+ ctx.automationName,
1058
+ { sourceXML: ctx.sourceXML, parsedDOM: ctx.parsedDOM, isAndroid: ctx.platform === "android" },
1059
+ targetNode || void 0
1060
+ );
1061
+ if (locators.length === 0) return;
1062
+ const transformed = transformElement(element, locators, ctx);
1063
+ if (Object.keys(transformed.locators).length === 0) return;
1064
+ ctx.results.push(transformed);
1065
+ } catch (error) {
1066
+ console.error(`[processElement] Error at path ${element.path}:`, error);
1067
+ }
1068
+ }
1069
+ function traverseTree(element, ctx) {
1070
+ if (!element) return;
1071
+ processElement(element, ctx);
1072
+ for (const child of element.children || []) {
1073
+ traverseTree(child, ctx);
1074
+ }
1075
+ }
1076
+ function generateAllElementLocators(sourceXML, options) {
1077
+ const sourceJSON = xmlToJSON(sourceXML);
1078
+ if (!sourceJSON) {
1079
+ console.error("[generateAllElementLocators] Failed to parse page source XML");
1080
+ return [];
1081
+ }
1082
+ const parsedDOM = xmlToDOM(sourceXML);
1083
+ const ctx = {
1084
+ sourceXML,
1085
+ platform: options.platform,
1086
+ automationName: options.platform === "android" ? "uiautomator2" : "xcuitest",
1087
+ isNative: options.isNative ?? true,
1088
+ viewportSize: options.viewportSize ?? { width: 9999, height: 9999 },
1089
+ filters: options.filters ?? {},
1090
+ results: [],
1091
+ parsedDOM
1092
+ };
1093
+ traverseTree(sourceJSON, ctx);
1094
+ return ctx.results;
1095
+ }
1096
+
1097
+ // src/scripts/get-visible-mobile-elements.ts
1098
+ var LOCATOR_PRIORITY = [
1099
+ "accessibility-id",
1100
+ // Most stable, cross-platform
1101
+ "id",
1102
+ // Android resource-id
1103
+ "text",
1104
+ // Text-based (can be fragile but readable)
1105
+ "predicate-string",
1106
+ // iOS predicate
1107
+ "class-chain",
1108
+ // iOS class chain
1109
+ "uiautomator",
1110
+ // Android UiAutomator compound
1111
+ "xpath"
1112
+ // XPath (last resort, brittle)
1113
+ // 'class-name' intentionally excluded - too generic
1114
+ ];
1115
+ function selectBestLocators(locators) {
1116
+ const selected = [];
1117
+ for (const strategy of LOCATOR_PRIORITY) {
1118
+ if (locators[strategy]) {
1119
+ selected.push(locators[strategy]);
1120
+ break;
1121
+ }
1122
+ }
1123
+ for (const strategy of LOCATOR_PRIORITY) {
1124
+ if (locators[strategy] && !selected.includes(locators[strategy])) {
1125
+ selected.push(locators[strategy]);
1126
+ break;
1127
+ }
1128
+ }
1129
+ return selected;
1130
+ }
1131
+ function toMobileElementInfo(element, includeBounds) {
1132
+ const selectedLocators = selectBestLocators(element.locators);
1133
+ const accessId = element.accessibilityId || element.contentDesc;
1134
+ const info = {
1135
+ selector: selectedLocators[0] || "",
1136
+ tagName: element.tagName,
1137
+ isInViewport: element.isInViewport,
1138
+ text: element.text || "",
1139
+ resourceId: element.resourceId || "",
1140
+ accessibilityId: accessId || "",
1141
+ isEnabled: element.enabled !== false,
1142
+ altSelector: selectedLocators[1] || ""
1143
+ // Single alternative (flattened for tabular)
1144
+ };
1145
+ if (includeBounds) {
1146
+ info.bounds = element.bounds;
1147
+ }
1148
+ return info;
1149
+ }
1150
+ async function getViewportSize(browser) {
1151
+ try {
1152
+ const size = await browser.getWindowSize();
1153
+ return { width: size.width, height: size.height };
1154
+ } catch {
1155
+ return { width: 9999, height: 9999 };
1156
+ }
1157
+ }
1158
+ async function getMobileVisibleElements(browser, platform, options = {}) {
1159
+ const { includeContainers = false, includeBounds = false, filterOptions } = options;
1160
+ const viewportSize = await getViewportSize(browser);
1161
+ const pageSource = await browser.getPageSource();
1162
+ const filters = {
1163
+ ...getDefaultFilters(platform, includeContainers),
1164
+ ...filterOptions
1165
+ };
1166
+ const elements = generateAllElementLocators(pageSource, {
1167
+ platform,
1168
+ viewportSize,
1169
+ filters
1170
+ });
1171
+ return elements.map((el) => toMobileElementInfo(el, includeBounds));
1172
+ }
1173
+ export {
1174
+ getBrowserAccessibilityTree,
1175
+ getBrowserInteractableElements,
1176
+ getMobileVisibleElements
1177
+ };
1178
+ //# sourceMappingURL=snapshot.js.map