diff --git a/test/lib/browsers/base.ts b/test/lib/browsers/base.ts index 748270aca302435..224be5e7bbc50a9 100644 --- a/test/lib/browsers/base.ts +++ b/test/lib/browsers/base.ts @@ -1,11 +1,18 @@ export type Event = 'request' -// This is the base Browser interface all browser -// classes should build off of, it is the bare -// methods we aim to support across tests -export class BrowserInterface { +/** + * This is the base Browser interface all browser + * classes should build off of, it is the bare + * methods we aim to support across tests + * + * They will always await last executed command. + * The interface is mutable - it doesn't have to be in sequece. + * + * You can manually await this interface to wait for completion of the last scheduled command. + */ +export class BrowserInterface implements PromiseLike { private promise: any - private then: any + then: PromiseLike['then'] private catch: any protected chain(nextCall: any): BrowserInterface { @@ -18,6 +25,24 @@ export class BrowserInterface { return this } + /** + * This function will run in chain - it will wait for previous commands. + * But it won't have an effect on chain value and chain will still be green if this throws. + */ + protected chainWithReturnValue( + callback: (...args: any[]) => Promise + ): Promise { + return new Promise((resolve, reject) => { + this.chain(async (...args: any[]) => { + try { + resolve(await callback(...args)) + } catch (error) { + reject(error) + } + }) + }) + } + async setup(browserName: string, locale?: string): Promise {} async close(): Promise {} async quit(): Promise {} diff --git a/test/lib/browsers/playwright.ts b/test/lib/browsers/playwright.ts index 68ee05ec6886bad..818da5147b053c5 100644 --- a/test/lib/browsers/playwright.ts +++ b/test/lib/browsers/playwright.ts @@ -281,8 +281,8 @@ export class Playwright extends BrowserInterface { return this.chain((el) => el.getAttribute(attr)) } - async hasElementByCssSelector(selector: string) { - return this.eval(`!!document.querySelector('${selector}')`) as any + hasElementByCssSelector(selector: string) { + return this.eval(`!!document.querySelector('${selector}')`) } keydown(key: string): BrowserInterface { @@ -344,19 +344,19 @@ export class Playwright extends BrowserInterface { }) } - async eval(snippet) { - // TODO: should this and evalAsync be chained? Might lead - // to bad chains - return page - .evaluate(snippet) - .catch((err) => { - console.error('eval error:', err) - return null - }) - .then(async (val) => { - await page.waitForLoadState() - return val - }) + eval(snippet): Promise { + return this.chainWithReturnValue(() => + page + .evaluate(snippet) + .catch((err) => { + console.error('eval error:', err) + return null + }) + .then(async (val) => { + await page.waitForLoadState() + return val as T + }) + ) } async evalAsync(snippet) {