diff --git a/test/network.spec.js b/test/network.spec.js index 996f8f0e..f47611dd 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -374,465 +374,6 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe('Page.setRequestInterception', function() { - it('should intercept', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - if (utils.isFavicon(request)) { - request.continue(); - return; - } - expect(request.url()).toContain('empty.html'); - expect(request.headers()['user-agent']).toBeTruthy(); - expect(request.method()).toBe('GET'); - expect(request.postData()).toBe(undefined); - expect(request.isNavigationRequest()).toBe(true); - expect(request.resourceType()).toBe('document'); - expect(request.frame() === page.mainFrame()).toBe(true); - expect(request.frame().url()).toBe('about:blank'); - request.continue(); - }); - const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok()).toBe(true); - expect(response.remoteAddress().port).toBe(server.PORT); - }); - it('should work when POST is redirected with 302', async({page, server}) => { - server.setRedirect('/rredirect', '/empty.html'); - await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - await page.setContent(` -
- -
- `); - await Promise.all([ - page.$eval('form', form => form.submit()), - page.waitForNavigation() - ]); - }); - // @see https://github.com/GoogleChrome/puppeteer/issues/3973 - it('should work when header manipulation headers with redirect', async({page, server}) => { - server.setRedirect('/rrredirect', '/empty.html'); - await page.setRequestInterception(true); - page.on('request', request => { - const headers = Object.assign({}, request.headers(), { - foo: 'bar' - }); - request.continue({ headers }); - }); - await page.goto(server.PREFIX + '/rrredirect'); - }); - it('should contain referer header', async({page, server}) => { - await page.setRequestInterception(true); - const requests = []; - page.on('request', request => { - if (!utils.isFavicon(request)) - requests.push(request); - request.continue(); - }); - await page.goto(server.PREFIX + '/one-style.html'); - expect(requests[1].url()).toContain('/one-style.css'); - expect(requests[1].headers().referer).toContain('/one-style.html'); - }); - it('should properly return navigation response when URL has cookies', async({page, server}) => { - // Setup cookie. - await page.goto(server.EMPTY_PAGE); - await page.setCookie({ name: 'foo', value: 'bar'}); - - // Setup request interception. - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - const response = await page.reload(); - expect(response.status()).toBe(200); - }); - it('should stop intercepting', async({page, server}) => { - await page.setRequestInterception(true); - page.once('request', request => request.continue()); - await page.goto(server.EMPTY_PAGE); - await page.setRequestInterception(false); - await page.goto(server.EMPTY_PAGE); - }); - it('should show custom HTTP headers', async({page, server}) => { - await page.setExtraHTTPHeaders({ - foo: 'bar' - }); - await page.setRequestInterception(true); - page.on('request', request => { - expect(request.headers()['foo']).toBe('bar'); - request.continue(); - }); - const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok()).toBe(true); - }); - it('should works with customizing referer headers', async({page, server}) => { - await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE }); - await page.setRequestInterception(true); - page.on('request', request => { - expect(request.headers()['referer']).toBe(server.EMPTY_PAGE); - request.continue(); - }); - const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok()).toBe(true); - }); - it('should be abortable', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - if (request.url().endsWith('.css')) - request.abort(); - else - request.continue(); - }); - let failedRequests = 0; - page.on('requestfailed', event => ++failedRequests); - const response = await page.goto(server.PREFIX + '/one-style.html'); - expect(response.ok()).toBe(true); - expect(response.request().failure()).toBe(null); - expect(failedRequests).toBe(1); - }); - it_fails_ffox('should be abortable with custom error codes', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - request.abort('internetdisconnected'); - }); - let failedRequest = null; - page.on('requestfailed', request => failedRequest = request); - await page.goto(server.EMPTY_PAGE).catch(e => {}); - expect(failedRequest).toBeTruthy(); - expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED'); - }); - it('should send referer', async({page, server}) => { - await page.setExtraHTTPHeaders({ - referer: 'http://google.com/' - }); - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - const [request] = await Promise.all([ - server.waitForRequest('/grid.html'), - page.goto(server.PREFIX + '/grid.html'), - ]); - expect(request.headers['referer']).toBe('http://google.com/'); - }); - it('should fail navigation when aborting main resource', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => request.abort()); - let error = null; - await page.goto(server.EMPTY_PAGE).catch(e => error = e); - expect(error).toBeTruthy(); - if (CHROME) - expect(error.message).toContain('net::ERR_FAILED'); - else - expect(error.message).toContain('NS_ERROR_FAILURE'); - }); - it('should work with redirects', async({page, server}) => { - await page.setRequestInterception(true); - const requests = []; - page.on('request', request => { - request.continue(); - requests.push(request); - }); - server.setRedirect('/non-existing-page.html', '/non-existing-page-2.html'); - server.setRedirect('/non-existing-page-2.html', '/non-existing-page-3.html'); - server.setRedirect('/non-existing-page-3.html', '/non-existing-page-4.html'); - server.setRedirect('/non-existing-page-4.html', '/empty.html'); - const response = await page.goto(server.PREFIX + '/non-existing-page.html'); - expect(response.status()).toBe(200); - expect(response.url()).toContain('empty.html'); - expect(requests.length).toBe(5); - expect(requests[2].resourceType()).toBe('document'); - // Check redirect chain - const redirectChain = response.request().redirectChain(); - expect(redirectChain.length).toBe(4); - expect(redirectChain[0].url()).toContain('/non-existing-page.html'); - expect(redirectChain[2].url()).toContain('/non-existing-page-3.html'); - for (let i = 0; i < redirectChain.length; ++i) { - const request = redirectChain[i]; - expect(request.isNavigationRequest()).toBe(true); - expect(request.redirectChain().indexOf(request)).toBe(i); - } - }); - it('should work with redirects for subresources', async({page, server}) => { - await page.setRequestInterception(true); - const requests = []; - page.on('request', request => { - request.continue(); - if (!utils.isFavicon(request)) - requests.push(request); - }); - server.setRedirect('/one-style.css', '/two-style.css'); - server.setRedirect('/two-style.css', '/three-style.css'); - server.setRedirect('/three-style.css', '/four-style.css'); - server.setRoute('/four-style.css', (req, res) => res.end('body {box-sizing: border-box; }')); - - const response = await page.goto(server.PREFIX + '/one-style.html'); - expect(response.status()).toBe(200); - expect(response.url()).toContain('one-style.html'); - expect(requests.length).toBe(5); - expect(requests[0].resourceType()).toBe('document'); - expect(requests[1].resourceType()).toBe('stylesheet'); - // Check redirect chain - const redirectChain = requests[1].redirectChain(); - expect(redirectChain.length).toBe(3); - expect(redirectChain[0].url()).toContain('/one-style.css'); - expect(redirectChain[2].url()).toContain('/three-style.css'); - }); - it('should be able to abort redirects', async({page, server}) => { - await page.setRequestInterception(true); - server.setRedirect('/non-existing.json', '/non-existing-2.json'); - server.setRedirect('/non-existing-2.json', '/simple.html'); - page.on('request', request => { - if (request.url().includes('non-existing-2')) - request.abort(); - else - request.continue(); - }); - await page.goto(server.EMPTY_PAGE); - const result = await page.evaluate(async() => { - try { - await fetch('/non-existing.json'); - } catch (e) { - return e.message; - } - }); - if (CHROME) - expect(result).toContain('Failed to fetch'); - else - expect(result).toContain('NetworkError'); - }); - it('should work with equal requests', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - let responseCount = 1; - server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + '')); - await page.setRequestInterception(true); - - let spinner = false; - // Cancel 2nd request. - page.on('request', request => { - if (utils.isFavicon(request)) { - request.continue(); - return; - } - spinner ? request.abort() : request.continue(); - spinner = !spinner; - }); - const results = await page.evaluate(() => Promise.all([ - fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), - fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), - fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), - ])); - expect(results).toEqual(['11', 'FAILED', '22']); - }); - it_fails_ffox('should navigate to dataURL and fire dataURL requests', async({page, server}) => { - await page.setRequestInterception(true); - const requests = []; - page.on('request', request => { - requests.push(request); - request.continue(); - }); - const dataURL = 'data:text/html,
yo
'; - const response = await page.goto(dataURL); - expect(response.status()).toBe(200); - expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(dataURL); - }); - it_fails_ffox('should navigate to URL with hash and and fire requests without hash', async({page, server}) => { - await page.setRequestInterception(true); - const requests = []; - page.on('request', request => { - requests.push(request); - request.continue(); - }); - const response = await page.goto(server.EMPTY_PAGE + '#hash'); - expect(response.status()).toBe(200); - expect(response.url()).toBe(server.EMPTY_PAGE); - expect(requests.length).toBe(1); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); - }); - it('should work with encoded server', async({page, server}) => { - // The requestWillBeSent will report encoded URL, whereas interception will - // report URL as-is. @see crbug.com/759388 - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - const response = await page.goto(server.PREFIX + '/some nonexisting page'); - expect(response.status()).toBe(404); - }); - it('should work with badly encoded server', async({page, server}) => { - await page.setRequestInterception(true); - server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); - page.on('request', request => request.continue()); - const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); - expect(response.status()).toBe(200); - }); - it_fails_ffox('should work with encoded server - 2', async({page, server}) => { - // The requestWillBeSent will report URL as-is, whereas interception will - // report encoded URL for stylesheet. @see crbug.com/759388 - await page.setRequestInterception(true); - const requests = []; - page.on('request', request => { - request.continue(); - requests.push(request); - }); - const response = await page.goto(`data:text/html,`); - expect(response.status()).toBe(200); - expect(requests.length).toBe(2); - expect(requests[1].response().status()).toBe(404); - }); - it_fails_ffox('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => { - await page.setContent(''); - await page.setRequestInterception(true); - let request = null; - page.on('request', async r => request = r); - page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE), - // Wait for request interception. - await utils.waitEvent(page, 'request'); - // Delete frame to cause request to be canceled. - await page.$eval('iframe', frame => frame.remove()); - let error = null; - await request.continue().catch(e => error = e); - expect(error).toBe(null); - }); - it('should throw if interception is not enabled', async({page, server}) => { - let error = null; - page.on('request', async request => { - try { - await request.continue(); - } catch (e) { - error = e; - } - }); - await page.goto(server.EMPTY_PAGE); - expect(error.message).toContain('Request Interception is not enabled'); - }); - it_fails_ffox('should work with file URLs', async({page, server}) => { - await page.setRequestInterception(true); - const urls = new Set(); - page.on('request', request => { - urls.add(request.url().split('/').pop()); - request.continue(); - }); - await page.goto(pathToFileURL(path.join(__dirname, 'assets', 'one-style.html'))); - expect(urls.size).toBe(2); - expect(urls.has('one-style.html')).toBe(true); - expect(urls.has('one-style.css')).toBe(true); - }); - }); - - describe('Request.continue', function() { - it('should work', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - await page.goto(server.EMPTY_PAGE); - }); - it('should amend HTTP headers', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - const headers = Object.assign({}, request.headers()); - headers['FOO'] = 'bar'; - request.continue({ headers }); - }); - await page.goto(server.EMPTY_PAGE); - const [request] = await Promise.all([ - server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz')) - ]); - expect(request.headers['foo']).toBe('bar'); - }); - it_fails_ffox('should redirect in a way non-observable to page', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - const redirectURL = request.url().includes('/empty.html') ? server.PREFIX + '/consolelog.html' : undefined; - request.continue({ url: redirectURL }); - }); - let consoleMessage = null; - page.on('console', msg => consoleMessage = msg); - await page.goto(server.EMPTY_PAGE); - expect(page.url()).toBe(server.EMPTY_PAGE); - expect(consoleMessage.text()).toBe('yellow'); - }); - it_fails_ffox('should amend method', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - - await page.setRequestInterception(true); - page.on('request', request => { - request.continue({ method: 'POST' }); - }); - const [request] = await Promise.all([ - server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz')) - ]); - expect(request.method).toBe('POST'); - }); - it_fails_ffox('should amend post data', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - - await page.setRequestInterception(true); - page.on('request', request => { - request.continue({ postData: 'doggo' }); - }); - const [serverRequest] = await Promise.all([ - server.waitForRequest('/sleep.zzz'), - page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) - ]); - expect(await serverRequest.postBody).toBe('doggo'); - }); - }); - - describe_fails_ffox('Request.respond', function() { - it('should work', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - request.respond({ - status: 201, - headers: { - foo: 'bar' - }, - body: 'Yo, page!' - }); - }); - const response = await page.goto(server.EMPTY_PAGE); - expect(response.status()).toBe(201); - expect(response.headers().foo).toBe('bar'); - expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); - }); - it('should redirect', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - if (!request.url().includes('rrredirect')) { - request.continue(); - return; - } - request.respond({ - status: 302, - headers: { - location: server.EMPTY_PAGE, - }, - }); - }); - const response = await page.goto(server.PREFIX + '/rrredirect'); - expect(response.request().redirectChain().length).toBe(1); - expect(response.request().redirectChain()[0].url()).toBe(server.PREFIX + '/rrredirect'); - expect(response.url()).toBe(server.EMPTY_PAGE); - }); - it('should allow mocking binary responses', async({page, server}) => { - await page.setRequestInterception(true); - page.on('request', request => { - const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); - request.respond({ - contentType: 'image/png', - body: imageBuffer - }); - }); - await page.evaluate(PREFIX => { - const img = document.createElement('img'); - img.src = PREFIX + '/does-not-exist.png'; - document.body.appendChild(img); - return new Promise(fulfill => img.onload = fulfill); - }, server.PREFIX); - const img = await page.$('img'); - expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); - }); - }); - describe('Page.setExtraHTTPHeaders', function() { it('should work', async({page, server}) => { await page.setExtraHTTPHeaders({ @@ -894,14 +435,3 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }; -/** - * @param {string} path - * @return {string} - */ -function pathToFileURL(path) { - let pathName = path.replace(/\\/g, '/'); - // Windows drive letter must be prefixed with a slash. - if (!pathName.startsWith('/')) - pathName = '/' + pathName; - return 'file://' + pathName; -} diff --git a/test/puppeteer.spec.js b/test/puppeteer.spec.js index 81acb29f..b737e3cf 100644 --- a/test/puppeteer.spec.js +++ b/test/puppeteer.spec.js @@ -137,6 +137,7 @@ module.exports.addTests = ({testRunner, product, puppeteerPath}) => { require('./mouse.spec.js').addTests(testOptions); require('./navigation.spec.js').addTests(testOptions); require('./network.spec.js').addTests(testOptions); + require('./requestinterception.spec.js').addTests(testOptions); require('./page.spec.js').addTests(testOptions); require('./screenshot.spec.js').addTests(testOptions); require('./queryselector.spec.js').addTests(testOptions); diff --git a/test/requestinterception.spec.js b/test/requestinterception.spec.js new file mode 100644 index 00000000..04ac8244 --- /dev/null +++ b/test/requestinterception.spec.js @@ -0,0 +1,497 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs'); +const path = require('path'); +const utils = require('./utils'); + +module.exports.addTests = function({testRunner, expect, CHROME}) { + const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; + const {it, fit, xit, it_fails_ffox} = testRunner; + const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; + + describe('Page.setRequestInterception', function() { + it('should intercept', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + if (utils.isFavicon(request)) { + request.continue(); + return; + } + expect(request.url()).toContain('empty.html'); + expect(request.headers()['user-agent']).toBeTruthy(); + expect(request.method()).toBe('GET'); + expect(request.postData()).toBe(undefined); + expect(request.isNavigationRequest()).toBe(true); + expect(request.resourceType()).toBe('document'); + expect(request.frame() === page.mainFrame()).toBe(true); + expect(request.frame().url()).toBe('about:blank'); + request.continue(); + }); + const response = await page.goto(server.EMPTY_PAGE); + expect(response.ok()).toBe(true); + expect(response.remoteAddress().port).toBe(server.PORT); + }); + it('should work when POST is redirected with 302', async({page, server}) => { + server.setRedirect('/rredirect', '/empty.html'); + await page.goto(server.EMPTY_PAGE); + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + await page.setContent(` +
+ +
+ `); + await Promise.all([ + page.$eval('form', form => form.submit()), + page.waitForNavigation() + ]); + }); + // @see https://github.com/GoogleChrome/puppeteer/issues/3973 + it('should work when header manipulation headers with redirect', async({page, server}) => { + server.setRedirect('/rrredirect', '/empty.html'); + await page.setRequestInterception(true); + page.on('request', request => { + const headers = Object.assign({}, request.headers(), { + foo: 'bar' + }); + request.continue({ headers }); + }); + await page.goto(server.PREFIX + '/rrredirect'); + }); + it('should contain referer header', async({page, server}) => { + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + if (!utils.isFavicon(request)) + requests.push(request); + request.continue(); + }); + await page.goto(server.PREFIX + '/one-style.html'); + expect(requests[1].url()).toContain('/one-style.css'); + expect(requests[1].headers().referer).toContain('/one-style.html'); + }); + it('should properly return navigation response when URL has cookies', async({page, server}) => { + // Setup cookie. + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ name: 'foo', value: 'bar'}); + + // Setup request interception. + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + const response = await page.reload(); + expect(response.status()).toBe(200); + }); + it('should stop intercepting', async({page, server}) => { + await page.setRequestInterception(true); + page.once('request', request => request.continue()); + await page.goto(server.EMPTY_PAGE); + await page.setRequestInterception(false); + await page.goto(server.EMPTY_PAGE); + }); + it('should show custom HTTP headers', async({page, server}) => { + await page.setExtraHTTPHeaders({ + foo: 'bar' + }); + await page.setRequestInterception(true); + page.on('request', request => { + expect(request.headers()['foo']).toBe('bar'); + request.continue(); + }); + const response = await page.goto(server.EMPTY_PAGE); + expect(response.ok()).toBe(true); + }); + it('should works with customizing referer headers', async({page, server}) => { + await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE }); + await page.setRequestInterception(true); + page.on('request', request => { + expect(request.headers()['referer']).toBe(server.EMPTY_PAGE); + request.continue(); + }); + const response = await page.goto(server.EMPTY_PAGE); + expect(response.ok()).toBe(true); + }); + it('should be abortable', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + if (request.url().endsWith('.css')) + request.abort(); + else + request.continue(); + }); + let failedRequests = 0; + page.on('requestfailed', event => ++failedRequests); + const response = await page.goto(server.PREFIX + '/one-style.html'); + expect(response.ok()).toBe(true); + expect(response.request().failure()).toBe(null); + expect(failedRequests).toBe(1); + }); + it_fails_ffox('should be abortable with custom error codes', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + request.abort('internetdisconnected'); + }); + let failedRequest = null; + page.on('requestfailed', request => failedRequest = request); + await page.goto(server.EMPTY_PAGE).catch(e => {}); + expect(failedRequest).toBeTruthy(); + expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED'); + }); + it('should send referer', async({page, server}) => { + await page.setExtraHTTPHeaders({ + referer: 'http://google.com/' + }); + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + const [request] = await Promise.all([ + server.waitForRequest('/grid.html'), + page.goto(server.PREFIX + '/grid.html'), + ]); + expect(request.headers['referer']).toBe('http://google.com/'); + }); + it('should fail navigation when aborting main resource', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => request.abort()); + let error = null; + await page.goto(server.EMPTY_PAGE).catch(e => error = e); + expect(error).toBeTruthy(); + if (CHROME) + expect(error.message).toContain('net::ERR_FAILED'); + else + expect(error.message).toContain('NS_ERROR_FAILURE'); + }); + it('should work with redirects', async({page, server}) => { + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + request.continue(); + requests.push(request); + }); + server.setRedirect('/non-existing-page.html', '/non-existing-page-2.html'); + server.setRedirect('/non-existing-page-2.html', '/non-existing-page-3.html'); + server.setRedirect('/non-existing-page-3.html', '/non-existing-page-4.html'); + server.setRedirect('/non-existing-page-4.html', '/empty.html'); + const response = await page.goto(server.PREFIX + '/non-existing-page.html'); + expect(response.status()).toBe(200); + expect(response.url()).toContain('empty.html'); + expect(requests.length).toBe(5); + expect(requests[2].resourceType()).toBe('document'); + // Check redirect chain + const redirectChain = response.request().redirectChain(); + expect(redirectChain.length).toBe(4); + expect(redirectChain[0].url()).toContain('/non-existing-page.html'); + expect(redirectChain[2].url()).toContain('/non-existing-page-3.html'); + for (let i = 0; i < redirectChain.length; ++i) { + const request = redirectChain[i]; + expect(request.isNavigationRequest()).toBe(true); + expect(request.redirectChain().indexOf(request)).toBe(i); + } + }); + it('should work with redirects for subresources', async({page, server}) => { + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + request.continue(); + if (!utils.isFavicon(request)) + requests.push(request); + }); + server.setRedirect('/one-style.css', '/two-style.css'); + server.setRedirect('/two-style.css', '/three-style.css'); + server.setRedirect('/three-style.css', '/four-style.css'); + server.setRoute('/four-style.css', (req, res) => res.end('body {box-sizing: border-box; }')); + + const response = await page.goto(server.PREFIX + '/one-style.html'); + expect(response.status()).toBe(200); + expect(response.url()).toContain('one-style.html'); + expect(requests.length).toBe(5); + expect(requests[0].resourceType()).toBe('document'); + expect(requests[1].resourceType()).toBe('stylesheet'); + // Check redirect chain + const redirectChain = requests[1].redirectChain(); + expect(redirectChain.length).toBe(3); + expect(redirectChain[0].url()).toContain('/one-style.css'); + expect(redirectChain[2].url()).toContain('/three-style.css'); + }); + it('should be able to abort redirects', async({page, server}) => { + await page.setRequestInterception(true); + server.setRedirect('/non-existing.json', '/non-existing-2.json'); + server.setRedirect('/non-existing-2.json', '/simple.html'); + page.on('request', request => { + if (request.url().includes('non-existing-2')) + request.abort(); + else + request.continue(); + }); + await page.goto(server.EMPTY_PAGE); + const result = await page.evaluate(async() => { + try { + await fetch('/non-existing.json'); + } catch (e) { + return e.message; + } + }); + if (CHROME) + expect(result).toContain('Failed to fetch'); + else + expect(result).toContain('NetworkError'); + }); + it('should work with equal requests', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + let responseCount = 1; + server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + '')); + await page.setRequestInterception(true); + + let spinner = false; + // Cancel 2nd request. + page.on('request', request => { + if (utils.isFavicon(request)) { + request.continue(); + return; + } + spinner ? request.abort() : request.continue(); + spinner = !spinner; + }); + const results = await page.evaluate(() => Promise.all([ + fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), + fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), + fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), + ])); + expect(results).toEqual(['11', 'FAILED', '22']); + }); + it_fails_ffox('should navigate to dataURL and fire dataURL requests', async({page, server}) => { + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + requests.push(request); + request.continue(); + }); + const dataURL = 'data:text/html,
yo
'; + const response = await page.goto(dataURL); + expect(response.status()).toBe(200); + expect(requests.length).toBe(1); + expect(requests[0].url()).toBe(dataURL); + }); + it_fails_ffox('should navigate to URL with hash and and fire requests without hash', async({page, server}) => { + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + requests.push(request); + request.continue(); + }); + const response = await page.goto(server.EMPTY_PAGE + '#hash'); + expect(response.status()).toBe(200); + expect(response.url()).toBe(server.EMPTY_PAGE); + expect(requests.length).toBe(1); + expect(requests[0].url()).toBe(server.EMPTY_PAGE); + }); + it('should work with encoded server', async({page, server}) => { + // The requestWillBeSent will report encoded URL, whereas interception will + // report URL as-is. @see crbug.com/759388 + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + const response = await page.goto(server.PREFIX + '/some nonexisting page'); + expect(response.status()).toBe(404); + }); + it('should work with badly encoded server', async({page, server}) => { + await page.setRequestInterception(true); + server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); + page.on('request', request => request.continue()); + const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); + expect(response.status()).toBe(200); + }); + it_fails_ffox('should work with encoded server - 2', async({page, server}) => { + // The requestWillBeSent will report URL as-is, whereas interception will + // report encoded URL for stylesheet. @see crbug.com/759388 + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + request.continue(); + requests.push(request); + }); + const response = await page.goto(`data:text/html,`); + expect(response.status()).toBe(200); + expect(requests.length).toBe(2); + expect(requests[1].response().status()).toBe(404); + }); + it_fails_ffox('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => { + await page.setContent(''); + await page.setRequestInterception(true); + let request = null; + page.on('request', async r => request = r); + page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE), + // Wait for request interception. + await utils.waitEvent(page, 'request'); + // Delete frame to cause request to be canceled. + await page.$eval('iframe', frame => frame.remove()); + let error = null; + await request.continue().catch(e => error = e); + expect(error).toBe(null); + }); + it('should throw if interception is not enabled', async({page, server}) => { + let error = null; + page.on('request', async request => { + try { + await request.continue(); + } catch (e) { + error = e; + } + }); + await page.goto(server.EMPTY_PAGE); + expect(error.message).toContain('Request Interception is not enabled'); + }); + it_fails_ffox('should work with file URLs', async({page, server}) => { + await page.setRequestInterception(true); + const urls = new Set(); + page.on('request', request => { + urls.add(request.url().split('/').pop()); + request.continue(); + }); + await page.goto(pathToFileURL(path.join(__dirname, 'assets', 'one-style.html'))); + expect(urls.size).toBe(2); + expect(urls.has('one-style.html')).toBe(true); + expect(urls.has('one-style.css')).toBe(true); + }); + }); + + describe('Request.continue', function() { + it('should work', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + await page.goto(server.EMPTY_PAGE); + }); + it('should amend HTTP headers', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + const headers = Object.assign({}, request.headers()); + headers['FOO'] = 'bar'; + request.continue({ headers }); + }); + await page.goto(server.EMPTY_PAGE); + const [request] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz')) + ]); + expect(request.headers['foo']).toBe('bar'); + }); + it_fails_ffox('should redirect in a way non-observable to page', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + const redirectURL = request.url().includes('/empty.html') ? server.PREFIX + '/consolelog.html' : undefined; + request.continue({ url: redirectURL }); + }); + let consoleMessage = null; + page.on('console', msg => consoleMessage = msg); + await page.goto(server.EMPTY_PAGE); + expect(page.url()).toBe(server.EMPTY_PAGE); + expect(consoleMessage.text()).toBe('yellow'); + }); + it_fails_ffox('should amend method', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + + await page.setRequestInterception(true); + page.on('request', request => { + request.continue({ method: 'POST' }); + }); + const [request] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz')) + ]); + expect(request.method).toBe('POST'); + }); + it_fails_ffox('should amend post data', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + + await page.setRequestInterception(true); + page.on('request', request => { + request.continue({ postData: 'doggo' }); + }); + const [serverRequest] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) + ]); + expect(await serverRequest.postBody).toBe('doggo'); + }); + }); + + describe_fails_ffox('Request.respond', function() { + it('should work', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + request.respond({ + status: 201, + headers: { + foo: 'bar' + }, + body: 'Yo, page!' + }); + }); + const response = await page.goto(server.EMPTY_PAGE); + expect(response.status()).toBe(201); + expect(response.headers().foo).toBe('bar'); + expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); + }); + it('should redirect', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + if (!request.url().includes('rrredirect')) { + request.continue(); + return; + } + request.respond({ + status: 302, + headers: { + location: server.EMPTY_PAGE, + }, + }); + }); + const response = await page.goto(server.PREFIX + '/rrredirect'); + expect(response.request().redirectChain().length).toBe(1); + expect(response.request().redirectChain()[0].url()).toBe(server.PREFIX + '/rrredirect'); + expect(response.url()).toBe(server.EMPTY_PAGE); + }); + it('should allow mocking binary responses', async({page, server}) => { + await page.setRequestInterception(true); + page.on('request', request => { + const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); + request.respond({ + contentType: 'image/png', + body: imageBuffer + }); + }); + await page.evaluate(PREFIX => { + const img = document.createElement('img'); + img.src = PREFIX + '/does-not-exist.png'; + document.body.appendChild(img); + return new Promise(fulfill => img.onload = fulfill); + }, server.PREFIX); + const img = await page.$('img'); + expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); + }); + }); + +}; + +/** + * @param {string} path + * @return {string} + */ +function pathToFileURL(path) { + let pathName = path.replace(/\\/g, '/'); + // Windows drive letter must be prefixed with a slash. + if (!pathName.startsWith('/')) + pathName = '/' + pathName; + return 'file://' + pathName; +}