} A promise that will be resolved with the
* requested CSS value.
*/
getCssValue(cssStyleProperty) {
const name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY
return this.execute_(
new command.Command(name).setParameter('propertyName', cssStyleProperty)
)
}
/**
* Retrieves the current value of the given attribute of this element.
* Will return the current value, even if it has been modified after the page
* has been loaded. More exactly, this method will return the value
* of the given attribute, unless that attribute is not present, in which case
* the value of the property with the same name is returned. If neither value
* is set, null is returned (for example, the "value" property of a textarea
* element). The "style" attribute is converted as best can be to a
* text representation with a trailing semicolon. The following are deemed to
* be "boolean" attributes and will return either "true" or null:
*
* async, autofocus, autoplay, checked, compact, complete, controls, declare,
* defaultchecked, defaultselected, defer, disabled, draggable, ended,
* formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
* loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
* paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
* selected, spellcheck, truespeed, willvalidate
*
* Finally, the following commonly mis-capitalized attribute/property names
* are evaluated as expected:
*
* - "class"
* - "readonly"
*
* @param {string} attributeName The name of the attribute to query.
* @return {!Promise} A promise that will be
* resolved with the attribute's value. The returned value will always be
* either a string or null.
*/
getAttribute(attributeName) {
return this.execute_(
new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).setParameter(
'name',
attributeName
)
)
}
/**
* Get the value of the given attribute of the element.
*
* This method, unlike {@link #getAttribute(String)}, returns the value of the attribute with the
* given name but not the property with the same name.
*
* The following are deemed to be "boolean" attributes, and will return either "true" or null:
*
* async, autofocus, autoplay, checked, compact, complete, controls, declare, defaultchecked,
* defaultselected, defer, disabled, draggable, ended, formnovalidate, hidden, indeterminate,
* iscontenteditable, ismap, itemscope, loop, multiple, muted, nohref, noresize, noshade,
* novalidate, nowrap, open, paused, pubdate, readonly, required, reversed, scoped, seamless,
* seeking, selected, truespeed, willvalidate
*
* See W3C WebDriver specification
* for more details.
*
* @param attributeName The name of the attribute.
* @return The attribute's value or null if the value is not set.
*/
getDomAttribute(attributeName) {
return this.execute_(
new command.Command(command.Name.GET_DOM_ATTRIBUTE).setParameter(
'name',
attributeName
)
)
}
/**
* Get the given property of the referenced web element
* @param {string} propertyName The name of the attribute to query.
* @return {!Promise} A promise that will be
* resolved with the element's property value
*/
getProperty(propertyName) {
return this.execute_(
new command.Command(command.Name.GET_ELEMENT_PROPERTY).setParameter(
'name',
propertyName
)
)
}
/**
* Get the shadow root of the current web element.
* @returns {!Promise} A promise that will be
* resolved with the elements shadow root or rejected
* with {@link NoSuchShadowRootError}
*/
getShadowRoot() {
return this.execute_(new command.Command(command.Name.GET_SHADOW_ROOT))
}
/**
* Get the visible (i.e. not hidden by CSS) innerText of this element,
* including sub-elements, without any leading or trailing whitespace.
*
* @return {!Promise} A promise that will be
* resolved with the element's visible text.
*/
getText() {
return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT))
}
/**
* Get the computed WAI-ARIA role of element.
*
* @return {!Promise} A promise that will be
* resolved with the element's computed role.
*/
getAriaRole() {
return this.execute_(new command.Command(command.Name.GET_COMPUTED_ROLE))
}
/**
* Get the computed WAI-ARIA label of element.
*
* @return {!Promise} A promise that will be
* resolved with the element's computed label.
*/
getAccessibleName() {
return this.execute_(new command.Command(command.Name.GET_COMPUTED_LABEL))
}
/**
* Returns an object describing an element's location, in pixels relative to
* the document element, and the element's size in pixels.
*
* @return {!Promise<{width: number, height: number, x: number, y: number}>}
* A promise that will resolve with the element's rect.
*/
getRect() {
return this.execute_(new command.Command(command.Name.GET_ELEMENT_RECT))
}
/**
* Tests whether this element is enabled, as dictated by the `disabled`
* attribute.
*
* @return {!Promise} A promise that will be
* resolved with whether this element is currently enabled.
*/
isEnabled() {
return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED))
}
/**
* Tests whether this element is selected.
*
* @return {!Promise} A promise that will be
* resolved with whether this element is currently selected.
*/
isSelected() {
return this.execute_(new command.Command(command.Name.IS_ELEMENT_SELECTED))
}
/**
* Submits the form containing this element (or this element if it is itself
* a FORM element). his command is a no-op if the element is not contained in
* a form.
*
* @return {!Promise} A promise that will be resolved
* when the form has been submitted.
*/
submit() {
const script =
'/* submitForm */var form = arguments[0];\n' +
'while (form.nodeName != "FORM" && form.parentNode) {\n' +
' form = form.parentNode;\n' +
'}\n' +
"if (!form) { throw Error('Unable to find containing form element'); }\n" +
"if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" +
"var e = form.ownerDocument.createEvent('Event');\n" +
"e.initEvent('submit', true, true);\n" +
'if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n'
this.driver_.executeScript(script, this)
}
/**
* Clear the `value` of this element. This command has no effect if the
* underlying DOM element is neither a text INPUT element nor a TEXTAREA
* element.
*
* @return {!Promise} A promise that will be resolved
* when the element has been cleared.
*/
clear() {
return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT))
}
/**
* Test whether this element is currently displayed.
*
* @return {!Promise} A promise that will be
* resolved with whether this element is currently visible on the page.
*/
isDisplayed() {
return this.execute_(new command.Command(command.Name.IS_ELEMENT_DISPLAYED))
}
/**
* Take a screenshot of the visible region encompassed by this element's
* bounding rectangle.
*
* @return {!Promise} A promise that will be
* resolved to the screenshot as a base-64 encoded PNG.
*/
takeScreenshot() {
return this.execute_(
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
)
}
}
/**
* WebElementPromise is a promise that will be fulfilled with a WebElement.
* This serves as a forward proxy on WebElement, allowing calls to be
* scheduled without directly on this instance before the underlying
* WebElement has been fulfilled. In other words, the following two statements
* are equivalent:
*
* driver.findElement({id: 'my-button'}).click();
* driver.findElement({id: 'my-button'}).then(function(el) {
* return el.click();
* });
*
* @implements {IThenable}
* @final
*/
class WebElementPromise extends WebElement {
/**
* @param {!WebDriver} driver The parent WebDriver instance for this
* element.
* @param {!Promise} el A promise
* that will resolve to the promised element.
*/
constructor(driver, el) {
super(driver, 'unused')
/** @override */
this.then = el.then.bind(el)
/** @override */
this.catch = el.catch.bind(el)
/**
* Defers returning the element ID until the wrapped WebElement has been
* resolved.
* @override
*/
this.getId = function () {
return el.then(function (el) {
return el.getId()
})
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// ShadowRoot
//
//////////////////////////////////////////////////////////////////////////////
/**
* Represents a ShadowRoot of a {@link WebElement}. Provides functions to
* retrieve elements that live in the DOM below the ShadowRoot.
*/
class ShadowRoot {
constructor(driver, id) {
this.driver_ = driver
this.id_ = id
}
/**
* Extracts the encoded ShadowRoot ID from the object.
*
* @param {?} obj The object to extract the ID from.
* @return {string} the extracted ID.
* @throws {TypeError} if the object is not a valid encoded ID.
*/
static extractId(obj) {
if (obj && typeof obj === 'object') {
if (typeof obj[SHADOW_ROOT_ID_KEY] === 'string') {
return obj[SHADOW_ROOT_ID_KEY]
}
}
throw new TypeError('object is not a ShadowRoot ID')
}
/**
* @param {?} obj the object to test.
* @return {boolean} whether the object is a valid encoded WebElement ID.
*/
static isId(obj) {
return (
obj &&
typeof obj === 'object' &&
typeof obj[SHADOW_ROOT_ID_KEY] === 'string'
)
}
/**
* @return {!Object} Returns the serialized representation of this ShadowRoot.
*/
[Symbols.serialize]() {
return this.getId()
}
/**
* Schedules a command that targets this element with the parent WebDriver
* instance. Will ensure this element's ID is included in the command
* parameters under the "id" key.
*
* @param {!command.Command} command The command to schedule.
* @return {!Promise} A promise that will be resolved with the result.
* @template T
* @see WebDriver#schedule
* @private
*/
execute_(command) {
command.setParameter('id', this)
return this.driver_.execute(command)
}
/**
* Schedule a command to find a descendant of this ShadowROot. If the element
* cannot be found, the returned promise will be rejected with a
* {@linkplain error.NoSuchElementError NoSuchElementError}.
*
* The search criteria for an element may be defined using one of the static
* factories on the {@link by.By} class, or as a short-hand
* {@link ./by.ByHash} object. For example, the following two statements
* are equivalent:
*
* var e1 = shadowroot.findElement(By.id('foo'));
* var e2 = shadowroot.findElement({id:'foo'});
*
* You may also provide a custom locator function, which takes as input this
* instance and returns a {@link WebElement}, or a promise that will resolve
* to a WebElement. If the returned promise resolves to an array of
* WebElements, WebDriver will use the first element. For example, to find the
* first visible link on a page, you could write:
*
* var link = element.findElement(firstVisibleLink);
*
* function firstVisibleLink(shadowRoot) {
* var links = shadowRoot.findElements(By.tagName('a'));
* return promise.filter(links, function(link) {
* return link.isDisplayed();
* });
* }
*
* @param {!(by.By|Function)} locator The locator strategy to use when
* searching for the element.
* @return {!WebElementPromise} A WebElement that can be used to issue
* commands against the located element. If the element is not found, the
* element will be invalidated and all scheduled commands aborted.
*/
findElement(locator) {
locator = by.checkedLocator(locator)
let id
if (typeof locator === 'function') {
id = this.driver_.findElementInternal_(locator, this)
} else {
let cmd = new command.Command(command.Name.FIND_ELEMENT_FROM_SHADOWROOT)
.setParameter('using', locator.using)
.setParameter('value', locator.value)
id = this.execute_(cmd)
}
return new ShadowRootPromise(this.driver_, id)
}
/**
* Locates all the descendants of this element that match the given search
* criteria.
*
* @param {!(by.By|Function)} locator The locator strategy to use when
* searching for the element.
* @return {!Promise>} A promise that will resolve to an
* array of WebElements.
*/
async findElements(locator) {
locator = by.checkedLocator(locator)
if (typeof locator === 'function') {
return this.driver_.findElementsInternal_(locator, this)
} else {
let cmd = new command.Command(command.Name.FIND_ELEMENTS_FROM_SHADOWROOT)
.setParameter('using', locator.using)
.setParameter('value', locator.value)
let result = await this.execute_(cmd)
return Array.isArray(result) ? result : []
}
}
getId() {
return this.id_
}
}
/**
* ShadowRootPromise is a promise that will be fulfilled with a WebElement.
* This serves as a forward proxy on ShadowRoot, allowing calls to be
* scheduled without directly on this instance before the underlying
* ShadowRoot has been fulfilled.
*
* @implements { IThenable}
* @final
*/
class ShadowRootPromise extends ShadowRoot {
/**
* @param {!WebDriver} driver The parent WebDriver instance for this
* element.
* @param {!Promise} shadow A promise
* that will resolve to the promised element.
*/
constructor(driver, shadow) {
super(driver, 'unused')
/** @override */
this.then = shadow.then.bind(shadow)
/** @override */
this.catch = shadow.catch.bind(shadow)
/**
* Defers returning the ShadowRoot ID until the wrapped WebElement has been
* resolved.
* @override
*/
this.getId = function () {
return shadow.then(function (shadow) {
return shadow.getId()
})
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Alert
//
//////////////////////////////////////////////////////////////////////////////
/**
* Represents a modal dialog such as {@code alert}, {@code confirm}, or
* {@code prompt}. Provides functions to retrieve the message displayed with
* the alert, accept or dismiss the alert, and set the response text (in the
* case of {@code prompt}).
*/
class Alert {
/**
* @param {!WebDriver} driver The driver controlling the browser this alert
* is attached to.
* @param {string} text The message text displayed with this alert.
*/
constructor(driver, text) {
/** @private {!WebDriver} */
this.driver_ = driver
/** @private {!Promise} */
this.text_ = Promise.resolve(text)
}
/**
* Retrieves the message text displayed with this alert. For instance, if the
* alert were opened with alert("hello"), then this would return "hello".
*
* @return {!Promise} A promise that will be
* resolved to the text displayed with this alert.
*/
getText() {
return this.text_
}
/**
* Accepts this alert.
*
* @return {!Promise} A promise that will be resolved
* when this command has completed.
*/
accept() {
return this.driver_.execute(new command.Command(command.Name.ACCEPT_ALERT))
}
/**
* Dismisses this alert.
*
* @return {!Promise} A promise that will be resolved
* when this command has completed.
*/
dismiss() {
return this.driver_.execute(new command.Command(command.Name.DISMISS_ALERT))
}
/**
* Sets the response text on this alert. This command will return an error if
* the underlying alert does not support response text (e.g. window.alert and
* window.confirm).
*
* @param {string} text The text to set.
* @return {!Promise} A promise that will be resolved
* when this command has completed.
*/
sendKeys(text) {
return this.driver_.execute(
new command.Command(command.Name.SET_ALERT_TEXT).setParameter(
'text',
text
)
)
}
}
/**
* AlertPromise is a promise that will be fulfilled with an Alert. This promise
* serves as a forward proxy on an Alert, allowing calls to be scheduled
* directly on this instance before the underlying Alert has been fulfilled. In
* other words, the following two statements are equivalent:
*
* driver.switchTo().alert().dismiss();
* driver.switchTo().alert().then(function(alert) {
* return alert.dismiss();
* });
*
* @implements {IThenable}
* @final
*/
class AlertPromise extends Alert {
/**
* @param {!WebDriver} driver The driver controlling the browser this
* alert is attached to.
* @param {!Promise} alert A thenable
* that will be fulfilled with the promised alert.
*/
constructor(driver, alert) {
super(driver, 'unused')
/** @override */
this.then = alert.then.bind(alert)
/** @override */
this.catch = alert.catch.bind(alert)
/**
* Defer returning text until the promised alert has been resolved.
* @override
*/
this.getText = function () {
return alert.then(function (alert) {
return alert.getText()
})
}
/**
* Defers action until the alert has been located.
* @override
*/
this.accept = function () {
return alert.then(function (alert) {
return alert.accept()
})
}
/**
* Defers action until the alert has been located.
* @override
*/
this.dismiss = function () {
return alert.then(function (alert) {
return alert.dismiss()
})
}
/**
* Defers action until the alert has been located.
* @override
*/
this.sendKeys = function (text) {
return alert.then(function (alert) {
return alert.sendKeys(text)
})
}
}
}
// PUBLIC API
module.exports = {
Alert,
AlertPromise,
Condition,
Logs,
Navigation,
Options,
ShadowRoot,
TargetLocator,
IWebDriver,
WebDriver,
WebElement,
WebElementCondition,
WebElementPromise,
Window,
}