diff --git a/lib/NetworkManager.js b/lib/NetworkManager.js index a91a866bcf6..8cc76bf3f5c 100644 --- a/lib/NetworkManager.js +++ b/lib/NetworkManager.js @@ -218,7 +218,9 @@ class NetworkManager extends EventEmitter { } if (event.redirectResponse) { const request = this._requestIdToRequest.get(event.requestId); - this._handleRequestRedirect(request, event.redirectResponse.status, event.redirectResponse.headers); + // If we connect late to the target, we could have missed the requestWillBeSent event. + if (request) + this._handleRequestRedirect(request, event.redirectResponse.status, event.redirectResponse.headers); } this._handleRequestStart(event.requestId, null, event.request.url, event.type, event.request); } diff --git a/test/test.js b/test/test.js index c404c782ef9..9d11e31fff4 100644 --- a/test/test.js +++ b/test/test.js @@ -26,6 +26,7 @@ const SimpleServer = require('./server/SimpleServer'); const GoldenUtils = require('./golden-utils'); const YELLOW_COLOR = '\x1b[33m'; +const RED_COLOR = '\x1b[31m'; const RESET_COLOR = '\x1b[0m'; const GOLDEN_DIR = path.join(__dirname, 'golden'); @@ -64,6 +65,25 @@ else console.assert(revisionInfo.downloaded, `Chromium r${chromiumRevision} is not downloaded. Run 'npm install' and try to re-run tests.`); } +// Hack to get the currently-running spec name. +let specName = null; +jasmine.getEnv().addReporter({ + specStarted: result => specName = result.fullName +}); + +// Setup unhandledRejectionHandlers +let hasUnhandledRejection = false; +process.on('unhandledRejection', error => { + hasUnhandledRejection = true; + const textLines = [ + '', + `${RED_COLOR}[UNHANDLED PROMISE REJECTION]${RESET_COLOR} "${specName}"`, + error.stack, + '', + ]; + console.error(textLines.join('\n')); +}); + let server; let httpsServer; beforeAll(SX(async function() { @@ -1001,16 +1021,11 @@ describe('Page', function() { expect(error.message).toContain('net::ERR_CONNECTION_REFUSED'); })); it('should fail when exceeding maximum navigation timeout', SX(async function() { - let hasUnhandledRejection = false; - const unhandledRejectionHandler = () => hasUnhandledRejection = true; - process.on('unhandledRejection', unhandledRejectionHandler); // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; await page.goto(PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); - expect(hasUnhandledRejection).toBe(false); expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); - process.removeListener('unhandledRejection', unhandledRejectionHandler); })); it('should disable timeout when its set to 0', SX(async function() { let error = null; @@ -3239,9 +3254,35 @@ describe('Page', function() { await evaluatePromise; await newPage.close(); })); + it('should not crash while redirecting if original request was missed', SX(async function() { + let serverResponse = null; + server.setRoute('/one-style.css', (req, res) => serverResponse = res); + // Open a new page. Use window.open to connect to the page later. + await Promise.all([ + page.evaluate(url => window.open(url), PREFIX + '/one-style.html'), + server.waitForRequest('/one-style.css') + ]); + // Connect to the opened page. + const target = browser.targets().find(target => target.url().includes('one-style.html')); + const newPage = await target.page(); + // Issue a redirect. + serverResponse.writeHead(302, { location: '/injectedstyle.css' }); + serverResponse.end(); + // Wait for the new page to load. + await waitForEvents(newPage, 'load'); + + expect(hasUnhandledRejection).toBe(false); + + // Cleanup. + await newPage.close(); + })); }); }); +it('Unhandled promise rejections should not be thrown', function() { + expect(hasUnhandledRejection).toBe(false); +}); + if (process.env.COVERAGE) { describe('COVERAGE', function(){ const coverage = helper.publicAPICoverage();