browsertime
Version:
Get performance metrics from your web page using Browsertime.
267 lines (254 loc) • 9.62 kB
JavaScript
import webdriver from 'selenium-webdriver';
import { getLogger } from '@sitespeed.io/log';
import { executeCommand } from './commandHelper.js';
import { parseSelector } from './selectorParser.js';
const log = getLogger('browsertime.command.select');
/**
* Provides functionality to interact with `<select>` elements on a web page.
*
* @class
* @hideconstructor
*/
export class Select {
constructor(browser, options) {
/**
* @private
*/
this.browser = browser;
/**
* @private
*/
this.options = options;
}
/**
* @private
*/
async _waitForElement(driver, locator) {
const timeout = this.options?.timeouts?.elementWait ?? 0;
if (timeout > 0) {
await driver.wait(webdriver.until.elementLocated(locator), timeout);
}
}
/**
* Selects an option in a select element using a unified selector string and a value.
* Supports CSS selectors (default), and prefix-based strategies:
* 'id:mySelect', 'name:country', 'class:dropdown'.
*
* @async
* @param {string} selector - The selector string for the select element.
* @param {string} value - The value of the option to select.
* @returns {Promise<void>} A promise that resolves when the option is selected.
* @throws {Error} Throws an error if the select element is not found.
*/
async run(selector, value) {
const { locator, description } = parseSelector(selector);
return executeCommand(
log,
'Could not select value for %s',
description,
async () => {
const driver = this.browser.getDriver();
await this._waitForElement(driver, locator);
const element = await driver.findElement(locator);
await driver.executeScript(
`arguments[0].value = '${value}'; arguments[0].dispatchEvent(new Event('change'));`,
element
);
},
this.browser
);
}
/**
* Selects an option in a select element by its visible text using a unified selector string.
*
* @async
* @param {string} selector - The selector string for the select element.
* @param {string} text - The visible text of the option to select.
* @returns {Promise<void>} A promise that resolves when the option is selected.
* @throws {Error} Throws an error if the select element or option is not found.
*/
async runByText(selector, text) {
const { locator, description } = parseSelector(selector);
return executeCommand(
log,
'Could not select option by text for %s',
description,
async () => {
const driver = this.browser.getDriver();
await this._waitForElement(driver, locator);
const element = await driver.findElement(locator);
await driver.executeScript(
`const select = arguments[0];
const text = arguments[1];
for (const option of select.options) {
if (option.text === text || option.textContent.trim() === text) {
select.value = option.value;
select.dispatchEvent(new Event('change'));
return;
}
}
throw new Error('Option with text "' + text + '" not found');`,
element,
text
);
},
this.browser
);
}
/**
* Selects an option in a `<select>` element by its ID and the value of the option.
*
* @async
* @private
* @param {string} selectId - The ID of the `<select>` element.
* @param {string} value - The value of the option to select.
* @returns {Promise<void>} A promise that resolves when the option is selected.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async selectByIdAndValue(selectId, value) {
try {
const script = `const select = document.getElementById('${selectId}'); select.value = '${value}'; select.dispatchEvent(new Event('change'));`;
await this.browser.runScript(script, 'CUSTOM');
} catch (error) {
log.error('Could not select value for select with id %s', selectId);
log.verbose(error);
throw new Error(`Could not select value for select with id ${selectId}`);
}
}
/**
* Selects an option in a `<select>` element by its name and the value of the option.
*
* @async
* @private
* @param {string} selectName - The name of the `<select>` element.
* @param {string} value - The value of the option to select.
* @returns {Promise<void>} A promise that resolves when the option is selected.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async selectByNameAndValue(selectName, value) {
try {
const script = `const select = document.querySelector("select[name='${selectName}']"); select.value = '${value}'; select.dispatchEvent(new Event('change'));`;
await this.browser.runScript(script, 'CUSTOM');
} catch (error) {
log.error('Could not select value for select with name %s', selectName);
log.verbose(error);
throw new Error(
`Could not select value for select with name ${selectName}`
);
}
}
/**
* Selects an option in a `<select>` element by its ID and the index of the option.
*
* @async
* @private
* @param {string} selectId - The ID of the `<select>` element.
* @param {number} index - The index of the option to select.
* @returns {Promise<void>} A promise that resolves when the option is selected.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async selectByIdAndIndex(selectId, index) {
try {
const script = `const select = document.getElementById('${selectId}'); select.selectedIndex = ${index};select.dispatchEvent(new Event('change'));`;
const value = await this.browser.runScript(script, 'CUSTOM');
return value;
} catch (error) {
log.error(
'Could not select value for select with id %s by index',
selectId
);
log.verbose(error);
throw new Error(`Could not select value for select with id ${selectId}`);
}
}
/**
* Selects an option in a `<select>` element by its name and the index of the option.
*
* @async
* @private
* @param {string} selectName - The name of the `<select>` element.
* @param {number} index - The index of the option to select.
* @returns {Promise<void>} A promise that resolves when the option is selected.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async selectByNameAndIndex(selectName, index) {
try {
const script = `const select = document.querySelector("select[name='${selectName}']"); select.selectedIndex = ${index};select.dispatchEvent(new Event('change'));`;
const value = await this.browser.runScript(script, 'CUSTOM');
return value;
} catch (error) {
log.error(
'Could not select value for select with name %s by index',
selectName
);
log.verbose(error);
throw new Error(`Could not select value for select name ${selectName}`);
}
}
/**
* Deselects all options in a `<select>` element by its ID.
*
* @async
* @private
* @param {string} selectId - The ID of the `<select>` element.
* @returns {Promise<void>} A promise that resolves when all options are deselected.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async deselectById(selectId) {
try {
const script = `const select = document.getElementById('${selectId}'); select.selectedIndex = -1;select.dispatchEvent(new Event('change'));`;
const value = await this.browser.runScript(script, 'CUSTOM');
return value;
} catch (error) {
log.error('Could not deselect by select id %s', selectId);
log.verbose(error);
throw new Error(`Could not deselect by select id ${selectId}`);
}
}
/**
* Retrieves all option values in a `<select>` element by its ID.
*
* @async
* @private
* @param {string} selectId - The ID of the `<select>` element.
* @returns {Promise<string[]>} A promise that resolves with an array of the values of the options.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async getValuesById(selectId) {
const script = `const select = document.getElementById('${selectId}');
const values = [];
for (let option of select.options) {
values.push(option.value);
}
return values;
`;
try {
const value = await this.browser.runScript(script, 'CUSTOM');
return value;
} catch (error) {
log.error('Could not get select options for id %s', selectId);
log.verbose(error);
throw new Error(`Could not get select options for id ${selectId}`);
}
}
/**
* Retrieves the value of the selected option in a `<select>` element by its ID.
*
* @async
* @private
* @param {string} selectId - The ID of the `<select>` element.
* @returns {Promise<string>} A promise that resolves with the value of the selected option.
* @throws {Error} Throws an error if the `<select>` element is not found.
*/
async getSelectedValueById(selectId) {
try {
const script = `const select = document.getElementById('${selectId}'); return select.options[select.selectedIndex].value;`;
const value = await this.browser.runScript(script, 'CUSTOM');
return value;
} catch (error) {
log.error('Could not select value for select with id %s', selectId);
log.verbose(error);
throw new Error(`Could not select value for select with id ${selectId}`);
}
}
}