2022-08-25 15:55:19 +02:00
|
|
|
// Licensed to the Software Freedom Conservancy (SFC) under one
|
|
|
|
|
// or more contributor license agreements. See the NOTICE file
|
|
|
|
|
// distributed with this work for additional information
|
|
|
|
|
// regarding copyright ownership. The SFC licenses this file
|
|
|
|
|
// to you under the Apache License, Version 2.0 (the
|
|
|
|
|
// "License"); you may not use this file except in compliance
|
|
|
|
|
// with the License. You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing,
|
|
|
|
|
// software distributed under the License is distributed on an
|
|
|
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
|
|
|
// KIND, either express or implied. See the License for the
|
|
|
|
|
// specific language governing permissions and limitations
|
|
|
|
|
// under the License.
|
|
|
|
|
|
2022-08-18 01:00:12 +05:30
|
|
|
/*
|
|
|
|
|
* Licensed to the Software Freedom Conservancy (SFC) under one
|
|
|
|
|
* or more contributor license agreements. See the NOTICE file
|
|
|
|
|
* distributed with this work for additional information
|
|
|
|
|
* regarding copyright ownership. The SFC licenses this file
|
|
|
|
|
* to you under the Apache License, Version 2.0 (the
|
|
|
|
|
* "License"); you may not use this file except in compliance
|
|
|
|
|
* with the License. You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing,
|
|
|
|
|
* software distributed under the License is distributed on an
|
|
|
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
|
|
|
* KIND, either express or implied. See the License for the
|
|
|
|
|
* specific language governing permissions and limitations
|
|
|
|
|
* under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
'use strict'
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-08 16:53:35 +05:30
|
|
|
const { By } = require('./by')
|
2022-09-18 22:29:57 +05:30
|
|
|
const error = require('./error')
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ISelect interface makes a protocol for all kind of select elements (standard html and custom
|
|
|
|
|
* model)
|
|
|
|
|
*
|
|
|
|
|
* @interface
|
|
|
|
|
*/
|
2022-08-22 19:42:48 +03:00
|
|
|
// eslint-disable-next-line no-unused-vars
|
2022-08-18 01:00:12 +05:30
|
|
|
class ISelect {
|
|
|
|
|
/**
|
|
|
|
|
* @return {!Promise<boolean>} Whether this select element supports selecting multiple options at the same time? This
|
|
|
|
|
* is done by checking the value of the "multiple" attribute.
|
|
|
|
|
*/
|
2024-04-16 01:50:58 +05:30
|
|
|
isMultiple() {}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return {!Promise<!Array<!WebElement>>} All options belonging to this select tag
|
|
|
|
|
*/
|
2024-04-16 01:50:58 +05:30
|
|
|
getOptions() {}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return {!Promise<!Array<!WebElement>>} All selected options belonging to this select tag
|
|
|
|
|
*/
|
2024-04-16 01:50:58 +05:30
|
|
|
getAllSelectedOptions() {}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return {!Promise<!WebElement>} The first selected option in this select tag (or the currently selected option in a
|
|
|
|
|
* normal select)
|
|
|
|
|
*/
|
2024-04-16 01:50:58 +05:30
|
|
|
getFirstSelectedOption() {}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Select all options that display text matching the argument. That is, when given "Bar" this
|
|
|
|
|
* would select an option like:
|
|
|
|
|
*
|
|
|
|
|
* <option value="foo">Bar</option>
|
|
|
|
|
*
|
|
|
|
|
* @param {string} text The visible text to match against
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
selectByVisibleText(text) {} // eslint-disable-line
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Select all options that have a value matching the argument. That is, when given "foo" this
|
|
|
|
|
* would select an option like:
|
|
|
|
|
*
|
|
|
|
|
* <option value="foo">Bar</option>
|
|
|
|
|
*
|
|
|
|
|
* @param {string} value The value to match against
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
selectByValue(value) {} // eslint-disable-line
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Select the option at the given index. This is done by examining the "index" attribute of an
|
|
|
|
|
* element, and not merely by counting.
|
|
|
|
|
*
|
|
|
|
|
* @param {Number} index The option at this index will be selected
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
selectByIndex(index) {} // eslint-disable-line
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear all selected entries. This is only valid when the SELECT supports multiple selections.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
2024-04-16 01:50:58 +05:30
|
|
|
deselectAll() {}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deselect all options that display text matching the argument. That is, when given "Bar" this
|
|
|
|
|
* would deselect an option like:
|
|
|
|
|
*
|
|
|
|
|
* <option value="foo">Bar</option>
|
|
|
|
|
*
|
|
|
|
|
* @param {string} text The visible text to match against
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
deselectByVisibleText(text) {} // eslint-disable-line
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deselect all options that have a value matching the argument. That is, when given "foo" this
|
|
|
|
|
* would deselect an option like:
|
|
|
|
|
*
|
|
|
|
|
* @param {string} value The value to match against
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
deselectByValue(value) {} // eslint-disable-line
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deselect the option at the given index. This is done by examining the "index" attribute of an
|
|
|
|
|
* element, and not merely by counting.
|
|
|
|
|
*
|
|
|
|
|
* @param {Number} index The option at this index will be deselected
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
deselectByIndex(index) {} // eslint-disable-line
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @implements ISelect
|
|
|
|
|
*/
|
|
|
|
|
class Select {
|
|
|
|
|
/**
|
|
|
|
|
* Create an Select Element
|
|
|
|
|
* @param {WebElement} element Select WebElement.
|
|
|
|
|
*/
|
2022-08-22 19:42:48 +03:00
|
|
|
constructor(element) {
|
2024-05-08 16:53:35 +05:30
|
|
|
if (element === null) {
|
|
|
|
|
throw new Error(`Element must not be null. Please provide a valid <select> element.`)
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-18 01:00:12 +05:30
|
|
|
this.element = element
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
this.element.getAttribute('tagName').then(function (tagName) {
|
|
|
|
|
if (tagName.toLowerCase() !== 'select') {
|
2022-08-18 01:00:12 +05:30
|
|
|
throw new Error(`Select only works on <select> elements`)
|
|
|
|
|
}
|
|
|
|
|
})
|
2024-05-02 16:23:09 +05:30
|
|
|
|
|
|
|
|
this.element.getAttribute('multiple').then((multiple) => {
|
|
|
|
|
this.multiple = multiple !== null && multiple !== 'false'
|
|
|
|
|
})
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Select option with specified index.
|
|
|
|
|
*
|
|
|
|
|
* <example>
|
|
|
|
|
<select id="selectbox">
|
2024-03-08 12:13:00 +00:00
|
|
|
<option value="1">Option 1</option>
|
|
|
|
|
<option value="2">Option 2</option>
|
|
|
|
|
<option value="3">Option 3</option>
|
2022-08-18 01:00:12 +05:30
|
|
|
</select>
|
|
|
|
|
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
|
|
|
await selectObject.selectByIndex(1);
|
|
|
|
|
* </example>
|
|
|
|
|
*
|
|
|
|
|
* @param index
|
|
|
|
|
*/
|
|
|
|
|
async selectByIndex(index) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if (index < 0) {
|
2022-08-18 01:00:12 +05:30
|
|
|
throw new Error('Index needs to be 0 or any other positive number')
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
let options = await this.element.findElements(By.tagName('option'))
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
if (options.length === 0) {
|
2022-08-22 19:42:48 +03:00
|
|
|
throw new Error("Select element doesn't contain any option element")
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options.length - 1 < index) {
|
2022-08-22 19:42:48 +03:00
|
|
|
throw new Error(
|
2024-02-07 16:07:24 +00:00
|
|
|
`Option with index "${index}" not found. Select element only contains ${options.length - 1} option elements`,
|
2022-08-22 19:42:48 +03:00
|
|
|
)
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (let option of options) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if ((await option.getAttribute('index')) === index.toString()) {
|
2022-09-18 22:29:57 +05:30
|
|
|
await this.setSelected(option)
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Select option by specific value.
|
|
|
|
|
*
|
|
|
|
|
* <example>
|
|
|
|
|
<select id="selectbox">
|
|
|
|
|
<option value="1">Option 1</option>
|
|
|
|
|
<option value="2">Option 2</option>
|
|
|
|
|
<option value="3">Option 3</option>
|
|
|
|
|
</select>
|
|
|
|
|
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
|
|
|
await selectObject.selectByVisibleText("Option 2");
|
|
|
|
|
* </example>
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @param {string} value value of option element to be selected
|
|
|
|
|
*/
|
|
|
|
|
async selectByValue(value) {
|
2022-08-22 19:42:48 +03:00
|
|
|
let matched = false
|
|
|
|
|
let isMulti = await this.isMultiple()
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-08 16:53:35 +05:30
|
|
|
let options = await this.element.findElements(By.xpath('.//option[@value = ' + escapeQuotes(value) + ']'))
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
for (let option of options) {
|
2022-09-18 22:29:57 +05:30
|
|
|
await this.setSelected(option)
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!isMulti) {
|
2022-08-18 01:00:12 +05:30
|
|
|
return
|
|
|
|
|
}
|
2022-08-22 19:42:48 +03:00
|
|
|
matched = true
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!matched) {
|
2022-08-18 01:00:12 +05:30
|
|
|
throw new Error(`Cannot locate option with value: ${value}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Select option with displayed text matching the argument.
|
|
|
|
|
*
|
|
|
|
|
* <example>
|
|
|
|
|
<select id="selectbox">
|
|
|
|
|
<option value="1">Option 1</option>
|
|
|
|
|
<option value="2">Option 2</option>
|
|
|
|
|
<option value="3">Option 3</option>
|
|
|
|
|
</select>
|
|
|
|
|
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
|
|
|
await selectObject.selectByVisibleText("Option 2");
|
|
|
|
|
* </example>
|
|
|
|
|
*
|
|
|
|
|
* @param {String|Number} text text of option element to get selected
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async selectByVisibleText(text) {
|
2022-08-22 19:42:48 +03:00
|
|
|
text = typeof text === 'number' ? text.toString() : text
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-02 16:23:09 +05:30
|
|
|
const xpath = './/option[normalize-space(.) = ' + escapeQuotes(text) + ']'
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-02 16:23:09 +05:30
|
|
|
const options = await this.element.findElements(By.xpath(xpath))
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-02 16:23:09 +05:30
|
|
|
for (let option of options) {
|
|
|
|
|
await this.setSelected(option)
|
|
|
|
|
if (!(await this.isMultiple())) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-02 16:23:09 +05:30
|
|
|
let matched = Array.isArray(options) && options.length > 0
|
|
|
|
|
|
|
|
|
|
if (!matched && text.includes(' ')) {
|
|
|
|
|
const subStringWithoutSpace = getLongestSubstringWithoutSpace(text)
|
|
|
|
|
let candidates
|
|
|
|
|
if ('' === subStringWithoutSpace) {
|
|
|
|
|
candidates = await this.element.findElements(By.tagName('option'))
|
|
|
|
|
} else {
|
|
|
|
|
const xpath = './/option[contains(., ' + escapeQuotes(subStringWithoutSpace) + ')]'
|
|
|
|
|
candidates = await this.element.findElements(By.xpath(xpath))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const trimmed = text.trim()
|
|
|
|
|
|
|
|
|
|
for (let option of candidates) {
|
|
|
|
|
const optionText = await option.getText()
|
|
|
|
|
if (trimmed === optionText.trim()) {
|
|
|
|
|
await this.setSelected(option)
|
|
|
|
|
if (!(await this.isMultiple())) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
matched = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!matched) {
|
|
|
|
|
throw new Error(`Cannot locate option with text: ${text}`)
|
|
|
|
|
}
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a list of all options belonging to this select tag
|
|
|
|
|
* @returns {!Promise<!Array<!WebElement>>}
|
|
|
|
|
*/
|
|
|
|
|
async getOptions() {
|
2022-08-22 19:42:48 +03:00
|
|
|
return await this.element.findElements({ tagName: 'option' })
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-11-12 10:04:54 +01:00
|
|
|
* Returns a boolean value if the select tag is multiple
|
2022-08-18 01:00:12 +05:30
|
|
|
* @returns {Promise<boolean>}
|
|
|
|
|
*/
|
|
|
|
|
async isMultiple() {
|
2024-05-02 16:23:09 +05:30
|
|
|
return this.multiple
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a list of all selected options belonging to this select tag
|
|
|
|
|
*
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
async getAllSelectedOptions() {
|
2022-08-22 19:42:48 +03:00
|
|
|
const opts = await this.getOptions()
|
|
|
|
|
const results = []
|
|
|
|
|
for (let options of opts) {
|
|
|
|
|
if (await options.isSelected()) {
|
|
|
|
|
results.push(options)
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
}
|
2022-08-22 19:42:48 +03:00
|
|
|
return results
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns first Selected Option
|
|
|
|
|
* @returns {Promise<Element>}
|
|
|
|
|
*/
|
|
|
|
|
async getFirstSelectedOption() {
|
2022-08-22 19:42:48 +03:00
|
|
|
return (await this.getAllSelectedOptions())[0]
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deselects all selected options
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
async deselectAll() {
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!this.isMultiple()) {
|
|
|
|
|
throw new Error('You may only deselect all options of a multi-select')
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
const options = await this.getOptions()
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
for (let option of options) {
|
|
|
|
|
if (await option.isSelected()) {
|
|
|
|
|
await option.click()
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {string|Number}text text of option to deselect
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
async deselectByVisibleText(text) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!(await this.isMultiple())) {
|
|
|
|
|
throw new Error('You may only deselect options of a multi-select')
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
/**
|
2022-08-18 01:00:12 +05:30
|
|
|
* convert value into string
|
|
|
|
|
*/
|
2022-08-22 19:42:48 +03:00
|
|
|
text = typeof text === 'number' ? text.toString() : text
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-08 16:53:35 +05:30
|
|
|
const optionElement = await this.element.findElement(
|
|
|
|
|
By.xpath('.//option[normalize-space(.) = ' + escapeQuotes(text) + ']'),
|
|
|
|
|
)
|
2022-08-22 19:42:48 +03:00
|
|
|
if (await optionElement.isSelected()) {
|
2022-08-18 01:00:12 +05:30
|
|
|
await optionElement.click()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {Number} index index of option element to deselect
|
|
|
|
|
* Deselect the option at the given index.
|
|
|
|
|
* This is done by examining the "index"
|
|
|
|
|
* attribute of an element, and not merely by counting.
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
async deselectByIndex(index) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!(await this.isMultiple())) {
|
|
|
|
|
throw new Error('You may only deselect options of a multi-select')
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
if (index < 0) {
|
2022-08-18 01:00:12 +05:30
|
|
|
throw new Error('Index needs to be 0 or any other positive number')
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
let options = await this.element.findElements(By.tagName('option'))
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
if (options.length === 0) {
|
2022-08-22 19:42:48 +03:00
|
|
|
throw new Error("Select element doesn't contain any option element")
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options.length - 1 < index) {
|
2022-08-22 19:42:48 +03:00
|
|
|
throw new Error(
|
2024-02-07 16:07:24 +00:00
|
|
|
`Option with index "${index}" not found. Select element only contains ${options.length - 1} option elements`,
|
2022-08-22 19:42:48 +03:00
|
|
|
)
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (let option of options) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if ((await option.getAttribute('index')) === index.toString()) {
|
|
|
|
|
if (await option.isSelected()) {
|
2022-08-18 01:00:12 +05:30
|
|
|
await option.click()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {String} value value of an option to deselect
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
async deselectByValue(value) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!(await this.isMultiple())) {
|
|
|
|
|
throw new Error('You may only deselect options of a multi-select')
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
let matched = false
|
2022-08-18 01:00:12 +05:30
|
|
|
|
2024-05-08 16:53:35 +05:30
|
|
|
let options = await this.element.findElements(By.xpath('.//option[@value = ' + escapeQuotes(value) + ']'))
|
|
|
|
|
|
|
|
|
|
if (options.length === 0) {
|
|
|
|
|
throw new Error(`Cannot locate option with value: ${value}`)
|
|
|
|
|
}
|
2022-08-18 01:00:12 +05:30
|
|
|
|
|
|
|
|
for (let option of options) {
|
2022-08-22 19:42:48 +03:00
|
|
|
if (await option.isSelected()) {
|
2022-08-18 01:00:12 +05:30
|
|
|
await option.click()
|
|
|
|
|
}
|
2022-08-22 19:42:48 +03:00
|
|
|
matched = true
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2022-08-22 19:42:48 +03:00
|
|
|
if (!matched) {
|
2022-08-18 01:00:12 +05:30
|
|
|
throw new Error(`Cannot locate option with value: ${value}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-18 22:29:57 +05:30
|
|
|
|
|
|
|
|
async setSelected(option) {
|
|
|
|
|
if (!(await option.isSelected())) {
|
|
|
|
|
if (!(await option.isEnabled())) {
|
2024-02-07 16:07:24 +00:00
|
|
|
throw new error.UnsupportedOperationError(`You may not select a disabled option`)
|
2022-09-18 22:29:57 +05:30
|
|
|
}
|
|
|
|
|
await option.click()
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-18 01:00:12 +05:30
|
|
|
}
|
|
|
|
|
|
2024-05-02 16:23:09 +05:30
|
|
|
function escapeQuotes(toEscape) {
|
|
|
|
|
if (toEscape.includes(`"`) && toEscape.includes(`'`)) {
|
|
|
|
|
const quoteIsLast = toEscape.lastIndexOf(`"`) === toEscape.length - 1
|
|
|
|
|
const substrings = toEscape.split(`"`)
|
|
|
|
|
|
|
|
|
|
// Remove the last element if it's an empty string
|
|
|
|
|
if (substrings[substrings.length - 1] === '') {
|
|
|
|
|
substrings.pop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let result = 'concat('
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < substrings.length; i++) {
|
|
|
|
|
result += `"${substrings[i]}"`
|
|
|
|
|
result += i === substrings.length - 1 ? (quoteIsLast ? `, '"')` : `)`) : `, '"', `
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (toEscape.includes('"')) {
|
|
|
|
|
return `'${toEscape}'`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise return the quoted string
|
|
|
|
|
return `"${toEscape}"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLongestSubstringWithoutSpace(text) {
|
|
|
|
|
let words = text.split(' ')
|
|
|
|
|
let longestString = ''
|
|
|
|
|
for (let word of words) {
|
|
|
|
|
if (word.length > longestString.length) {
|
|
|
|
|
longestString = word
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return longestString
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = { Select, escapeQuotes }
|