@nodebug/browser-element-finder 1.0.8 → 1.1.6
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 +215 -111
- package/index.js +452 -205
- package/index.min.js +1 -0
- package/package.json +10 -4
- package/src/element-definitions.json +4 -1
- package/src/searchable-attributes.json +0 -1
package/README.md
CHANGED
|
@@ -9,26 +9,35 @@
|
|
|
9
9
|
Inject the library and find elements in any browser context (Selenium, Playwright, Puppeteer, or browser console):
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
|
-
// Find all
|
|
13
|
-
const results = ElementFinder.
|
|
12
|
+
// Find all buttons
|
|
13
|
+
const results = ElementFinder.findElements('button')
|
|
14
14
|
|
|
15
15
|
// Find a button by text (substring match)
|
|
16
|
-
const results = ElementFinder.
|
|
16
|
+
const results = ElementFinder.findElements('button', 'Submit')
|
|
17
17
|
|
|
18
18
|
// Find by text only (any type)
|
|
19
|
-
const results = ElementFinder.
|
|
19
|
+
const results = ElementFinder.findElements(null, 'seleniumbase')
|
|
20
20
|
|
|
21
21
|
// Find in all frames (default)
|
|
22
|
-
const results = ElementFinder.
|
|
22
|
+
const results = ElementFinder.findElements('button')
|
|
23
|
+
|
|
24
|
+
// Find with fallback to nearby elements
|
|
25
|
+
const results = ElementFinder.findProbableElements('button', 'Click Me')
|
|
26
|
+
// Returns button even if "Click Me" is in a nearby label
|
|
23
27
|
|
|
24
28
|
// Highlight found elements
|
|
25
29
|
ElementFinder.highlight(results.elements.map((e) => e.element))
|
|
26
30
|
|
|
27
31
|
// Remove highlight
|
|
28
32
|
ElementFinder.unhighlight(results.elements.map((e) => e.element))
|
|
33
|
+
|
|
34
|
+
// Check element properties
|
|
35
|
+
results.elements.forEach((e) => {
|
|
36
|
+
console.log('Tag:', e.tagName, 'Frame:', e.frameIndex)
|
|
37
|
+
})
|
|
29
38
|
```
|
|
30
39
|
|
|
31
|
-
**Agent/Automation Best Practices
|
|
40
|
+
**Agent/Automation Best Practices**:
|
|
32
41
|
|
|
33
42
|
- Always check `frameIndex`: `-1` = main frame, `0+` = iframe (see below for iframe handling)
|
|
34
43
|
- For iframe results, switch context before interacting (see Selenium/Playwright docs)
|
|
@@ -43,10 +52,20 @@ ElementFinder.unhighlight(results.elements.map((e) => e.element))
|
|
|
43
52
|
- **Text content search**: Search within element text, attributes, and placeholders
|
|
44
53
|
- **Shadow DOM support**: Automatically traverses shadow roots to find nested elements
|
|
45
54
|
- **Iframe support**: Automatically searches all frames (main document + iframes) by default
|
|
46
|
-
- **Visibility
|
|
55
|
+
- **Visibility detection**: All elements returned with `isVisible` property (`true`/`false`)
|
|
47
56
|
- **Bounding box data**: Returns position and dimensions for each found element
|
|
48
57
|
- **XPath-like type definitions**: Extensible element type matching using XPath-like expressions
|
|
49
|
-
- **Optimized performance**: O(n) innermost element filtering and efficient Set-based lookups
|
|
58
|
+
- **Optimized performance**: Pre-compiled type matchers, O(n) innermost element filtering, and efficient Set-based lookups
|
|
59
|
+
|
|
60
|
+
## Performance Optimizations
|
|
61
|
+
|
|
62
|
+
The library includes several performance improvements:
|
|
63
|
+
|
|
64
|
+
- **Pre-compiled type matchers**: Type definitions are compiled into cached matcher functions at module load time, avoiding XPath re-parsing for every element
|
|
65
|
+
- **O(n) innermost element filtering**: Set-based lookups instead of nested loops
|
|
66
|
+
- **Map-based column expansion**: O(1) element-to-column-position lookups for table cells
|
|
67
|
+
- **Optimized text content matching**: Direct text node iteration instead of expensive textContent calls
|
|
68
|
+
- **Loop optimizations**: Traditional for-loops with cached array lengths for hot paths
|
|
50
69
|
|
|
51
70
|
## Installation
|
|
52
71
|
|
|
@@ -58,10 +77,7 @@ npm install @nodebug/browser-element-finder
|
|
|
58
77
|
|
|
59
78
|
```
|
|
60
79
|
browser-element-finder/
|
|
61
|
-
├── index.js # Browser-injected library (generated)
|
|
62
|
-
├── build.js # Build script to generate index.js
|
|
63
80
|
├── src/
|
|
64
|
-
│ ├── element-finder.js # Canonical source (ES module)
|
|
65
81
|
│ ├── element-definitions.json # XPath-like type definitions
|
|
66
82
|
│ └── searchable-attributes.json # Attributes searched for text matching
|
|
67
83
|
├── tests/
|
|
@@ -75,14 +91,16 @@ browser-element-finder/
|
|
|
75
91
|
### In Browser Console or Automation Script
|
|
76
92
|
|
|
77
93
|
```js
|
|
78
|
-
// Find all visible
|
|
94
|
+
// Find all elements (visible and hidden)
|
|
79
95
|
const results = ElementFinder.findElement('button')
|
|
80
96
|
// Find by text
|
|
81
97
|
const results = ElementFinder.findElement('button', 'Submit')
|
|
82
98
|
// Find by text only
|
|
83
99
|
const results = ElementFinder.findElement(null, 'seleniumbase')
|
|
84
|
-
//
|
|
85
|
-
|
|
100
|
+
// Check visibility of found elements
|
|
101
|
+
results.elements.forEach((e) => {
|
|
102
|
+
console.log('Visible:', e.isVisible)
|
|
103
|
+
})
|
|
86
104
|
```
|
|
87
105
|
|
|
88
106
|
## Working with Iframes (Agent Pattern)
|
|
@@ -92,7 +110,7 @@ The library automatically searches all frames (main + iframes). For agent/automa
|
|
|
92
110
|
- **Main frame**: `item.frameIndex === -1` and `item.element` is available for direct interaction.
|
|
93
111
|
- **Iframe**: `item.frameIndex >= 0` and `item.element` is `undefined`. Use `frameIndex` to switch context, then re-run `findElement` inside the iframe to get interactable elements.
|
|
94
112
|
|
|
95
|
-
**Example
|
|
113
|
+
**Example**:
|
|
96
114
|
|
|
97
115
|
```js
|
|
98
116
|
const results = ElementFinder.findElement('button')
|
|
@@ -107,40 +125,6 @@ for (const item of results.elements) {
|
|
|
107
125
|
}
|
|
108
126
|
```
|
|
109
127
|
|
|
110
|
-
### ESM Import
|
|
111
|
-
|
|
112
|
-
```js
|
|
113
|
-
import {
|
|
114
|
-
findElement,
|
|
115
|
-
highlight,
|
|
116
|
-
unhighlight,
|
|
117
|
-
getValidTypes,
|
|
118
|
-
} from '@nodebug/browser-element-finder/src/element-finder.js'
|
|
119
|
-
|
|
120
|
-
// Find elements (requires DOM environment)
|
|
121
|
-
const results = findElement('button', 'Submit')
|
|
122
|
-
|
|
123
|
-
// Access metadata from results
|
|
124
|
-
results.elements.forEach((item) => {
|
|
125
|
-
console.log('Tag:', item.tagName)
|
|
126
|
-
console.log('Position:', item.boundingBox.x, item.boundingBox.y)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
// Highlight elements (extract DOM elements from wrapper objects)
|
|
130
|
-
highlight(results.elements.map((e) => e.element))
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### CommonJS Import
|
|
134
|
-
|
|
135
|
-
```js
|
|
136
|
-
const {
|
|
137
|
-
findElement,
|
|
138
|
-
highlight,
|
|
139
|
-
unhighlight,
|
|
140
|
-
getValidTypes,
|
|
141
|
-
} = require('@nodebug/browser-element-finder/src/element-finder.js')
|
|
142
|
-
```
|
|
143
|
-
|
|
144
128
|
### Accessing Element Definitions and Searchable Attributes
|
|
145
129
|
|
|
146
130
|
The package exports JSON files containing element type definitions and searchable attributes:
|
|
@@ -167,42 +151,101 @@ const SEARCHABLE_ATTRIBUTES = require('@nodebug/browser-element-finder/searchabl
|
|
|
167
151
|
|
|
168
152
|
## API Summary
|
|
169
153
|
|
|
170
|
-
| Function
|
|
171
|
-
|
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
154
|
+
| Function | Description |
|
|
155
|
+
| ------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
156
|
+
| `findElements(type, text, exact, parent)` | Find elements by type/text, returns `{ elements: [...] }` |
|
|
157
|
+
| `findElementByType(type, parent)` | Find elements by type only, returns `{ elements: [...] }` |
|
|
158
|
+
| `findElementByAttributes(value, exact, parent)` | Find elements by text/attribute, returns `{ elements: [...] }` |
|
|
159
|
+
| `findProbableElements(type, text, exact, parent)` | Find elements with fallback to nearby elements, returns `{ elements: [...] }` |
|
|
160
|
+
| `highlight(elements, color, width)` | Highlight elements with outline |
|
|
161
|
+
| `unhighlight(elements)` | Remove highlight |
|
|
162
|
+
| `getValidTypes()` | List all supported element types |
|
|
163
|
+
| `getBoundingBox(element)` | Get bounding box for an element |
|
|
164
|
+
| `setSearchableAttributes(attributes)` | Set custom attributes for text search |
|
|
165
|
+
| `getSearchableAttributes()` | Get current searchable attributes |
|
|
166
|
+
| `matchesType(el, type)` | Check if element matches a type |
|
|
167
|
+
| `matchesAttribute(el, value, exact)` | Check if element matches text/attribute |
|
|
168
|
+
| `getAllElements(root)` | Get all elements (with shadow DOM) |
|
|
169
|
+
| `getAllFrames(root)` | Get all frames (main + iframes) |
|
|
170
|
+
| `parseXPath(expr, el, depth)` | Parse XPath-like type expressions |
|
|
171
|
+
| `splitByOperator(expr, op)` | Split XPath by operator |
|
|
185
172
|
|
|
186
173
|
---
|
|
187
174
|
|
|
188
|
-
### `
|
|
175
|
+
### `findElements(type, text, exact, parent)`
|
|
189
176
|
|
|
190
|
-
Finds elements matching the specified
|
|
177
|
+
Finds elements matching the specified type and/or text. Combines type and attribute matching in a single call. Searches all frames (main document + iframes) by default.
|
|
191
178
|
|
|
192
|
-
| Parameter
|
|
193
|
-
|
|
|
194
|
-
| `type`
|
|
195
|
-
| `text`
|
|
196
|
-
| `exact`
|
|
197
|
-
| `
|
|
198
|
-
| `parent` | `Element` | `null` | Parent element to search within |
|
|
179
|
+
| Parameter | Type | Default | Description |
|
|
180
|
+
| --------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
181
|
+
| `type` | `string` | `null` | Element type (see supported types below). If `null`, matches any type. Throws `TypeError` for non-string values. |
|
|
182
|
+
| `text` | `string` | `null` | Text to search for in content/attributes. If `null`/`''`/`undefined`, matches any text. |
|
|
183
|
+
| `exact` | `boolean` | `false` | Exact text match vs substring (only used when text is provided) |
|
|
184
|
+
| `parent` | `Element` | `null` | Parent element to search within |
|
|
199
185
|
|
|
200
186
|
**Returns**: `{ elements: [{ element, boundingBox, tagName, frameIndex }] }`
|
|
201
187
|
|
|
202
188
|
- `element`: Raw DOM element (main frame only; for iframes, use `frameIndex` and re-query after switching context)
|
|
203
189
|
- `frameIndex`: `-1` for main frame, `0, 1, 2...` for iframes
|
|
204
190
|
|
|
205
|
-
**Agent/Automation Note
|
|
191
|
+
**Agent/Automation Note**: Iframe elements cannot be interacted with directly. Use `frameIndex` to switch context, then re-run `findElements` inside the iframe.
|
|
192
|
+
|
|
193
|
+
### `findProbableElements(type, text, exact, parent)`
|
|
194
|
+
|
|
195
|
+
Finds elements matching the specified type with intelligent fallback to nearby elements. This function first attempts a direct match (element contains both type and text), then falls back to finding elements of the specified type near elements that match the text.
|
|
196
|
+
|
|
197
|
+
**Use Case**: When UI patterns separate content from interactive elements (e.g., a label with text "Email" next to an input field), `findProbableElements` will find the input even though the text isn't inside it.
|
|
198
|
+
|
|
199
|
+
| Parameter | Type | Default | Description |
|
|
200
|
+
| --------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
201
|
+
| `type` | `string` | `null` | Element type (see supported types below). If `null`/`undefined`/`''`, matches any type. Throws `TypeError` for non-string values. |
|
|
202
|
+
| `text` | `string` | `null` | Text to search for in content/attributes. If `null`/`undefined`/`''`, matches any text. Throws `TypeError` for non-string values. |
|
|
203
|
+
| `exact` | `boolean` | `false` | Exact text match vs substring (only used when text is provided) |
|
|
204
|
+
| `parent` | `Element` | `null` | Parent element to search within |
|
|
205
|
+
|
|
206
|
+
**Returns**: `{ elements: [{ element, boundingBox, tagName, frameIndex }] }`
|
|
207
|
+
|
|
208
|
+
**Behavior**:
|
|
209
|
+
|
|
210
|
+
- If only `type` is provided: delegates to `findElementByType(type, parent)`
|
|
211
|
+
- If only `text` is provided: delegates to `findElementByAttributes(text, exact, parent)`
|
|
212
|
+
- If both are provided: attempts direct match, then falls back to nearby elements
|
|
213
|
+
|
|
214
|
+
**Fallback Strategy**: When no element matches both type and text directly, searches for nearby elements in this order:
|
|
215
|
+
|
|
216
|
+
1. Parent elements (walk up the DOM tree)
|
|
217
|
+
2. Sibling elements (same parent)
|
|
218
|
+
3. Child elements (descendants)
|
|
219
|
+
|
|
220
|
+
**Example**:
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
// Type-only search (delegates to findElementByType)
|
|
224
|
+
const result1 = ElementFinder.findProbableElements('button')
|
|
225
|
+
// Returns all buttons on the page
|
|
226
|
+
|
|
227
|
+
// Text-only search (delegates to findElementByAttributes)
|
|
228
|
+
const result2 = ElementFinder.findProbableElements(null, 'Submit')
|
|
229
|
+
// Returns all elements containing "Submit"
|
|
230
|
+
|
|
231
|
+
// Direct match - element contains text
|
|
232
|
+
const result3 = ElementFinder.findProbableElements('button', 'Submit')
|
|
233
|
+
// Returns button with text "Submit" inside it
|
|
234
|
+
|
|
235
|
+
// Fallback match - text in nearby element
|
|
236
|
+
const result4 = ElementFinder.findProbableElements('textbox', 'Email')
|
|
237
|
+
// Returns input element when "Email" text is in a nearby label
|
|
238
|
+
|
|
239
|
+
// Fallback match - text in parent
|
|
240
|
+
const result5 = ElementFinder.findProbableElements('button', 'Menu Item 1')
|
|
241
|
+
// Returns button when "Menu Item 1" is in a child span
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**When to use**:
|
|
245
|
+
|
|
246
|
+
- Use `findElements` when you need strict matching (element must contain the text)
|
|
247
|
+
- Use `findProbableElements` when text might be in a nearby element (labels, icons, wrappers)
|
|
248
|
+
- Both functions search all frames by default
|
|
206
249
|
|
|
207
250
|
### `highlight(elements, color, width)`
|
|
208
251
|
|
|
@@ -238,22 +281,37 @@ Returns the current searchable attributes array.
|
|
|
238
281
|
|
|
239
282
|
Checks if an element matches the specified type definition.
|
|
240
283
|
|
|
241
|
-
### `
|
|
284
|
+
### `matchesAttribute(el, value, exact)`
|
|
285
|
+
|
|
286
|
+
Checks if an element matches the specified text/attribute value. Safely handles edge case elements that may throw errors on attribute access.
|
|
287
|
+
|
|
288
|
+
### `findElementByType(type, parent)`
|
|
289
|
+
|
|
290
|
+
Finds elements by type only. Searches all frames by default.
|
|
291
|
+
|
|
292
|
+
| Parameter | Type | Default | Description |
|
|
293
|
+
| --------- | --------- | ----------- | ----------------------------------------------------------------------------------- |
|
|
294
|
+
| `type` | `string` | `"element"` | Element type (see supported types below). Throws `TypeError` for non-string values. |
|
|
295
|
+
| `parent` | `Element` | `null` | Parent element to search within |
|
|
242
296
|
|
|
243
|
-
|
|
297
|
+
### `findElementByAttributes(value, exact, parent)`
|
|
298
|
+
|
|
299
|
+
Finds elements by text/attribute value only. Searches all frames by default.
|
|
300
|
+
|
|
301
|
+
| Parameter | Type | Default | Description |
|
|
302
|
+
| --------- | --------- | ------- | -------------------------------------------------------------------------------------------------- |
|
|
303
|
+
| `value` | `string` | `''` | Text/attribute value to search for. If `null`/`undefined`, defaults to empty string (matches all). |
|
|
304
|
+
| `exact` | `boolean` | `false` | Exact text match vs substring |
|
|
305
|
+
| `parent` | `Element` | `null` | Parent element to search within |
|
|
244
306
|
|
|
245
307
|
### `getAllElements(root)`
|
|
246
308
|
|
|
247
309
|
Gets all elements including shadow DOM contents.
|
|
248
310
|
|
|
249
|
-
### `getAllFrames(root
|
|
311
|
+
### `getAllFrames(root)`
|
|
250
312
|
|
|
251
313
|
Gets all frames (main document + iframes) in the window. Returns array with `frameIndex` (-1 for main, 0+ for iframes). Cross-origin iframes (SecurityError) are automatically skipped with a specific warning message, while other errors are logged separately.
|
|
252
314
|
|
|
253
|
-
### `getConfig()`
|
|
254
|
-
|
|
255
|
-
Returns the current configuration object.
|
|
256
|
-
|
|
257
315
|
### `parseXPath(expr, el, depth)`
|
|
258
316
|
|
|
259
317
|
Parses XPath-like expressions for element type matching. The `depth` parameter is used internally for recursion tracking and has a maximum limit of 100 to prevent stack overflow from deeply nested expressions.
|
|
@@ -262,9 +320,11 @@ Parses XPath-like expressions for element type matching. The `depth` parameter i
|
|
|
262
320
|
|
|
263
321
|
Splits XPath expressions by operator (and/or).
|
|
264
322
|
|
|
323
|
+
---
|
|
324
|
+
|
|
265
325
|
## Working with Iframes
|
|
266
326
|
|
|
267
|
-
The library automatically searches all frames (main
|
|
327
|
+
The library automatically searches all frames (main + iframes) by default. However, there are important limitations when working with iframe elements:
|
|
268
328
|
|
|
269
329
|
### Iframe Element Limitations
|
|
270
330
|
|
|
@@ -309,32 +369,70 @@ if (iframeElements.length > 0) {
|
|
|
309
369
|
}
|
|
310
370
|
```
|
|
311
371
|
|
|
372
|
+
---
|
|
373
|
+
|
|
312
374
|
## Supported Element Types
|
|
313
375
|
|
|
314
|
-
| Type
|
|
315
|
-
|
|
|
316
|
-
| `button`
|
|
317
|
-
| `checkbox`
|
|
318
|
-
| `switch`
|
|
319
|
-
| `slider`
|
|
320
|
-
| `
|
|
321
|
-
| `
|
|
322
|
-
| `
|
|
323
|
-
| `
|
|
324
|
-
| `
|
|
325
|
-
| `
|
|
326
|
-
| `
|
|
327
|
-
| `
|
|
328
|
-
| `
|
|
329
|
-
| `
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
376
|
+
| Type | Description |
|
|
377
|
+
| ------------- | ------------------------------------------------------- |
|
|
378
|
+
| `button` | `<button>`, `[role="button"]`, `[type="button"]` |
|
|
379
|
+
| `checkbox` | `<input type="checkbox">`, `[role="checkbox"]` |
|
|
380
|
+
| `switch` | Toggle switches, checkboxes with switch role |
|
|
381
|
+
| `slider` | `<input type="range">`, `[role="slider"]` |
|
|
382
|
+
| `datepicker` | `<input type="date">` |
|
|
383
|
+
| `colorpicker` | `<input type="color">` |
|
|
384
|
+
| `radio` | `<input type="radio">`, `[role="radio"]` |
|
|
385
|
+
| `dropdown` | `<select>`, `[role="combobox"]`, `[role="listbox"]` |
|
|
386
|
+
| `textbox` | `<input>`, `<textarea>`, `[role="textbox"]` |
|
|
387
|
+
| `link` | `<a>`, `[role="link"]`, `[href]` |
|
|
388
|
+
| `heading` | `<h1>-<h6>`, `[role="heading"]` |
|
|
389
|
+
| `navigation` | `<nav>`, `[role="navigation"]` |
|
|
390
|
+
| `list` | `<ul>`, `<ol>`, `[role="list"]` |
|
|
391
|
+
| `listitem` | `<li>`, `[role="listitem"]` |
|
|
392
|
+
| `menu` | `<menu>`, `[role="menu"]` |
|
|
393
|
+
| `menuitem` | `[role="menuitem"]` |
|
|
394
|
+
| `toolbar` | `[role="toolbar"]` |
|
|
395
|
+
| `dialog` | `[role="dialog"]` |
|
|
396
|
+
| `table` | `<table>`, `[role="table"]` |
|
|
397
|
+
| `row` | `<tr>`, `[role="row"]` |
|
|
398
|
+
| `column` | `<td>`, `<th>`, `[role="cell"]` |
|
|
399
|
+
| `cell` | `<td>`, `[role="cell"]` (data cells only, no expansion) |
|
|
400
|
+
| `image` | `<img>`, `[role="img"]` |
|
|
401
|
+
| `file` | `<input type="file"]` |
|
|
402
|
+
| `element` | Matches all elements |
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Table Element Types: `column` vs `cell`
|
|
407
|
+
|
|
408
|
+
Both `column` and `cell` types find table cells, but they behave differently:
|
|
409
|
+
|
|
410
|
+
| Type | Matches | With Text Search |
|
|
411
|
+
| -------- | ----------------------- | --------------------------------------------------------- |
|
|
412
|
+
| `column` | `<td>`, `<th>` elements | Returns **all cells** in the column (header + data cells) |
|
|
413
|
+
| `cell` | `<td>` elements only | Returns **only the specific cell** (no expansion) |
|
|
414
|
+
|
|
415
|
+
**Example**:
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
// Find all cells in the "City" column (header + 3 data cells = 4 total)
|
|
419
|
+
const columnResult = ElementFinder.findElement('column', 'City')
|
|
420
|
+
// Returns: [th:City, td:New York, td:London, td:Paris]
|
|
421
|
+
|
|
422
|
+
// Find all cells when searching for a data cell value
|
|
423
|
+
const columnResult2 = ElementFinder.findElement('column', 'Paris')
|
|
424
|
+
// Returns: [th:City, td:New York, td:London, td:Paris]
|
|
425
|
+
|
|
426
|
+
// Find only the specific cell containing "Paris"
|
|
427
|
+
const cellResult = ElementFinder.findElement('cell', 'Paris')
|
|
428
|
+
// Returns: [td:Paris]
|
|
429
|
+
|
|
430
|
+
// Find by header text with cell type - returns only the header cell
|
|
431
|
+
const headerCell = ElementFinder.findElement('cell', 'City')
|
|
432
|
+
// Returns: [] (no td elements match "City" header text)
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
---
|
|
338
436
|
|
|
339
437
|
## Searchable Attributes
|
|
340
438
|
|
|
@@ -344,15 +442,19 @@ By default, the library searches these attributes (in priority order):
|
|
|
344
442
|
- `resource-id`, `name`, `aria-label`, `class`, `hint`
|
|
345
443
|
- `title`, `tooltip`, `alt`, `src`, `aria-labelledby`
|
|
346
444
|
|
|
445
|
+
---
|
|
446
|
+
|
|
347
447
|
## Performance
|
|
348
448
|
|
|
349
449
|
The library is optimized for large DOM trees with efficient algorithms:
|
|
350
450
|
|
|
351
|
-
- **
|
|
352
|
-
- **
|
|
353
|
-
- **
|
|
451
|
+
- **Pre-compiled type matchers**: Type definitions are compiled into cached matcher functions at module load time
|
|
452
|
+
- **O(n) innermost element filtering**: Set-based lookups instead of O(n²) nested loops
|
|
453
|
+
- **Map-based column expansion**: O(1) element-to-column-position lookups for table cells
|
|
454
|
+
- **Optimized text matching**: Direct text node iteration avoids expensive textContent calls
|
|
455
|
+
- **Loop optimizations**: Traditional for-loops with cached lengths for hot paths
|
|
354
456
|
|
|
355
|
-
|
|
457
|
+
---
|
|
356
458
|
|
|
357
459
|
## Development
|
|
358
460
|
|
|
@@ -369,7 +471,7 @@ npm run test:watch
|
|
|
369
471
|
npm run test:coverage
|
|
370
472
|
```
|
|
371
473
|
|
|
372
|
-
**Note
|
|
474
|
+
**Note**: The `tests/integration/helpers/` folder is excluded from vitest runs as it contains helper utilities.
|
|
373
475
|
|
|
374
476
|
### Linting
|
|
375
477
|
|
|
@@ -383,6 +485,8 @@ The library includes a Node.js-compatible module (`src/element-finder.js`) that
|
|
|
383
485
|
|
|
384
486
|
The original `index.js` is browser-injected code executed via Selenium's `executeScript`. Coverage for browser-injected code requires browser-based tools like Istanbul or running tests in a browser environment.
|
|
385
487
|
|
|
488
|
+
---
|
|
489
|
+
|
|
386
490
|
## License
|
|
387
491
|
|
|
388
492
|
MIT
|