fix(handles): always create proper handle type (#4879)

This commit is contained in:
Dmitry Gozman 2021-01-04 13:54:55 -08:00 committed by GitHub
parent 31ffeb32e3
commit c4df522555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 3 deletions

View File

@ -176,6 +176,8 @@ export class DispatcherConnection {
} }
try { try {
const validated = this._validateParams(dispatcher._type, method, params); const validated = this._validateParams(dispatcher._type, method, params);
if (typeof (dispatcher as any)[method] !== 'function')
throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
const result = await (dispatcher as any)[method](validated, this._validateMetadata(metadata)); const result = await (dispatcher as any)[method](validated, this._validateMetadata(metadata));
this.onmessage({ id, result: this._replaceDispatchersWithGuids(result) }); this.onmessage({ id, result: this._replaceDispatchersWithGuids(result) });
} catch (e) { } catch (e) {

View File

@ -23,6 +23,7 @@ import { parseSerializedValue, serializeValue } from '../protocol/serializers';
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandleInitializer> implements channels.JSHandleChannel { export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandleInitializer> implements channels.JSHandleChannel {
constructor(scope: DispatcherScope, jsHandle: js.JSHandle) { constructor(scope: DispatcherScope, jsHandle: js.JSHandle) {
// Do not call this directly, use createHandle() instead.
super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', { super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', {
preview: jsHandle.toString(), preview: jsHandle.toString(),
}); });
@ -47,7 +48,7 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
const map = await this._object.getProperties(); const map = await this._object.getProperties();
const properties = []; const properties = [];
for (const [name, value] of map) for (const [name, value] of map)
properties.push({ name, value: new JSHandleDispatcher(this._scope, value) }); properties.push({ name, value: createHandle(this._scope, value) });
return { properties }; return { properties };
} }

View File

@ -26,7 +26,7 @@ import { DialogDispatcher } from './dialogDispatcher';
import { DownloadDispatcher } from './downloadDispatcher'; import { DownloadDispatcher } from './downloadDispatcher';
import { FrameDispatcher } from './frameDispatcher'; import { FrameDispatcher } from './frameDispatcher';
import { RequestDispatcher, ResponseDispatcher, RouteDispatcher, WebSocketDispatcher } from './networkDispatchers'; import { RequestDispatcher, ResponseDispatcher, RouteDispatcher, WebSocketDispatcher } from './networkDispatchers';
import { serializeResult, parseArgument, JSHandleDispatcher } from './jsHandleDispatcher'; import { serializeResult, parseArgument } from './jsHandleDispatcher';
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher'; import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
import { FileChooser } from '../server/fileChooser'; import { FileChooser } from '../server/fileChooser';
import { CRCoverage } from '../server/chromium/crCoverage'; import { CRCoverage } from '../server/chromium/crCoverage';
@ -274,7 +274,7 @@ export class BindingCallDispatcher extends Dispatcher<{}, channels.BindingCallIn
frame: lookupDispatcher<FrameDispatcher>(source.frame), frame: lookupDispatcher<FrameDispatcher>(source.frame),
name, name,
args: needsHandle ? undefined : args.map(serializeResult), args: needsHandle ? undefined : args.map(serializeResult),
handle: needsHandle ? new JSHandleDispatcher(scope, args[0] as JSHandle) : undefined, handle: needsHandle ? createHandle(scope, args[0] as JSHandle) : undefined,
}); });
this._promise = new Promise((resolve, reject) => { this._promise = new Promise((resolve, reject) => {
this._resolve = resolve; this._resolve = resolve;

View File

@ -16,6 +16,7 @@
*/ */
import { it, expect } from './fixtures'; import { it, expect } from './fixtures';
import type { ElementHandle } from '..';
it('should work', async ({page}) => { it('should work', async ({page}) => {
const aHandle = await page.evaluateHandle(() => ({ const aHandle = await page.evaluateHandle(() => ({
@ -94,3 +95,12 @@ it('getProperties should return even non-own properties', async ({page}) => {
expect(await properties.get('a').jsonValue()).toBe('1'); expect(await properties.get('a').jsonValue()).toBe('1');
expect(await properties.get('b').jsonValue()).toBe('2'); expect(await properties.get('b').jsonValue()).toBe('2');
}); });
it('getProperties should work with elements', async ({page}) => {
await page.setContent(`<div>Hello</div>`);
const handle = await page.evaluateHandle(() => ({ body: document.body }));
const properties = await handle.getProperties();
const body = properties.get('body') as ElementHandle;
expect(body).toBeTruthy();
expect(await body.textContent()).toBe('Hello');
});

View File

@ -17,6 +17,7 @@
import { it, expect } from './fixtures'; import { it, expect } from './fixtures';
import { attachFrame } from './utils'; import { attachFrame } from './utils';
import type { ElementHandle } from '..';
it('exposeBinding should work', async ({page}) => { it('exposeBinding should work', async ({page}) => {
let bindingSource; let bindingSource;
@ -264,3 +265,19 @@ it('should work with internal bindings', (test, { mode, browserName }) => {
expect(foo).toBe(789); expect(foo).toBe(789);
}); });
it('exposeBinding(handle) should work with element handles', async ({ page}) => {
let cb;
const promise = new Promise(f => cb = f);
await page.exposeBinding('clicked', async (source, element: ElementHandle) => {
cb(await element.innerText().catch(e => e));
}, { handle: true });
await page.goto('about:blank');
await page.setContent(`
<script>
document.addEventListener('click', event => window.clicked(event.target));
</script>
<div id="a1">Click me</div>
`);
await page.click('#a1');
expect(await promise).toBe('Click me');
});