diff --git a/test/functional/services/common/find.ts b/test/functional/services/common/find.ts index 0cd4c14683f6e..8d037e2df2109 100644 --- a/test/functional/services/common/find.ts +++ b/test/functional/services/common/find.ts @@ -7,511 +7,474 @@ */ import { WebDriver, WebElement, By, until } from 'selenium-webdriver'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { WebElementWrapper } from '../lib/web_element_wrapper'; -export async function FindProvider({ getService }: FtrProviderContext) { - const log = getService('log'); - const config = getService('config'); - const { driver, browserType } = await getService('__webdriver__').init(); - const retry = getService('retry'); +import { Browsers } from '../remote/browsers'; +import { FtrService, FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; - const WAIT_FOR_EXISTS_TIME = config.get('timeouts.waitForExists'); - const POLLING_TIME = 500; - const defaultFindTimeout = config.get('timeouts.find'); - const fixedHeaderHeight = config.get('layout.fixedHeaderHeight'); +export class FindService extends FtrService { + private readonly log = this.ctx.getService('log'); + private readonly config = this.ctx.getService('config'); + private readonly retry = this.ctx.getService('retry'); - const wrap = (webElement: WebElement | WebElementWrapper, locator: By | null = null) => - WebElementWrapper.create( - webElement, - locator, - driver, - defaultFindTimeout, - fixedHeaderHeight, - log, - browserType - ); + private readonly WAIT_FOR_EXISTS_TIME = this.config.get('timeouts.waitForExists'); + private readonly POLLING_TIME = 500; + private readonly defaultFindTimeout = this.config.get('timeouts.find'); + private readonly fixedHeaderHeight = this.config.get('layout.fixedHeaderHeight'); - const wrapAll = (webElements: Array) => - webElements.map((e) => wrap(e)); + public currentWait = this.defaultFindTimeout; - const findAndWrap = async (locator: By, timeout: number): Promise => { - const webElement = await driver.wait(until.elementLocated(locator), timeout); - return wrap(webElement, locator); - }; + constructor( + ctx: FtrProviderContext, + private readonly browserType: Browsers, + private readonly driver: WebDriver + ) { + super(ctx); + } - class Find { - public currentWait = defaultFindTimeout; + public async byName( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.byName('${selector}') with timeout=${timeout}`); + return await this.findAndWrap(By.name(selector), timeout); + } - public async byName( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.byName('${selector}') with timeout=${timeout}`); - return await findAndWrap(By.name(selector), timeout); - } + public async byCssSelector( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.findByCssSelector('${selector}') with timeout=${timeout}`); + return this.findAndWrap(By.css(selector), timeout); + } - public async byCssSelector( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.findByCssSelector('${selector}') with timeout=${timeout}`); - return findAndWrap(By.css(selector), timeout); - } + public async byXPath( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.byXPath('${selector}') with timeout=${timeout}`); + return this.findAndWrap(By.xpath(selector), timeout); + } - public async byXPath( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.byXPath('${selector}') with timeout=${timeout}`); - return findAndWrap(By.xpath(selector), timeout); - } + public async byClassName( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.findByClassName('${selector}') with timeout=${timeout}`); + return this.findAndWrap(By.className(selector), timeout); + } - public async byClassName( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.findByClassName('${selector}') with timeout=${timeout}`); - return findAndWrap(By.className(selector), timeout); - } + public async activeElement(): Promise { + return this.wrap(await this.driver.switchTo().activeElement()); + } - public async activeElement(): Promise { - return wrap(await driver.switchTo().activeElement()); - } + public async setValue(selector: string, text: string, topOffset?: number): Promise { + this.log.debug(`Find.setValue('${selector}', '${text}')`); + return await this.retry.try(async () => { + const element = await this.byCssSelector(selector); + await element.click(topOffset); + + // in case the input element is actually a child of the testSubject, we + // call clearValue() and type() on the element that is focused after + // clicking on the testSubject + const input = await this.activeElement(); + if (input) { + await input.clearValue(); + await input.type(text); + } else { + await element.clearValue(); + await element.type(text); + } + }); + } - public async setValue(selector: string, text: string, topOffset?: number): Promise { - log.debug(`Find.setValue('${selector}', '${text}')`); - return await retry.try(async () => { - const element = await this.byCssSelector(selector); - await element.click(topOffset); + public async selectValue(selector: string, value: string): Promise { + this.log.debug(`Find.selectValue('${selector}', option[value="${value}"]')`); + const combobox = await this.byCssSelector(selector); + const $ = await combobox.parseDomContent(); + const text = $(`option[value="${value}"]`).text(); + await combobox.type(text); + } - // in case the input element is actually a child of the testSubject, we - // call clearValue() and type() on the element that is focused after - // clicking on the testSubject - const input = await this.activeElement(); - if (input) { - await input.clearValue(); - await input.type(text); - } else { - await element.clearValue(); - await element.type(text); + public async filterElementIsDisplayed(elements: WebElementWrapper[]) { + if (elements.length === 0) { + return []; + } else { + const displayed = []; + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < elements.length; i++) { + const isDisplayed = await elements[i].isDisplayed(); + if (isDisplayed) { + displayed.push(elements[i]); } - }); + } + return displayed; } + } - public async selectValue(selector: string, value: string): Promise { - log.debug(`Find.selectValue('${selector}', option[value="${value}"]')`); - const combobox = await this.byCssSelector(selector); - const $ = await combobox.parseDomContent(); - const text = $(`option[value="${value}"]`).text(); - await combobox.type(text); - } + public async allByCssSelector( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.allByCssSelector('${selector}') with timeout=${timeout}`); + await this._withTimeout(timeout); + const elements = await this.driver.findElements(By.css(selector)); + await this._withTimeout(this.defaultFindTimeout); + return this.wrapAll(elements); + } - public async filterElementIsDisplayed(elements: WebElementWrapper[]) { - if (elements.length === 0) { - return []; - } else { - const displayed = []; - // tslint:disable-next-line:prefer-for-of - for (let i = 0; i < elements.length; i++) { - const isDisplayed = await elements[i].isDisplayed(); - if (isDisplayed) { - displayed.push(elements[i]); - } - } - return displayed; - } - } + public async descendantExistsByCssSelector( + selector: string, + parentElement: WebElementWrapper, + timeout: number = this.WAIT_FOR_EXISTS_TIME + ): Promise { + this.log.debug(`Find.descendantExistsByCssSelector('${selector}') with timeout=${timeout}`); + const els = await parentElement._webElement.findElements(By.css(selector)); + return await this.exists(async () => this.wrapAll(els), timeout); + } - public async allByCustom( - findAllFunction: (drive: WebDriver) => WebElementWrapper[], - timeout = defaultFindTimeout - ): Promise { - await this._withTimeout(timeout); - return await retry.try(async () => { - let elements = await findAllFunction(driver); - if (!elements) { - elements = []; - } - // Force isStale checks for all the retrieved elements. - await Promise.all(elements.map(async (element) => await element.isEnabled())); - await this._withTimeout(defaultFindTimeout); - return elements; - }); + public async descendantDisplayedByCssSelector( + selector: string, + parentElement: WebElementWrapper + ): Promise { + this.log.debug(`Find.descendantDisplayedByCssSelector('${selector}')`); + const element = await parentElement._webElement.findElement(By.css(selector)); + const descendant = this.wrap(element, By.css(selector)); + const isDisplayed = await descendant.isDisplayed(); + if (isDisplayed) { + return descendant; + } else { + throw new Error(`Element "${selector}" is not displayed`); } + } - public async allByLinkText( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.allByLinkText('${selector}') with timeout=${timeout}`); - await this._withTimeout(timeout); - const elements = await driver.findElements(By.linkText(selector)); - await this._withTimeout(defaultFindTimeout); - return wrapAll(elements); - } + public async allDescendantDisplayedByCssSelector( + selector: string, + parentElement: WebElementWrapper + ): Promise { + this.log.debug(`Find.allDescendantDisplayedByCssSelector('${selector}')`); + const allElements = await this.wrapAll( + await parentElement._webElement.findElements(By.css(selector)) + ); + return await this.filterElementIsDisplayed(allElements); + } - public async allByButtonText( - buttonText: string, - element: WebDriver | WebElement | WebElementWrapper = driver, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`); - return await retry.tryForTime(timeout, async () => { - // tslint:disable-next-line:variable-name - const _element = element instanceof WebElementWrapper ? element._webElement : element; - await this._withTimeout(0); - const allButtons = wrapAll(await _element.findElements(By.tagName('button'))); - await this._withTimeout(defaultFindTimeout); - const buttonTexts = await Promise.all( - allButtons.map(async (el) => { - return el.getVisibleText(); - }) - ); - return buttonTexts.filter((text) => text.trim() === buttonText.trim()); - }); - } + public async allDescendantDisplayedByTagName( + tagName: string, + parentElement: WebElementWrapper + ): Promise { + this.log.debug(`Find.allDescendantDisplayedByTagName('${tagName}')`); + const allElements = await this.wrapAll( + await parentElement._webElement.findElements(By.tagName(tagName)) + ); + return await this.filterElementIsDisplayed(allElements); + } - public async allByCssSelector( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.allByCssSelector('${selector}') with timeout=${timeout}`); - await this._withTimeout(timeout); - const elements = await driver.findElements(By.css(selector)); - await this._withTimeout(defaultFindTimeout); - return wrapAll(elements); - } + public async displayedByLinkText( + linkText: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.displayedByLinkText('${linkText}') with timeout=${timeout}`); + const element = await this.byLinkText(linkText, timeout); + this.log.debug(`Wait for element become visible: ${linkText} with timeout=${timeout}`); + await this.driver.wait(until.elementIsVisible(element._webElement), timeout); + return this.wrap(element, By.linkText(linkText)); + } - public async descendantExistsByCssSelector( - selector: string, - parentElement: WebElementWrapper, - timeout: number = WAIT_FOR_EXISTS_TIME - ): Promise { - log.debug(`Find.descendantExistsByCssSelector('${selector}') with timeout=${timeout}`); - const els = await parentElement._webElement.findElements(By.css(selector)); - return await this.exists(async () => wrapAll(els), timeout); - } + public async displayedByCssSelector( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.displayedByCssSelector(${selector})`); + const element = await this.byCssSelector(selector, timeout); + this.log.debug(`Wait for element become visible: ${selector} with timeout=${timeout}`); + await this.driver.wait(until.elementIsVisible(element._webElement), timeout); + return this.wrap(element, By.css(selector)); + } + + public async byLinkText( + selector: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.byLinkText('${selector}') with timeout=${timeout}`); + return this.findAndWrap(By.linkText(selector), timeout); + } - public async descendantDisplayedByCssSelector( - selector: string, - parentElement: WebElementWrapper - ): Promise { - log.debug(`Find.descendantDisplayedByCssSelector('${selector}')`); - const element = await parentElement._webElement.findElement(By.css(selector)); - const descendant = wrap(element, By.css(selector)); - const isDisplayed = await descendant.isDisplayed(); - if (isDisplayed) { - return descendant; + public async byPartialLinkText( + partialLinkText: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.byPartialLinkText('${partialLinkText}') with timeout=${timeout}`); + return this.findAndWrap(By.partialLinkText(partialLinkText), timeout); + } + + public async exists( + findFunction: ( + el: WebDriver + ) => + | WebElementWrapper + | WebElementWrapper[] + | Promise + | Promise, + timeout: number = this.WAIT_FOR_EXISTS_TIME + ): Promise { + await this._withTimeout(timeout); + try { + const found = await findFunction(this.driver); + await this._withTimeout(this.defaultFindTimeout); + if (Array.isArray(found)) { + return found.length > 0; } else { - throw new Error(`Element "${selector}" is not displayed`); + return found instanceof WebElementWrapper; } + } catch (err) { + await this._withTimeout(this.defaultFindTimeout); + return false; } + } - public async allDescendantDisplayedByCssSelector( - selector: string, - parentElement: WebElementWrapper - ): Promise { - log.debug(`Find.allDescendantDisplayedByCssSelector('${selector}')`); - const allElements = await wrapAll( - await parentElement._webElement.findElements(By.css(selector)) - ); - return await this.filterElementIsDisplayed(allElements); - } - - public async allDescendantDisplayedByTagName( - tagName: string, - parentElement: WebElementWrapper - ): Promise { - log.debug(`Find.allDescendantDisplayedByTagName('${tagName}')`); - const allElements = await wrapAll( - await parentElement._webElement.findElements(By.tagName(tagName)) - ); - return await this.filterElementIsDisplayed(allElements); - } + public async existsByLinkText( + linkText: string, + timeout: number = this.WAIT_FOR_EXISTS_TIME + ): Promise { + this.log.debug(`Find.existsByLinkText('${linkText}') with timeout=${timeout}`); + return await this.exists( + async (drive) => this.wrapAll(await drive.findElements(By.linkText(linkText))), + timeout + ); + } - public async displayedByLinkText( - linkText: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.displayedByLinkText('${linkText}') with timeout=${timeout}`); - const element = await this.byLinkText(linkText, timeout); - log.debug(`Wait for element become visible: ${linkText} with timeout=${timeout}`); - await driver.wait(until.elementIsVisible(element._webElement), timeout); - return wrap(element, By.linkText(linkText)); + public async existsByDisplayedByCssSelector( + selector: string, + timeout: number = this.WAIT_FOR_EXISTS_TIME + ): Promise { + this.log.debug(`Find.existsByDisplayedByCssSelector('${selector}') with timeout=${timeout}`); + try { + await this.retry.tryForTime(timeout, async () => { + // make sure that the find timeout is not longer than the retry timeout + await this._withTimeout(Math.min(timeout, this.WAIT_FOR_EXISTS_TIME)); + const elements = await this.driver.findElements(By.css(selector)); + await this._withTimeout(this.defaultFindTimeout); + const displayed = await this.filterElementIsDisplayed(this.wrapAll(elements)); + if (displayed.length === 0) { + throw new Error(`${selector} is not displayed`); + } + }); + } catch (err) { + await this._withTimeout(this.defaultFindTimeout); + return false; } + return true; + } - public async displayedByCssSelector( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.displayedByCssSelector(${selector})`); - const element = await this.byCssSelector(selector, timeout); - log.debug(`Wait for element become visible: ${selector} with timeout=${timeout}`); - await driver.wait(until.elementIsVisible(element._webElement), timeout); - return wrap(element, By.css(selector)); - } + public async existsByCssSelector( + selector: string, + timeout: number = this.WAIT_FOR_EXISTS_TIME + ): Promise { + this.log.debug(`Find.existsByCssSelector('${selector}') with timeout=${timeout}`); + return await this.exists(async (drive) => { + return this.wrapAll(await drive.findElements(By.css(selector))); + }, timeout); + } - public async byLinkText( - selector: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.byLinkText('${selector}') with timeout=${timeout}`); - return findAndWrap(By.linkText(selector), timeout); - } + public async clickByCssSelectorWhenNotDisabled( + selector: string, + { timeout } = { timeout: this.defaultFindTimeout } + ): Promise { + this.log.debug(`Find.clickByCssSelectorWhenNotDisabled('${selector}') with timeout=${timeout}`); + + // Don't wrap this code in a retry, or stale element checks may get caught here and the element + // will never be re-grabbed. Let errors bubble, but continue checking for disabled property until + // it's gone. + const element = await this.byCssSelector(selector, timeout); + await element.moveMouseTo(); + await this.driver.wait(until.elementIsEnabled(element._webElement), timeout); + await element.click(); + } - public async byPartialLinkText( - partialLinkText: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.byPartialLinkText('${partialLinkText}') with timeout=${timeout}`); - return findAndWrap(By.partialLinkText(partialLinkText), timeout); - } + public async clickByPartialLinkText( + linkText: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.clickByPartialLinkText('${linkText}') with timeout=${timeout}`); + await this.retry.try(async () => { + const element = await this.byPartialLinkText(linkText, timeout); + await element.moveMouseTo(); + await element.click(); + }); + } - public async exists( - findFunction: ( - el: WebDriver - ) => - | WebElementWrapper - | WebElementWrapper[] - | Promise - | Promise, - timeout: number = WAIT_FOR_EXISTS_TIME - ): Promise { - await this._withTimeout(timeout); - try { - const found = await findFunction(driver); - await this._withTimeout(defaultFindTimeout); - if (Array.isArray(found)) { - return found.length > 0; - } else { - return found instanceof WebElementWrapper; - } - } catch (err) { - await this._withTimeout(defaultFindTimeout); - return false; - } - } + public async clickByLinkText( + linkText: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.clickByLinkText('${linkText}') with timeout=${timeout}`); + await this.retry.try(async () => { + const element = await this.byLinkText(linkText, timeout); + await element.moveMouseTo(); + await element.click(); + }); + } - public async existsByLinkText( - linkText: string, - timeout: number = WAIT_FOR_EXISTS_TIME - ): Promise { - log.debug(`Find.existsByLinkText('${linkText}') with timeout=${timeout}`); - return await this.exists( - async (drive) => wrapAll(await drive.findElements(By.linkText(linkText))), - timeout + public async byButtonText( + buttonText: string, + element: WebDriver | WebElement | WebElementWrapper = this.driver, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`); + return await this.retry.tryForTime(timeout, async () => { + // tslint:disable-next-line:variable-name + const _element = element instanceof WebElementWrapper ? element._webElement : element; + const allButtons = this.wrapAll(await _element.findElements(By.tagName('button'))); + const buttonTexts = await Promise.all( + allButtons.map(async (el) => { + return el.getVisibleText(); + }) ); - } - - public async existsByDisplayedByCssSelector( - selector: string, - timeout: number = WAIT_FOR_EXISTS_TIME - ): Promise { - log.debug(`Find.existsByDisplayedByCssSelector('${selector}') with timeout=${timeout}`); - try { - await retry.tryForTime(timeout, async () => { - // make sure that the find timeout is not longer than the retry timeout - await this._withTimeout(Math.min(timeout, WAIT_FOR_EXISTS_TIME)); - const elements = await driver.findElements(By.css(selector)); - await this._withTimeout(defaultFindTimeout); - const displayed = await this.filterElementIsDisplayed(wrapAll(elements)); - if (displayed.length === 0) { - throw new Error(`${selector} is not displayed`); - } - }); - } catch (err) { - await this._withTimeout(defaultFindTimeout); - return false; + const index = buttonTexts.findIndex((text) => text.trim() === buttonText.trim()); + if (index === -1) { + throw new Error('Button not found'); } - return true; - } - - public async existsByCssSelector( - selector: string, - timeout: number = WAIT_FOR_EXISTS_TIME - ): Promise { - log.debug(`Find.existsByCssSelector('${selector}') with timeout=${timeout}`); - return await this.exists(async (drive) => { - return wrapAll(await drive.findElements(By.css(selector))); - }, timeout); - } + return allButtons[index]; + }); + } - public async clickByCssSelectorWhenNotDisabled( - selector: string, - { timeout } = { timeout: defaultFindTimeout } - ): Promise { - log.debug(`Find.clickByCssSelectorWhenNotDisabled('${selector}') with timeout=${timeout}`); + public async clickByButtonText( + buttonText: string, + element: WebDriver | WebElement | WebElementWrapper = this.driver, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.clickByButtonText('${buttonText}') with timeout=${timeout}`); + await this.retry.try(async () => { + const button = await this.byButtonText(buttonText, element, timeout); + await button.click(); + }); + } - // Don't wrap this code in a retry, or stale element checks may get caught here and the element - // will never be re-grabbed. Let errors bubble, but continue checking for disabled property until - // it's gone. + public async clickByCssSelector( + selector: string, + timeout: number = this.defaultFindTimeout, + topOffset?: number + ): Promise { + this.log.debug(`Find.clickByCssSelector('${selector}') with timeout=${timeout}`); + await this.retry.try(async () => { const element = await this.byCssSelector(selector, timeout); - await element.moveMouseTo(); - await driver.wait(until.elementIsEnabled(element._webElement), timeout); - await element.click(); - } + if (element) { + // await element.moveMouseTo(); + await element.click(topOffset); + } else { + throw new Error(`Element with css='${selector}' is not found`); + } + }); + } - public async clickByPartialLinkText( - linkText: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.clickByPartialLinkText('${linkText}') with timeout=${timeout}`); - await retry.try(async () => { - const element = await this.byPartialLinkText(linkText, timeout); + public async clickByDisplayedLinkText( + linkText: string, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.clickByDisplayedLinkText('${linkText}') with timeout=${timeout}`); + await this.retry.try(async () => { + const element = await this.displayedByLinkText(linkText, timeout); + if (element) { await element.moveMouseTo(); await element.click(); - }); - } + } else { + throw new Error(`Element with linkText='${linkText}' is not found`); + } + }); + } - public async clickByLinkText( - linkText: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.clickByLinkText('${linkText}') with timeout=${timeout}`); - await retry.try(async () => { - const element = await this.byLinkText(linkText, timeout); + public async clickDisplayedByCssSelector( + selector: string, + timeout: number = this.defaultFindTimeout + ) { + this.log.debug(`Find.clickDisplayedByCssSelector('${selector}') with timeout=${timeout}`); + await this.retry.try(async () => { + const element = await this.displayedByCssSelector(selector, timeout); + if (element) { await element.moveMouseTo(); await element.click(); - }); - } - - public async byButtonText( - buttonText: string, - element: WebDriver | WebElement | WebElementWrapper = driver, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`); - return await retry.tryForTime(timeout, async () => { - // tslint:disable-next-line:variable-name - const _element = element instanceof WebElementWrapper ? element._webElement : element; - const allButtons = wrapAll(await _element.findElements(By.tagName('button'))); - const buttonTexts = await Promise.all( - allButtons.map(async (el) => { - return el.getVisibleText(); - }) - ); - const index = buttonTexts.findIndex((text) => text.trim() === buttonText.trim()); - if (index === -1) { - throw new Error('Button not found'); - } - return allButtons[index]; - }); - } - - public async clickByButtonText( - buttonText: string, - element: WebDriver | WebElement | WebElementWrapper = driver, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.clickByButtonText('${buttonText}') with timeout=${timeout}`); - await retry.try(async () => { - const button = await this.byButtonText(buttonText, element, timeout); - await button.click(); - }); - } + } else { + throw new Error(`Element with css='${selector}' is not found`); + } + }); + } - public async clickByCssSelector( - selector: string, - timeout: number = defaultFindTimeout, - topOffset?: number - ): Promise { - log.debug(`Find.clickByCssSelector('${selector}') with timeout=${timeout}`); - await retry.try(async () => { - const element = await this.byCssSelector(selector, timeout); - if (element) { - // await element.moveMouseTo(); - await element.click(topOffset); - } else { - throw new Error(`Element with css='${selector}' is not found`); - } - }); - } + public async waitForDeletedByCssSelector( + selector: string, + timeout: number = this.defaultFindTimeout + ) { + this.log.debug(`Find.waitForDeletedByCssSelector('${selector}') with timeout=${timeout}`); + await this._withTimeout(this.POLLING_TIME); + await this.driver.wait( + async () => { + const found = await this.driver.findElements(By.css(selector)); + return found.length === 0; + }, + timeout, + `The element ${selector} was still present when it should have disappeared.` + ); + await this._withTimeout(this.defaultFindTimeout); + } - public async clickByDisplayedLinkText( - linkText: string, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.clickByDisplayedLinkText('${linkText}') with timeout=${timeout}`); - await retry.try(async () => { - const element = await this.displayedByLinkText(linkText, timeout); - if (element) { - await element.moveMouseTo(); - await element.click(); - } else { - throw new Error(`Element with linkText='${linkText}' is not found`); - } - }); - } + public async waitForAttributeToChange( + selector: string, + attribute: string, + value: string + ): Promise { + this.log.debug(`Find.waitForAttributeToChange('${selector}', '${attribute}', '${value}')`); + await this.retry.waitFor(`${attribute} to equal "${value}"`, async () => { + const el = await this.byCssSelector(selector); + return value === (await el.getAttribute(attribute)); + }); + } - public async clickDisplayedByCssSelector( - selector: string, - timeout: number = defaultFindTimeout - ) { - log.debug(`Find.clickDisplayedByCssSelector('${selector}') with timeout=${timeout}`); - await retry.try(async () => { - const element = await this.displayedByCssSelector(selector, timeout); - if (element) { - await element.moveMouseTo(); - await element.click(); - } else { - throw new Error(`Element with css='${selector}' is not found`); - } - }); - } + public async waitForElementStale( + element: WebElementWrapper, + timeout: number = this.defaultFindTimeout + ): Promise { + this.log.debug(`Find.waitForElementStale with timeout=${timeout}`); + await this.driver.wait(until.stalenessOf(element._webElement), timeout); + } - public async waitForDeletedByCssSelector( - selector: string, - timeout: number = defaultFindTimeout - ) { - log.debug(`Find.waitForDeletedByCssSelector('${selector}') with timeout=${timeout}`); - await this._withTimeout(POLLING_TIME); - await driver.wait( - async () => { - const found = await driver.findElements(By.css(selector)); - return found.length === 0; - }, - timeout, - `The element ${selector} was still present when it should have disappeared.` - ); - await this._withTimeout(defaultFindTimeout); - } + public async waitForElementHidden( + element: WebElementWrapper, + timeout: number = this.defaultFindTimeout + ) { + this.log.debug(`Find.waitForElementHidden with timeout=${timeout}`); + await this.driver.wait(until.elementIsNotVisible(element._webElement), timeout); + } - public async waitForAttributeToChange( - selector: string, - attribute: string, - value: string - ): Promise { - log.debug(`Find.waitForAttributeToChange('${selector}', '${attribute}', '${value}')`); - await retry.waitFor(`${attribute} to equal "${value}"`, async () => { - const el = await this.byCssSelector(selector); - return value === (await el.getAttribute(attribute)); - }); + private async _withTimeout(timeout: number) { + if (timeout !== this.currentWait) { + this.currentWait = timeout; + await this.driver.manage().setTimeouts({ implicit: timeout }); } + } - public async waitForElementStale( - element: WebElementWrapper, - timeout: number = defaultFindTimeout - ): Promise { - log.debug(`Find.waitForElementStale with timeout=${timeout}`); - await driver.wait(until.stalenessOf(element._webElement), timeout); - } + private wrap(webElement: WebElement | WebElementWrapper, locator: By | null = null) { + return WebElementWrapper.create( + webElement, + locator, + this.driver, + this.defaultFindTimeout, + this.fixedHeaderHeight, + this.log, + this.browserType + ); + } - public async waitForElementHidden( - element: WebElementWrapper, - timeout: number = defaultFindTimeout - ) { - log.debug(`Find.waitForElementHidden with timeout=${timeout}`); - await driver.wait(until.elementIsNotVisible(element._webElement), timeout); - } + private wrapAll(webElements: Array) { + return webElements.map((e) => this.wrap(e)); + } - private async _withTimeout(timeout: number) { - if (timeout !== this.currentWait) { - this.currentWait = timeout; - await driver.manage().setTimeouts({ implicit: timeout }); - } - } + private async findAndWrap(locator: By, timeout: number): Promise { + const webElement = await this.driver.wait(until.elementLocated(locator), timeout); + return this.wrap(webElement, locator); } +} - return new Find(); +export async function FindProvider(ctx: FtrProviderContext) { + const { browserType, driver } = await ctx.getService('__webdriver__').init(); + return new FindService(ctx, browserType, driver); }