From e3a0948ca89b2f56c02d1da223fdf0002c5f9f3b Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 2 Aug 2019 08:55:17 -0700 Subject: [PATCH] [ftr/cheerio] improve cheerio types to include test subject methods --- test/functional/services/apps_menu.ts | 7 +- test/functional/services/doc_table.ts | 16 +- .../web_element_wrapper/custom_cheerio_api.ts | 246 ++++++++++++++++++ .../web_element_wrapper.ts | 15 +- 4 files changed, 264 insertions(+), 20 deletions(-) create mode 100644 test/functional/services/lib/web_element_wrapper/custom_cheerio_api.ts diff --git a/test/functional/services/apps_menu.ts b/test/functional/services/apps_menu.ts index 93858cdb44523..a4cd98b2a06ec 100644 --- a/test/functional/services/apps_menu.ts +++ b/test/functional/services/apps_menu.ts @@ -31,12 +31,9 @@ export function AppsMenuProvider({ getService }: FtrProviderContext) { const appMenu = await testSubjects.find('navDrawer'); const $ = await appMenu.parseDomContent(); - const links: Array<{ - text: string; - href: string; - }> = $.findTestSubjects('navDrawerAppsMenuLink') + const links = $.findTestSubjects('navDrawerAppsMenuLink') .toArray() - .map((link: any) => { + .map(link => { return { text: $(link).text(), href: $(link).attr('href'), diff --git a/test/functional/services/doc_table.ts b/test/functional/services/doc_table.ts index 0b3b77157d562..e09500317cd32 100644 --- a/test/functional/services/doc_table.ts +++ b/test/functional/services/doc_table.ts @@ -34,10 +34,10 @@ export function DocTableProvider({ getService, getPageObjects }: FtrProviderCont return await testSubjects.find('docTable'); } - public async getRowsText(): Promise { + public async getRowsText() { const table = await this.getTable(); const $ = await table.parseDomContent(); - return $('[data-test-subj~="docTableRow"]') + return $.findTestSubjects('docTableRow') .toArray() .map((row: any) => $(row) @@ -93,15 +93,11 @@ export function DocTableProvider({ getService, getPageObjects }: FtrProviderCont return await detailsRow.findAllByCssSelector('[data-test-subj~="docTableRowAction"]'); } - public async getFields( - options: { isAnchorRow: boolean } = { isAnchorRow: false } - ): Promise { + public async getFields(options: { isAnchorRow: boolean } = { isAnchorRow: false }) { const table = await this.getTable(); const $ = await table.parseDomContent(); - const rowLocator = options.isAnchorRow - ? '[data-test-subj~="docTableAnchorRow"]' - : '[data-test-subj~="docTableRow"]'; - const rows = $(rowLocator).toArray(); + const rowLocator = options.isAnchorRow ? 'docTableAnchorRow' : 'docTableRow'; + const rows = $.findTestSubjects(rowLocator).toArray(); const fields = rows.map((row: any) => $(row) .find('[data-test-subj~="docTableField"]') @@ -114,7 +110,7 @@ export function DocTableProvider({ getService, getPageObjects }: FtrProviderCont public async getHeaderFields(): Promise { const table = await this.getTable(); const $ = await table.parseDomContent(); - return $('[data-test-subj~="docTableHeaderField"]') + return $.findTestSubjects('docTableHeaderField') .toArray() .map((field: any) => $(field) diff --git a/test/functional/services/lib/web_element_wrapper/custom_cheerio_api.ts b/test/functional/services/lib/web_element_wrapper/custom_cheerio_api.ts new file mode 100644 index 0000000000000..301eb656ed6f6 --- /dev/null +++ b/test/functional/services/lib/web_element_wrapper/custom_cheerio_api.ts @@ -0,0 +1,246 @@ +/* eslint-disable */ + +/** + * Type interfaces extracted from node_modules/@types/cheerio/index.d.ts + * and customized to include our custom methods + */ + +interface CheerioSelector { + (selector: string): CustomCheerio; + (selector: string, context: string): CustomCheerio; + (selector: string, context: CheerioElement): CustomCheerio; + (selector: string, context: CheerioElement[]): CustomCheerio; + (selector: string, context: Cheerio): CustomCheerio; + (selector: string, context: string, root: string): CustomCheerio; + (selector: string, context: CheerioElement, root: string): CustomCheerio; + (selector: string, context: CheerioElement[], root: string): CustomCheerio; + (selector: string, context: Cheerio, root: string): CustomCheerio; + (selector: any): CustomCheerio; +} + +export interface CustomCheerioStatic extends CheerioSelector { + // Document References + // Cheerio https://github.com/cheeriojs/cheerio + // JQuery http://api.jquery.com + xml(): string; + root(): CustomCheerio; + contains(container: CheerioElement, contained: CheerioElement): boolean; + parseHTML(data: string, context?: Document, keepScripts?: boolean): Document[]; + + html(options?: CheerioOptionsInterface): string; + html(selector: string, options?: CheerioOptionsInterface): string; + html(element: CustomCheerio, options?: CheerioOptionsInterface): string; + html(element: CheerioElement, options?: CheerioOptionsInterface): string; + + // + // CUSTOM METHODS + // + findTestSubjects(selector: string): CustomCheerio; + findTestSubject(selector: string): CustomCheerio; +} + +export interface CustomCheerio { + // Document References + // Cheerio https://github.com/cheeriojs/cheerio + // JQuery http://api.jquery.com + + [index: number]: CheerioElement; + length: number; + + // Attributes + + attr(): { [attr: string]: string }; + attr(name: string): string; + attr(name: string, value: any): CustomCheerio; + + data(): any; + data(name: string): any; + data(name: string, value: any): any; + + val(): string; + val(value: string): CustomCheerio; + + removeAttr(name: string): CustomCheerio; + + has(selector: string): CustomCheerio; + has(element: CheerioElement): CustomCheerio; + + hasClass(className: string): boolean; + addClass(classNames: string): CustomCheerio; + + removeClass(): CustomCheerio; + removeClass(className: string): CustomCheerio; + removeClass(func: (index: number, className: string) => string): CustomCheerio; + + toggleClass(className: string): CustomCheerio; + toggleClass(className: string, toggleSwitch: boolean): CustomCheerio; + toggleClass(toggleSwitch?: boolean): CustomCheerio; + toggleClass( + func: (index: number, className: string, toggleSwitch: boolean) => string, + toggleSwitch?: boolean + ): CustomCheerio; + + is(selector: string): boolean; + is(element: CheerioElement): boolean; + is(element: CheerioElement[]): boolean; + is(selection: CustomCheerio): boolean; + is(func: (index: number, element: CheerioElement) => boolean): boolean; + + // Form + serialize(): string; + serializeArray(): Array<{ name: string; value: string }>; + + // Traversing + + find(selector: string): CustomCheerio; + find(element: CustomCheerio): CustomCheerio; + + parent(selector?: string): CustomCheerio; + parents(selector?: string): CustomCheerio; + parentsUntil(selector?: string, filter?: string): CustomCheerio; + parentsUntil(element: CheerioElement, filter?: string): CustomCheerio; + parentsUntil(element: CustomCheerio, filter?: string): CustomCheerio; + + prop(name: string): any; + prop(name: string, value: any): CustomCheerio; + + closest(): CustomCheerio; + closest(selector: string): CustomCheerio; + + next(selector?: string): CustomCheerio; + nextAll(): CustomCheerio; + nextAll(selector: string): CustomCheerio; + + nextUntil(selector?: string, filter?: string): CustomCheerio; + nextUntil(element: CheerioElement, filter?: string): CustomCheerio; + nextUntil(element: CustomCheerio, filter?: string): CustomCheerio; + + prev(selector?: string): CustomCheerio; + prevAll(): CustomCheerio; + prevAll(selector: string): CustomCheerio; + + prevUntil(selector?: string, filter?: string): CustomCheerio; + prevUntil(element: CheerioElement, filter?: string): CustomCheerio; + prevUntil(element: CustomCheerio, filter?: string): CustomCheerio; + + slice(start: number, end?: number): CustomCheerio; + + siblings(selector?: string): CustomCheerio; + + children(selector?: string): CustomCheerio; + + contents(): CustomCheerio; + + each(func: (index: number, element: CheerioElement) => any): CustomCheerio; + map(func: (index: number, element: CheerioElement) => any): CustomCheerio; + + filter(selector: string): CustomCheerio; + filter(selection: CustomCheerio): CustomCheerio; + filter(element: CheerioElement): CustomCheerio; + filter(elements: CheerioElement[]): CustomCheerio; + filter(func: (index: number, element: CheerioElement) => boolean): CustomCheerio; + + not(selector: string): CustomCheerio; + not(selection: CustomCheerio): CustomCheerio; + not(element: CheerioElement): CustomCheerio; + not(func: (index: number, element: CheerioElement) => boolean): CustomCheerio; + + first(): CustomCheerio; + last(): CustomCheerio; + + eq(index: number): CustomCheerio; + + get(): any[]; + get(index: number): any; + + index(): number; + index(selector: string): number; + index(selection: CustomCheerio): number; + + end(): CustomCheerio; + + add(selectorOrHtml: string): CustomCheerio; + add(selector: string, context: Document): CustomCheerio; + add(element: CheerioElement): CustomCheerio; + add(elements: CheerioElement[]): CustomCheerio; + add(selection: CustomCheerio): CustomCheerio; + + addBack(): CustomCheerio; + addBack(filter: string): CustomCheerio; + + // Manipulation + appendTo(target: CustomCheerio): CustomCheerio; + prependTo(target: CustomCheerio): CustomCheerio; + + append(content: string, ...contents: any[]): CustomCheerio; + append(content: Document, ...contents: any[]): CustomCheerio; + append(content: Document[], ...contents: any[]): CustomCheerio; + append(content: CustomCheerio, ...contents: any[]): CustomCheerio; + + prepend(content: string, ...contents: any[]): CustomCheerio; + prepend(content: Document, ...contents: any[]): CustomCheerio; + prepend(content: Document[], ...contents: any[]): CustomCheerio; + prepend(content: CustomCheerio, ...contents: any[]): CustomCheerio; + + after(content: string, ...contents: any[]): CustomCheerio; + after(content: Document, ...contents: any[]): CustomCheerio; + after(content: Document[], ...contents: any[]): CustomCheerio; + after(content: CustomCheerio, ...contents: any[]): CustomCheerio; + + insertAfter(content: string): CustomCheerio; + insertAfter(content: Document): CustomCheerio; + insertAfter(content: CustomCheerio): CustomCheerio; + + before(content: string, ...contents: any[]): CustomCheerio; + before(content: Document, ...contents: any[]): CustomCheerio; + before(content: Document[], ...contents: any[]): CustomCheerio; + before(content: CustomCheerio, ...contents: any[]): CustomCheerio; + + insertBefore(content: string): CustomCheerio; + insertBefore(content: Document): CustomCheerio; + insertBefore(content: CustomCheerio): CustomCheerio; + + remove(selector?: string): CustomCheerio; + + replaceWith(content: string): CustomCheerio; + replaceWith(content: CheerioElement): CustomCheerio; + replaceWith(content: CheerioElement[]): CustomCheerio; + replaceWith(content: CustomCheerio): CustomCheerio; + replaceWith(content: () => CustomCheerio): CustomCheerio; + + empty(): CustomCheerio; + + html(): string | null; + html(html: string): CustomCheerio; + + text(): string; + text(text: string): CustomCheerio; + + wrap(content: string): CustomCheerio; + wrap(content: Document): CustomCheerio; + wrap(content: CustomCheerio): CustomCheerio; + + css(propertyName: string): string; + css(propertyNames: string[]): string[]; + css(propertyName: string, value: string): CustomCheerio; + css(propertyName: string, value: number): CustomCheerio; + css(propertyName: string, func: (index: number, value: string) => string): CustomCheerio; + css(propertyName: string, func: (index: number, value: string) => number): CustomCheerio; + css(properties: Record): CustomCheerio; + + // Rendering + + // Miscellaneous + + clone(): CustomCheerio; + + // Not Documented + + toArray(): CheerioElement[]; + + // + // CUSTOM METHODS + // + findTestSubjects(selector: string): CustomCheerio; + findTestSubject(selector: string): CustomCheerio; +} diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts index 2ae4616283205..b05485618da01 100644 --- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts +++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts @@ -25,6 +25,7 @@ import { PNG } from 'pngjs'; import cheerio from 'cheerio'; import testSubjSelector from '@kbn/test-subj-selector'; import { ToolingLog } from '@kbn/dev-utils'; +import { CustomCheerio, CustomCheerioStatic } from './custom_cheerio_api'; // @ts-ignore not supported yet import { scrollIntoViewIfNecessary } from './scroll_into_view_if_necessary'; import { Browsers } from '../../remote/browsers'; @@ -650,24 +651,28 @@ export class WebElementWrapper { * Gets element innerHTML and wrap it up with cheerio * * @nonstandard - * @return {Promise} + * @return {Promise} */ - public async parseDomContent(): Promise { + public async parseDomContent(): Promise { const htmlContent: any = await this.getAttribute('innerHTML'); const $: any = cheerio.load(htmlContent, { normalizeWhitespace: true, xmlMode: true, }); - $.findTestSubjects = function testSubjects(selector: string) { + $.findTestSubjects = function findTestSubjects(this: CustomCheerioStatic, selector: string) { return this(testSubjSelector(selector)); }; - $.fn.findTestSubjects = function testSubjects(selector: string) { + $.fn.findTestSubjects = function findTestSubjects(this: CustomCheerio, selector: string) { return this.find(testSubjSelector(selector)); }; - $.findTestSubject = $.fn.findTestSubject = function testSubjects(selector: string) { + $.findTestSubject = function findTestSubject(this: CustomCheerioStatic, selector: string) { + return this.findTestSubjects(selector).first(); + }; + + $.fn.findTestSubject = function findTestSubject(this: CustomCheerio, selector: string) { return this.findTestSubjects(selector).first(); };