mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat: introduce page.on('crash')
event (#1782)
Currently, whenever the page crashes, it emits an `'error'` event. Error event is a special type of event in node.js; if unhandled, it crashes the process. Instead of emitting `'error'` event, this patch switches to emitting `'crash'` event. Playwright users are free to handle the event however they like, or just to ignore it.
This commit is contained in:
parent
aeabf9d707
commit
0ba823dd6f
@ -632,6 +632,7 @@ page.removeListener('request', logRequest);
|
||||
<!-- GEN:toc -->
|
||||
- [event: 'close'](#event-close-1)
|
||||
- [event: 'console'](#event-console)
|
||||
- [event: 'crash'](#event-crash)
|
||||
- [event: 'dialog'](#event-dialog)
|
||||
- [event: 'domcontentloaded'](#event-domcontentloaded)
|
||||
- [event: 'download'](#event-download)
|
||||
@ -726,6 +727,10 @@ page.on('console', msg => {
|
||||
page.evaluate(() => console.log('hello', 5, {foo: 'bar'}));
|
||||
```
|
||||
|
||||
#### event: 'crash'
|
||||
|
||||
Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory.
|
||||
|
||||
#### event: 'dialog'
|
||||
- <[Dialog]>
|
||||
|
||||
|
@ -121,6 +121,7 @@ export class CRSession extends EventEmitter {
|
||||
private readonly _targetType: string;
|
||||
private readonly _sessionId: string;
|
||||
private readonly _rootSessionId: string;
|
||||
private _crashed: boolean = false;
|
||||
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
@ -141,10 +142,16 @@ export class CRSession extends EventEmitter {
|
||||
this.once = super.once;
|
||||
}
|
||||
|
||||
_markAsCrashed() {
|
||||
this._crashed = true;
|
||||
}
|
||||
|
||||
async send<T extends keyof Protocol.CommandParameters>(
|
||||
method: T,
|
||||
params?: Protocol.CommandParameters[T]
|
||||
): Promise<Protocol.CommandReturnValues[T]> {
|
||||
if (this._crashed)
|
||||
throw new Error('Target crashed');
|
||||
if (!this._connection)
|
||||
throw new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`);
|
||||
const id = this._connection._rawSend(this._sessionId, method, params);
|
||||
|
@ -613,7 +613,8 @@ class FrameSession {
|
||||
this._page.emit(Events.Page.PageError, exceptionToError(exceptionDetails));
|
||||
}
|
||||
|
||||
_onTargetCrashed() {
|
||||
async _onTargetCrashed() {
|
||||
this._client._markAsCrashed();
|
||||
this._page._didCrash();
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ export const Events = {
|
||||
|
||||
Page: {
|
||||
Close: 'close',
|
||||
Crash: 'crash',
|
||||
Console: 'console',
|
||||
Dialog: 'dialog',
|
||||
Download: 'download',
|
||||
|
@ -140,6 +140,7 @@ export class FFSession extends EventEmitter {
|
||||
private _targetType: string;
|
||||
private _sessionId: string;
|
||||
private _rawSend: (message: any) => void;
|
||||
private _crashed: boolean = false;
|
||||
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
@ -161,10 +162,16 @@ export class FFSession extends EventEmitter {
|
||||
this.once = super.once;
|
||||
}
|
||||
|
||||
markAsCrashed() {
|
||||
this._crashed = true;
|
||||
}
|
||||
|
||||
async send<T extends keyof Protocol.CommandParameters>(
|
||||
method: T,
|
||||
params?: Protocol.CommandParameters[T]
|
||||
): Promise<Protocol.CommandReturnValues[T]> {
|
||||
if (this._crashed)
|
||||
throw new Error('Page crashed');
|
||||
if (this._disposed)
|
||||
throw new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`);
|
||||
const id = this._connection.nextMessageId();
|
||||
|
@ -251,6 +251,7 @@ export class FFPage implements PageDelegate {
|
||||
}
|
||||
|
||||
async _onCrashed(event: Protocol.Page.crashedPayload) {
|
||||
this._session.markAsCrashed();
|
||||
this._page._didCrash();
|
||||
}
|
||||
|
||||
|
@ -159,10 +159,7 @@ export class Page extends ExtendedEventEmitter {
|
||||
}
|
||||
|
||||
_didCrash() {
|
||||
const error = new Error('Page crashed!');
|
||||
// Do not report node.js stack.
|
||||
error.stack = 'Error: ' + error.message; // Stack is supposed to contain error message as the first line.
|
||||
this.emit('error', error);
|
||||
this.emit(Events.Page.Crash);
|
||||
}
|
||||
|
||||
_didDisconnect() {
|
||||
|
@ -97,6 +97,7 @@ export class WKSession extends EventEmitter {
|
||||
private _disposed = false;
|
||||
private readonly _rawSend: (message: any) => void;
|
||||
private readonly _callbacks = new Map<number, {resolve: (o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
private _crashed: boolean = false;
|
||||
|
||||
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
@ -122,6 +123,8 @@ export class WKSession extends EventEmitter {
|
||||
method: T,
|
||||
params?: Protocol.CommandParameters[T]
|
||||
): Promise<Protocol.CommandReturnValues[T]> {
|
||||
if (this._crashed)
|
||||
throw new Error('Target crashed');
|
||||
if (this._disposed)
|
||||
throw new Error(`Protocol error (${method}): ${this.errorText}`);
|
||||
const id = this.connection.nextMessageId();
|
||||
@ -133,6 +136,10 @@ export class WKSession extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
markAsCrashed() {
|
||||
this._crashed = true;
|
||||
}
|
||||
|
||||
isDisposed(): boolean {
|
||||
return this._disposed;
|
||||
}
|
||||
|
@ -194,8 +194,10 @@ export class WKPage implements PageDelegate {
|
||||
} else if (this._session.sessionId === targetId) {
|
||||
this._session.dispose();
|
||||
helper.removeEventListeners(this._sessionListeners);
|
||||
if (crashed)
|
||||
if (crashed) {
|
||||
this._session.markAsCrashed();
|
||||
this._page._didCrash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,19 +104,28 @@ describe('Async stacks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.Events.error', function() {
|
||||
it('should throw when page crashes', async({page}) => {
|
||||
await page.setContent(`<div>This page should crash</div>`);
|
||||
let error = null;
|
||||
page.on('error', err => error = err);
|
||||
describe('Page.Events.Crash', function() {
|
||||
function crash(page) {
|
||||
if (CHROMIUM)
|
||||
page.goto('chrome://crash').catch(e => {});
|
||||
else if (WEBKIT)
|
||||
page._delegate._session.send('Page.crash', {}).catch(e => {});
|
||||
else if (FFOX)
|
||||
page._delegate._session.send('Page.crash', {}).catch(e => {});
|
||||
await new Promise(f => page.on('error', f));
|
||||
expect(error.message).toBe('Page crashed!');
|
||||
}
|
||||
|
||||
it('should emit crash event when page crashes', async({page}) => {
|
||||
await page.setContent(`<div>This page should crash</div>`);
|
||||
crash(page);
|
||||
await new Promise(f => page.on('crash', f));
|
||||
});
|
||||
it('should throw on any action after page crashes', async({page}) => {
|
||||
await page.setContent(`<div>This page should crash</div>`);
|
||||
crash(page);
|
||||
await page.waitForEvent('crash');
|
||||
const err = await page.evaluate(() => {}).then(() => null, e => e);
|
||||
expect(err).toBeTruthy();
|
||||
expect(err.message).toContain('crash');
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user