chore: explicitly type SerializedArgument, fix rpc dispatchEvent (#2988)
We now have types for SerializedValue/SerializedArgument. This will allow us to avoid double parse/serialize for evaluation arguments/results. Drive-by: typing exposed a bug in ElementHandle.dispatchEvent().
This commit is contained in:
parent
070a257600
commit
d8bedd851d
|
|
@ -14,6 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type SerializedValue =
|
||||
undefined | boolean | number | string |
|
||||
{ v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } |
|
||||
{ d: string } |
|
||||
{ r: [string, string] } |
|
||||
{ a: SerializedValue[] } |
|
||||
{ o: { [key: string]: SerializedValue } } |
|
||||
{ h: number };
|
||||
|
||||
function isRegExp(obj: any): obj is RegExp {
|
||||
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||
}
|
||||
|
|
@ -26,44 +35,48 @@ function isError(obj: any): obj is Error {
|
|||
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
||||
}
|
||||
|
||||
export function parseEvaluationResultValue(value: any, handles: any[] = []): any {
|
||||
export function parseEvaluationResultValue(value: SerializedValue, handles: any[] = []): any {
|
||||
if (value === undefined)
|
||||
return undefined;
|
||||
if (typeof value === 'object') {
|
||||
if (value.v === 'undefined')
|
||||
return undefined;
|
||||
if (value.v === null)
|
||||
return null;
|
||||
if (value.v === 'NaN')
|
||||
return NaN;
|
||||
if (value.v === 'Infinity')
|
||||
return Infinity;
|
||||
if (value.v === '-Infinity')
|
||||
return -Infinity;
|
||||
if (value.v === '-0')
|
||||
return -0;
|
||||
if (value.d)
|
||||
return new Date(value.d);
|
||||
if (value.r)
|
||||
return new RegExp(value.r[0], value.r[1]);
|
||||
if (value.a)
|
||||
return value.a.map((a: any) => parseEvaluationResultValue(a, handles));
|
||||
if (value.o) {
|
||||
for (const name of Object.keys(value.o))
|
||||
value.o[name] = parseEvaluationResultValue(value.o[name], handles);
|
||||
return value.o;
|
||||
if ('v' in value) {
|
||||
if (value.v === 'undefined')
|
||||
return undefined;
|
||||
if (value.v === 'null')
|
||||
return null;
|
||||
if (value.v === 'NaN')
|
||||
return NaN;
|
||||
if (value.v === 'Infinity')
|
||||
return Infinity;
|
||||
if (value.v === '-Infinity')
|
||||
return -Infinity;
|
||||
if (value.v === '-0')
|
||||
return -0;
|
||||
}
|
||||
if (typeof value.h === 'number')
|
||||
if ('d' in value)
|
||||
return new Date(value.d);
|
||||
if ('r' in value)
|
||||
return new RegExp(value.r[0], value.r[1]);
|
||||
if ('a' in value)
|
||||
return value.a.map((a: any) => parseEvaluationResultValue(a, handles));
|
||||
if ('o' in value) {
|
||||
const result: any = {};
|
||||
for (const name of Object.keys(value.o))
|
||||
result[name] = parseEvaluationResultValue(value.o[name], handles);
|
||||
return result;
|
||||
}
|
||||
if ('h' in value)
|
||||
return handles[value.h];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function serializeAsCallArgument(value: any, jsHandleSerializer: (value: any) => { fallThrough?: any }): any {
|
||||
export type HandleOrValue = { h: number } | { fallThrough: any };
|
||||
export function serializeAsCallArgument(value: any, jsHandleSerializer: (value: any) => HandleOrValue): SerializedValue {
|
||||
return serialize(value, jsHandleSerializer, new Set());
|
||||
}
|
||||
|
||||
function serialize(value: any, jsHandleSerializer: (value: any) => { fallThrough?: any }, visited: Set<any>): any {
|
||||
function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue, visited: Set<any>): SerializedValue {
|
||||
const result = jsHandleSerializer(value);
|
||||
if ('fallThrough' in result)
|
||||
value = result.fallThrough;
|
||||
|
|
@ -77,7 +90,7 @@ function serialize(value: any, jsHandleSerializer: (value: any) => { fallThrough
|
|||
if (Object.is(value, undefined))
|
||||
return { v: 'undefined' };
|
||||
if (Object.is(value, null))
|
||||
return { v: null };
|
||||
return { v: 'null' };
|
||||
if (Object.is(value, NaN))
|
||||
return { v: 'NaN' };
|
||||
if (Object.is(value, Infinity))
|
||||
|
|
@ -86,7 +99,12 @@ function serialize(value: any, jsHandleSerializer: (value: any) => { fallThrough
|
|||
return { v: '-Infinity' };
|
||||
if (Object.is(value, -0))
|
||||
return { v: '-0' };
|
||||
if (isPrimitiveValue(value))
|
||||
|
||||
if (typeof value === 'boolean')
|
||||
return value;
|
||||
if (typeof value === 'number')
|
||||
return value;
|
||||
if (typeof value === 'string')
|
||||
return value;
|
||||
|
||||
if (isError(value)) {
|
||||
|
|
@ -130,14 +148,3 @@ function serialize(value: any, jsHandleSerializer: (value: any) => { fallThrough
|
|||
return { o: result };
|
||||
}
|
||||
}
|
||||
|
||||
export function isPrimitiveValue(value: any): boolean {
|
||||
switch (typeof value) {
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
case 'string':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ export async function evaluateExpression(context: ExecutionContext, returnByValu
|
|||
return handles.length - 1;
|
||||
};
|
||||
|
||||
args = args.map(arg => serializeAsCallArgument(arg, (handle: any): { h?: number, fallThrough?: any } => {
|
||||
args = args.map(arg => serializeAsCallArgument(arg, handle => {
|
||||
if (handle instanceof JSHandle) {
|
||||
if (!handle._objectId)
|
||||
return { fallThrough: handle._value };
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@
|
|||
|
||||
import { EventEmitter } from 'events';
|
||||
import * as types from '../types';
|
||||
import { SerializedValue } from '../common/utilityScriptSerializers';
|
||||
|
||||
export type Binary = string;
|
||||
export type SerializedArgument = { value: SerializedValue, handles: Channel[] };
|
||||
|
||||
export type BrowserContextOptions = {
|
||||
viewport?: types.Size | null,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
|
|
@ -215,17 +218,17 @@ export interface FrameChannel extends Channel {
|
|||
on(event: 'loadstate', callback: (params: { add?: types.LifecycleEvent, remove?: types.LifecycleEvent }) => void): this;
|
||||
on(event: 'navigated', callback: (params: FrameNavigatedEvent) => void): this;
|
||||
|
||||
evalOnSelector(params: { selector: string; expression: string, isFunction: boolean, arg: any}): Promise<{ value: any }>;
|
||||
evalOnSelectorAll(params: { selector: string; expression: string, isFunction: boolean, arg: any}): Promise<{ value: any }>;
|
||||
evalOnSelector(params: { selector: string; expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
evalOnSelectorAll(params: { selector: string; expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
addScriptTag(params: { url?: string, content?: string, type?: string }): Promise<{ element: ElementHandleChannel }>;
|
||||
addStyleTag(params: { url?: string, content?: string }): Promise<{ element: ElementHandleChannel }>;
|
||||
check(params: { selector: string, force?: boolean, noWaitAfter?: boolean } & types.TimeoutOptions): Promise<void>;
|
||||
click(params: { selector: string, force?: boolean, noWaitAfter?: boolean } & types.PointerActionOptions & types.MouseClickOptions & types.TimeoutOptions): Promise<void>;
|
||||
content(): Promise<{ value: string }>;
|
||||
dblclick(params: { selector: string, force?: boolean } & types.PointerActionOptions & types.MouseMultiClickOptions & types.TimeoutOptions): Promise<void>;
|
||||
dispatchEvent(params: { selector: string, type: string, eventInit: any } & types.TimeoutOptions): Promise<void>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any}): Promise<{ value: any }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<{ handle: JSHandleChannel }>;
|
||||
dispatchEvent(params: { selector: string, type: string, eventInit: SerializedArgument } & types.TimeoutOptions): Promise<void>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }>;
|
||||
fill(params: { selector: string, value: string } & types.NavigatingActionWaitOptions): Promise<void>;
|
||||
focus(params: { selector: string } & types.TimeoutOptions): Promise<void>;
|
||||
frameElement(): Promise<{ element: ElementHandleChannel }>;
|
||||
|
|
@ -244,7 +247,7 @@ export interface FrameChannel extends Channel {
|
|||
title(): Promise<{ value: string }>;
|
||||
type(params: { selector: string, text: string, delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions): Promise<void>;
|
||||
uncheck(params: { selector: string, force?: boolean, noWaitAfter?: boolean } & types.TimeoutOptions): Promise<void>;
|
||||
waitForFunction(params: { expression: string, isFunction: boolean, arg: any } & types.WaitForFunctionOptions): Promise<{ handle: JSHandleChannel }>;
|
||||
waitForFunction(params: { expression: string, isFunction: boolean, arg: SerializedArgument } & types.WaitForFunctionOptions): Promise<{ handle: JSHandleChannel }>;
|
||||
waitForSelector(params: { selector: string } & types.WaitForElementOptions): Promise<{ element: ElementHandleChannel | null }>;
|
||||
}
|
||||
export type FrameInitializer = {
|
||||
|
|
@ -256,8 +259,8 @@ export type FrameInitializer = {
|
|||
|
||||
|
||||
export interface WorkerChannel extends Channel {
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ handle: JSHandleChannel }>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }>;
|
||||
}
|
||||
export type WorkerInitializer = {
|
||||
url: string,
|
||||
|
|
@ -268,11 +271,11 @@ export interface JSHandleChannel extends Channel {
|
|||
on(event: 'previewUpdated', callback: (params: { preview: string }) => void): this;
|
||||
|
||||
dispose(): Promise<void>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<{ handle: JSHandleChannel }>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }>;
|
||||
getPropertyList(): Promise<{ properties: { name: string, value: JSHandleChannel}[] }>;
|
||||
getProperty(params: { name: string }): Promise<{ handle: JSHandleChannel }>;
|
||||
jsonValue(): Promise<{ value: any }>;
|
||||
jsonValue(): Promise<{ value: SerializedValue }>;
|
||||
}
|
||||
export type JSHandleInitializer = {
|
||||
preview: string,
|
||||
|
|
@ -280,14 +283,14 @@ export type JSHandleInitializer = {
|
|||
|
||||
|
||||
export interface ElementHandleChannel extends JSHandleChannel {
|
||||
evalOnSelector(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }>;
|
||||
evalOnSelectorAll(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }>;
|
||||
evalOnSelector(params: { selector: string; expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
evalOnSelectorAll(params: { selector: string; expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
boundingBox(): Promise<{ value: types.Rect | null }>;
|
||||
check(params: { force?: boolean } & { noWaitAfter?: boolean } & types.TimeoutOptions): Promise<void>;
|
||||
click(params: { force?: boolean, noWaitAfter?: boolean } & types.PointerActionOptions & types.MouseClickOptions & types.TimeoutOptions): Promise<void>;
|
||||
contentFrame(): Promise<{ frame: FrameChannel | null }>;
|
||||
dblclick(params: { force?: boolean, noWaitAfter?: boolean } & types.PointerActionOptions & types.MouseMultiClickOptions & types.TimeoutOptions): Promise<void>;
|
||||
dispatchEvent(params: { type: string, eventInit: any }): Promise<void>;
|
||||
dispatchEvent(params: { type: string, eventInit: SerializedArgument }): Promise<void>;
|
||||
fill(params: { value: string } & types.NavigatingActionWaitOptions): Promise<void>;
|
||||
focus(): Promise<void>;
|
||||
getAttribute(params: { name: string }): Promise<{ value: string | null }>;
|
||||
|
|
@ -359,11 +362,12 @@ export type ConsoleMessageInitializer = {
|
|||
|
||||
export interface BindingCallChannel extends Channel {
|
||||
reject(params: { error: types.Error }): void;
|
||||
resolve(params: { result: any }): void;
|
||||
resolve(params: { result: SerializedArgument }): void;
|
||||
}
|
||||
export type BindingCallInitializer = {
|
||||
frame: FrameChannel,
|
||||
name: string,
|
||||
// TODO: migrate this to SerializedArgument.
|
||||
args: any[]
|
||||
};
|
||||
|
||||
|
|
@ -443,9 +447,9 @@ export interface ElectronApplicationChannel extends Channel {
|
|||
on(event: 'close', callback: () => void): this;
|
||||
on(event: 'window', callback: (params: { page: PageChannel, browserWindow: JSHandleChannel }) => void): this;
|
||||
|
||||
newBrowserWindow(params: { arg: any }): Promise<{ page: PageChannel }>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ handle: JSHandleChannel }>;
|
||||
newBrowserWindow(params: { arg: SerializedArgument }): Promise<{ page: PageChannel }>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }>;
|
||||
close(): Promise<void>;
|
||||
}
|
||||
export type ElectronApplicationInitializer = {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
|
|||
|
||||
async dispatchEvent(type: string, eventInit: Object = {}) {
|
||||
return this._wrapApiCall('elementHandle.dispatchEvent', async () => {
|
||||
await this._elementChannel.dispatchEvent({ type, eventInit });
|
||||
await this._elementChannel.dispatchEvent({ type, eventInit: serializeArgument(eventInit) });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JSHandleChannel, JSHandleInitializer } from '../channels';
|
||||
import { JSHandleChannel, JSHandleInitializer, SerializedArgument, Channel } from '../channels';
|
||||
import { ElementHandle } from './elementHandle';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { serializeAsCallArgument, parseEvaluationResultValue } from '../../common/utilityScriptSerializers';
|
||||
import { serializeAsCallArgument, parseEvaluationResultValue, SerializedValue } from '../../common/utilityScriptSerializers';
|
||||
|
||||
type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
|
||||
type Unboxed<Arg> =
|
||||
|
|
@ -95,20 +95,22 @@ export class JSHandle<T = any> extends ChannelOwner<JSHandleChannel, JSHandleIni
|
|||
}
|
||||
}
|
||||
|
||||
export function serializeArgument(arg: any): any {
|
||||
const guids: { guid: string }[] = [];
|
||||
const pushHandle = (guid: string): number => {
|
||||
guids.push({ guid });
|
||||
return guids.length - 1;
|
||||
// This function takes care of converting all JSHandles to their channels,
|
||||
// so that generic channel serializer converts them to guids.
|
||||
export function serializeArgument(arg: any): SerializedArgument {
|
||||
const handles: Channel[] = [];
|
||||
const pushHandle = (channel: Channel): number => {
|
||||
handles.push(channel);
|
||||
return handles.length - 1;
|
||||
};
|
||||
const value = serializeAsCallArgument(arg, value => {
|
||||
if (value instanceof ChannelOwner)
|
||||
return { h: pushHandle(value._guid) };
|
||||
if (value instanceof JSHandle)
|
||||
return { h: pushHandle(value._channel) };
|
||||
return { fallThrough: value };
|
||||
});
|
||||
return { value, guids };
|
||||
return { value, handles };
|
||||
}
|
||||
|
||||
export function parseResult(arg: any): any {
|
||||
export function parseResult(arg: SerializedValue): any {
|
||||
return parseEvaluationResultValue(arg, []);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@
|
|||
|
||||
import { Dispatcher, DispatcherScope, lookupDispatcher } from './dispatcher';
|
||||
import { Electron, ElectronApplication, ElectronEvents, ElectronPage } from '../../server/electron';
|
||||
import { ElectronApplicationChannel, ElectronApplicationInitializer, PageChannel, JSHandleChannel, ElectronInitializer, ElectronChannel, ElectronLaunchOptions } from '../channels';
|
||||
import { ElectronApplicationChannel, ElectronApplicationInitializer, PageChannel, JSHandleChannel, ElectronInitializer, ElectronChannel, ElectronLaunchOptions, SerializedArgument } from '../channels';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { BrowserContextBase } from '../../browserContext';
|
||||
import { PageDispatcher } from './pageDispatcher';
|
||||
import { parseArgument } from './jsHandleDispatcher';
|
||||
import { createHandle } from './elementHandlerDispatcher';
|
||||
import { SerializedValue } from '../../common/utilityScriptSerializers';
|
||||
|
||||
export class ElectronDispatcher extends Dispatcher<Electron, ElectronInitializer> implements ElectronChannel {
|
||||
constructor(scope: DispatcherScope, electron: Electron) {
|
||||
|
|
@ -49,17 +50,17 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
|||
});
|
||||
}
|
||||
|
||||
async newBrowserWindow(params: { arg: any }): Promise<{ page: PageChannel }> {
|
||||
async newBrowserWindow(params: { arg: SerializedArgument }): Promise<{ page: PageChannel }> {
|
||||
const page = await this._object.newBrowserWindow(parseArgument(params.arg));
|
||||
return { page: lookupDispatcher<PageChannel>(page) };
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
const handle = this._object._nodeElectronHandle!;
|
||||
return { value: await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg)) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<{ handle: JSHandleChannel }> {
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }> {
|
||||
const handle = this._object._nodeElectronHandle!;
|
||||
const result = await handle._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
return { handle: createHandle(this._scope, result) };
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@
|
|||
import { ElementHandle } from '../../dom';
|
||||
import * as js from '../../javascript';
|
||||
import * as types from '../../types';
|
||||
import { ElementHandleChannel, FrameChannel, Binary } from '../channels';
|
||||
import { ElementHandleChannel, FrameChannel, Binary, SerializedArgument } from '../channels';
|
||||
import { DispatcherScope, lookupNullableDispatcher } from './dispatcher';
|
||||
import { JSHandleDispatcher, serializeResult, parseArgument } from './jsHandleDispatcher';
|
||||
import { FrameDispatcher } from './frameDispatcher';
|
||||
import { SerializedValue } from '../../common/utilityScriptSerializers';
|
||||
|
||||
export function createHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
|
||||
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
|
||||
|
|
@ -64,8 +65,8 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements Eleme
|
|||
return { value: await this._elementHandle.innerHTML() };
|
||||
}
|
||||
|
||||
async dispatchEvent(params: { type: string, eventInit: Object }) {
|
||||
await this._elementHandle.dispatchEvent(params.type, params.eventInit);
|
||||
async dispatchEvent(params: { type: string, eventInit: SerializedArgument }) {
|
||||
await this._elementHandle.dispatchEvent(params.type, parseArgument(params.eventInit));
|
||||
}
|
||||
|
||||
async scrollIntoViewIfNeeded(params: types.TimeoutOptions) {
|
||||
|
|
@ -138,11 +139,11 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements Eleme
|
|||
return { elements: elements.map(e => new ElementHandleDispatcher(this._scope, e)) };
|
||||
}
|
||||
|
||||
async evalOnSelector(params: { selector: string, expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evalOnSelector(params: { selector: string, expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._elementHandle._$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evalOnSelectorAll(params: { selector: string, expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evalOnSelectorAll(params: { selector: string, expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._elementHandle._$$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@
|
|||
|
||||
import { Frame, kAddLifecycleEvent, kRemoveLifecycleEvent, kNavigationEvent, NavigationEvent } from '../../frames';
|
||||
import * as types from '../../types';
|
||||
import { ElementHandleChannel, FrameChannel, FrameInitializer, JSHandleChannel, ResponseChannel } from '../channels';
|
||||
import { ElementHandleChannel, FrameChannel, FrameInitializer, JSHandleChannel, ResponseChannel, SerializedArgument } from '../channels';
|
||||
import { Dispatcher, DispatcherScope, lookupNullableDispatcher, existingDispatcher } from './dispatcher';
|
||||
import { convertSelectOptionValues, ElementHandleDispatcher, createHandle, convertInputFiles } from './elementHandlerDispatcher';
|
||||
import { parseArgument, serializeResult } from './jsHandleDispatcher';
|
||||
import { ResponseDispatcher, RequestDispatcher } from './networkDispatchers';
|
||||
import { SerializedValue } from '../../common/utilityScriptSerializers';
|
||||
|
||||
export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> implements FrameChannel {
|
||||
private _frame: Frame;
|
||||
|
|
@ -64,11 +65,11 @@ export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> impleme
|
|||
return { element: new ElementHandleDispatcher(this._scope, await this._frame.frameElement()) };
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._frame._evaluateExpression(params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ handle: JSHandleChannel }> {
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }> {
|
||||
return { handle: createHandle(this._scope, await this._frame._evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
|
|
@ -76,15 +77,15 @@ export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> impleme
|
|||
return { element: ElementHandleDispatcher.createNullable(this._scope, await this._frame.waitForSelector(params.selector, params)) };
|
||||
}
|
||||
|
||||
async dispatchEvent(params: { selector: string, type: string, eventInit: any } & types.TimeoutOptions): Promise<void> {
|
||||
async dispatchEvent(params: { selector: string, type: string, eventInit: SerializedArgument } & types.TimeoutOptions): Promise<void> {
|
||||
return this._frame.dispatchEvent(params.selector, params.type, parseArgument(params.eventInit), params);
|
||||
}
|
||||
|
||||
async evalOnSelector(params: { selector: string, expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evalOnSelector(params: { selector: string, expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._frame._$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evalOnSelectorAll(params: { selector: string, expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evalOnSelectorAll(params: { selector: string, expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._frame._$$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +174,7 @@ export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> impleme
|
|||
await this._frame.uncheck(params.selector, params);
|
||||
}
|
||||
|
||||
async waitForFunction(params: { expression: string, isFunction: boolean, arg: any } & types.WaitForFunctionOptions): Promise<{ handle: JSHandleChannel }> {
|
||||
async waitForFunction(params: { expression: string, isFunction: boolean, arg: SerializedArgument } & types.WaitForFunctionOptions): Promise<{ handle: JSHandleChannel }> {
|
||||
return { handle: createHandle(this._scope, await this._frame._waitForFunctionExpression(params.expression, params.isFunction, parseArgument(params.arg), params)) };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
|
||||
import * as js from '../../javascript';
|
||||
import { JSHandleChannel, JSHandleInitializer } from '../channels';
|
||||
import { JSHandleChannel, JSHandleInitializer, SerializedArgument } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { parseEvaluationResultValue, serializeAsCallArgument } from '../../common/utilityScriptSerializers';
|
||||
import { parseEvaluationResultValue, serializeAsCallArgument, SerializedValue } from '../../common/utilityScriptSerializers';
|
||||
import { createHandle } from './elementHandlerDispatcher';
|
||||
|
||||
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitializer> implements JSHandleChannel {
|
||||
|
|
@ -29,11 +29,11 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitiali
|
|||
jsHandle._setPreviewCallback(preview => this._dispatchEvent('previewUpdated', { preview }));
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<{ value: any }> {
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._object._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<{ handle: JSHandleChannel }> {
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument}): Promise<{ handle: JSHandleChannel }> {
|
||||
const jsHandle = await this._object._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
return { handle: createHandle(this._scope, jsHandle) };
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitiali
|
|||
return { properties };
|
||||
}
|
||||
|
||||
async jsonValue(): Promise<{ value: any }> {
|
||||
async jsonValue(): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._object.jsonValue()) };
|
||||
}
|
||||
|
||||
|
|
@ -60,26 +60,12 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitiali
|
|||
}
|
||||
}
|
||||
|
||||
export function parseArgument(arg: { value: any, guids: JSHandleDispatcher[] }): any {
|
||||
return parseEvaluationResultValue(arg.value, convertDispatchersToObjects(arg.guids));
|
||||
// Generic channel parser converts guids to JSHandleDispatchers,
|
||||
// and this function takes care of coverting them into underlying JSHandles.
|
||||
export function parseArgument(arg: SerializedArgument): any {
|
||||
return parseEvaluationResultValue(arg.value, arg.handles.map(arg => (arg as JSHandleDispatcher)._object));
|
||||
}
|
||||
|
||||
export function serializeResult(arg: any): any {
|
||||
export function serializeResult(arg: any): SerializedValue {
|
||||
return serializeAsCallArgument(arg, value => ({ fallThrough: value }));
|
||||
}
|
||||
|
||||
function convertDispatchersToObjects(arg: any): any {
|
||||
if (arg === null)
|
||||
return null;
|
||||
if (Array.isArray(arg))
|
||||
return arg.map(item => convertDispatchersToObjects(item));
|
||||
if (arg instanceof JSHandleDispatcher)
|
||||
return arg._object;
|
||||
if (typeof arg === 'object') {
|
||||
const result: any = {};
|
||||
for (const key of Object.keys(arg))
|
||||
result[key] = convertDispatchersToObjects(arg[key]);
|
||||
return result;
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { Frame } from '../../frames';
|
|||
import { Request } from '../../network';
|
||||
import { Page, Worker } from '../../page';
|
||||
import * as types from '../../types';
|
||||
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel, WorkerInitializer, WorkerChannel, JSHandleChannel, Binary, PDFOptions } from '../channels';
|
||||
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel, WorkerInitializer, WorkerChannel, JSHandleChannel, Binary, PDFOptions, SerializedArgument } from '../channels';
|
||||
import { Dispatcher, DispatcherScope, lookupDispatcher, lookupNullableDispatcher } from './dispatcher';
|
||||
import { parseError, serializeError, headersArrayToObject } from '../serializers';
|
||||
import { ConsoleMessageDispatcher } from './consoleMessageDispatcher';
|
||||
|
|
@ -32,6 +32,7 @@ import { serializeResult, parseArgument } from './jsHandleDispatcher';
|
|||
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
|
||||
import { FileChooser } from '../../fileChooser';
|
||||
import { CRCoverage } from '../../chromium/crCoverage';
|
||||
import { SerializedValue } from '../../common/utilityScriptSerializers';
|
||||
|
||||
export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements PageChannel {
|
||||
private _page: Page;
|
||||
|
|
@ -232,11 +233,11 @@ export class WorkerDispatcher extends Dispatcher<Worker, WorkerInitializer> impl
|
|||
worker.on(Events.Worker.Close, () => this._dispatchEvent('close'));
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<{ value: any }> {
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
|
||||
return { value: serializeResult(await this._object._evaluateExpression(params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<{ handle: JSHandleChannel }> {
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }> {
|
||||
return { handle: createHandle(this._scope, await this._object._evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
}
|
||||
|
|
@ -262,7 +263,7 @@ export class BindingCallDispatcher extends Dispatcher<{}, BindingCallInitializer
|
|||
return this._promise;
|
||||
}
|
||||
|
||||
resolve(params: { result: any }) {
|
||||
resolve(params: { result: SerializedArgument }) {
|
||||
this._resolve!(parseArgument(params.result));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,20 @@ describe('Page.dispatchEvent(drag)', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.dispatchEvent(drag)', function() {
|
||||
it.fail(WEBKIT)('should dispatch drag drop events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
const source = await page.$('#source');
|
||||
await source.dispatchEvent('dragstart', { dataTransfer });
|
||||
const target = await page.$('#target');
|
||||
await target.dispatchEvent('drop', { dataTransfer });
|
||||
expect(await page.evaluate(() => {
|
||||
return source.parentElement === target;
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.dispatchEvent(click)', function() {
|
||||
it('should dispatch click event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
|
|
|
|||
Loading…
Reference in a new issue