From 4824a25cc62cc4e9f2e96deac030c973883b8c45 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 11 Dec 2019 13:51:03 -0800 Subject: [PATCH] fix(csp): fix some of the csp tests (#211) --- src/frames.ts | 101 +++++++++++++++++++++++++++------------------- test/page.spec.js | 16 ++++---- 2 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/frames.ts b/src/frames.ts index ec496c255b..7624253452 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -25,6 +25,7 @@ import { ClickOptions, MultiClickOptions, PointerActionOptions, SelectOption } f import { TimeoutError } from './Errors'; import { Events } from './events'; import { Page } from './page'; +import { ConsoleMessage } from './console'; const readFileAsync = helper.promisify(fs.readFile); @@ -193,28 +194,21 @@ export class Frame { content = null, type = '' } = options; - if (url !== null) { - try { - const context = await this._mainContext(); + if (!url && !path && !content) + throw new Error('Provide an object with a `url`, `path` or `content` property'); + + const context = await this._mainContext(); + return this._raceWithCSPError(async () => { + if (url !== null) return (await context.evaluateHandle(addScriptUrl, url, type)).asElement(); - } catch (error) { - throw new Error(`Loading script from ${url} failed`); + if (path !== null) { + let contents = await readFileAsync(path, 'utf8'); + contents += '//# sourceURL=' + path.replace(/\n/g, ''); + return (await context.evaluateHandle(addScriptContent, contents, type)).asElement(); } - } - - if (path !== null) { - let contents = await readFileAsync(path, 'utf8'); - contents += '//# sourceURL=' + path.replace(/\n/g, ''); - const context = await this._mainContext(); - return (await context.evaluateHandle(addScriptContent, contents, type)).asElement(); - } - - if (content !== null) { - const context = await this._mainContext(); - return (await context.evaluateHandle(addScriptContent, content, type)).asElement(); - } - - throw new Error('Provide an object with a `url`, `path` or `content` property'); + if (content !== null) + return (await context.evaluateHandle(addScriptContent, content, type)).asElement(); + }); async function addScriptUrl(url: string, type: string): Promise { const script = document.createElement('script'); @@ -249,29 +243,24 @@ export class Frame { path = null, content = null } = options; - if (url !== null) { - try { - const context = await this._mainContext(); + if (!url && !path && !content) + throw new Error('Provide an object with a `url`, `path` or `content` property'); + + const context = await this._mainContext(); + return this._raceWithCSPError(async () => { + if (url !== null) return (await context.evaluateHandle(addStyleUrl, url)).asElement(); - } catch (error) { - throw new Error(`Loading style from ${url} failed`); + + if (path !== null) { + let contents = await readFileAsync(path, 'utf8'); + contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/'; + return (await context.evaluateHandle(addStyleContent, contents)).asElement(); } - } - - if (path !== null) { - let contents = await readFileAsync(path, 'utf8'); - contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/'; - const context = await this._mainContext(); - return (await context.evaluateHandle(addStyleContent, contents)).asElement(); - } - - if (content !== null) { - const context = await this._mainContext(); - return (await context.evaluateHandle(addStyleContent, content)).asElement(); - } - - throw new Error('Provide an object with a `url`, `path` or `content` property'); - + + if (content !== null) + return (await context.evaluateHandle(addStyleContent, content)).asElement(); + }); + async function addStyleUrl(url: string): Promise { const link = document.createElement('link'); link.rel = 'stylesheet'; @@ -299,6 +288,36 @@ export class Frame { } } + private async _raceWithCSPError(func: () => Promise): Promise { + const listeners: RegisteredListener[] = []; + let result: dom.ElementHandle | undefined; + let error: Error | undefined; + let cspMessage: ConsoleMessage | undefined; + const actionPromise = new Promise(async (resolve) => { + try { + result = await func(); + } catch (e) { + error = e; + } + resolve(); + }); + const errorPromise = new Promise(resolve => { + listeners.push(helper.addEventListener(this._page, Events.Page.Console, (message: ConsoleMessage) => { + if (message.type() === 'error' && message.text().includes('Content Security Policy')) { + cspMessage = message; + resolve(); + } + })); + }); + await Promise.race([actionPromise, errorPromise]); + helper.removeEventListeners(listeners); + if (cspMessage) + throw new Error(cspMessage.text()); + if (error) + throw error; + return result; + } + async click(selector: string | types.Selector, options?: ClickOptions) { const domWorld = await this._utilityDOMWorld(); const handle = await domWorld.$(types.clearSelector(selector)); diff --git a/test/page.spec.js b/test/page.spec.js index 99fcf792be..e2c4542a64 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -709,7 +709,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF } catch (e) { error = e; } - expect(error.message).toBe('Loading script from /nonexistfile.js failed'); + expect(error).not.toBe(null); }); it('should work with a path', async({page, server}) => { @@ -719,7 +719,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF expect(await page.evaluate(() => __injected)).toBe(42); }); - it.skip(WEBKIT)('should include sourcemap when path is provided', async({page, server}) => { + it('should include sourceURL when path is provided', async({page, server}) => { await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); const result = await page.evaluate(() => __injectedError.stack); @@ -733,8 +733,8 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF expect(await page.evaluate(() => __injected)).toBe(35); }); - // @see https://github.com/GoogleChrome/puppeteer/issues/4840 - xit('should throw when added with content to the CSP page', async({page, server}) => { + // Firefox fires onload for blocked script before it issues the CSP console error. + it.skip(FFOX)('should throw when added with content to the CSP page', async({page, server}) => { await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addScriptTag({ content: 'window.__injected = 35;' }).catch(e => error = e); @@ -775,7 +775,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF } catch (e) { error = e; } - expect(error.message).toBe('Loading style from /nonexistfile.js failed'); + expect(error).not.toBe(null); }); it('should work with a path', async({page, server}) => { @@ -785,7 +785,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); }); - it('should include sourcemap when path is provided', async({page, server}) => { + it('should include sourceURL when path is provided', async({page, server}) => { await page.goto(server.EMPTY_PAGE); await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); const styleHandle = await page.$('style'); @@ -800,14 +800,14 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(0, 128, 0)'); }); - it.skip(FFOX || WEBKIT)('should throw when added with content to the CSP page', async({page, server}) => { + it('should throw when added with content to the CSP page', async({page, server}) => { await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addStyleTag({ content: 'body { background-color: green; }' }).catch(e => error = e); expect(error).toBeTruthy(); }); - it.skip(WEBKIT)('should throw when added with URL to the CSP page', async({page, server}) => { + it('should throw when added with URL to the CSP page', async({page, server}) => { await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addStyleTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedstyle.css' }).catch(e => error = e);