Skip to content

Conversation

@sukrosono
Copy link
Contributor

@sukrosono sukrosono commented Oct 5, 2017

This patch add browser.disconnected event :

@sukrosono
Copy link
Contributor Author

sukrosono commented Oct 5, 2017

this just take one point on existing issues.
When the other one still open i will take care of it when i got free time.

Out of context

My attempt was add beforeEach block under Page.close suite, but the linting yell Unexpected token browser with plain function or arrow syntax. So i giving up on that.

docs/api.md Outdated
```

#### event: 'closed'
Emitted when page closed or crashed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Emitted when page gets closed or page renderer crashes.

lib/Page.js Outdated
return this.mainFrame().title();
}

_closed() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's inline this method

test/test.js Outdated
it('should emmited when page got crashed', SX(async function() {
let emitted = false;
page.on('closed', () => emitted = true);
page.goto('chrome://crash');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should be able to await page.goto here, instead of doing the waitForEvents on the following line

Copy link
Contributor Author

@sukrosono sukrosono Oct 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it fail on my machine if i do await page.goto(url); , with jasmine timeout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here the travis test failure. That's why i wait on error, maybe you can assist me?

@sukrosono
Copy link
Contributor Author

@aslushnikov dude, i need help on browser.closed, when it crashes. I have no idea how to make the browser crashes and how to make spec about it. I already trying a couple of hour but no result.

@ebidel
Copy link
Contributor

ebidel commented Oct 7, 2017

@brutalcrozt the page chrome://crash/ will crash the browser. you can use that for testing!

@sukrosono
Copy link
Contributor Author

@ebidel Thanks Eric, i notice that now. But surely i still don't know how to make browser._onCrash. Maybe that because my lack of understanding the relation browser and page in the codebase.

@sukrosono
Copy link
Contributor Author

Since now we get Browser.pages and Browser.targets ,

Browser.closed now should only fired when get closed via Browser.close.
ping @aslushnikov for review, fix #952

lib/Browser.js Outdated
}

Browser.Events = {
Closed: 'closed'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be called 'disconnected' and should be emitted whenever the connection is closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks for put a trust in me.

lib/Page.js Outdated
}

_onTargetCrashed() {
this._closed();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be no need to emit the error event now; it should be just 'crashed'

cc @JoelEinbinder

lib/Page.js Outdated
return this.mainFrame().title();
}

_closed() { this.emit(Page.Events.Closed, 'page closed'); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the 'closed' event is not needed any more since we have target events; they should cover everything

@sukrosono sukrosono changed the title feat(page): add page.closed event feat(browser): add browser.disconnected event Oct 21, 2017
@sukrosono
Copy link
Contributor Author

@aslushnikov IDK why this PR fail on node 7 when running yarn run lint.

lib/Browser.js Outdated

disconnect() {
this._connection.dispose();
this._disconnected();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the event should also be fired whenever the connection itself gets disconnected (e.g. whenever the browser gets closed)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aslushnikov Sorry, i don't get it. Could you re-explain it to me? 😕

lib/Browser.js Outdated
return version.product;
}

_disconnected() { this.emit(Browser.Events.Disconnected, 'connection disconnected');}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: formatting here is improper. The "inlining" is to remove the function _disconnected and just emit the event in disconnected method right away.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@sukrosono
Copy link
Contributor Author

ping @aslushnikov for review

lib/Browser.js Outdated

disconnect() {
this._connection.dispose();
this.emit(Browser.Events.Disconnected, 'connection disconnected');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this event should be emitted when the browser gets closed; e.g. when it crashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, let me know if it still wrong. So it's introduce breaking changes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I think there's some miscommunication here.

I actually meant the browser crash, not the page crash.

The only signal we get when the browser crashes is the disconnect of web socket (see lib/Connection.js). We should capture this event and dispatch the disconnected for it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, we take a look on it. I also need your advice to reflect the documentation changes for this one. FYI build fail because travis are down.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope that was the right one, i skip the test for this case.

test/test.js Outdated
newBrowser.disconnect();
expect(isDisconnected).toBe(true);
}));
xit('should be emitted when underlying websocket gets closed', SX(async function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should be able to:

  1. launch a browser
  2. connect to it with pptr.connect
  3. close the browser, make sure the connected one issues the disconnected event

Copy link
Contributor Author

@sukrosono sukrosono Nov 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if it still not fit, is that a parrot in your profile picture?

lib/Browser.js Outdated
return version.product;
}


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: stray line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

lib/Browser.js Outdated
/** @type {Map<string, Target>} */
this._targets = new Map();
this._connection.on('ws.closed', closed => {
this.disconnect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to run this.disconnect() here - you only need to emit the event.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

docs/api.md Outdated
});
```
#### event: 'disconnected'
emitted whenever the connection is closed `Browser.close() Browser.disconnect()`.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this one, let me know if any better suggestion.

Copy link
Contributor

@aslushnikov aslushnikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're getting close!

