From bff524e25b30ff3eedf33a37cdc61e364643e0b2 Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 24 May 2021 15:03:08 -0700 Subject: [PATCH] [ftr] migrate "testSubjects" to FtrService class --- test/functional/services/common/index.ts | 2 +- .../services/common/test_subjects.ts | 560 +++++++++--------- test/functional/services/index.ts | 4 +- 3 files changed, 284 insertions(+), 282 deletions(-) diff --git a/test/functional/services/common/index.ts b/test/functional/services/common/index.ts index e491b964c8ff6..a928b3ddf9378 100644 --- a/test/functional/services/common/index.ts +++ b/test/functional/services/common/index.ts @@ -11,4 +11,4 @@ export { FailureDebuggingProvider } from './failure_debugging'; export { FindProvider } from './find'; export { ScreenshotsProvider } from './screenshots'; export { SnapshotsProvider } from './snapshots'; -export { TestSubjectsProvider, TestSubjects } from './test_subjects'; +export { TestSubjects } from './test_subjects'; diff --git a/test/functional/services/common/test_subjects.ts b/test/functional/services/common/test_subjects.ts index d0050859cbb32..ae04fe5d2b939 100644 --- a/test/functional/services/common/test_subjects.ts +++ b/test/functional/services/common/test_subjects.ts @@ -8,9 +8,8 @@ import testSubjSelector from '@kbn/test-subj-selector'; import { map as mapAsync } from 'bluebird'; -import { ProvidedType } from '@kbn/test'; import { WebElementWrapper } from '../lib/web_element_wrapper'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrService } from '../../ftr_provider_context'; interface ExistsOptions { timeout?: number; @@ -22,326 +21,329 @@ interface SetValueOptions { typeCharByChar?: boolean; } -export type TestSubjects = ProvidedType; -export function TestSubjectsProvider({ getService }: FtrProviderContext) { - const log = getService('log'); - const retry = getService('retry'); - const find = getService('find'); - const config = getService('config'); - - const FIND_TIME = config.get('timeouts.find'); - const TRY_TIME = config.get('timeouts.try'); - const WAIT_FOR_EXISTS_TIME = config.get('timeouts.waitForExists'); - - class TestSubjects { - public async exists(selector: string, options: ExistsOptions = {}): Promise { - const { timeout = WAIT_FOR_EXISTS_TIME, allowHidden = false } = options; - - log.debug(`TestSubjects.exists(${selector})`); - return await (allowHidden - ? find.existsByCssSelector(testSubjSelector(selector), timeout) - : find.existsByDisplayedByCssSelector(testSubjSelector(selector), timeout)); - } - - public async existOrFail( - selector: string, - existsOptions?: ExistsOptions - ): Promise { - if (!(await this.exists(selector, { timeout: TRY_TIME, ...existsOptions }))) { - throw new Error(`expected testSubject(${selector}) to exist`); - } - } +export class TestSubjects extends FtrService { + public readonly log = this.ctx.getService('log'); + public readonly retry = this.ctx.getService('retry'); + public readonly findService = this.ctx.getService('find'); + public readonly config = this.ctx.getService('config'); - public async missingOrFail( - selector: string, - options: ExistsOptions = {} - ): Promise { - const { timeout = WAIT_FOR_EXISTS_TIME, allowHidden = false } = options; + public readonly FIND_TIME = this.config.get('timeouts.find'); + public readonly TRY_TIME = this.config.get('timeouts.try'); + public readonly WAIT_FOR_EXISTS_TIME = this.config.get('timeouts.waitForExists'); - log.debug(`TestSubjects.missingOrFail(${selector})`); - return await (allowHidden - ? this.waitForHidden(selector, timeout) - : find.waitForDeletedByCssSelector(testSubjSelector(selector), timeout)); - } + public async exists(selector: string, options: ExistsOptions = {}): Promise { + const { timeout = this.WAIT_FOR_EXISTS_TIME, allowHidden = false } = options; - async stringExistsInCodeBlockOrFail(codeBlockSelector: string, stringToFind: string) { - await retry.try(async () => { - const responseCodeBlock = await this.find(codeBlockSelector); - const spans = await find.allDescendantDisplayedByTagName('span', responseCodeBlock); - const foundInSpans = await Promise.all( - spans.map(async (span) => { - const text = await span.getVisibleText(); - if (text === stringToFind) { - log.debug(`"${text}" matched "${stringToFind}"!`); - return true; - } else { - log.debug(`"${text}" did not match "${stringToFind}"`); - } - }) - ); - if (!foundInSpans.find((foundInSpan) => foundInSpan)) { - throw new Error(`"${stringToFind}" was not found. Trying again...`); - } - }); - } + this.log.debug(`TestSubjects.exists(${selector})`); + return await (allowHidden + ? this.findService.existsByCssSelector(testSubjSelector(selector), timeout) + : this.findService.existsByDisplayedByCssSelector(testSubjSelector(selector), timeout)); + } - public async append(selector: string, text: string): Promise { - log.debug(`TestSubjects.append(${selector}, ${text})`); - const input = await this.find(selector); - await input.click(); - await input.type(text); + public async existOrFail(selector: string, existsOptions?: ExistsOptions): Promise { + if (!(await this.exists(selector, { timeout: this.TRY_TIME, ...existsOptions }))) { + throw new Error(`expected testSubject(${selector}) to exist`); } + } - public async clickWhenNotDisabled( - selector: string, - { timeout = FIND_TIME }: { timeout?: number } = {} - ): Promise { - log.debug(`TestSubjects.clickWhenNotDisabled(${selector})`); - await find.clickByCssSelectorWhenNotDisabled(testSubjSelector(selector), { timeout }); - } + public async missingOrFail(selector: string, options: ExistsOptions = {}): Promise { + const { timeout = this.WAIT_FOR_EXISTS_TIME, allowHidden = false } = options; - public async click( - selector: string, - timeout: number = FIND_TIME, - topOffset?: number - ): Promise { - log.debug(`TestSubjects.click(${selector})`); - await find.clickByCssSelector(testSubjSelector(selector), timeout, topOffset); - } + this.log.debug(`TestSubjects.missingOrFail(${selector})`); + return await (allowHidden + ? this.waitForHidden(selector, timeout) + : this.findService.waitForDeletedByCssSelector(testSubjSelector(selector), timeout)); + } - public async doubleClick(selector: string, timeout: number = FIND_TIME): Promise { - log.debug(`TestSubjects.doubleClick(${selector})`); - const element = await this.find(selector, timeout); - await element.moveMouseTo(); - await element.doubleClick(); - } + async stringExistsInCodeBlockOrFail(codeBlockSelector: string, stringToFind: string) { + await this.retry.try(async () => { + const responseCodeBlock = await this.find(codeBlockSelector); + const spans = await this.findService.allDescendantDisplayedByTagName( + 'span', + responseCodeBlock + ); + const foundInSpans = await Promise.all( + spans.map(async (span) => { + const text = await span.getVisibleText(); + if (text === stringToFind) { + this.log.debug(`"${text}" matched "${stringToFind}"!`); + return true; + } else { + this.log.debug(`"${text}" did not match "${stringToFind}"`); + } + }) + ); + if (!foundInSpans.find((foundInSpan) => foundInSpan)) { + throw new Error(`"${stringToFind}" was not found. Trying again...`); + } + }); + } - async descendantExists(selector: string, parentElement: WebElementWrapper): Promise { - log.debug(`TestSubjects.descendantExists(${selector})`); - return await find.descendantExistsByCssSelector(testSubjSelector(selector), parentElement); - } + public async append(selector: string, text: string): Promise { + this.log.debug(`TestSubjects.append(${selector}, ${text})`); + const input = await this.find(selector); + await input.click(); + await input.type(text); + } - public async findDescendant( - selector: string, - parentElement: WebElementWrapper - ): Promise { - log.debug(`TestSubjects.findDescendant(${selector})`); - return await find.descendantDisplayedByCssSelector(testSubjSelector(selector), parentElement); - } + public async clickWhenNotDisabled( + selector: string, + { timeout = this.FIND_TIME }: { timeout?: number } = {} + ): Promise { + this.log.debug(`TestSubjects.clickWhenNotDisabled(${selector})`); + await this.findService.clickByCssSelectorWhenNotDisabled(testSubjSelector(selector), { + timeout, + }); + } - public async findAllDescendant( - selector: string, - parentElement: WebElementWrapper - ): Promise { - log.debug(`TestSubjects.findAllDescendant(${selector})`); - return await find.allDescendantDisplayedByCssSelector( - testSubjSelector(selector), - parentElement - ); - } + public async click( + selector: string, + timeout: number = this.FIND_TIME, + topOffset?: number + ): Promise { + this.log.debug(`TestSubjects.click(${selector})`); + await this.findService.clickByCssSelector(testSubjSelector(selector), timeout, topOffset); + } - public async find(selector: string, timeout: number = FIND_TIME): Promise { - log.debug(`TestSubjects.find(${selector})`); - return await find.byCssSelector(testSubjSelector(selector), timeout); - } + public async doubleClick(selector: string, timeout: number = this.FIND_TIME): Promise { + this.log.debug(`TestSubjects.doubleClick(${selector})`); + const element = await this.find(selector, timeout); + await element.moveMouseTo(); + await element.doubleClick(); + } - public async findAll(selector: string, timeout?: number): Promise { - return await retry.try(async () => { - log.debug(`TestSubjects.findAll(${selector})`); - const all = await find.allByCssSelector(testSubjSelector(selector), timeout); - return await find.filterElementIsDisplayed(all); - }); - } + async descendantExists(selector: string, parentElement: WebElementWrapper): Promise { + this.log.debug(`TestSubjects.descendantExists(${selector})`); + return await this.findService.descendantExistsByCssSelector( + testSubjSelector(selector), + parentElement + ); + } - public async getAttributeAll(selector: string, attribute: string): Promise { - log.debug(`TestSubjects.getAttributeAll(${selector}, ${attribute})`); - return await this._mapAll(selector, async (element: WebElementWrapper) => { - return await element.getAttribute(attribute); - }); - } + public async findDescendant( + selector: string, + parentElement: WebElementWrapper + ): Promise { + this.log.debug(`TestSubjects.findDescendant(${selector})`); + return await this.findService.descendantDisplayedByCssSelector( + testSubjSelector(selector), + parentElement + ); + } - public async getAttribute( - selector: string, - attribute: string, - options?: - | number - | { - findTimeout?: number; - tryTimeout?: number; - } - ): Promise { - const findTimeout = - (typeof options === 'number' ? options : options?.findTimeout) ?? - config.get('timeouts.find'); + public async findAllDescendant( + selector: string, + parentElement: WebElementWrapper + ): Promise { + this.log.debug(`TestSubjects.findAllDescendant(${selector})`); + return await this.findService.allDescendantDisplayedByCssSelector( + testSubjSelector(selector), + parentElement + ); + } - const tryTimeout = - (typeof options !== 'number' ? options?.tryTimeout : undefined) ?? - config.get('timeouts.try'); + public async find( + selector: string, + timeout: number = this.FIND_TIME + ): Promise { + this.log.debug(`TestSubjects.find(${selector})`); + return await this.findService.byCssSelector(testSubjSelector(selector), timeout); + } - log.debug( - `TestSubjects.getAttribute(${selector}, ${attribute}, tryTimeout=${tryTimeout}, findTimeout=${findTimeout})` - ); + public async findAll(selector: string, timeout?: number): Promise { + return await this.retry.try(async () => { + this.log.debug(`TestSubjects.findAll(${selector})`); + const all = await this.findService.allByCssSelector(testSubjSelector(selector), timeout); + return await this.findService.filterElementIsDisplayed(all); + }); + } - return await retry.tryForTime(tryTimeout, async () => { - const element = await this.find(selector, findTimeout); - return await element.getAttribute(attribute); - }); - } + public async getAttributeAll(selector: string, attribute: string): Promise { + this.log.debug(`TestSubjects.getAttributeAll(${selector}, ${attribute})`); + return await this._mapAll(selector, async (element: WebElementWrapper) => { + return await element.getAttribute(attribute); + }); + } - public async setValue( - selector: string, - text: string, - options: SetValueOptions = {}, - topOffset?: number - ): Promise { - return await retry.try(async () => { - const { clearWithKeyboard = false, typeCharByChar = false } = options; - log.debug(`TestSubjects.setValue(${selector}, ${text})`); - await this.click(selector, undefined, 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 find.activeElement(); - if (clearWithKeyboard === true) { - await input.clearValueWithKeyboard(); - } else { - await input.clearValue(); + public async getAttribute( + selector: string, + attribute: string, + options?: + | number + | { + findTimeout?: number; + tryTimeout?: number; } - await input.type(text, { charByChar: typeCharByChar }); - }); - } + ): Promise { + const findTimeout = + (typeof options === 'number' ? options : options?.findTimeout) ?? + this.config.get('timeouts.find'); + + const tryTimeout = + (typeof options !== 'number' ? options?.tryTimeout : undefined) ?? + this.config.get('timeouts.try'); + + this.log.debug( + `TestSubjects.getAttribute(${selector}, ${attribute}, tryTimeout=${tryTimeout}, findTimeout=${findTimeout})` + ); + + return await this.retry.tryForTime(tryTimeout, async () => { + const element = await this.find(selector, findTimeout); + return await element.getAttribute(attribute); + }); + } - public async selectValue(selector: string, value: string): Promise { - await find.selectValue(`[data-test-subj="${selector}"]`, value); - } + public async setValue( + selector: string, + text: string, + options: SetValueOptions = {}, + topOffset?: number + ): Promise { + return await this.retry.try(async () => { + const { clearWithKeyboard = false, typeCharByChar = false } = options; + this.log.debug(`TestSubjects.setValue(${selector}, ${text})`); + await this.click(selector, undefined, 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.findService.activeElement(); + if (clearWithKeyboard === true) { + await input.clearValueWithKeyboard(); + } else { + await input.clearValue(); + } + await input.type(text, { charByChar: typeCharByChar }); + }); + } - public async isEnabled(selector: string): Promise { - log.debug(`TestSubjects.isEnabled(${selector})`); - const element = await this.find(selector); - return await element.isEnabled(); - } + public async selectValue(selector: string, value: string): Promise { + await this.findService.selectValue(`[data-test-subj="${selector}"]`, value); + } - public async isDisplayed(selector: string): Promise { - log.debug(`TestSubjects.isDisplayed(${selector})`); - const element = await this.find(selector); - return await element.isDisplayed(); - } + public async isEnabled(selector: string): Promise { + this.log.debug(`TestSubjects.isEnabled(${selector})`); + const element = await this.find(selector); + return await element.isEnabled(); + } - public async isSelected(selector: string): Promise { - log.debug(`TestSubjects.isSelected(${selector})`); - const element = await this.find(selector); + public async isDisplayed(selector: string): Promise { + this.log.debug(`TestSubjects.isDisplayed(${selector})`); + const element = await this.find(selector); + return await element.isDisplayed(); + } + + public async isSelected(selector: string): Promise { + this.log.debug(`TestSubjects.isSelected(${selector})`); + const element = await this.find(selector); + return await element.isSelected(); + } + + public async isSelectedAll(selectorAll: string): Promise { + this.log.debug(`TestSubjects.isSelectedAll(${selectorAll})`); + return await this._mapAll(selectorAll, async (element: WebElementWrapper) => { return await element.isSelected(); - } + }); + } - public async isSelectedAll(selectorAll: string): Promise { - log.debug(`TestSubjects.isSelectedAll(${selectorAll})`); - return await this._mapAll(selectorAll, async (element: WebElementWrapper) => { - return await element.isSelected(); - }); - } + public async getVisibleText(selector: string): Promise { + this.log.debug(`TestSubjects.getVisibleText(${selector})`); + const element = await this.find(selector); + return await element.getVisibleText(); + } - public async getVisibleText(selector: string): Promise { - log.debug(`TestSubjects.getVisibleText(${selector})`); - const element = await this.find(selector); + async getVisibleTextAll(selectorAll: string): Promise { + this.log.debug(`TestSubjects.getVisibleTextAll(${selectorAll})`); + return await this._mapAll(selectorAll, async (element: WebElementWrapper) => { return await element.getVisibleText(); - } + }); + } - async getVisibleTextAll(selectorAll: string): Promise { - log.debug(`TestSubjects.getVisibleTextAll(${selectorAll})`); - return await this._mapAll(selectorAll, async (element: WebElementWrapper) => { - return await element.getVisibleText(); - }); - } + public async moveMouseTo(selector: string): Promise { + // Wrapped in a retry because even though the find should do a stale element check of it's own, we seem to + // have run into a case where the element becomes stale after the find succeeds, throwing an error during the + // moveMouseTo function. + await this.retry.try(async () => { + this.log.debug(`TestSubjects.moveMouseTo(${selector})`); + const element = await this.find(selector); + await element.moveMouseTo(); + }); + } - public async moveMouseTo(selector: string): Promise { - // Wrapped in a retry because even though the find should do a stale element check of it's own, we seem to - // have run into a case where the element becomes stale after the find succeeds, throwing an error during the - // moveMouseTo function. - await retry.try(async () => { - log.debug(`TestSubjects.moveMouseTo(${selector})`); - const element = await this.find(selector); - await element.moveMouseTo(); - }); - } + private async _mapAll( + selectorAll: string, + mapFn: (element: WebElementWrapper, index?: number, arrayLength?: number) => Promise + ): Promise { + return await this.retry.try(async () => { + const elements = await this.findAll(selectorAll); + return await mapAsync(elements, mapFn); + }); + } - private async _mapAll( - selectorAll: string, - mapFn: (element: WebElementWrapper, index?: number, arrayLength?: number) => Promise - ): Promise { - return await retry.try(async () => { - const elements = await this.findAll(selectorAll); - return await mapAsync(elements, mapFn); - }); + public async waitForDeleted(selectorOrElement: string | WebElementWrapper): Promise { + if (typeof selectorOrElement === 'string') { + await this.findService.waitForDeletedByCssSelector(testSubjSelector(selectorOrElement)); + } else { + await this.findService.waitForElementStale(selectorOrElement); } + } - public async waitForDeleted(selectorOrElement: string | WebElementWrapper): Promise { - if (typeof selectorOrElement === 'string') { - await find.waitForDeletedByCssSelector(testSubjSelector(selectorOrElement)); - } else { - await find.waitForElementStale(selectorOrElement); - } - } + public async waitForAttributeToChange( + selector: string, + attribute: string, + value: string + ): Promise { + await this.findService.waitForAttributeToChange(testSubjSelector(selector), attribute, value); + } - public async waitForAttributeToChange( - selector: string, - attribute: string, - value: string - ): Promise { - await find.waitForAttributeToChange(testSubjSelector(selector), attribute, value); - } + public async waitForHidden(selector: string, timeout?: number): Promise { + this.log.debug(`TestSubjects.waitForHidden(${selector})`); + const element = await this.find(selector); + await this.findService.waitForElementHidden(element, timeout); + } - public async waitForHidden(selector: string, timeout?: number): Promise { - log.debug(`TestSubjects.waitForHidden(${selector})`); + public async waitForEnabled(selector: string, timeout: number = this.TRY_TIME): Promise { + await this.retry.tryForTime(timeout, async () => { const element = await this.find(selector); - await find.waitForElementHidden(element, timeout); - } - - public async waitForEnabled(selector: string, timeout: number = TRY_TIME): Promise { - await retry.tryForTime(timeout, async () => { - const element = await this.find(selector); - return (await element.isDisplayed()) && (await element.isEnabled()); - }); - } + return (await element.isDisplayed()) && (await element.isEnabled()); + }); + } - public getCssSelector(selector: string): string { - return testSubjSelector(selector); - } + public getCssSelector(selector: string): string { + return testSubjSelector(selector); + } - public async scrollIntoView(selector: string) { - const element = await this.find(selector); - await element.scrollIntoViewIfNecessary(); - } + public async scrollIntoView(selector: string) { + const element = await this.find(selector); + await element.scrollIntoViewIfNecessary(); + } - // isChecked always returns false when run on an euiSwitch, because they use the aria-checked attribute - public async isChecked(selector: string) { - const checkbox = await this.find(selector); - return await checkbox.isSelected(); - } + // isChecked always returns false when run on an euiSwitch, because they use the aria-checked attribute + public async isChecked(selector: string) { + const checkbox = await this.find(selector); + return await checkbox.isSelected(); + } - public async setCheckbox(selector: string, state: 'check' | 'uncheck') { - const isChecked = await this.isChecked(selector); - const states = { check: true, uncheck: false }; - if (isChecked !== states[state]) { - log.debug(`updating checkbox ${selector} from ${isChecked} to ${states[state]}`); - await this.click(selector); - } + public async setCheckbox(selector: string, state: 'check' | 'uncheck') { + const isChecked = await this.isChecked(selector); + const states = { check: true, uncheck: false }; + if (isChecked !== states[state]) { + this.log.debug(`updating checkbox ${selector} from ${isChecked} to ${states[state]}`); + await this.click(selector); } + } - public async isEuiSwitchChecked(selector: string) { - const euiSwitch = await this.find(selector); - const isChecked = await euiSwitch.getAttribute('aria-checked'); - return isChecked === 'true'; - } + public async isEuiSwitchChecked(selector: string) { + const euiSwitch = await this.find(selector); + const isChecked = await euiSwitch.getAttribute('aria-checked'); + return isChecked === 'true'; + } - public async setEuiSwitch(selector: string, state: 'check' | 'uncheck') { - const isChecked = await this.isEuiSwitchChecked(selector); - const states = { check: true, uncheck: false }; - if (isChecked !== states[state]) { - log.debug(`updating checkbox ${selector} from ${isChecked} to ${states[state]}`); - await this.click(selector); - } + public async setEuiSwitch(selector: string, state: 'check' | 'uncheck') { + const isChecked = await this.isEuiSwitchChecked(selector); + const states = { check: true, uncheck: false }; + if (isChecked !== states[state]) { + this.log.debug(`updating checkbox ${selector} from ${isChecked} to ${states[state]}`); + await this.click(selector); } } - - return new TestSubjects(); } diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 0dd7f20debcbd..c2f0371bcabe0 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -15,7 +15,7 @@ import { FindProvider, ScreenshotsProvider, SnapshotsProvider, - TestSubjectsProvider, + TestSubjects, } from './common'; import { ComboBoxProvider } from './combo_box'; import { @@ -56,7 +56,7 @@ export const services = { filterBar: FilterBarProvider, queryBar: QueryBarProvider, find: FindProvider, - testSubjects: TestSubjectsProvider, + testSubjects: TestSubjects, docTable: DocTableProvider, screenshots: ScreenshotsProvider, snapshots: SnapshotsProvider,