Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(domworld): fix waitfor bindings (#6766) #6775

Merged
merged 2 commits into from
Jan 25, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/common/DOMWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,12 @@ export class DOMWorld {
const bind = async (name: string) => {
const expression = helper.pageBindingInitString('internal', name);
try {
// TODO: In theory, it would be enough to call this just once
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would the binding also survive page navigations if we only call it once?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The binding is added to m_activeBindings in both cases and stays there until the binding is removed by calling the removeBinding cdp command. And whenever a new ExecutionContext is created, it automatically restores the bindings. This works except for the case when the binding is only exposed to a single execution context identified by an id because it will not be able to restore it.

Adding the binding by name will therefore restore it after navigations. I also added a test for that case to make sure that we don't regress.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I see. It sounds like addressing contexts by name instead of id is quite appropriate for this use case then. Thanks!

await context._client.send('Runtime.addBinding', {
name,
executionContextId: context._contextId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore The protocol defintion is not up to date.
jschfflr marked this conversation as resolved.
Show resolved Hide resolved
executionContextName: context._contextName,
});
await context.evaluate(expression);
} catch (error) {
Expand Down
5 changes: 5 additions & 0 deletions src/common/ExecutionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export class ExecutionContext {
* @internal
*/
_contextId: number;
/**
* @internal
*/
_contextName: string;

/**
* @internal
Expand All @@ -68,6 +72,7 @@ export class ExecutionContext {
this._client = client;
this._world = world;
this._contextId = contextPayload.id;
this._contextName = contextPayload.name;
}

/**
Expand Down
10 changes: 10 additions & 0 deletions test/ariaqueryhandler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ describeChromeOnly('AriaQueryHandler', () => {
await page.waitForSelector('aria/[role="button"]');
});

it('should persist query handler bindings across reloads', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
await page.reload();
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
});

it('should work independently of `exposeFunction`', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
Expand Down