docs/api.md Outdated
});
```
#### event: 'disconnected'
emitted whenever the connection is closed `Browser.close() Browser.disconnect()`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do the following wording:

Emitted when puppeteer gets disconnected from the browser instance. This might happen because one of the following:

  • browser closed or crashed
  • browser.disconnect method was called

lib/Browser.js Outdated
this._closeCallback = closeCallback || new Function();
/** @type {Map<string, Target>} */
this._targets = new Map();
this._connection.on('ws.closed', closed => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldn't whitelist certain events and give them special treatment.

Instead, let's introduce a "closedCallback" for the connection and initialize it here:

this._connection.setClosedCallback(() => {
  this.emit(Browser.Events.Disconnected);
});

lib/Browser.js Outdated
/** @type {Map<string, Target>} */
this._targets = new Map();
this._connection.on('ws.closed', closed => {
this.emit(Browser.Events.Disconnected, 'connection disconnected');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's drop the argument for the event, it's unclear if it's useful.

lib/Browser.js Outdated

disconnect() {
this._connection.dispose();
this.emit(Browser.Events.Disconnected, 'connection disconnected');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to emit here: this will result in disconnected event dispatched twice (one from here, another from the connection been closed)


_onClose() {
_onClose(code, reason) {
if (code) this.emit('ws.closed', [code, reason]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. let's emit as the very last line in the method. This way, if there's an exception in user-land code, we won't be left in a non-consistent state.
  2. We should emit unconditionally
  3. Let's not emit any details for now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, hopefully i am not misunderstand again.

test/test.js Outdated
isDisconnected = true;
});
newBrowser.disconnect();
expect(isDisconnected).toBe(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, close originalBrowser here so that it's not left dangling

test/test.js Outdated
isDisconnected = true;
resolver();
});
browser._connection._ws.close(3006, 'intentionally kill it');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not reach to private variables in tests.
Instead, this test could be written like the previous one, but instead of disconnecting
the new browser, closing original one:

it('should be emitted when underlying websocket gets closed', SX(async function() {
  const browser = await puppeteer.launch(defaultBrowserOptions);
  const newBrowser = await puppeteer.connect({
    browserWSEndpoint: originalBrowser.wsEndpoint()
  });
  let isDisconnected = false;
  newBrowser.once('disconnected', () => isDisconnected = true);
  await originalBrowser.close();
  expect(isDisconnected).toBe(true);
}));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry i didn't know that, i just try to get closer to

should be emitted when underlying websocket gets closed

Most of my time yesterday was spend here, i wish know it earlier. Next time i should be more clever on testing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brutalcrozt feel free to ask questions next time if you're getting stuck.

}

module.exports = {Connection, Session};
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these different line-endings? Can you please revert the change so that the actual changes are clear?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, certainly i will do.

test/test.js Outdated
});

describe('Browser.Events.disconnected', function() {
it('should emitted when browser gets closed', SX(async function() {
Copy link
Contributor Author

@sukrosono sukrosono Nov 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one fired disconnected twice, i notice that. Same effect when i use the browser, Assuming it added to two instance the browser and originalBrowser. For me it's important you to know. If i am wrong please leave some suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

That's because:

  1. Connection should reset it's this._ws to null once the COnnection._onClose method is called.
  2. Connection._onClose should remove added event listeners (use helper.addEventListener / helper.removeEventListeners for this)
  3. The Connection.dispose() method should fast-return if this._ws === null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not fully understand it, but i do the trick. Hopefully good to go 😄

@sukrosono
Copy link
Contributor Author

@aslushnikov This one is ready captain. 😄


/**
* @param {function()} callback
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please format this?

setClosedCallback(callback) {
  this._closeCallback = callback;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, so no inline on class method? Inline only on normal code?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Inlining" is not about code formatting.
To "inline a function" means to get the function body and paste it instead of function invocations.

More on wiki: inline expanstion

}

_onClose() {
this._closeCallback();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. this should be the last line in the method
  2. for the sake of cleanliness, let's check that delegate is set:
if (this._closeCallback)
  this._closeCallback();

test/test.js Outdated
});

describe('Browser.Events.disconnected', function() {
it('should emitted when browser gets closed', SX(async function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

That's because:

  1. Connection should reset it's this._ws to null once the COnnection._onClose method is called.
  2. Connection._onClose should remove added event listeners (use helper.addEventListener / helper.removeEventListeners for this)
  3. The Connection.dispose() method should fast-return if this._ws === null.

test/test.js Outdated
await originalBrowser.close();
expect(isDisconnected).toBe(true);
}));
it('should emitted when browser.disconnect() called', SX(async function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's unify these three tests into one. This will save us on some time while running tests.

const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browserWSEndpoint = originalBrowser.wsEndpoint();
const remoteBrowser1 = await puppeteer.connect({browserWSEndpoint});
const remoteBrowser2 = await puppeteer.connect({browserWSEndpoint});

let disconnectedOriginal = 0;
let disconnectedRemote1 = 0;
let disconnectedRemote2 = 0;
originalBrowser.on('disconnected', () => ++disconnectedOriginal);
remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);

await remoteBrowser2.disconnect();
expect(disconnectedOriginal).toBe(0);
expect(disconnectedRemote1).toBe(0);
expect(disconnectedRemote2).toBe(1);

await originalBrowser.close();
expect(disconnectedOriginal).toBe(1);
expect(disconnectedRemote1).toBe(1);
expect(disconnectedRemote2).toBe(1);

@aslushnikov
Copy link
Contributor

Thanks!

@aslushnikov aslushnikov merged commit 03fefb5 into puppeteer:master Nov 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add browser.closed and page.closed events

3 participants