fix(Network): be prepared to miss requestWillBeSent events (#1420)

With the addition of `browser.targets()` api, we now can connect to
in-flight targets.

For Puppeteer, it means that it can "miss" certain events happenning
while it wasn't attached to the target.

This patch:
- fixes this problem with NetworkManager, preparing it for the
  missed `requestWillBeSent` event.
- adds a new test to ensure that not a single unhandled promise
  rejection has happened during test execution.

Fixes #1363.
This commit is contained in:
Andrey Lushnikov 2017-11-20 15:59:07 -08:00 committed by GitHub
parent ea70ac9003
commit cafd040bf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 6 deletions

View File

@ -218,6 +218,8 @@ class NetworkManager extends EventEmitter {
}
if (event.redirectResponse) {
const request = this._requestIdToRequest.get(event.requestId);
// 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);

View File

@ -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();