diff --git a/packages/injected/src/utilityScript.ts b/packages/injected/src/utilityScript.ts index 5c257ef25d7ca..5dc12744a417a 100644 --- a/packages/injected/src/utilityScript.ts +++ b/packages/injected/src/utilityScript.ts @@ -82,7 +82,17 @@ export class UtilityScript { if (typeof result === 'function') result = result(...parameters); } - return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result; + if (isThenable(result)) { + return (async () => { + // By using async function we ensure that return value is a native Promise, + // and not some overridden Promise in the page. + // This makes Firefox and WebKit debugging protocols recognize it as a Promise, + // properly await and return the value. + const value = await result; + return returnByValue ? this._jsonValueNoThrow(value) : value; + })(); + } + return returnByValue ? this._jsonValueNoThrow(result) : result; } jsonValue(returnByValue: true, value: any) { @@ -92,25 +102,15 @@ export class UtilityScript { return serializeAsCallArgument(value, (value: any) => ({ fallThrough: value })); } - private _promiseAwareJsonValueNoThrow(value: any) { - const safeJson = (value: any) => { - try { - return this.jsonValue(true, value); - } catch (e) { - return undefined; - } - }; - - if (value && typeof value === 'object' && typeof value.then === 'function') { - return (async () => { - // By using async function we ensure that return value is a native Promise, - // and not some overridden Promise in the page. - // This makes Firefox and WebKit debugging protocols recognize it as a Promise, - // properly await and return the value. - const promiseValue = await value; - return safeJson(promiseValue); - })(); + private _jsonValueNoThrow(value: any) { + try { + return this.jsonValue(true, value); + } catch (e) { + return undefined; } - return safeJson(value); } } + +function isThenable(value: any): boolean { + return value && typeof value === 'object' && typeof value.then === 'function'; +} diff --git a/tests/page/page-evaluate.spec.ts b/tests/page/page-evaluate.spec.ts index 30cd7a3462c93..85b7246d07a27 100644 --- a/tests/page/page-evaluate.spec.ts +++ b/tests/page/page-evaluate.spec.ts @@ -341,6 +341,12 @@ it('should work with overwritten Promise', async ({ page }) => { // Now, the new promise should be awaitable. expect(await page.evaluate(() => Promise.resolve(42))).toBe(42); + const handle = await page.evaluateHandle(() => new Promise(f => setTimeout(() => f(42), 50))); + expect(await handle.jsonValue()).toBe(42); + const error = await page.waitForFunction(() => false, undefined, { timeout: 500 }).catch(e => e); + expect(error.message).toContain('Timeout 500ms exceeded'); + await page.evaluate(() => setTimeout(() => (window as any).__done = true, 50)); + await page.waitForFunction(() => (window as any).__done); }); it('should throw when passed more than one parameter', async ({ page }) => {