diff --git a/docs/api.md b/docs/api.md index ca6f50c9..d14161f4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -21,7 +21,7 @@ * [page.httpHeaders()](#pagehttpheaders) * [page.injectFile(filePath)](#pageinjectfilefilepath) * [page.mainFrame()](#pagemainframe) - * [page.navigate(url)](#pagenavigateurl) + * [page.navigate(url, options)](#pagenavigateurl-options) * [page.plainText()](#pageplaintext) * [page.printToPDF(filePath[, options])](#pageprinttopdffilepath-options) * [page.screenshot([options])](#pagescreenshotoptions) @@ -162,12 +162,22 @@ Pages could be closed by `page.close()` method. #### page.mainFrame() -#### page.navigate(url) +#### page.navigate(url, options) - `url` <[string]> URL to navigate page to -- returns: <[Promise]<[boolean]>> Promise which resolves when the page is navigated. The promise resolves to: - - `true` if the navigation succeeds and page's `load` event is fired. - - `false` if the navigation fails due to bad URL or SSL errors. +- `options` <[Object]> Navigation parameters which might have the following properties: + - `maxTime` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds. + - `waitFor` <[string]> When to consider navigation succeeded, defaults to `load`. Could be either: + - `load` - consider navigation to be finished when the `load` event is fired. + - `networkidle` - consider navigation to be finished when the network activity stays "idle" for at least `networkIdleTimeout`ms. + - `networkIdleInflight` <[number]> Maximum amount of inflight requests which are considered "idle". Takes effect only with `waitFor: 'networkidle'` parameter. + - `networkIdleTimeout` <[number]> A timeout to wait before completing navigation. Takes effect only with `waitFor: 'networkidle'` parameter. +- returns: <[Promise]<[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. + +The `page.navigate` will throw an error if: +- there's an SSL error (e.g. in case of self-signed certificates). +- target URL is invalid. +- the `maxTime` is exceeded during navigation. #### page.plainText() diff --git a/lib/Navigator.js b/lib/Navigator.js index 8d2ff4a4..af933bc8 100644 --- a/lib/Navigator.js +++ b/lib/Navigator.js @@ -34,26 +34,28 @@ class Navigator { } /** - * @return {!Promise} + * @return {!Promise} */ async navigate() { this._init(); - let certificateError = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false); - let networkIdle = new Promise(fulfill => this._networkIdleCallback = fulfill).then(() => true); - let loadEventFired = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)).then(() => true); - let watchdog = new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._maxTime)).then(() => false); + let certificateError = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)) + .then(error => new Error('SSL Certiciate error: ' + error.errorType)); + let networkIdle = new Promise(fulfill => this._networkIdleCallback = fulfill).then(() => null); + let loadEventFired = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)).then(() => null); + let watchdog = new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._maxTime)).then(() => new Error('Navigation Timeout Exceeded: ' + this._maxTime + 'ms exceeded')); // Await for the command to throw exception in case of illegal arguments. try { await this._client.send('Page.navigate', {url: this._url, referrer: this._referrer}); } catch (e) { this._cleanup(); - return false; + throw e; } - let result = await Promise.race([certificateError, watchdog, this._waitFor === 'load' ? loadEventFired : networkIdle]); + const error = await Promise.race([certificateError, watchdog, this._waitFor === 'load' ? loadEventFired : networkIdle]); this._cleanup(); - return result; + if (error) + throw error; } /** diff --git a/lib/Page.js b/lib/Page.js index c66267e8..4789e7c3 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -260,10 +260,26 @@ class Page extends EventEmitter { /** * @param {string} html * @param {!Object=} options - * @return {!Promise} + * @return {!Promise} */ - navigate(url, options) { - return new Navigator(this._client, url, this._networkManager.httpHeaders().referer, options).navigate(); + async navigate(url, options) { + /** @type {!Map} */ + const responses = new Map(); + const onResponse = response => responses.set(response.url, response); + const navigator = new Navigator(this._client, url, this._networkManager.httpHeaders().referer, options); + + try { + this._networkManager.on(NetworkManager.Events.Response, onResponse); + await navigator.navigate(); + } catch (e) { + this._networkManager.removeListener(NetworkManager.Events.Response, onResponse); + throw e; + } + + this._networkManager.removeListener(NetworkManager.Events.Response, onResponse); + const response = responses.get(this.mainFrame().url()); + console.assert(response); + return response; } /** diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index cd9e3ed1..c72f7e04 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -355,22 +355,25 @@ class WebPage { this._deferEvaluate = false; this.loading = true; this.loadingProgress = 50; - this._page.navigate(url).then(result => { + + const handleNavigation = (error, response) => { this.loadingProgress = 100; this.loading = false; - let status = result ? 'success' : 'fail'; - if (!result) { + if (error) { this.onResourceError.call(null, { url, errorString: 'SSL handshake failed' }); } + let status = error ? 'fail' : 'success'; if (this.onLoadFinished) this.onLoadFinished.call(null, status); if (callback) callback.call(null, status); this.loadingProgress = 0; - }); + }; + this._page.navigate(url).then(response => handleNavigation(null, response)) + .catch(e => handleNavigation(e, null)); } /** diff --git a/test/SimpleServer.js b/test/server/SimpleServer.js similarity index 86% rename from test/SimpleServer.js rename to test/server/SimpleServer.js index 048ec9a1..c5f5cccf 100644 --- a/test/SimpleServer.js +++ b/test/server/SimpleServer.js @@ -15,6 +15,7 @@ */ let http = require('http'); +let https = require('https'); let url = require('url'); let fs = require('fs'); let path = require('path'); @@ -39,9 +40,28 @@ class SimpleServer { /** * @param {string} dirPath * @param {number} port + * @return {!SimpleServer} */ - constructor(dirPath, port) { - this._server = http.createServer(this._onRequest.bind(this)); + static async createHTTPS(dirPath, port) { + let server = new SimpleServer(dirPath, port, { + key: fs.readFileSync(path.join(__dirname, 'key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'cert.pem')), + passphrase: 'aaaa', + }); + await new Promise(x => server._server.once('listening', x)); + return server; + } + + /** + * @param {string} dirPath + * @param {number} port + * @param {!Object=} sslOptions + */ + constructor(dirPath, port, sslOptions) { + if (sslOptions) + this._server = https.createServer(sslOptions, this._onRequest.bind(this)); + else + this._server = http.createServer(this._onRequest.bind(this)); this._server.on('connection', socket => this._onSocket(socket)); this._wsServer = new WebSocketServer({server: this._server}); this._wsServer.on('connection', this._onWebSocketConnection.bind(this)); diff --git a/test/server/cert.pem b/test/server/cert.pem new file mode 100644 index 00000000..45be1e20 --- /dev/null +++ b/test/server/cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFXzCCA0egAwIBAgIJAM+8uXXn61zZMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwIBcNMTcwNzEwMjI1MDA2WhgPMzAxNjExMTAyMjUwMDZa +MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQCfsIbw1Q91wooUdSu5tDiyrSYB6ZQmY49Y141KQ0ll0ZXzf1sPTpPg +OuBjJE8Fre2Wn3jJ0SfLFyQBMvE49hqWyY/U5Xc367ujqKFQVoItnoV5MM2TPu5J +/zhtf26Vq0Pcrujt5LfRe1JSYKdJ21Tquqa0MAGI0HghaCdSdtyo2xuotnukirKb +QrvP/YNa+ONZT6KW8MFAwfoCOJMo1idrkBA1Wve5xcCd7J9Oy5mWCBxTSR67W2vQ +izoOTSkzD0xoXpFF5V/34zJGWU9t6Z5qytV/w5ROY3Tk9EaKs0NcQmXlCxSmkXil +KSTlZ/VDeDliI92jGn4hT+apisglm3aaTnVVAP0EbZ/CF9Fwb601M7IcAP9ejaeB +EEs+smXpuzhAfxPa5SpZCWeaXLcFq6Ewi2LXrMaChWvbu9AUi3QjuT3u9PW3B0w5 +C54FLfvcy9X9dQQ/jCgydF3eyhiO3SuLZqrhofHUn53z4UCEYgbC7uQSv08ep2UD +kT2ARN6aetXVgiQBYS8hcGaFrdsUTSAmT0flt0g8ZoHn+NmuAWxbAx8UnPd0p/EP +B4cZByDOUDGgDMSOEluheiCFlZBQEJnvOhim6rwSje87EzQazGkwRpOrBtzGIGcM +xmotG9IrMbzKb4Z+yg5pOEW2WKEy3f2h8P2bisbbHwYa/tgTHVWbGwIDAQABo1Aw +TjAdBgNVHQ4EFgQUZvIOJVkyQTAd0fpUkpqgcXVID+4wHwYDVR0jBBgwFoAUZvIO +JVkyQTAd0fpUkpqgcXVID+4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEASmqzfr4TOVFbNo3r85QlCQB8W4xaArRpY6k7ugxJm2/9iyyovMKMhCcRvmPV +HZXDTV8WEbLDg3uNF56WU39YmtfjghnsYcGQ/UhnH2PoHQxGIKzOoalLew7kP0Uc +HCmXi7OPjqeFQgjWVhLkL/RIzZ3vOVFQfqodTVIdYDe0O1cNRD0U0rtGa0MHzZlD +y/ZaksiB0E3UL/GsgyJoCxCAF1E80PU9aw1K9COyTOChpUKnhSHC964KlhnJKyOe +HKCYtb5RVrF7ryBcGneLTcQm/oNUp8aRyiwyQIDH7djFSp11MakXBF+EeGR523pp +u+sleR7ZFBGyb3Z5K9NdRdUk0SWu7Hu4jQtJHn+PmIZ1qjfbPv5qDfVd1vmFwqsu +7NfsLoNm0dQNu5WOMLISQHmQiT86AH2wWQ3l5Sm+g8/BdNLQLklhtZcRhp2efyiL +ciUmGugKqoX+nPIZ36kuoRTZy++AnTiid011vZFe1qrfug/ykWiqWmBSvD/cfRU4 +ydoK87cfjIixqmpRZ7j2q+/cDK2SbYN0t/Xrufw3L6TjDgUEL7ZCImcwqqWJz9I8 +ASnnL5PhX8bbsUrtE21Ugqk2zYnVnqRO5FjINtlTb07y9pGC/QpBkb1AasF5okhe +7lw/sMiryKKzS4A10nRV/+gErDBsIBj+cpGPM8brLfZuy2o= +-----END CERTIFICATE----- diff --git a/test/server/key.pem b/test/server/key.pem new file mode 100644 index 00000000..e4837ce9 --- /dev/null +++ b/test/server/key.pem @@ -0,0 +1,54 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +4Ig= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test/test.js b/test/test.js index 3a5e9701..07d4b7da 100644 --- a/test/test.js +++ b/test/test.js @@ -17,12 +17,14 @@ let fs = require('fs'); let path = require('path'); let Browser = require('../lib/Browser'); -let SimpleServer = require('./SimpleServer'); +let SimpleServer = require('./server/SimpleServer'); let GoldenUtils = require('./golden-utils'); let PORT = 8907; -let STATIC_PREFIX = 'http://localhost:' + PORT; -let EMPTY_PAGE = STATIC_PREFIX + '/empty.html'; +let PREFIX = 'http://localhost:' + PORT; +let EMPTY_PAGE = PREFIX + '/empty.html'; +let HTTPS_PORT = 8908; +let HTTPS_PREFIX = 'https://localhost:' + HTTPS_PORT; if (process.env.DEBUG_TEST) jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 1000 * 1000; @@ -32,22 +34,29 @@ else describe('Puppeteer', function() { let browser; let server; + let httpsServer; let page; beforeAll(SX(async function() { browser = new Browser({args: ['--no-sandbox']}); - server = await SimpleServer.create(path.join(__dirname, 'assets'), PORT); + const assetsPath = path.join(__dirname, 'assets'); + server = await SimpleServer.create(assetsPath, PORT); + httpsServer = await SimpleServer.createHTTPS(assetsPath, HTTPS_PORT); GoldenUtils.removeOutputDir(); })); afterAll(SX(async function() { - await server.stop(); + await Promise.all([ + server.stop(), + httpsServer.stop(), + ]); browser.close(); })); beforeEach(SX(async function() { page = await browser.newPage(); server.reset(); + httpsServer.reset(); GoldenUtils.addMatchers(jasmine); })); @@ -186,12 +195,54 @@ describe('Puppeteer', function() { describe('Page.navigate', function() { it('should fail when navigating to bad url', SX(async function() { - let success = await page.navigate('asdfasdf'); - expect(success).toBe(false); + let error = null; + try { + await page.navigate('asdfasdf'); + } catch (e) { + error = e; + } + expect(error.message).toContain('Cannot navigate to invalid URL'); })); - it('should succeed when navigating to good url', SX(async function() { - let success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); + it('should fail when navigating to bad SSL', SX(async function() { + let error = null; + try { + await page.navigate(HTTPS_PREFIX + '/empty.html'); + } catch (e) { + error = e; + } + expect(error.message).toContain('SSL Certiciate error'); + })); + it('should fail when exceeding maximum navigation timeout', SX(async function() { + let error = null; + // Hang for request to the empty.html + server.setRoute('/empty.html', (req, res) => { }); + try { + await page.navigate(PREFIX + '/empty.html', {maxTime: 59}); + } catch (e) { + error = e; + } + expect(error.message).toContain('Navigation Timeout Exceeded: 59ms'); + })); + it('should work when navigating to valid url', SX(async function() { + const response = await page.navigate(EMPTY_PAGE); + expect(response.ok).toBe(true); + })); + it('should work when navigating to data url', SX(async function() { + const response = await page.navigate('data:text/html,hello'); + expect(response.ok).toBe(true); + })); + it('should work when navigating to 404', SX(async function() { + const response = await page.navigate(PREFIX + '/not-found'); + expect(response.ok).toBe(false); + expect(response.status).toBe(404); + })); + it('should return last response in redirect chain', SX(async function() { + server.setRedirect('/redirect/1.html', '/redirect/2.html'); + server.setRedirect('/redirect/2.html', '/redirect/3.html'); + server.setRedirect('/redirect/3.html', EMPTY_PAGE); + const response = await page.navigate(PREFIX + '/redirect/1.html'); + expect(response.ok).toBe(true); + expect(response.url).toBe(EMPTY_PAGE); })); it('should wait for network idle to succeed navigation', SX(async function() { let responses = []; @@ -209,7 +260,7 @@ describe('Puppeteer', function() { // Navigate to a page which loads immediately and then does a bunch of // requests via javascript's fetch method. - let navigationPromise = page.navigate(STATIC_PREFIX + '/networkidle.html', { + let navigationPromise = page.navigate(PREFIX + '/networkidle.html', { waitFor: 'networkidle', networkIdleTimeout: 100, networkIdleInflight: 0, // Only be idle when there are 0 inflight requests @@ -248,9 +299,9 @@ describe('Puppeteer', function() { response.end(`File not found`); } - let success = await navigationPromise; + const response = await navigationPromise; // Expect navigation to succeed. - expect(success).toBe(true); + expect(response.ok).toBe(true); })); it('should wait for websockets to succeed navigation', SX(async function() { let responses = []; @@ -259,7 +310,7 @@ describe('Puppeteer', function() { let fetchResourceRequested = server.waitForRequest('/fetch-request.js'); // Navigate to a page which loads immediately and then opens a bunch of // websocket connections and then a fetch request. - let navigationPromise = page.navigate(STATIC_PREFIX + '/websocket.html', { + let navigationPromise = page.navigate(PREFIX + '/websocket.html', { waitFor: 'networkidle', networkIdleTimeout: 100, networkIdleInflight: 0, // Only be idle when there are 0 inflight requests/connections @@ -283,9 +334,9 @@ describe('Puppeteer', function() { response.statusCode = 404; response.end(`File not found`); } - let success = await navigationPromise; + const response = await navigationPromise; // Expect navigation to succeed. - expect(success).toBe(true); + expect(response.ok).toBe(true); })); }); @@ -331,8 +382,8 @@ describe('Puppeteer', function() { expect(request.postData).toBe(undefined); request.continue(); }); - let success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); + const response = await page.navigate(EMPTY_PAGE); + expect(response.ok).toBe(true); })); it('should show custom HTTP headers', SX(async function() { await page.setHTTPHeaders({ @@ -342,8 +393,8 @@ describe('Puppeteer', function() { expect(request.headers.get('foo')).toBe('bar'); request.continue(); }); - let success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); + const response = await page.navigate(EMPTY_PAGE); + expect(response.ok).toBe(true); })); it('should be abortable', SX(async function() { page.setRequestInterceptor(request => { @@ -354,8 +405,8 @@ describe('Puppeteer', function() { }); let failedRequests = 0; page.on('requestfailed', event => ++failedRequests); - let success = await page.navigate(STATIC_PREFIX + '/one-style.html'); - expect(success).toBe(true); + const response = await page.navigate(PREFIX + '/one-style.html'); + expect(response.ok).toBe(true); expect(failedRequests).toBe(1); })); it('should amend HTTP headers', SX(async function() { @@ -400,7 +451,7 @@ describe('Puppeteer', function() { expect(error.message).toContain('Fancy'); done(); }); - page.navigate(STATIC_PREFIX + '/error.html'); + page.navigate(PREFIX + '/error.html'); }); }); @@ -417,13 +468,13 @@ describe('Puppeteer', function() { describe('Page.screenshot', function() { it('should work', SX(async function() { await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); + await page.navigate(PREFIX + '/grid.html'); let screenshot = await page.screenshot(); expect(screenshot).toBeGolden('screenshot-sanity.png'); })); it('should clip rect', SX(async function() { await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); + await page.navigate(PREFIX + '/grid.html'); let screenshot = await page.screenshot({ clip: { x: 50, @@ -436,7 +487,7 @@ describe('Puppeteer', function() { })); it('should work for offscreen clip', SX(async function() { await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); + await page.navigate(PREFIX + '/grid.html'); let screenshot = await page.screenshot({ clip: { x: 50, @@ -449,7 +500,7 @@ describe('Puppeteer', function() { })); it('should run in parallel', SX(async function() { await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); + await page.navigate(PREFIX + '/grid.html'); let promises = []; for (let i = 0; i < 3; ++i) { promises.push(page.screenshot({ @@ -466,7 +517,7 @@ describe('Puppeteer', function() { })); it('should take fullPage screenshots', SX(async function() { await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); + await page.navigate(PREFIX + '/grid.html'); let screenshot = await page.screenshot({ fullPage: true }); @@ -477,7 +528,7 @@ describe('Puppeteer', function() { describe('Frame Management', function() { let FrameUtils = require('./frame-utils'); it('should handle nested frames', SX(async function() { - await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); + await page.navigate(PREFIX + '/frames/nested-frames.html'); expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt'); })); it('should send events when frames are manipulated dynamically', SX(async function() { @@ -523,7 +574,7 @@ describe('Puppeteer', function() { page.on('frameattached', frame => attachedFrames.push(frame)); page.on('framedetached', frame => detachedFrames.push(frame)); page.on('framenavigated', frame => navigatedFrames.push(frame)); - await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); + await page.navigate(PREFIX + '/frames/nested-frames.html'); expect(attachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(0); expect(navigatedFrames.length).toBe(5); @@ -540,12 +591,12 @@ describe('Puppeteer', function() { describe('input', function() { it('should click the button', SX(async function() { - await page.navigate(STATIC_PREFIX + '/input/button.html'); + await page.navigate(PREFIX + '/input/button.html'); await page.click('button'); expect(await page.evaluate(() => result)).toBe('Clicked'); })); it('should fail to click a missing button', SX(async function() { - await page.navigate(STATIC_PREFIX + '/input/button.html'); + await page.navigate(PREFIX + '/input/button.html'); try { await page.click('button.does-not-exist'); fail('Clicking the button did not throw.'); @@ -554,20 +605,20 @@ describe('Puppeteer', function() { } })); it('should type into the textarea', SX(async function() { - await page.navigate(STATIC_PREFIX + '/input/textarea.html'); + await page.navigate(PREFIX + '/input/textarea.html'); await page.focus('textarea'); await page.type('Type in this text!'); expect(await page.evaluate(() => result)).toBe('Type in this text!'); })); it('should click the button after navigation ', SX(async function() { - await page.navigate(STATIC_PREFIX + '/input/button.html'); + await page.navigate(PREFIX + '/input/button.html'); await page.click('button'); - await page.navigate(STATIC_PREFIX + '/input/button.html'); + await page.navigate(PREFIX + '/input/button.html'); await page.click('button'); expect(await page.evaluate(() => result)).toBe('Clicked'); })); it('should upload the file', SX(async function(){ - await page.navigate(STATIC_PREFIX + '/input/fileupload.html'); + await page.navigate(PREFIX + '/input/fileupload.html'); await page.uploadFile('input', __dirname + '/assets/file-to-upload.txt'); expect(await page.evaluate(() => { let input = document.querySelector('input'); @@ -626,7 +677,7 @@ describe('Puppeteer', function() { it('should work', SX(async function() { let response = null; page.on('response', r => response = r); - await page.navigate(STATIC_PREFIX + '/simple.json'); + await page.navigate(PREFIX + '/simple.json'); expect(response).toBeTruthy(); expect(response.bodyUsed).toBe(false); expect(await response.text()).toBe('{"foo": "bar"}\n'); @@ -663,7 +714,7 @@ describe('Puppeteer', function() { }); let failedRequests = []; page.on('requestfailed', request => failedRequests.push(request)); - await page.navigate(STATIC_PREFIX + '/one-style.html'); + await page.navigate(PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); @@ -691,7 +742,7 @@ describe('Puppeteer', function() { page.on('requestfinished', request => events.push(`DONE ${request.url}`)); page.on('requestfailed', request => events.push(`FAIL ${request.url}`)); server.setRedirect('/foo.html', '/empty.html'); - const FOO_URL = STATIC_PREFIX + '/foo.html'; + const FOO_URL = PREFIX + '/foo.html'; await page.navigate(FOO_URL); expect(events).toEqual([ `GET ${FOO_URL}`, @@ -733,7 +784,7 @@ describe('Puppeteer', function() { await page.evaluateOnInitialized(function(){ window.injected = 123; }); - await page.navigate(STATIC_PREFIX + '/tamperable.html'); + await page.navigate(PREFIX + '/tamperable.html'); expect(await page.evaluate(() => window.result)).toBe(123); })); }); @@ -745,7 +796,7 @@ describe('Puppeteer', function() { }); it('should print to pdf', SX(async function() { - await page.navigate(STATIC_PREFIX + '/grid.html'); + await page.navigate(PREFIX + '/grid.html'); await page.printToPDF(outputFile); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); })); @@ -760,7 +811,7 @@ describe('Puppeteer', function() { describe('Page.title', function() { it('should return the page title', SX(async function(){ - await page.navigate(STATIC_PREFIX + '/input/button.html'); + await page.navigate(PREFIX + '/input/button.html'); expect(await page.title()).toBe('Button test'); })); });