diff --git a/src/dispatchers/dispatcher.ts b/src/dispatchers/dispatcher.ts index 709ec5fffc..32533c3944 100644 --- a/src/dispatchers/dispatcher.ts +++ b/src/dispatchers/dispatcher.ts @@ -176,6 +176,8 @@ export class DispatcherConnection { } try { 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)); this.onmessage({ id, result: this._replaceDispatchersWithGuids(result) }); } catch (e) { diff --git a/src/dispatchers/jsHandleDispatcher.ts b/src/dispatchers/jsHandleDispatcher.ts index 225181f45b..0901af2971 100644 --- a/src/dispatchers/jsHandleDispatcher.ts +++ b/src/dispatchers/jsHandleDispatcher.ts @@ -23,6 +23,7 @@ import { parseSerializedValue, serializeValue } from '../protocol/serializers'; export class JSHandleDispatcher extends Dispatcher implements channels.JSHandleChannel { constructor(scope: DispatcherScope, jsHandle: js.JSHandle) { + // Do not call this directly, use createHandle() instead. super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', { preview: jsHandle.toString(), }); @@ -47,7 +48,7 @@ export class JSHandleDispatcher extends Dispatcher(source.frame), name, 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._resolve = resolve; diff --git a/test/jshandle-properties.spec.ts b/test/jshandle-properties.spec.ts index 53ffdbce28..fcf161d3c0 100644 --- a/test/jshandle-properties.spec.ts +++ b/test/jshandle-properties.spec.ts @@ -16,6 +16,7 @@ */ import { it, expect } from './fixtures'; +import type { ElementHandle } from '..'; it('should work', async ({page}) => { 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('b').jsonValue()).toBe('2'); }); + +it('getProperties should work with elements', async ({page}) => { + await page.setContent(`
Hello
`); + 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'); +}); diff --git a/test/page-expose-function.spec.ts b/test/page-expose-function.spec.ts index f0244b9524..9b6d964042 100644 --- a/test/page-expose-function.spec.ts +++ b/test/page-expose-function.spec.ts @@ -17,6 +17,7 @@ import { it, expect } from './fixtures'; import { attachFrame } from './utils'; +import type { ElementHandle } from '..'; it('exposeBinding should work', async ({page}) => { let bindingSource; @@ -264,3 +265,19 @@ it('should work with internal bindings', (test, { mode, browserName }) => { 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(` + +
Click me
+ `); + await page.click('#a1'); + expect(await promise).toBe('Click me'); +});