|
17 | 17 |
|
18 | 18 | const { InvalidArgumentError, NoSuchFrameError } = require('../lib/error')
|
19 | 19 | 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 | + |
20 | 70 | class BrowsingContext {
|
21 | 71 | constructor(driver) {
|
22 | 72 | this._driver = driver
|
@@ -331,6 +381,90 @@ class BrowsingContext {
|
331 | 381 | async back() {
|
332 | 382 | await this.traverseHistory(-1)
|
333 | 383 | }
|
| 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 | + } |
334 | 468 | }
|
335 | 469 |
|
336 | 470 | class NavigateResult {
|
@@ -380,3 +514,4 @@ async function getBrowsingContextInstance(
|
380 | 514 | * @type {function(*, {*,*,*}): Promise<BrowsingContext>}
|
381 | 515 | */
|
382 | 516 | module.exports = getBrowsingContextInstance
|
| 517 | +module.exports.Locator = Locator |
0 commit comments