12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const selenium_webdriver_1 = require("selenium-webdriver");
- const locators_1 = require("./locators");
- const logger_1 = require("./logger");
- const util_1 = require("./util");
- let clientSideScripts = require('./clientsidescripts');
- let logger = new logger_1.Logger('element');
- class WebdriverWebElement {
- }
- exports.WebdriverWebElement = WebdriverWebElement;
- let WEB_ELEMENT_FUNCTIONS = [
- 'click', 'sendKeys', 'getTagName', 'getCssValue', 'getAttribute', 'getText', 'getSize',
- 'getLocation', 'isEnabled', 'isSelected', 'submit', 'clear', 'isDisplayed', 'getId',
- 'takeScreenshot'
- ];
- /**
- * ElementArrayFinder is used for operations on an array of elements (as opposed
- * to a single element).
- *
- * The ElementArrayFinder is used to set up a chain of conditions that identify
- * an array of elements. In particular, you can call all(locator) and
- * filter(filterFn) to return a new ElementArrayFinder modified by the
- * conditions, and you can call get(index) to return a single ElementFinder at
- * position 'index'.
- *
- * Similar to jquery, ElementArrayFinder will search all branches of the DOM
- * to find the elements that satisfy the conditions (i.e. all, filter, get).
- * However, an ElementArrayFinder will not actually retrieve the elements until
- * an action is called, which means it can be set up in helper files (i.e.
- * page objects) before the page is available, and reused as the page changes.
- *
- * You can treat an ElementArrayFinder as an array of WebElements for most
- * purposes, in particular, you may perform actions (i.e. click, getText) on
- * them as you would an array of WebElements. The action will apply to
- * every element identified by the ElementArrayFinder. ElementArrayFinder
- * extends Promise, and once an action is performed on an ElementArrayFinder,
- * the latest result can be accessed using then, and will be returned as an
- * array of the results; the array has length equal to the length of the
- * elements found by the ElementArrayFinder and each result represents the
- * result of performing the action on the element. Unlike a WebElement, an
- * ElementArrayFinder will wait for the angular app to settle before
- * performing finds or actions.
- *
- * @alias element.all(locator)
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * element.all(by.css('.items li')).then(function(items) {
- * expect(items.length).toBe(3);
- * expect(items[0].getText()).toBe('First');
- * });
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * $$('.items li').then(function(items) {
- * expect(items.length).toBe(3);
- * expect(items[0].getText()).toBe('First');
- * });
- *
- * @constructor
- * @param {ProtractorBrowser} browser A browser instance.
- * @param {function(): Array.<webdriver.WebElement>} getWebElements A function
- * that returns a list of the underlying Web Elements.
- * @param {webdriver.Locator} locator The most relevant locator. It is only
- * used for error reporting and ElementArrayFinder.locator.
- * @param {Array.<webdriver.promise.Promise>} opt_actionResults An array
- * of promises which will be retrieved with then. Resolves to the latest
- * action result, or null if no action has been called.
- * @returns {ElementArrayFinder}
- */
- class ElementArrayFinder extends WebdriverWebElement {
- constructor(browser_, getWebElements = null, locator_, actionResults_ = null) {
- super();
- this.browser_ = browser_;
- this.getWebElements = getWebElements;
- this.locator_ = locator_;
- this.actionResults_ = actionResults_;
- // TODO(juliemr): might it be easier to combine this with our docs and just
- // wrap each one explicity with its own documentation?
- WEB_ELEMENT_FUNCTIONS.forEach((fnName) => {
- this[fnName] = (...args) => {
- let actionFn = (webElem) => {
- return webElem[fnName].apply(webElem, args);
- };
- return this.applyAction_(actionFn);
- };
- });
- }
- /**
- * Create a shallow copy of ElementArrayFinder.
- *
- * @returns {!ElementArrayFinder} A shallow copy of this.
- */
- clone() {
- // A shallow copy is all we need since the underlying fields can never be
- // modified. (Locator can be modified by the user, but that should
- // rarely/never happen and it doesn't affect functionalities).
- return new ElementArrayFinder(this.browser_, this.getWebElements, this.locator_, this.actionResults_);
- }
- /**
- * Calls to ElementArrayFinder may be chained to find an array of elements
- * using the current elements in this ElementArrayFinder as the starting
- * point. This function returns a new ElementArrayFinder which would contain
- * the children elements found (and could also be empty).
- *
- * @alias element.all(locator).all(locator)
- * @view
- * <div id='id1' class="parent">
- * <ul>
- * <li class="foo">1a</li>
- * <li class="baz">1b</li>
- * </ul>
- * </div>
- * <div id='id2' class="parent">
- * <ul>
- * <li class="foo">2a</li>
- * <li class="bar">2b</li>
- * </ul>
- * </div>
- *
- * @example
- * let foo = element.all(by.css('.parent')).all(by.css('.foo'));
- * expect(foo.getText()).toEqual(['1a', '2a']);
- * let baz = element.all(by.css('.parent')).all(by.css('.baz'));
- * expect(baz.getText()).toEqual(['1b']);
- * let nonexistent = element.all(by.css('.parent'))
- * .all(by.css('.NONEXISTENT'));
- * expect(nonexistent.getText()).toEqual(['']);
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let foo = $$('.parent').$$('.foo');
- * expect(foo.getText()).toEqual(['1a', '2a']);
- * let baz = $$('.parent').$$('.baz');
- * expect(baz.getText()).toEqual(['1b']);
- * let nonexistent = $$('.parent').$$('.NONEXISTENT');
- * expect(nonexistent.getText()).toEqual(['']);
- *
- * @param {webdriver.Locator} subLocator
- * @returns {ElementArrayFinder}
- */
- all(locator) {
- let ptor = this.browser_;
- let getWebElements = () => {
- if (this.getWebElements === null) {
- // This is the first time we are looking for an element
- return ptor.waitForAngular('Locator: ' + locator)
- .then(() => {
- if (locators_1.isProtractorLocator(locator)) {
- return locator.findElementsOverride(ptor.driver, null, ptor.rootEl);
- }
- else {
- return ptor.driver.findElements(locator);
- }
- });
- }
- else {
- return this.getWebElements().then((parentWebElements) => {
- // For each parent web element, find their children and construct
- // a list of Promise<List<child_web_element>>
- let childrenPromiseList = parentWebElements.map((parentWebElement) => {
- return locators_1.isProtractorLocator(locator) ?
- locator.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl) :
- parentWebElement.findElements(locator);
- });
- // Resolve the list of Promise<List<child_web_elements>> and merge
- // into a single list
- return selenium_webdriver_1.promise.all(childrenPromiseList)
- .then((resolved) => {
- return resolved.reduce((childrenList, resolvedE) => {
- return childrenList.concat(resolvedE);
- }, []);
- });
- });
- }
- };
- return new ElementArrayFinder(this.browser_, getWebElements, locator);
- }
- /**
- * Apply a filter function to each element within the ElementArrayFinder.
- * Returns a new ElementArrayFinder with all elements that pass the filter
- * function. The filter function receives the ElementFinder as the first
- * argument and the index as a second arg. This does not actually retrieve
- * the underlying list of elements, so it can be used in page objects.
- *
- * @alias element.all(locator).filter(filterFn)
- * @view
- * <ul class="items">
- * <li class="one">First</li>
- * <li class="two">Second</li>
- * <li class="three">Third</li>
- * </ul>
- *
- * @example
- * element.all(by.css('.items li')).filter(function(elem, index) {
- * return elem.getText().then(function(text) {
- * return text === 'Third';
- * });
- * }).first().click();
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * $$('.items li').filter(function(elem, index) {
- * return elem.getText().then(function(text) {
- * return text === 'Third';
- * });
- * }).first().click();
- *
- * @param {function(ElementFinder, number): webdriver.WebElement.Promise}
- * filterFn
- * Filter function that will test if an element should be returned.
- * filterFn can either return a boolean or a promise that resolves to a
- * boolean
- * @returns {!ElementArrayFinder} A ElementArrayFinder that represents an
- * array
- * of element that satisfy the filter function.
- */
- filter(filterFn) {
- let getWebElements = () => {
- return this.getWebElements().then((parentWebElements) => {
- let list = parentWebElements.map((parentWebElement, index) => {
- let elementFinder = ElementFinder.fromWebElement_(this.browser_, parentWebElement, this.locator_);
- return filterFn(elementFinder, index);
- });
- return selenium_webdriver_1.promise.all(list).then((resolvedList) => {
- return parentWebElements.filter((parentWebElement, index) => {
- return resolvedList[index];
- });
- });
- });
- };
- return new ElementArrayFinder(this.browser_, getWebElements, this.locator_);
- }
- /**
- * Get an element within the ElementArrayFinder by index. The index starts at 0.
- * Negative indices are wrapped (i.e. -i means ith element from last)
- * This does not actually retrieve the underlying element.
- *
- * @alias element.all(locator).get(index)
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * let list = element.all(by.css('.items li'));
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let list = $$('.items li');
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * @param {number|webdriver.promise.Promise} index Element index.
- * @returns {ElementFinder} finder representing element at the given index.
- */
- get(index) {
- let getWebElements = () => {
- return selenium_webdriver_1.promise.all([index, this.getWebElements()]).then(([i, parentWebElements]) => {
- if (i < 0) {
- i += parentWebElements.length;
- }
- if (i < 0 || i >= parentWebElements.length) {
- throw new selenium_webdriver_1.error.NoSuchElementError('Index out of bound. Trying to access element at index: ' + index +
- ', but there are only ' + parentWebElements.length + ' elements that match ' +
- 'locator ' + this.locator_.toString());
- }
- return [parentWebElements[i]];
- });
- };
- return new ElementArrayFinder(this.browser_, getWebElements, this.locator_).toElementFinder_();
- }
- /**
- * Get the first matching element for the ElementArrayFinder. This does not
- * actually retrieve the underlying element.
- *
- * @alias element.all(locator).first()
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * let first = element.all(by.css('.items li')).first();
- * expect(first.getText()).toBe('First');
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let first = $$('.items li').first();
- * expect(first.getText()).toBe('First');
- *
- * @returns {ElementFinder} finder representing the first matching element
- */
- first() {
- return this.get(0);
- }
- ;
- /**
- * Get the last matching element for the ElementArrayFinder. This does not
- * actually retrieve the underlying element.
- *
- * @alias element.all(locator).last()
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * let last = element.all(by.css('.items li')).last();
- * expect(last.getText()).toBe('Third');
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let last = $$('.items li').last();
- * expect(last.getText()).toBe('Third');
- *
- * @returns {ElementFinder} finder representing the last matching element
- */
- last() {
- return this.get(-1);
- }
- /**
- * Shorthand function for finding arrays of elements by css.
- * `element.all(by.css('.abc'))` is equivalent to `$$('.abc')`
- *
- * @alias $$(cssSelector)
- * @view
- * <div class="count">
- * <span class="one">First</span>
- * <span class="two">Second</span>
- * </div>
- *
- * @example
- * // The following two blocks of code are equivalent.
- * let list = element.all(by.css('.count span'));
- * expect(list.count()).toBe(2);
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let list = $$('.count span');
- * expect(list.count()).toBe(2);
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * @param {string} selector a css selector
- * @returns {ElementArrayFinder} which identifies the
- * array of the located {@link webdriver.WebElement}s.
- */
- $$(selector) {
- return this.all(selenium_webdriver_1.By.css(selector));
- }
- /**
- * Returns an ElementFinder representation of ElementArrayFinder. It ensures
- * that the ElementArrayFinder resolves to one and only one underlying
- * element.
- *
- * @returns {ElementFinder} An ElementFinder representation
- * @private
- */
- toElementFinder_() {
- return new ElementFinder(this.browser_, this);
- }
- /**
- * Count the number of elements represented by the ElementArrayFinder.
- *
- * @alias element.all(locator).count()
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * let list = element.all(by.css('.items li'));
- * expect(list.count()).toBe(3);
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let list = $$('.items li');
- * expect(list.count()).toBe(3);
- *
- * @returns {!webdriver.promise.Promise} A promise which resolves to the
- * number of elements matching the locator.
- */
- count() {
- return this.getWebElements().then((arr) => {
- return arr.length;
- }, (err) => {
- if (err instanceof selenium_webdriver_1.error.NoSuchElementError) {
- return 0;
- }
- else {
- throw err;
- }
- });
- }
- /**
- * Returns true if there are any elements present that match the finder.
- *
- * @alias element.all(locator).isPresent()
- *
- * @example
- * expect($('.item').isPresent()).toBeTruthy();
- *
- * @returns {Promise<boolean>}
- */
- isPresent() {
- return this.count().then((count) => {
- return count > 0;
- });
- }
- /**
- * Returns the most relevant locator.
- *
- * @example
- * // returns by.css('#ID1')
- * $('#ID1').locator();
- *
- * // returns by.css('#ID2')
- * $('#ID1').$('#ID2').locator();
- *
- * // returns by.css('#ID1')
- * $$('#ID1').filter(filterFn).get(0).click().locator();
- *
- * @returns {webdriver.Locator}
- */
- locator() {
- return this.locator_;
- }
- /**
- * Apply an action function to every element in the ElementArrayFinder,
- * and return a new ElementArrayFinder that contains the results of the
- * actions.
- *
- * @param {function(ElementFinder)} actionFn
- *
- * @returns {ElementArrayFinder}
- * @private
- */
- // map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
- applyAction_(actionFn) {
- let callerError = new Error();
- let actionResults = this.getWebElements()
- .then((arr) => selenium_webdriver_1.promise.all(arr.map(actionFn)))
- .then((value) => {
- return { passed: true, value: value };
- }, (error) => {
- return { passed: false, value: error };
- });
- let getWebElements = () => actionResults.then(() => this.getWebElements());
- actionResults = actionResults.then((result) => {
- if (result.passed) {
- return result.value;
- }
- else {
- let noSuchErr;
- if (result.value instanceof Error) {
- noSuchErr = result.value;
- noSuchErr.stack = noSuchErr.stack + callerError.stack;
- }
- else {
- noSuchErr = new Error(result.value);
- noSuchErr.stack = callerError.stack;
- }
- throw noSuchErr;
- }
- });
- return new ElementArrayFinder(this.browser_, getWebElements, this.locator_, actionResults);
- }
- /**
- * Represents the ElementArrayFinder as an array of ElementFinders.
- *
- * @returns {Array.<ElementFinder>} Return a promise, which resolves to a list
- * of ElementFinders specified by the locator.
- */
- asElementFinders_() {
- return this.getWebElements().then((arr) => {
- return arr.map((webElem) => {
- return ElementFinder.fromWebElement_(this.browser_, webElem, this.locator_);
- });
- });
- }
- /**
- * Retrieve the elements represented by the ElementArrayFinder. The input
- * function is passed to the resulting promise, which resolves to an
- * array of ElementFinders.
- *
- * @alias element.all(locator).then(thenFunction)
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * element.all(by.css('.items li')).then(function(arr) {
- * expect(arr.length).toEqual(3);
- * });
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * $$('.items li').then(function(arr) {
- * expect(arr.length).toEqual(3);
- * });
- *
- * @param {function(Array.<ElementFinder>)} fn
- * @param {function(Error)} errorFn
- *
- * @returns {!webdriver.promise.Promise} A promise which will resolve to
- * an array of ElementFinders represented by the ElementArrayFinder.
- */
- then(fn, errorFn) {
- if (this.actionResults_) {
- return this.actionResults_.then(fn, errorFn);
- }
- else {
- return this.asElementFinders_().then(fn, errorFn);
- }
- }
- /**
- * Calls the input function on each ElementFinder represented by the
- * ElementArrayFinder.
- *
- * @alias element.all(locator).each(eachFunction)
- * @view
- * <ul class="items">
- * <li>First</li>
- * <li>Second</li>
- * <li>Third</li>
- * </ul>
- *
- * @example
- * element.all(by.css('.items li')).each(function(element, index) {
- * // Will print 0 First, 1 Second, 2 Third.
- * element.getText().then(function (text) {
- * console.log(index, text);
- * });
- * });
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * $$('.items li').each(function(element, index) {
- * // Will print 0 First, 1 Second, 2 Third.
- * element.getText().then(function (text) {
- * console.log(index, text);
- * });
- * });
- *
- * @param {function(ElementFinder)} fn Input function
- *
- * @returns {!webdriver.promise.Promise} A promise that will resolve when the
- * function has been called on all the ElementFinders. The promise will
- * resolve to null.
- */
- each(fn) {
- return this.map(fn).then(() => {
- return null;
- });
- }
- /**
- * Apply a map function to each element within the ElementArrayFinder. The
- * callback receives the ElementFinder as the first argument and the index as
- * a second arg.
- *
- * @alias element.all(locator).map(mapFunction)
- * @view
- * <ul class="items">
- * <li class="one">First</li>
- * <li class="two">Second</li>
- * <li class="three">Third</li>
- * </ul>
- *
- * @example
- * let items = element.all(by.css('.items li')).map(function(elm, index) {
- * return {
- * index: index,
- * text: elm.getText(),
- * class: elm.getAttribute('class')
- * };
- * });
- * expect(items).toEqual([
- * {index: 0, text: 'First', class: 'one'},
- * {index: 1, text: 'Second', class: 'two'},
- * {index: 2, text: 'Third', class: 'three'}
- * ]);
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let items = $$('.items li').map(function(elm, index) {
- * return {
- * index: index,
- * text: elm.getText(),
- * class: elm.getAttribute('class')
- * };
- * });
- * expect(items).toEqual([
- * {index: 0, text: 'First', class: 'one'},
- * {index: 1, text: 'Second', class: 'two'},
- * {index: 2, text: 'Third', class: 'three'}
- * ]);
- *
- * @param {function(ElementFinder, number)} mapFn Map function that
- * will be applied to each element.
- * @returns {!webdriver.promise.Promise} A promise that resolves to an array
- * of values returned by the map function.
- */
- map(mapFn) {
- return this.asElementFinders_().then((arr) => {
- let list = arr.map((elementFinder, index) => {
- let mapResult = mapFn(elementFinder, index);
- // All nested arrays and objects will also be fully resolved.
- return selenium_webdriver_1.promise.fullyResolved(mapResult);
- });
- return selenium_webdriver_1.promise.all(list);
- });
- }
- ;
- /**
- * Apply a reduce function against an accumulator and every element found
- * using the locator (from left-to-right). The reduce function has to reduce
- * every element into a single value (the accumulator). Returns promise of
- * the accumulator. The reduce function receives the accumulator, current
- * ElementFinder, the index, and the entire array of ElementFinders,
- * respectively.
- *
- * @alias element.all(locator).reduce(reduceFn)
- * @view
- * <ul class="items">
- * <li class="one">First</li>
- * <li class="two">Second</li>
- * <li class="three">Third</li>
- * </ul>
- *
- * @example
- * let value = element.all(by.css('.items li')).reduce(function(acc, elem) {
- * return elem.getText().then(function(text) {
- * return acc + text + ' ';
- * });
- * }, '');
- *
- * expect(value).toEqual('First Second Third ');
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let value = $$('.items li').reduce(function(acc, elem) {
- * return elem.getText().then(function(text) {
- * return acc + text + ' ';
- * });
- * }, '');
- *
- * expect(value).toEqual('First Second Third ');
- *
- * @param {function(number, ElementFinder, number, Array.<ElementFinder>)}
- * reduceFn Reduce function that reduces every element into a single
- * value.
- * @param {*} initialValue Initial value of the accumulator.
- * @returns {!webdriver.promise.Promise} A promise that resolves to the final
- * value of the accumulator.
- */
- reduce(reduceFn, initialValue) {
- let valuePromise = selenium_webdriver_1.promise.when(initialValue);
- return this.asElementFinders_().then((arr) => {
- return arr.reduce((valuePromise, elementFinder, index) => {
- return valuePromise.then((value) => {
- return reduceFn(value, elementFinder, index, arr);
- });
- }, valuePromise);
- });
- }
- /**
- * Evaluates the input as if it were on the scope of the current underlying
- * elements.
- *
- * @view
- * <span class="foo">{{letiableInScope}}</span>
- *
- * @example
- * let value = element.all(by.css('.foo')).evaluate('letiableInScope');
- *
- * // Or using the shortcut $$() notation instead of element.all(by.css()):
- *
- * let value = $$('.foo').evaluate('letiableInScope');
- *
- * @param {string} expression
- *
- * @returns {ElementArrayFinder} which resolves to the
- * evaluated expression for each underlying element.
- * The result will be resolved as in
- * {@link webdriver.WebDriver.executeScript}. In summary - primitives will
- * be resolved as is, functions will be converted to string, and elements
- * will be returned as a WebElement.
- */
- evaluate(expression) {
- let evaluationFn = (webElem) => {
- return webElem.getDriver().executeScript(clientSideScripts.evaluate, webElem, expression);
- };
- return this.applyAction_(evaluationFn);
- }
- /**
- * Determine if animation is allowed on the current underlying elements.
- * @param {string} value
- *
- * @example
- * // Turns off ng-animate animations for all elements in the <body>
- * element(by.css('body')).allowAnimations(false);
- *
- * // Or using the shortcut $() notation instead of element(by.css()):
- *
- * $('body').allowAnimations(false);
- *
- * @returns {ElementArrayFinder} which resolves to whether animation is
- * allowed.
- */
- allowAnimations(value) {
- let allowAnimationsTestFn = (webElem) => {
- return webElem.getDriver().executeScript(clientSideScripts.allowAnimations, webElem, value);
- };
- return this.applyAction_(allowAnimationsTestFn);
- }
- }
- exports.ElementArrayFinder = ElementArrayFinder;
- /**
- * The ElementFinder simply represents a single element of an
- * ElementArrayFinder (and is more like a convenience object). As a result,
- * anything that can be done with an ElementFinder, can also be done using
- * an ElementArrayFinder.
- *
- * The ElementFinder can be treated as a WebElement for most purposes, in
- * particular, you may perform actions (i.e. click, getText) on them as you
- * would a WebElement. Once an action is performed on an ElementFinder, the
- * latest result from the chain can be accessed using the then method.
- * Unlike a WebElement, an ElementFinder will wait for angular to settle before
- * performing finds or actions.
- *
- * ElementFinder can be used to build a chain of locators that is used to find
- * an element. An ElementFinder does not actually attempt to find the element
- * until an action is called, which means they can be set up in helper files
- * before the page is available.
- *
- * @alias element(locator)
- * @view
- * <span>{{person.name}}</span>
- * <span ng-bind="person.email"></span>
- * <input type="text" ng-model="person.name"/>
- *
- * @example
- * // Find element with {{scopelet}} syntax.
- * element(by.binding('person.name')).getText().then(function(name) {
- * expect(name).toBe('Foo');
- * });
- *
- * // Find element with ng-bind="scopelet" syntax.
- * expect(element(by.binding('person.email')).getText()).toBe('foo@bar.com');
- *
- * // Find by model.
- * let input = element(by.model('person.name'));
- * input.sendKeys('123');
- * expect(input.getAttribute('value')).toBe('Foo123');
- *
- * @constructor
- * @extends {webdriver.WebElement}
- * @param {ProtractorBrowser} browser_ A browser instance.
- * @param {ElementArrayFinder} elementArrayFinder The ElementArrayFinder
- * that this is branched from.
- * @returns {ElementFinder}
- */
- class ElementFinder extends WebdriverWebElement {
- constructor(browser_, elementArrayFinder) {
- super();
- this.browser_ = browser_;
- this.then = null;
- if (!elementArrayFinder) {
- throw new Error('BUG: elementArrayFinder cannot be empty');
- }
- this.parentElementArrayFinder = elementArrayFinder;
- // Only have a `then` method if the parent element array finder
- // has action results.
- if (this.parentElementArrayFinder.actionResults_) {
- // Access the underlying actionResult of ElementFinder.
- this.then =
- (fn, errorFn) => {
- return this.elementArrayFinder_.then((actionResults) => {
- if (!fn) {
- return actionResults[0];
- }
- return fn(actionResults[0]);
- }, errorFn);
- };
- }
- // This filter verifies that there is only 1 element returned by the
- // elementArrayFinder. It will warn if there are more than 1 element and
- // throw an error if there are no elements.
- let getWebElements = () => {
- return elementArrayFinder.getWebElements().then((webElements) => {
- if (webElements.length === 0) {
- throw new selenium_webdriver_1.error.NoSuchElementError('No element found using locator: ' + elementArrayFinder.locator().toString());
- }
- else {
- if (webElements.length > 1) {
- logger.warn('more than one element found for locator ' +
- elementArrayFinder.locator().toString() + ' - the first result will be used');
- }
- return [webElements[0]];
- }
- });
- };
- // Store a copy of the underlying elementArrayFinder, but with the more
- // restrictive getWebElements (which checks that there is only 1 element).
- this.elementArrayFinder_ = new ElementArrayFinder(this.browser_, getWebElements, elementArrayFinder.locator(), elementArrayFinder.actionResults_);
- WEB_ELEMENT_FUNCTIONS.forEach((fnName) => {
- (this)[fnName] = (...args) => {
- return (this.elementArrayFinder_)[fnName]
- .apply(this.elementArrayFinder_, args)
- .toElementFinder_();
- };
- });
- }
- static fromWebElement_(browser, webElem, locator) {
- let getWebElements = () => {
- return selenium_webdriver_1.promise.when([webElem]);
- };
- return new ElementArrayFinder(browser, getWebElements, locator).toElementFinder_();
- }
- /**
- * Create a shallow copy of ElementFinder.
- *
- * @returns {!ElementFinder} A shallow copy of this.
- */
- clone() {
- // A shallow copy is all we need since the underlying fields can never be
- // modified
- return new ElementFinder(this.browser_, this.parentElementArrayFinder);
- }
- /**
- * @see ElementArrayFinder.prototype.locator
- *
- * @returns {webdriver.Locator}
- */
- locator() {
- return this.elementArrayFinder_.locator();
- }
- /**
- * Returns the WebElement represented by this ElementFinder.
- * Throws the WebDriver error if the element doesn't exist.
- *
- * @alias element(locator).getWebElement()
- * @view
- * <div class="parent">
- * some text
- * </div>
- *
- * @example
- * // The following four expressions are equivalent.
- * $('.parent').getWebElement();
- * element(by.css('.parent')).getWebElement();
- * browser.driver.findElement(by.css('.parent'));
- * browser.findElement(by.css('.parent'));
- *
- * @returns {webdriver.WebElementPromise}
- */
- getWebElement() {
- let id = this.elementArrayFinder_.getWebElements().then((parentWebElements) => {
- return parentWebElements[0];
- });
- return new selenium_webdriver_1.WebElementPromise(this.browser_.driver, id);
- }
- /**
- * Calls to {@code all} may be chained to find an array of elements within a
- * parent.
- *
- * @alias element(locator).all(locator)
- * @view
- * <div class="parent">
- * <ul>
- * <li class="one">First</li>
- * <li class="two">Second</li>
- * <li class="three">Third</li>
- * </ul>
- * </div>
- *
- * @example
- * let items = element(by.css('.parent')).all(by.tagName('li'));
- *
- * // Or using the shortcut $() notation instead of element(by.css()):
- *
- * let items = $('.parent').all(by.tagName('li'));
- *
- * @param {webdriver.Locator} subLocator
- * @returns {ElementArrayFinder}
- */
- all(subLocator) {
- return this.elementArrayFinder_.all(subLocator);
- }
- /**
- * Calls to {@code element} may be chained to find elements within a parent.
- *
- * @alias element(locator).element(locator)
- * @view
- * <div class="parent">
- * <div class="child">
- * Child text
- * <div>{{person.phone}}</div>
- * </div>
- * </div>
- *
- * @example
- * // Chain 2 element calls.
- * let child = element(by.css('.parent')).
- * element(by.css('.child'));
- * expect(child.getText()).toBe('Child text\n555-123-4567');
- *
- * // Chain 3 element calls.
- * let triple = element(by.css('.parent')).
- * element(by.css('.child')).
- * element(by.binding('person.phone'));
- * expect(triple.getText()).toBe('555-123-4567');
- *
- * // Or using the shortcut $() notation instead of element(by.css()):
- *
- * // Chain 2 element calls.
- * let child = $('.parent').$('.child');
- * expect(child.getText()).toBe('Child text\n555-123-4567');
- *
- * // Chain 3 element calls.
- * let triple = $('.parent').$('.child').
- * element(by.binding('person.phone'));
- * expect(triple.getText()).toBe('555-123-4567');
- *
- * @param {webdriver.Locator} subLocator
- * @returns {ElementFinder}
- */
- element(subLocator) {
- return this.all(subLocator).toElementFinder_();
- }
- /**
- * Calls to {@code $$} may be chained to find an array of elements within a
- * parent.
- *
- * @alias element(locator).all(selector)
- * @view
- * <div class="parent">
- * <ul>
- * <li class="one">First</li>
- * <li class="two">Second</li>
- * <li class="three">Third</li>
- * </ul>
- * </div>
- *
- * @example
- * let items = element(by.css('.parent')).$$('li');
- *
- * // Or using the shortcut $() notation instead of element(by.css()):
- *
- * let items = $('.parent').$$('li');
- *
- * @param {string} selector a css selector
- * @returns {ElementArrayFinder}
- */
- $$(selector) {
- return this.all(selenium_webdriver_1.By.css(selector));
- }
- /**
- * Calls to {@code $} may be chained to find elements within a parent.
- *
- * @alias element(locator).$(selector)
- * @view
- * <div class="parent">
- * <div class="child">
- * Child text
- * <div>{{person.phone}}</div>
- * </div>
- * </div>
- *
- * @example
- * // Chain 2 element calls.
- * let child = element(by.css('.parent')).
- * $('.child');
- * expect(child.getText()).toBe('Child text\n555-123-4567');
- *
- * // Chain 3 element calls.
- * let triple = element(by.css('.parent')).
- * $('.child').
- * element(by.binding('person.phone'));
- * expect(triple.getText()).toBe('555-123-4567');
- *
- * // Or using the shortcut $() notation instead of element(by.css()):
- *
- * // Chain 2 element calls.
- * let child = $('.parent').$('.child');
- * expect(child.getText()).toBe('Child text\n555-123-4567');
- *
- * // Chain 3 element calls.
- * let triple = $('.parent').$('.child').
- * element(by.binding('person.phone'));
- * expect(triple.getText()).toBe('555-123-4567');
- *
- * @param {string} selector A css selector
- * @returns {ElementFinder}
- */
- $(selector) {
- return this.element(selenium_webdriver_1.By.css(selector));
- }
- /**
- * Determine whether the element is present on the page.
- *
- * @view
- * <span>{{person.name}}</span>
- *
- * @example
- * // Element exists.
- * expect(element(by.binding('person.name')).isPresent()).toBe(true);
- *
- * // Element not present.
- * expect(element(by.binding('notPresent')).isPresent()).toBe(false);
- *
- * @returns {webdriver.promise.Promise<boolean>} which resolves to whether
- * the element is present on the page.
- */
- isPresent() {
- return this.parentElementArrayFinder.getWebElements().then((arr) => {
- if (arr.length === 0) {
- return false;
- }
- return arr[0].isEnabled().then(() => {
- return true; // is present, whether it is enabled or not
- }, util_1.falseIfMissing);
- }, util_1.falseIfMissing);
- }
- /**
- * Same as ElementFinder.isPresent(), except this checks whether the element
- * identified by the subLocator is present, rather than the current element
- * finder, i.e.: `element(by.css('#abc')).element(by.css('#def')).isPresent()`
- * is identical to `element(by.css('#abc')).isElementPresent(by.css('#def'))`.
- *
- * // Or using the shortcut $() notation instead of element(by.css()):
- *
- * `$('#abc').$('#def').isPresent()` is identical to
- * `$('#abc').isElementPresent($('#def'))`.
- *
- * @see ElementFinder.isPresent
- *
- * @param {webdriver.Locator} subLocator Locator for element to look for.
- * @returns {webdriver.promise.Promise<boolean>} which resolves to whether
- * the subelement is present on the page.
- */
- isElementPresent(subLocator) {
- if (!subLocator) {
- throw new Error('SubLocator is not supplied as a parameter to ' +
- '`isElementPresent(subLocator)`. You are probably looking for the ' +
- 'function `isPresent()`.');
- }
- return this.element(subLocator).isPresent();
- }
- /**
- * Evaluates the input as if it were on the scope of the current element.
- * @see ElementArrayFinder.prototype.evaluate
- *
- * @view
- * <span id="foo">{{letiableInScope}}</span>
- *
- * @example
- * let value = element(by.id('foo')).evaluate('letiableInScope');
- *
- * @param {string} expression
- *
- * @returns {ElementFinder} which resolves to the evaluated expression.
- */
- evaluate(expression) {
- return this.elementArrayFinder_.evaluate(expression).toElementFinder_();
- }
- /**
- * @see ElementArrayFinder.prototype.allowAnimations.
- * @param {string} value
- *
- * @returns {ElementFinder} which resolves to whether animation is allowed.
- */
- allowAnimations(value) {
- return this.elementArrayFinder_.allowAnimations(value).toElementFinder_();
- }
- /**
- * Compares an element to this one for equality.
- *
- * @param {!ElementFinder|!webdriver.WebElement} The element to compare to.
- *
- * @returns {!webdriver.promise.Promise.<boolean>} A promise that will be
- * resolved to whether the two WebElements are equal.
- */
- equals(element) {
- return selenium_webdriver_1.WebElement.equals(this.getWebElement(), element.getWebElement ? element.getWebElement() :
- element);
- }
- }
- exports.ElementFinder = ElementFinder;
- /**
- * Shortcut for querying the document directly with css.
- * `element(by.css('.abc'))` is equivalent to `$('.abc')`
- *
- * @alias $(cssSelector)
- * @view
- * <div class="count">
- * <span class="one">First</span>
- * <span class="two">Second</span>
- * </div>
- *
- * @example
- * let item = $('.count .two');
- * expect(item.getText()).toBe('Second');
- *
- * @param {string} selector A css selector
- * @returns {ElementFinder} which identifies the located
- * {@link webdriver.WebElement}
- */
- exports.build$ = (element, by) => {
- return (selector) => {
- return element(by.css(selector));
- };
- };
- /**
- * Shortcut for querying the document directly with css.
- * `element.all(by.css('.abc'))` is equivalent to `$$('.abc')`
- *
- * @alias $$(cssSelector)
- * @view
- * <div class="count">
- * <span class="one">First</span>
- * <span class="two">Second</span>
- * </div>
- *
- * @example
- * // The following protractor expressions are equivalent.
- * let list = element.all(by.css('.count span'));
- * expect(list.count()).toBe(2);
- *
- * list = $$('.count span');
- * expect(list.count()).toBe(2);
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * @param {string} selector a css selector
- * @returns {ElementArrayFinder} which identifies the
- * array of the located {@link webdriver.WebElement}s.
- */
- exports.build$$ = (element, by) => {
- return (selector) => {
- return element.all(by.css(selector));
- };
- };
- //# sourceMappingURL=element.js.map
|