From 73b55c3b17dd0f257040c9e9b141b5f3d72e7a52 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Wed, 14 Jun 2023 15:52:44 -0600 Subject: [PATCH 1/6] fix: skip unloaded iframes for all apis --- packages/playwright/package-lock.json | 2 +- .../playwright/test/axe-playwright.spec.ts | 18 ++++++++++ packages/puppeteer/package-lock.json | 4 +-- packages/puppeteer/src/utils.ts | 11 +++++- packages/puppeteer/test/axe-puppeteer.spec.ts | 19 ++++++++++ packages/webdriverio/package-lock.json | 4 +-- packages/webdriverio/src/index.ts | 33 ++++++++++++++--- packages/webdriverio/src/utils.ts | 36 +++++++++++++++++++ .../webdriverio/test/axe-webdriverio.spec.ts | 34 ++++++++++++++++++ packages/webdriverjs/package-lock.json | 4 +-- .../webdriverjs/test/axe-webdriverjs.spec.ts | 10 +++--- .../test/fixtures/iframe-lazy-1.html | 15 -------- .../test/fixtures/lazy-loaded-iframe.html | 14 -------- 13 files changed, 157 insertions(+), 47 deletions(-) delete mode 100644 packages/webdriverjs/test/fixtures/iframe-lazy-1.html delete mode 100644 packages/webdriverjs/test/fixtures/lazy-loaded-iframe.html diff --git a/packages/playwright/package-lock.json b/packages/playwright/package-lock.json index 634b3971..f4e490d3 100644 --- a/packages/playwright/package-lock.json +++ b/packages/playwright/package-lock.json @@ -993,7 +993,7 @@ }, "node_modules/axe-test-fixtures": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "license": "MPL-2.0" }, diff --git a/packages/playwright/test/axe-playwright.spec.ts b/packages/playwright/test/axe-playwright.spec.ts index 057e5464..3c187c7c 100644 --- a/packages/playwright/test/axe-playwright.spec.ts +++ b/packages/playwright/test/axe-playwright.spec.ts @@ -437,6 +437,24 @@ describe('@axe-core/playwright', () => { assert.equal(res?.status(), 200); assert.strictEqual(count, 9); }); + + it('handles unloaded iframes (e.g. loading=lazy)', async () => { + const res = await page.goto(`${addr}/external/lazy-loaded-iframe.html`); + + const results = await new AxeBuilder({ page }) + .options({ runOnly: ['label', 'frame-tested'] }) + .analyze(); + + assert.equal(res?.status(), 200); + assert.lengthOf(results.incomplete, 0); + assert.equal(results.violations[0].id, 'label'); + assert.lengthOf(results.violations[0].nodes, 1); + assert.deepEqual(results.violations[0].nodes[0].target, [ + '#ifr-lazy', + '#lazy-baz', + 'input' + ]); + }) }); describe('include/exclude', () => { diff --git a/packages/puppeteer/package-lock.json b/packages/puppeteer/package-lock.json index e6e5c97f..59a92a41 100644 --- a/packages/puppeteer/package-lock.json +++ b/packages/puppeteer/package-lock.json @@ -1391,7 +1391,7 @@ }, "node_modules/axe-test-fixtures": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "license": "MPL-2.0" }, @@ -6377,7 +6377,7 @@ "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==" }, "axe-test-fixtures": { - "version": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "version": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "from": "axe-test-fixtures@github:dequelabs/axe-test-fixtures#v1" }, diff --git a/packages/puppeteer/src/utils.ts b/packages/puppeteer/src/utils.ts index 0434ace3..ed0815e4 100644 --- a/packages/puppeteer/src/utils.ts +++ b/packages/puppeteer/src/utils.ts @@ -76,7 +76,16 @@ export async function assertFrameReady(frame: Frame): Promise { // Check if the page is loaded. let pageReady = false; try { - pageReady = await frame.evaluate(pageIsLoaded); + // Puppeteer freezes on unloaded iframes. Set a race timeout in order to handle that. + // @see https://github.com/dequelabs/axe-core-npm/issues/727 + const timeoutPromise = new Promise(resolve => { + setTimeout(() => { + resolve('timeout'); + }, 1000) + }); + const evaluatePromise = frame.evaluate(pageIsLoaded); + const raceResults = await Promise.race([timeoutPromise, evaluatePromise]); + pageReady = raceResults !== 'timeout'; } catch { /* ignore */ } diff --git a/packages/puppeteer/test/axe-puppeteer.spec.ts b/packages/puppeteer/test/axe-puppeteer.spec.ts index 19eaa71f..30b48deb 100644 --- a/packages/puppeteer/test/axe-puppeteer.spec.ts +++ b/packages/puppeteer/test/axe-puppeteer.spec.ts @@ -872,6 +872,25 @@ describe('AxePuppeteer', function () { assert.equal(res?.status(), 200); assert.deepEqual(pageResults, frameResults); }); + + it.only('skips unloaded iframes (e.g. loading=lazy)', async () => { + const res = await page.goto(`${addr}/external/lazy-loaded-iframe.html`); + const results = await new AxePuppeteer(page) + .options({ runOnly: ['label', 'frame-tested'] }) + .analyze(); + + assert.equal(res?.status(), 200); + assert.equal(results.incomplete[0].id, 'frame-tested'); + assert.lengthOf(results.incomplete[0].nodes, 1); + assert.deepEqual(results.incomplete[0].nodes[0].target, ['#ifr-lazy', '#lazy-iframe']); + assert.equal(results.violations[0].id, 'label'); + assert.lengthOf(results.violations[0].nodes, 1); + assert.deepEqual(results.violations[0].nodes[0].target, [ + '#ifr-lazy', + '#lazy-baz', + 'input' + ]); + }) }); describe('axe.finishRun errors', () => { diff --git a/packages/webdriverio/package-lock.json b/packages/webdriverio/package-lock.json index 28982954..0d3c2f83 100644 --- a/packages/webdriverio/package-lock.json +++ b/packages/webdriverio/package-lock.json @@ -1707,7 +1707,7 @@ }, "node_modules/axe-test-fixtures": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "license": "MPL-2.0" }, @@ -8048,7 +8048,7 @@ "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==" }, "axe-test-fixtures": { - "version": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "version": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "from": "axe-test-fixtures@github:dequelabs/axe-test-fixtures#v1" }, diff --git a/packages/webdriverio/src/index.ts b/packages/webdriverio/src/index.ts index b603eeed..a757e84b 100644 --- a/packages/webdriverio/src/index.ts +++ b/packages/webdriverio/src/index.ts @@ -10,7 +10,8 @@ import { axeRunPartial, axeFinishRun, axeRunLegacy, - configureAllowedOrigins + configureAllowedOrigins, + FRAME_LOAD_TIMEOUT } from './utils'; import { getFilename } from 'cross-dirname'; import { pathToFileURL } from 'url'; @@ -241,7 +242,21 @@ export default class AxeBuilder { return await this.runLegacy(context); } - const partials = await this.runPartialRecursive(context); + // ensure we fail quickly if an iframe cannot be loaded (instead of waiting + // the default length of 30 seconds) + const { pageLoad } = await this.client.getTimeouts(); + this.client.setTimeout({ + pageLoad: FRAME_LOAD_TIMEOUT, + }); + + let partials: PartialResults | null + try { + partials = await this.runPartialRecursive(context); + } finally { + this.client.setTimeout({ + pageLoad, + }); + } try { return await this.finishRun(partials); @@ -300,8 +315,10 @@ export default class AxeBuilder { */ private async runPartialRecursive( - context: SerialContextObject + context: SerialContextObject, + frameStack: Element[] = [] ): Promise { + const title = await this.client.getTitle(); const frameContexts = await axeGetFrameContext(this.client, context); const partials: PartialResults = [ await axeRunPartial(this.client, context, this.option) @@ -313,10 +330,16 @@ export default class AxeBuilder { assert(frame, `Expect frame of "${frameSelector}" to be defined`); await this.client.switchToFrame(frame); await axeSourceInject(this.client, this.script); - partials.push(...(await this.runPartialRecursive(frameContext))); + partials.push(...(await this.runPartialRecursive(frameContext, [...frameStack, frame]))); } catch (error) { + const windows = await this.client.getWindowHandles() + await this.client.switchToWindow(windows[0]) + + for (const frameElm of frameStack) { + await this.client.switchToFrame(frameElm); + } + partials.push(null); - await this.client.switchToParentFrame(); } } await this.client.switchToParentFrame(); diff --git a/packages/webdriverio/src/utils.ts b/packages/webdriverio/src/utils.ts index 342b731e..737487e6 100644 --- a/packages/webdriverio/src/utils.ts +++ b/packages/webdriverio/src/utils.ts @@ -10,6 +10,8 @@ import type { SerialContextObject } from 'axe-core'; +export const FRAME_LOAD_TIMEOUT = 1000 + /** * Validates that the client provided is WebdriverIO v5 or v6. */ @@ -83,6 +85,7 @@ export const axeSourceInject = async ( client: Browser, axeSource: string ): Promise<{ runPartialSupported: boolean }> => { + await assertFrameReady(client); return promisify( // Had to use executeAsync() because we could not use multiline statements in client.execute() // we were able to return a single boolean in a line but not when assigned to a variable. @@ -98,6 +101,39 @@ export const axeSourceInject = async ( ); }; +async function assertFrameReady(client: Browser): Promise { + // Wait so that we know there is an execution context. + // Assume that if we have an html node we have an execution context. + // Check if the page is loaded. + let pageReady = false; + try { + /* + When using the devtools protocol trying to call + client.execute() on an unloaded iframe would cause + the code to hang indefinitely since it is using + Puppeteer which freezes on unloaded iframes. Set a + race timeout in order to handle that. Code taken + from our @axe-core/puppeteer utils function. + @see https://github.com/dequelabs/axe-core-npm/issues/727 + */ + const timeoutPromise = new Promise(resolve => { + setTimeout(() => { + resolve('timeout'); + }, FRAME_LOAD_TIMEOUT) + }); + const executePromise = client.execute(() => { + return document.readyState === 'complete' + }); + const raceResults = await Promise.race([timeoutPromise, executePromise]); + pageReady = raceResults !== 'timeout'; + } catch { + /* ignore */ + } + if (!pageReady) { + throw new Error('Page/Frame is not ready'); + } +} + export const axeRunPartial = ( client: Browser, context?: SerialContextObject, diff --git a/packages/webdriverio/test/axe-webdriverio.spec.ts b/packages/webdriverio/test/axe-webdriverio.spec.ts index f629dcea..5917d023 100644 --- a/packages/webdriverio/test/axe-webdriverio.spec.ts +++ b/packages/webdriverio/test/axe-webdriverio.spec.ts @@ -850,6 +850,40 @@ describe('@axe-core/webdriverio', () => { normalResults.testEngine.name = legacyResults.testEngine.name; assert.deepEqual(normalResults, legacyResults); }); + + it('handles unloaded iframes (e.g. loading=lazy)', async () => { + await client.url(`${addr}/lazy-loaded-iframe.html`); + const title = await client.getTitle(); + + const results = await new AxeBuilder({client}) + .options({ runOnly: ['label', 'frame-tested'] }) + .analyze(); + + assert.notEqual(title, 'Error'); + assert.equal(results.incomplete[0].id, 'frame-tested'); + assert.lengthOf(results.incomplete[0].nodes, 1); + assert.deepEqual(results.incomplete[0].nodes[0].target, ['#ifr-lazy', '#lazy-iframe']); + assert.equal(results.violations[0].id, 'label'); + assert.lengthOf(results.violations[0].nodes, 1); + assert.deepEqual(results.violations[0].nodes[0].target, [ + '#ifr-lazy', + '#lazy-baz', + 'input' + ]); + }); + + it('resets pageLoad timeout to user setting', async () => { + await client.url(`${addr}/lazy-loaded-iframe.html`); + client.setTimeout({ pageLoad: 500 }) + const title = await client.getTitle(); + + const results = await new AxeBuilder({client}) + .options({ runOnly: ['label', 'frame-tested'] }) + .analyze(); + + const timeout = await client.getTimeouts(); + assert.equal(timeout.pageLoad, 500); + }); }); describe('logOrRethrowError', () => { diff --git a/packages/webdriverjs/package-lock.json b/packages/webdriverjs/package-lock.json index ac662cfc..3afc799d 100644 --- a/packages/webdriverjs/package-lock.json +++ b/packages/webdriverjs/package-lock.json @@ -1436,7 +1436,7 @@ }, "node_modules/axe-test-fixtures": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "resolved": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "license": "MPL-2.0" }, @@ -7015,7 +7015,7 @@ "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==" }, "axe-test-fixtures": { - "version": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#09469d613991c108cd76f070839cc46f59668ebb", + "version": "git+ssh://git@github.com/dequelabs/axe-test-fixtures.git#23f638c574dd9be2a725fa81854f1560d858dcce", "dev": true, "from": "axe-test-fixtures@github:dequelabs/axe-test-fixtures#v1" }, diff --git a/packages/webdriverjs/test/axe-webdriverjs.spec.ts b/packages/webdriverjs/test/axe-webdriverjs.spec.ts index 27ae5c35..c4f09e74 100644 --- a/packages/webdriverjs/test/axe-webdriverjs.spec.ts +++ b/packages/webdriverjs/test/axe-webdriverjs.spec.ts @@ -398,7 +398,7 @@ describe('@axe-core/webdriverjs', () => { }); it('skips unloaded iframes (e.g. loading=lazy)', async () => { - await driver.get(`${addr}/lazy-loaded-iframe.html`); + await driver.get(`${addr}/external/lazy-loaded-iframe.html`); const title = await driver.getTitle(); const results = await new AxeBuilder(driver) @@ -408,18 +408,18 @@ describe('@axe-core/webdriverjs', () => { assert.notEqual(title, 'Error'); assert.equal(results.incomplete[0].id, 'frame-tested'); assert.lengthOf(results.incomplete[0].nodes, 1); - assert.deepEqual(results.incomplete[0].nodes[0].target, ['#parent', '#lazy-iframe']); + assert.deepEqual(results.incomplete[0].nodes[0].target, ['#ifr-lazy', '#lazy-iframe']); assert.equal(results.violations[0].id, 'label'); assert.lengthOf(results.violations[0].nodes, 1); assert.deepEqual(results.violations[0].nodes[0].target, [ - '#parent', - '#child', + '#ifr-lazy', + '#lazy-baz', 'input' ]); }) it('resets pageLoad timeout to user setting', async () => { - await driver.get(`${addr}/lazy-loaded-iframe.html`); + await driver.get(`${addr}/external/lazy-loaded-iframe.html`); driver.manage().setTimeouts({ pageLoad: 500 }) const title = await driver.getTitle(); diff --git a/packages/webdriverjs/test/fixtures/iframe-lazy-1.html b/packages/webdriverjs/test/fixtures/iframe-lazy-1.html deleted file mode 100644 index 65aea201..00000000 --- a/packages/webdriverjs/test/fixtures/iframe-lazy-1.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - Lazy Loading Iframe Parent - - -
-

iframe context test

-
- -
-
- - \ No newline at end of file diff --git a/packages/webdriverjs/test/fixtures/lazy-loaded-iframe.html b/packages/webdriverjs/test/fixtures/lazy-loaded-iframe.html deleted file mode 100644 index 53969219..00000000 --- a/packages/webdriverjs/test/fixtures/lazy-loaded-iframe.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - Lazy Loading Iframe Root - - -
-

iframe context test

-
- -
-
- - From ea13036034194c85df228f94a5d8873189b56afe Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Wed, 14 Jun 2023 15:54:52 -0600 Subject: [PATCH 2/6] no loaded --- packages/puppeteer/test/axe-puppeteer.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/puppeteer/test/axe-puppeteer.spec.ts b/packages/puppeteer/test/axe-puppeteer.spec.ts index 30b48deb..e1d11359 100644 --- a/packages/puppeteer/test/axe-puppeteer.spec.ts +++ b/packages/puppeteer/test/axe-puppeteer.spec.ts @@ -873,7 +873,7 @@ describe('AxePuppeteer', function () { assert.deepEqual(pageResults, frameResults); }); - it.only('skips unloaded iframes (e.g. loading=lazy)', async () => { + it('skips unloaded iframes (e.g. loading=lazy)', async () => { const res = await page.goto(`${addr}/external/lazy-loaded-iframe.html`); const results = await new AxePuppeteer(page) .options({ runOnly: ['label', 'frame-tested'] }) From 5e3e8fb8a546c1d801031646d8abe361188686ce Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Thu, 15 Jun 2023 08:28:20 -0600 Subject: [PATCH 3/6] Update packages/webdriverio/src/index.ts Co-authored-by: Wilco Fiers --- packages/webdriverio/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/webdriverio/src/index.ts b/packages/webdriverio/src/index.ts index a757e84b..c12dbcc0 100644 --- a/packages/webdriverio/src/index.ts +++ b/packages/webdriverio/src/index.ts @@ -318,7 +318,6 @@ export default class AxeBuilder { context: SerialContextObject, frameStack: Element[] = [] ): Promise { - const title = await this.client.getTitle(); const frameContexts = await axeGetFrameContext(this.client, context); const partials: PartialResults = [ await axeRunPartial(this.client, context, this.option) From c40c2e30a177d992aff454179285b59a479bb7dc Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Thu, 15 Jun 2023 08:28:30 -0600 Subject: [PATCH 4/6] Update packages/webdriverio/src/index.ts Co-authored-by: Stephen Mathieson <571265+stephenmathieson@users.noreply.github.com> --- packages/webdriverio/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/webdriverio/src/index.ts b/packages/webdriverio/src/index.ts index c12dbcc0..846b35e0 100644 --- a/packages/webdriverio/src/index.ts +++ b/packages/webdriverio/src/index.ts @@ -331,8 +331,8 @@ export default class AxeBuilder { await axeSourceInject(this.client, this.script); partials.push(...(await this.runPartialRecursive(frameContext, [...frameStack, frame]))); } catch (error) { - const windows = await this.client.getWindowHandles() - await this.client.switchToWindow(windows[0]) + const [topWindow] = await this.client.getWindowHandles() + await this.client.switchToWindow(topWindow) for (const frameElm of frameStack) { await this.client.switchToFrame(frameElm); From 84ee8405dfe52ec46b7d7b1c028e77fd1fc1f8a1 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Thu, 15 Jun 2023 09:26:12 -0600 Subject: [PATCH 5/6] reject --- packages/puppeteer/src/utils.ts | 12 +++--------- packages/webdriverio/src/utils.ts | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/puppeteer/src/utils.ts b/packages/puppeteer/src/utils.ts index ed0815e4..64438d84 100644 --- a/packages/puppeteer/src/utils.ts +++ b/packages/puppeteer/src/utils.ts @@ -73,23 +73,17 @@ export async function getChildFrame( export async function assertFrameReady(frame: Frame): Promise { // Wait so that we know there is an execution context. // Assume that if we have an html node we have an execution context. - // Check if the page is loaded. - let pageReady = false; try { // Puppeteer freezes on unloaded iframes. Set a race timeout in order to handle that. // @see https://github.com/dequelabs/axe-core-npm/issues/727 - const timeoutPromise = new Promise(resolve => { + const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => { - resolve('timeout'); + reject(); }, 1000) }); const evaluatePromise = frame.evaluate(pageIsLoaded); - const raceResults = await Promise.race([timeoutPromise, evaluatePromise]); - pageReady = raceResults !== 'timeout'; + await Promise.race([timeoutPromise, evaluatePromise]); } catch { - /* ignore */ - } - if (!pageReady) { throw new Error('Page/Frame is not ready'); } } diff --git a/packages/webdriverio/src/utils.ts b/packages/webdriverio/src/utils.ts index 737487e6..ca1c180f 100644 --- a/packages/webdriverio/src/utils.ts +++ b/packages/webdriverio/src/utils.ts @@ -104,8 +104,6 @@ export const axeSourceInject = async ( async function assertFrameReady(client: Browser): Promise { // Wait so that we know there is an execution context. // Assume that if we have an html node we have an execution context. - // Check if the page is loaded. - let pageReady = false; try { /* When using the devtools protocol trying to call @@ -116,20 +114,16 @@ async function assertFrameReady(client: Browser): Promise { from our @axe-core/puppeteer utils function. @see https://github.com/dequelabs/axe-core-npm/issues/727 */ - const timeoutPromise = new Promise(resolve => { + const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => { - resolve('timeout'); + reject(); }, FRAME_LOAD_TIMEOUT) }); const executePromise = client.execute(() => { return document.readyState === 'complete' }); - const raceResults = await Promise.race([timeoutPromise, executePromise]); - pageReady = raceResults !== 'timeout'; + await Promise.race([timeoutPromise, executePromise]); } catch { - /* ignore */ - } - if (!pageReady) { throw new Error('Page/Frame is not ready'); } } From f25fc61b42e0e21a1d83e7cd5f515129b7491468 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Thu, 15 Jun 2023 11:16:24 -0600 Subject: [PATCH 6/6] assert properly --- packages/puppeteer/src/utils.ts | 4 +++- packages/webdriverio/src/utils.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/puppeteer/src/utils.ts b/packages/puppeteer/src/utils.ts index 64438d84..e636a550 100644 --- a/packages/puppeteer/src/utils.ts +++ b/packages/puppeteer/src/utils.ts @@ -1,3 +1,4 @@ +import assert from 'assert'; import * as fs from 'fs'; import * as Axe from 'axe-core'; import { Frame } from 'puppeteer'; @@ -82,7 +83,8 @@ export async function assertFrameReady(frame: Frame): Promise { }, 1000) }); const evaluatePromise = frame.evaluate(pageIsLoaded); - await Promise.race([timeoutPromise, evaluatePromise]); + const readyState = await Promise.race([timeoutPromise, evaluatePromise]); + assert(readyState); } catch { throw new Error('Page/Frame is not ready'); } diff --git a/packages/webdriverio/src/utils.ts b/packages/webdriverio/src/utils.ts index ca1c180f..f916ca22 100644 --- a/packages/webdriverio/src/utils.ts +++ b/packages/webdriverio/src/utils.ts @@ -122,7 +122,8 @@ async function assertFrameReady(client: Browser): Promise { const executePromise = client.execute(() => { return document.readyState === 'complete' }); - await Promise.race([timeoutPromise, executePromise]); + const readyState = await Promise.race([timeoutPromise, executePromise]); + assert(readyState); } catch { throw new Error('Page/Frame is not ready'); }