Skip to content

Commit 4d6bdd0

Browse files
authored
[bidi] [js] Add locate node command (#13489)
1 parent 3187765 commit 4d6bdd0

File tree

2 files changed

+398
-0
lines changed

2 files changed

+398
-0
lines changed

javascript/node/selenium-webdriver/bidi/browsingContext.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,56 @@
1717

1818
const { InvalidArgumentError, NoSuchFrameError } = require('../lib/error')
1919
const { BrowsingContextInfo } = require('./browsingContextTypes')
20+
const {SerializationOptions, ReferenceValue, RemoteValue} = require("./protocolValue");
21+
const {WebElement} = require("../lib/webdriver");
22+
23+
class Locator {
24+
static Type = Object.freeze({
25+
CSS: 'css',
26+
INNER_TEXT: 'innerText',
27+
XPATH: 'xpath',
28+
})
29+
30+
#type
31+
#value
32+
#ignoreCase
33+
#matchType
34+
#maxDepth
35+
36+
constructor(
37+
type, value, ignoreCase = undefined, matchType = undefined, maxDepth = undefined) {
38+
this.#type = type
39+
this.#value = value
40+
this.#ignoreCase = ignoreCase
41+
this.#matchType = matchType
42+
this.#maxDepth = maxDepth
43+
}
44+
45+
static css(value) {
46+
return new Locator(Locator.Type.CSS, value)
47+
}
48+
49+
static xpath(value) {
50+
return new Locator(Locator.Type.XPATH, value)
51+
}
52+
53+
static innerText(value, ignoreCase = undefined, matchType = undefined, maxDepth = undefined) {
54+
return new Locator(Locator.Type.INNER_TEXT, value, ignoreCase, matchType, maxDepth)
55+
}
56+
57+
toMap() {
58+
const map = new Map()
59+
60+
map.set('type', this.#type.toString())
61+
map.set('value', this.#value)
62+
map.set('ignoreCase', this.#ignoreCase)
63+
map.set('matchType', this.#matchType)
64+
map.set('maxDepth', this.#maxDepth)
65+
66+
return map
67+
}
68+
}
69+
2070
class BrowsingContext {
2171
constructor(driver) {
2272
this._driver = driver
@@ -331,6 +381,90 @@ class BrowsingContext {
331381
async back() {
332382
await this.traverseHistory(-1)
333383
}
384+
385+
async locateNodes(
386+
locator,
387+
maxNodeCount = undefined,
388+
ownership = undefined,
389+
sandbox = undefined,
390+
serializationOptions = undefined,
391+
startNodes = undefined) {
392+
393+
if (!(locator instanceof Locator)) {
394+
throw Error(`Pass in a Locator object. Received: ${locator}`)
395+
}
396+
397+
if (serializationOptions !== undefined && !(serializationOptions instanceof SerializationOptions)) {
398+
throw Error(`Pass in SerializationOptions object. Received: ${serializationOptions} `)
399+
}
400+
401+
if (ownership !== undefined && !['root', 'none'].includes(ownership)) {
402+
throw Error(`Valid types are 'root' and 'none. Received: ${ownership}`)
403+
}
404+
405+
if (startNodes !== undefined && !Array.isArray(startNodes)) {
406+
throw Error(`Pass in an array of ReferenceValue objects. Received: ${startNodes}`)
407+
}
408+
409+
if (startNodes !== undefined && Array.isArray(startNodes)) {
410+
startNodes.forEach((node) => {
411+
if (!(node instanceof ReferenceValue)) {
412+
throw Error(`Pass in a ReferenceValue object. Received: ${node}`)
413+
}
414+
})
415+
}
416+
417+
const params = {
418+
method: 'browsingContext.locateNodes',
419+
params: {
420+
context: this._id,
421+
locator: Object.fromEntries(locator.toMap()),
422+
maxNodeCount: maxNodeCount,
423+
ownership: ownership,
424+
sandbox: sandbox,
425+
serializationOptions: serializationOptions,
426+
startNodes: startNodes
427+
},
428+
}
429+
430+
let response = await this.bidi.send(params)
431+
if ('error' in response) {
432+
throw Error(response['error'])
433+
}
434+
435+
const nodes = response.result.nodes
436+
const remoteValues = []
437+
438+
nodes.forEach((node) => {
439+
remoteValues.push(new RemoteValue(node))
440+
})
441+
return remoteValues
442+
}
443+
444+
async locateNode(
445+
locator,
446+
ownership = undefined,
447+
sandbox = undefined,
448+
serializationOptions = undefined,
449+
startNodes = undefined) {
450+
const elements = await this.locateNodes(locator, 1, ownership, sandbox, serializationOptions, startNodes)
451+
return elements[0]
452+
}
453+
454+
async locateElement(locator) {
455+
const elements = await this.locateNodes(locator, 1)
456+
return new WebElement(this._driver, elements[0].sharedId)
457+
}
458+
459+
async locateElements(locator) {
460+
const elements = await this.locateNodes(locator)
461+
462+
let webElements = []
463+
elements.forEach((element) => {
464+
webElements.push(new WebElement(this._driver, element.sharedId))
465+
})
466+
return webElements
467+
}
334468
}
335469

336470
class NavigateResult {
@@ -380,3 +514,4 @@ async function getBrowsingContextInstance(
380514
* @type {function(*, {*,*,*}): Promise<BrowsingContext>}
381515
*/
382516
module.exports = getBrowsingContextInstance
517+
module.exports.Locator = Locator

0 commit comments

Comments
 (0)