chore: drop wrapApiCall (2) (#10445)

This commit is contained in:
Pavel Feldman 2021-11-19 16:28:11 -08:00 committed by GitHub
parent 4eaeb3b59c
commit a73e6bbd0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 384 additions and 666 deletions

View file

@ -48,10 +48,8 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
} }
async devices(): Promise<AndroidDevice[]> { async devices(): Promise<AndroidDevice[]> {
return this._wrapApiCall(async (channel: channels.AndroidChannel) => { const { devices } = await this._channel.devices();
const { devices } = await channel.devices(); return devices.map(d => AndroidDevice.from(d));
return devices.map(d => AndroidDevice.from(d));
});
} }
} }
@ -114,15 +112,11 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
} }
async wait(selector: api.AndroidSelector, options?: { state?: 'gone' } & types.TimeoutOptions) { async wait(selector: api.AndroidSelector, options?: { state?: 'gone' } & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.wait({ selector: toSelectorChannel(selector), ...options });
await channel.wait({ selector: toSelectorChannel(selector), ...options });
});
} }
async fill(selector: api.AndroidSelector, text: string, options?: types.TimeoutOptions) { async fill(selector: api.AndroidSelector, text: string, options?: types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.fill({ selector: toSelectorChannel(selector), text, ...options });
await channel.fill({ selector: toSelectorChannel(selector), text, ...options });
});
} }
async press(selector: api.AndroidSelector, key: api.AndroidKey, options?: types.TimeoutOptions) { async press(selector: api.AndroidSelector, key: api.AndroidKey, options?: types.TimeoutOptions) {
@ -131,114 +125,82 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
} }
async tap(selector: api.AndroidSelector, options?: { duration?: number } & types.TimeoutOptions) { async tap(selector: api.AndroidSelector, options?: { duration?: number } & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.tap({ selector: toSelectorChannel(selector), ...options });
await channel.tap({ selector: toSelectorChannel(selector), ...options });
});
} }
async drag(selector: api.AndroidSelector, dest: types.Point, options?: SpeedOptions & types.TimeoutOptions) { async drag(selector: api.AndroidSelector, dest: types.Point, options?: SpeedOptions & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.drag({ selector: toSelectorChannel(selector), dest, ...options });
await channel.drag({ selector: toSelectorChannel(selector), dest, ...options });
});
} }
async fling(selector: api.AndroidSelector, direction: Direction, options?: SpeedOptions & types.TimeoutOptions) { async fling(selector: api.AndroidSelector, direction: Direction, options?: SpeedOptions & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.fling({ selector: toSelectorChannel(selector), direction, ...options });
await channel.fling({ selector: toSelectorChannel(selector), direction, ...options });
});
} }
async longTap(selector: api.AndroidSelector, options?: types.TimeoutOptions) { async longTap(selector: api.AndroidSelector, options?: types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.longTap({ selector: toSelectorChannel(selector), ...options });
await channel.longTap({ selector: toSelectorChannel(selector), ...options });
});
} }
async pinchClose(selector: api.AndroidSelector, percent: number, options?: SpeedOptions & types.TimeoutOptions) { async pinchClose(selector: api.AndroidSelector, percent: number, options?: SpeedOptions & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.pinchClose({ selector: toSelectorChannel(selector), percent, ...options });
await channel.pinchClose({ selector: toSelectorChannel(selector), percent, ...options });
});
} }
async pinchOpen(selector: api.AndroidSelector, percent: number, options?: SpeedOptions & types.TimeoutOptions) { async pinchOpen(selector: api.AndroidSelector, percent: number, options?: SpeedOptions & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.pinchOpen({ selector: toSelectorChannel(selector), percent, ...options });
await channel.pinchOpen({ selector: toSelectorChannel(selector), percent, ...options });
});
} }
async scroll(selector: api.AndroidSelector, direction: Direction, percent: number, options?: SpeedOptions & types.TimeoutOptions) { async scroll(selector: api.AndroidSelector, direction: Direction, percent: number, options?: SpeedOptions & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.scroll({ selector: toSelectorChannel(selector), direction, percent, ...options });
await channel.scroll({ selector: toSelectorChannel(selector), direction, percent, ...options });
});
} }
async swipe(selector: api.AndroidSelector, direction: Direction, percent: number, options?: SpeedOptions & types.TimeoutOptions) { async swipe(selector: api.AndroidSelector, direction: Direction, percent: number, options?: SpeedOptions & types.TimeoutOptions) {
await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.swipe({ selector: toSelectorChannel(selector), direction, percent, ...options });
await channel.swipe({ selector: toSelectorChannel(selector), direction, percent, ...options });
});
} }
async info(selector: api.AndroidSelector): Promise<api.AndroidElementInfo> { async info(selector: api.AndroidSelector): Promise<api.AndroidElementInfo> {
return await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { return (await this._channel.info({ selector: toSelectorChannel(selector) })).info;
return (await channel.info({ selector: toSelectorChannel(selector) })).info;
});
} }
async screenshot(options: { path?: string } = {}): Promise<Buffer> { async screenshot(options: { path?: string } = {}): Promise<Buffer> {
return await this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { const { binary } = await this._channel.screenshot();
const { binary } = await channel.screenshot(); const buffer = Buffer.from(binary, 'base64');
const buffer = Buffer.from(binary, 'base64'); if (options.path)
if (options.path) await fs.promises.writeFile(options.path, buffer);
await fs.promises.writeFile(options.path, buffer); return buffer;
return buffer;
});
} }
async close() { async close() {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.close();
await channel.close(); this.emit(Events.AndroidDevice.Close);
this.emit(Events.AndroidDevice.Close);
});
} }
async shell(command: string): Promise<Buffer> { async shell(command: string): Promise<Buffer> {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { const { result } = await this._channel.shell({ command });
const { result } = await channel.shell({ command }); return Buffer.from(result, 'base64');
return Buffer.from(result, 'base64');
});
} }
async open(command: string): Promise<AndroidSocket> { async open(command: string): Promise<AndroidSocket> {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { return AndroidSocket.from((await this._channel.open({ command })).socket);
return AndroidSocket.from((await channel.open({ command })).socket);
});
} }
async installApk(file: string | Buffer, options?: { args: string[] }): Promise<void> { async installApk(file: string | Buffer, options?: { args: string[] }): Promise<void> {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.installApk({ file: await loadFile(file), args: options && options.args });
await channel.installApk({ file: await loadFile(file), args: options && options.args });
});
} }
async push(file: string | Buffer, path: string, options?: { mode: number }): Promise<void> { async push(file: string | Buffer, path: string, options?: { mode: number }): Promise<void> {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._channel.push({ file: await loadFile(file), path, mode: options ? options.mode : undefined });
await channel.push({ file: await loadFile(file), path, mode: options ? options.mode : undefined });
});
} }
async launchBrowser(options: types.BrowserContextOptions & { pkg?: string } = {}): Promise<BrowserContext> { async launchBrowser(options: types.BrowserContextOptions & { pkg?: string } = {}): Promise<BrowserContext> {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { const contextOptions = await prepareBrowserContextParams(options);
const contextOptions = await prepareBrowserContextParams(options); const { context } = await this._channel.launchBrowser(contextOptions);
const { context } = await channel.launchBrowser(contextOptions); return BrowserContext.from(context) as BrowserContext;
return BrowserContext.from(context) as BrowserContext;
});
} }
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
return this._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { return this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(channel, event); const waiter = Waiter.createForEvent(this._channel, event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.AndroidDevice.Close) if (event !== Events.AndroidDevice.Close)
waiter.rejectOnEvent(this, Events.AndroidDevice.Close, new Error('Device closed')); waiter.rejectOnEvent(this, Events.AndroidDevice.Close, new Error('Device closed'));
@ -261,15 +223,11 @@ export class AndroidSocket extends ChannelOwner<channels.AndroidSocketChannel> i
} }
async write(data: Buffer): Promise<void> { async write(data: Buffer): Promise<void> {
return this._wrapApiCall(async (channel: channels.AndroidSocketChannel) => { await this._channel.write({ data: data.toString('base64') });
await channel.write({ data: data.toString('base64') });
});
} }
async close(): Promise<void> { async close(): Promise<void> {
return this._wrapApiCall(async (channel: channels.AndroidSocketChannel) => { await this._channel.close();
await channel.close();
});
} }
} }
@ -287,33 +245,23 @@ export class AndroidInput implements api.AndroidInput {
} }
async type(text: string) { async type(text: string) {
return this._device._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._device._channel.inputType({ text });
await channel.inputType({ text });
});
} }
async press(key: api.AndroidKey) { async press(key: api.AndroidKey) {
return this._device._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._device._channel.inputPress({ key });
await channel.inputPress({ key });
});
} }
async tap(point: types.Point) { async tap(point: types.Point) {
return this._device._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._device._channel.inputTap({ point });
await channel.inputTap({ point });
});
} }
async swipe(from: types.Point, segments: types.Point[], steps: number) { async swipe(from: types.Point, segments: types.Point[], steps: number) {
return this._device._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._device._channel.inputSwipe({ segments, steps });
await channel.inputSwipe({ segments, steps });
});
} }
async drag(from: types.Point, to: types.Point, steps: number) { async drag(from: types.Point, to: types.Point, steps: number) {
return this._device._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { await this._device._channel.inputDrag({ from, to, steps });
await channel.inputDrag({ from, to, steps });
});
} }
} }
@ -393,9 +341,7 @@ export class AndroidWebView extends EventEmitter implements api.AndroidWebView {
} }
private async _fetchPage(): Promise<Page> { private async _fetchPage(): Promise<Page> {
return this._device._wrapApiCall(async (channel: channels.AndroidDeviceChannel) => { const { context } = await this._device._channel.connectToWebView({ pid: this._data.pid });
const { context } = await channel.connectToWebView({ pid: this._data.pid }); return BrowserContext.from(context).pages()[0];
return BrowserContext.from(context).pages()[0];
});
} }
} }

View file

@ -29,54 +29,42 @@ export class Artifact extends ChannelOwner<channels.ArtifactChannel> {
async pathAfterFinished(): Promise<string | null> { async pathAfterFinished(): Promise<string | null> {
if (this._connection.isRemote()) if (this._connection.isRemote())
throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`); throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
return this._wrapApiCall(async (channel: channels.ArtifactChannel) => { return (await this._channel.pathAfterFinished()).value || null;
return (await channel.pathAfterFinished()).value || null;
});
} }
async saveAs(path: string): Promise<void> { async saveAs(path: string): Promise<void> {
return this._wrapApiCall(async (channel: channels.ArtifactChannel) => { if (!this._connection.isRemote()) {
if (!this._connection.isRemote()) { await this._channel.saveAs({ path });
await channel.saveAs({ path }); return;
return; }
}
const result = await channel.saveAsStream(); const result = await this._channel.saveAsStream();
const stream = Stream.from(result.stream); const stream = Stream.from(result.stream);
await mkdirIfNeeded(path); await mkdirIfNeeded(path);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
stream.stream().pipe(fs.createWriteStream(path)) stream.stream().pipe(fs.createWriteStream(path))
.on('finish' as any, resolve) .on('finish' as any, resolve)
.on('error' as any, reject); .on('error' as any, reject);
});
}); });
} }
async failure(): Promise<string | null> { async failure(): Promise<string | null> {
return this._wrapApiCall(async (channel: channels.ArtifactChannel) => { return (await this._channel.failure()).error || null;
return (await channel.failure()).error || null;
});
} }
async createReadStream(): Promise<Readable | null> { async createReadStream(): Promise<Readable | null> {
return this._wrapApiCall(async (channel: channels.ArtifactChannel) => { const result = await this._channel.stream();
const result = await channel.stream(); if (!result.stream)
if (!result.stream) return null;
return null; const stream = Stream.from(result.stream);
const stream = Stream.from(result.stream); return stream.stream();
return stream.stream();
});
} }
async cancel(): Promise<void> { async cancel(): Promise<void> {
return this._wrapApiCall(async (channel: channels.ArtifactChannel) => { return this._channel.cancel();
return channel.cancel();
});
} }
async delete(): Promise<void> { async delete(): Promise<void> {
return this._wrapApiCall(async (channel: channels.ArtifactChannel) => { return this._channel.delete();
return channel.delete();
});
} }
} }

View file

@ -55,17 +55,15 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
} }
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> { async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
return this._wrapApiCall(async (channel: channels.BrowserChannel) => { options = { ...this._browserType._defaultContextOptions, ...options };
options = { ...this._browserType._defaultContextOptions, ...options }; const contextOptions = await prepareBrowserContextParams(options);
const contextOptions = await prepareBrowserContextParams(options); const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
const context = BrowserContext.from((await channel.newContext(contextOptions)).context); context._options = contextOptions;
context._options = contextOptions; this._contexts.add(context);
this._contexts.add(context); context._logger = options.logger || this._logger;
context._logger = options.logger || this._logger; context._setBrowserType(this._browserType);
context._setBrowserType(this._browserType); await this._browserType._onDidCreateContext?.(context);
await this._browserType._onDidCreateContext?.(context); return context;
return context;
});
} }
contexts(): BrowserContext[] { contexts(): BrowserContext[] {
@ -89,32 +87,24 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
} }
async newBrowserCDPSession(): Promise<api.CDPSession> { async newBrowserCDPSession(): Promise<api.CDPSession> {
return this._wrapApiCall(async (channel: channels.BrowserChannel) => { return CDPSession.from((await this._channel.newBrowserCDPSession()).session);
return CDPSession.from((await channel.newBrowserCDPSession()).session);
});
} }
async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) {
return this._wrapApiCall(async (channel: channels.BrowserChannel) => { await this._channel.startTracing({ ...options, page: page ? page._channel : undefined });
await channel.startTracing({ ...options, page: page ? page._channel : undefined });
});
} }
async stopTracing(): Promise<Buffer> { async stopTracing(): Promise<Buffer> {
return this._wrapApiCall(async (channel: channels.BrowserChannel) => { return Buffer.from((await this._channel.stopTracing()).binary, 'base64');
return Buffer.from((await channel.stopTracing()).binary, 'base64');
});
} }
async close(): Promise<void> { async close(): Promise<void> {
try { try {
await this._wrapApiCall(async (channel: channels.BrowserChannel) => { if (this._shouldCloseConnectionOnClose)
if (this._shouldCloseConnectionOnClose) this._connection.close(kBrowserClosedError);
this._connection.close(kBrowserClosedError); else
else await this._channel.close();
await channel.close(); await this._closedPromise;
await this._closedPromise;
});
} catch (e) { } catch (e) {
if (isSafeCloseError(e)) if (isSafeCloseError(e))
return; return;

View file

@ -147,7 +147,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
if (routeHandler.handle(route, request)) { if (routeHandler.handle(route, request)) {
this._routes.splice(this._routes.indexOf(routeHandler), 1); this._routes.splice(this._routes.indexOf(routeHandler), 1);
if (!this._routes.length) if (!this._routes.length)
this._wrapApiCall(channel => this._disableInterception(channel), true).catch(() => {}); this._wrapApiCall(() => this._disableInterception(), true).catch(() => {});
} }
return; return;
} }
@ -182,11 +182,9 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
} }
async newPage(): Promise<Page> { async newPage(): Promise<Page> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { if (this._ownerPage)
if (this._ownerPage) throw new Error('Please use browser.newContext()');
throw new Error('Please use browser.newContext()'); return Page.from((await this._channel.newPage()).page);
return Page.from((await channel.newPage()).page);
});
} }
async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> { async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> {
@ -194,109 +192,81 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
urls = []; urls = [];
if (urls && typeof urls === 'string') if (urls && typeof urls === 'string')
urls = [ urls ]; urls = [ urls ];
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { return (await this._channel.cookies({ urls: urls as string[] })).cookies;
return (await channel.cookies({ urls: urls as string[] })).cookies;
});
} }
async addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void> { async addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.addCookies({ cookies });
await channel.addCookies({ cookies });
});
} }
async clearCookies(): Promise<void> { async clearCookies(): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.clearCookies();
await channel.clearCookies();
});
} }
async grantPermissions(permissions: string[], options?: { origin?: string }): Promise<void> { async grantPermissions(permissions: string[], options?: { origin?: string }): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.grantPermissions({ permissions, ...options });
await channel.grantPermissions({ permissions, ...options });
});
} }
async clearPermissions(): Promise<void> { async clearPermissions(): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.clearPermissions();
await channel.clearPermissions();
});
} }
async setGeolocation(geolocation: { longitude: number, latitude: number, accuracy?: number } | null): Promise<void> { async setGeolocation(geolocation: { longitude: number, latitude: number, accuracy?: number } | null): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.setGeolocation({ geolocation: geolocation || undefined });
await channel.setGeolocation({ geolocation: geolocation || undefined });
});
} }
async setExtraHTTPHeaders(headers: Headers): Promise<void> { async setExtraHTTPHeaders(headers: Headers): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { network.validateHeaders(headers);
network.validateHeaders(headers); await this._channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) });
await channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) });
});
} }
async setOffline(offline: boolean): Promise<void> { async setOffline(offline: boolean): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.setOffline({ offline });
await channel.setOffline({ offline });
});
} }
async setHTTPCredentials(httpCredentials: { username: string, password: string } | null): Promise<void> { async setHTTPCredentials(httpCredentials: { username: string, password: string } | null): Promise<void> {
if (!isUnderTest()) if (!isUnderTest())
deprecate(`context.setHTTPCredentials`, `warning: method |context.setHTTPCredentials()| is deprecated. Instead of changing credentials, create another browser context with new credentials.`); deprecate(`context.setHTTPCredentials`, `warning: method |context.setHTTPCredentials()| is deprecated. Instead of changing credentials, create another browser context with new credentials.`);
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.setHTTPCredentials({ httpCredentials: httpCredentials || undefined });
await channel.setHTTPCredentials({ httpCredentials: httpCredentials || undefined });
});
} }
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void> { async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { const source = await evaluationScript(script, arg);
const source = await evaluationScript(script, arg); await this._channel.addInitScript({ source });
await channel.addInitScript({ source });
});
} }
async exposeBinding(name: string, callback: (source: structs.BindingSource, ...args: any[]) => any, options: { handle?: boolean } = {}): Promise<void> { async exposeBinding(name: string, callback: (source: structs.BindingSource, ...args: any[]) => any, options: { handle?: boolean } = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.exposeBinding({ name, needsHandle: options.handle });
await channel.exposeBinding({ name, needsHandle: options.handle }); this._bindings.set(name, callback);
this._bindings.set(name, callback);
});
} }
async exposeFunction(name: string, callback: Function): Promise<void> { async exposeFunction(name: string, callback: Function): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._channel.exposeBinding({ name });
await channel.exposeBinding({ name }); const binding = (source: structs.BindingSource, ...args: any[]) => callback(...args);
const binding = (source: structs.BindingSource, ...args: any[]) => callback(...args); this._bindings.set(name, binding);
this._bindings.set(name, binding);
});
} }
async route(url: URLMatch, handler: network.RouteHandlerCallback, options: { times?: number } = {}): Promise<void> { async route(url: URLMatch, handler: network.RouteHandlerCallback, options: { times?: number } = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { this._routes.unshift(new network.RouteHandler(this._options.baseURL, url, handler, options.times));
this._routes.unshift(new network.RouteHandler(this._options.baseURL, url, handler, options.times)); if (this._routes.length === 1)
if (this._routes.length === 1) await this._channel.setNetworkInterceptionEnabled({ enabled: true });
await channel.setNetworkInterceptionEnabled({ enabled: true });
});
} }
async unroute(url: URLMatch, handler?: network.RouteHandlerCallback): Promise<void> { async unroute(url: URLMatch, handler?: network.RouteHandlerCallback): Promise<void> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler)); if (!this._routes.length)
if (!this._routes.length) await this._disableInterception();
await this._disableInterception(channel);
});
} }
private async _disableInterception(channel: channels.BrowserContextChannel) { private async _disableInterception() {
await channel.setNetworkInterceptionEnabled({ enabled: false }); await this._channel.setNetworkInterceptionEnabled({ enabled: false });
} }
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { return this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(channel, event); const waiter = Waiter.createForEvent(this._channel, event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.BrowserContext.Close) if (event !== Events.BrowserContext.Close)
waiter.rejectOnEvent(this, Events.BrowserContext.Close, new Error('Context closed')); waiter.rejectOnEvent(this, Events.BrowserContext.Close, new Error('Context closed'));
@ -307,14 +277,12 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
} }
async storageState(options: { path?: string } = {}): Promise<StorageState> { async storageState(options: { path?: string } = {}): Promise<StorageState> {
return await this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { const state = await this._channel.storageState();
const state = await channel.storageState(); if (options.path) {
if (options.path) { await mkdirIfNeeded(options.path);
await mkdirIfNeeded(options.path); await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8'); }
} return state;
return state;
});
} }
backgroundPages(): Page[] { backgroundPages(): Page[] {
@ -329,10 +297,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
// channelOwner.ts's validation messages don't handle the pseudo-union type, so we're explicit here // channelOwner.ts's validation messages don't handle the pseudo-union type, so we're explicit here
if (!(page instanceof Page) && !(page instanceof Frame)) if (!(page instanceof Page) && !(page instanceof Frame))
throw new Error('page: expected Page or Frame'); throw new Error('page: expected Page or Frame');
return this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { const result = await this._channel.newCDPSession(page instanceof Page ? { page: page._channel } : { frame: page._channel });
const result = await channel.newCDPSession(page instanceof Page ? { page: page._channel } : { frame: page._channel }); return CDPSession.from(result.session);
return CDPSession.from(result.session);
});
} }
_onClose() { _onClose() {
@ -344,7 +310,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
async close(): Promise<void> { async close(): Promise<void> {
try { try {
await this._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._wrapApiCall(async () => {
await this._browserType?._onWillCloseContext?.(this); await this._browserType?._onWillCloseContext?.(this);
if (this._options.recordHar) { if (this._options.recordHar) {
const har = await this._channel.harExport(); const har = await this._channel.harExport();
@ -352,7 +318,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
await artifact.saveAs(this._options.recordHar.path); await artifact.saveAs(this._options.recordHar.path);
await artifact.delete(); await artifact.delete();
} }
await channel.close(); await this._channel.close();
await this._closedPromise; await this._closedPromise;
}); });
} catch (e) { } catch (e) {

View file

@ -68,21 +68,19 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
async launch(options: LaunchOptions = {}): Promise<Browser> { async launch(options: LaunchOptions = {}): Promise<Browser> {
const logger = options.logger || this._defaultLaunchOptions.logger; const logger = options.logger || this._defaultLaunchOptions.logger;
return this._wrapApiCall(async (channel: channels.BrowserTypeChannel) => { assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
assert(!(options as any).port, 'Cannot specify a port without launching as a server.'); options = { ...this._defaultLaunchOptions, ...options };
options = { ...this._defaultLaunchOptions, ...options }; const launchOptions: channels.BrowserTypeLaunchParams = {
const launchOptions: channels.BrowserTypeLaunchParams = { ...options,
...options, ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), env: options.env ? envObjectToArray(options.env) : undefined,
env: options.env ? envObjectToArray(options.env) : undefined, };
}; const browser = Browser.from((await this._channel.launch(launchOptions)).browser);
const browser = Browser.from((await channel.launch(launchOptions)).browser); browser._logger = logger;
browser._logger = logger; browser._setBrowserType(this);
browser._setBrowserType(this); return browser;
return browser;
});
} }
async launchServer(options: LaunchServerOptions = {}): Promise<api.BrowserServer> { async launchServer(options: LaunchServerOptions = {}): Promise<api.BrowserServer> {
@ -94,26 +92,24 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
async launchPersistentContext(userDataDir: string, options: LaunchPersistentContextOptions = {}): Promise<BrowserContext> { async launchPersistentContext(userDataDir: string, options: LaunchPersistentContextOptions = {}): Promise<BrowserContext> {
const logger = options.logger || this._defaultLaunchOptions.logger; const logger = options.logger || this._defaultLaunchOptions.logger;
return this._wrapApiCall(async (channel: channels.BrowserTypeChannel) => { assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
assert(!(options as any).port, 'Cannot specify a port without launching as a server.'); options = { ...this._defaultLaunchOptions, ...this._defaultContextOptions, ...options };
options = { ...this._defaultLaunchOptions, ...this._defaultContextOptions, ...options }; const contextParams = await prepareBrowserContextParams(options);
const contextParams = await prepareBrowserContextParams(options); const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = { ...contextParams,
...contextParams, ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), env: options.env ? envObjectToArray(options.env) : undefined,
env: options.env ? envObjectToArray(options.env) : undefined, channel: options.channel,
channel: options.channel, userDataDir,
userDataDir, };
}; const result = await this._channel.launchPersistentContext(persistentParams);
const result = await channel.launchPersistentContext(persistentParams); const context = BrowserContext.from(result.context);
const context = BrowserContext.from(result.context); context._options = contextParams;
context._options = contextParams; context._logger = logger;
context._logger = logger; context._setBrowserType(this);
context._setBrowserType(this); await this._onDidCreateContext?.(context);
await this._onDidCreateContext?.(context); return context;
return context;
});
} }
connect(options: api.ConnectOptions & { wsEndpoint?: string }): Promise<api.Browser>; connect(options: api.ConnectOptions & { wsEndpoint?: string }): Promise<api.Browser>;
@ -127,10 +123,10 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
async _connect(wsEndpoint: string, params: Partial<ConnectOptions> = {}): Promise<Browser> { async _connect(wsEndpoint: string, params: Partial<ConnectOptions> = {}): Promise<Browser> {
const logger = params.logger; const logger = params.logger;
return await this._wrapApiCall(async (channel: channels.BrowserTypeChannel) => { return await this._wrapApiCall(async () => {
const deadline = params.timeout ? monotonicTime() + params.timeout : 0; const deadline = params.timeout ? monotonicTime() + params.timeout : 0;
let browser: Browser; let browser: Browser;
const { pipe } = await channel.connect({ wsEndpoint, headers: params.headers, slowMo: params.slowMo, timeout: params.timeout }); const { pipe } = await this._channel.connect({ wsEndpoint, headers: params.headers, slowMo: params.slowMo, timeout: params.timeout });
const closePipe = () => pipe.close().catch(() => {}); const closePipe = () => pipe.close().catch(() => {});
const connection = new Connection(); const connection = new Connection();
connection.markAsRemote(); connection.markAsRemote();
@ -207,21 +203,19 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
if (this.name() !== 'chromium') if (this.name() !== 'chromium')
throw new Error('Connecting over CDP is only supported in Chromium.'); throw new Error('Connecting over CDP is only supported in Chromium.');
const logger = params.logger; const logger = params.logger;
return this._wrapApiCall(async (channel: channels.BrowserTypeChannel) => { const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers);
const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers); const headers = paramsHeaders ? headersObjectToArray(paramsHeaders) : undefined;
const headers = paramsHeaders ? headersObjectToArray(paramsHeaders) : undefined; const result = await this._channel.connectOverCDP({
const result = await channel.connectOverCDP({ endpointURL,
endpointURL, headers,
headers, slowMo: params.slowMo,
slowMo: params.slowMo, timeout: params.timeout
timeout: params.timeout
});
const browser = Browser.from(result.browser);
if (result.defaultContext)
browser._contexts.add(BrowserContext.from(result.defaultContext));
browser._logger = logger;
browser._setBrowserType(this);
return browser;
}); });
const browser = Browser.from(result.browser);
if (result.defaultContext)
browser._contexts.add(BrowserContext.from(result.defaultContext));
browser._logger = logger;
browser._setBrowserType(this);
return browser;
} }
} }

View file

@ -42,15 +42,11 @@ export class CDPSession extends ChannelOwner<channels.CDPSessionChannel> impleme
method: T, method: T,
params?: Protocol.CommandParameters[T] params?: Protocol.CommandParameters[T]
): Promise<Protocol.CommandReturnValues[T]> { ): Promise<Protocol.CommandReturnValues[T]> {
return this._wrapApiCall(async (channel: channels.CDPSessionChannel) => { const result = await this._channel.send({ method, params });
const result = await channel.send({ method, params }); return result.result as Protocol.CommandReturnValues[T];
return result.result as Protocol.CommandReturnValues[T];
});
} }
async detach() { async detach() {
return this._wrapApiCall(async (channel: channels.CDPSessionChannel) => { return this._channel.detach();
return channel.detach();
});
} }
} }

View file

@ -84,7 +84,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
const validator = scheme[paramsName(this._type, prop)]; const validator = scheme[paramsName(this._type, prop)];
if (validator) { if (validator) {
return (params: any) => { return (params: any) => {
return this._wrapApiCall((channel, apiZone) => { return this._wrapApiCall(apiZone => {
const { stackTrace, csi, callCookie } = apiZone.reported ? { csi: undefined, callCookie: undefined, stackTrace: null } : apiZone; const { stackTrace, csi, callCookie } = apiZone.reported ? { csi: undefined, callCookie: undefined, stackTrace: null } : apiZone;
apiZone.reported = true; apiZone.reported = true;
if (csi && stackTrace && stackTrace.apiName) if (csi && stackTrace && stackTrace.apiName)
@ -101,12 +101,12 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
return channel; return channel;
} }
async _wrapApiCall<R, C extends channels.Channel = T>(func: (channel: C, apiZone: ApiZone) => Promise<R>, isInternal = false): Promise<R> { async _wrapApiCall<R>(func: (apiZone: ApiZone) => Promise<R>, isInternal = false): Promise<R> {
const logger = this._logger; const logger = this._logger;
const stack = captureRawStack(); const stack = captureRawStack();
const apiZone = zones.zoneData<ApiZone>('apiZone', stack); const apiZone = zones.zoneData<ApiZone>('apiZone', stack);
if (apiZone) if (apiZone)
return func(this._channel as any, apiZone); return func(apiZone);
const stackTrace = captureStackTrace(stack); const stackTrace = captureStackTrace(stack);
if (isInternal) if (isInternal)
@ -120,7 +120,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
logApiCall(logger, `=> ${apiName} started`, isInternal); logApiCall(logger, `=> ${apiName} started`, isInternal);
const apiZone = { stackTrace, isInternal, reported: false, csi, callCookie }; const apiZone = { stackTrace, isInternal, reported: false, csi, callCookie };
const result = await zones.run<ApiZone, R>('apiZone', apiZone, async () => { const result = await zones.run<ApiZone, R>('apiZone', apiZone, async () => {
return await func(this._channel as any, apiZone); return await func(apiZone);
}); });
csi?.onApiCallEnd(callCookie); csi?.onApiCallEnd(callCookie);
logApiCall(logger, `<= ${apiName} succeeded`, isInternal); logApiCall(logger, `<= ${apiName} succeeded`, isInternal);

View file

@ -40,14 +40,10 @@ export class Dialog extends ChannelOwner<channels.DialogChannel> implements api.
} }
async accept(promptText: string | undefined) { async accept(promptText: string | undefined) {
return this._wrapApiCall(async (channel: channels.DialogChannel) => { await this._channel.accept({ promptText });
await channel.accept({ promptText });
});
} }
async dismiss() { async dismiss() {
return this._wrapApiCall(async (channel: channels.DialogChannel) => { await this._channel.dismiss();
await channel.dismiss();
});
} }
} }

View file

@ -46,14 +46,12 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
} }
async launch(options: ElectronOptions = {}): Promise<ElectronApplication> { async launch(options: ElectronOptions = {}): Promise<ElectronApplication> {
return this._wrapApiCall(async (channel: channels.ElectronChannel) => { const params: channels.ElectronLaunchParams = {
const params: channels.ElectronLaunchParams = { ...options,
...options, extraHTTPHeaders: options.extraHTTPHeaders && headersObjectToArray(options.extraHTTPHeaders),
extraHTTPHeaders: options.extraHTTPHeaders && headersObjectToArray(options.extraHTTPHeaders), env: envObjectToArray(options.env ? options.env : process.env),
env: envObjectToArray(options.env ? options.env : process.env), };
}; return ElectronApplication.from((await this._channel.launch(params)).electronApplication);
return ElectronApplication.from((await channel.launch(params)).electronApplication);
});
} }
} }
@ -87,11 +85,9 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
} }
async firstWindow(): Promise<Page> { async firstWindow(): Promise<Page> {
return this._wrapApiCall(async (channel: channels.ElectronApplicationChannel) => { if (this._windows.size)
if (this._windows.size) return this._windows.values().next().value;
return this._windows.values().next().value; return this.waitForEvent('window');
return this.waitForEvent('window');
});
} }
context(): BrowserContext { context(): BrowserContext {
@ -99,16 +95,14 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
} }
async close() { async close() {
return this._wrapApiCall(async (channel: channels.ElectronApplicationChannel) => { await this._channel.close();
await channel.close();
});
} }
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
return this._wrapApiCall(async (channel: channels.ElectronApplicationChannel) => { return this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(channel, event); const waiter = Waiter.createForEvent(this._channel, event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.ElectronApplication.Close) if (event !== Events.ElectronApplication.Close)
waiter.rejectOnEvent(this, Events.ElectronApplication.Close, new Error('Electron application closed')); waiter.rejectOnEvent(this, Events.ElectronApplication.Close, new Error('Electron application closed'));
@ -119,23 +113,17 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
} }
async browserWindow(page: Page): Promise<JSHandle<BrowserWindow>> { async browserWindow(page: Page): Promise<JSHandle<BrowserWindow>> {
return this._wrapApiCall(async (channel: channels.ElectronApplicationChannel) => { const result = await this._channel.browserWindow({ page: page._channel });
const result = await channel.browserWindow({ page: page._channel }); return JSHandle.from(result.handle);
return JSHandle.from(result.handle);
});
} }
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<ElectronAppType, Arg, R>, arg: Arg): Promise<R> { async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<ElectronAppType, Arg, R>, arg: Arg): Promise<R> {
return this._wrapApiCall(async (channel: channels.ElectronApplicationChannel) => { const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return parseResult(result.value);
return parseResult(result.value);
});
} }
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<ElectronAppType, Arg, R>, arg: Arg): Promise<structs.SmartHandle<R>> { async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<ElectronAppType, Arg, R>, arg: Arg): Promise<structs.SmartHandle<R>> {
return this._wrapApiCall(async (channel: channels.ElectronApplicationChannel) => { const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
} }
} }

View file

@ -47,174 +47,118 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
} }
async ownerFrame(): Promise<Frame | null> { async ownerFrame(): Promise<Frame | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return Frame.fromNullable((await this._elementChannel.ownerFrame()).frame);
return Frame.fromNullable((await channel.ownerFrame()).frame);
});
} }
async contentFrame(): Promise<Frame | null> { async contentFrame(): Promise<Frame | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return Frame.fromNullable((await this._elementChannel.contentFrame()).frame);
return Frame.fromNullable((await channel.contentFrame()).frame);
});
} }
async getAttribute(name: string): Promise<string | null> { async getAttribute(name: string): Promise<string | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const value = (await this._elementChannel.getAttribute({ name })).value;
const value = (await channel.getAttribute({ name })).value; return value === undefined ? null : value;
return value === undefined ? null : value;
});
} }
async inputValue(): Promise<string> { async inputValue(): Promise<string> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.inputValue()).value;
return (await channel.inputValue()).value;
});
} }
async textContent(): Promise<string | null> { async textContent(): Promise<string | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const value = (await this._elementChannel.textContent()).value;
const value = (await channel.textContent()).value; return value === undefined ? null : value;
return value === undefined ? null : value;
});
} }
async innerText(): Promise<string> { async innerText(): Promise<string> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.innerText()).value;
return (await channel.innerText()).value;
});
} }
async innerHTML(): Promise<string> { async innerHTML(): Promise<string> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.innerHTML()).value;
return (await channel.innerHTML()).value;
});
} }
async isChecked(): Promise<boolean> { async isChecked(): Promise<boolean> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.isChecked()).value;
return (await channel.isChecked()).value;
});
} }
async isDisabled(): Promise<boolean> { async isDisabled(): Promise<boolean> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.isDisabled()).value;
return (await channel.isDisabled()).value;
});
} }
async isEditable(): Promise<boolean> { async isEditable(): Promise<boolean> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.isEditable()).value;
return (await channel.isEditable()).value;
});
} }
async isEnabled(): Promise<boolean> { async isEnabled(): Promise<boolean> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.isEnabled()).value;
return (await channel.isEnabled()).value;
});
} }
async isHidden(): Promise<boolean> { async isHidden(): Promise<boolean> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.isHidden()).value;
return (await channel.isHidden()).value;
});
} }
async isVisible(): Promise<boolean> { async isVisible(): Promise<boolean> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return (await this._elementChannel.isVisible()).value;
return (await channel.isVisible()).value;
});
} }
async dispatchEvent(type: string, eventInit: Object = {}) { async dispatchEvent(type: string, eventInit: Object = {}) {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.dispatchEvent({ type, eventInit: serializeArgument(eventInit) });
await channel.dispatchEvent({ type, eventInit: serializeArgument(eventInit) });
});
} }
async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions = {}) { async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions = {}) {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.scrollIntoViewIfNeeded(options);
await channel.scrollIntoViewIfNeeded(options);
});
} }
async hover(options: channels.ElementHandleHoverOptions = {}): Promise<void> { async hover(options: channels.ElementHandleHoverOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.hover(options);
await channel.hover(options);
});
} }
async click(options: channels.ElementHandleClickOptions = {}): Promise<void> { async click(options: channels.ElementHandleClickOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.click(options);
return await channel.click(options);
});
} }
async dblclick(options: channels.ElementHandleDblclickOptions = {}): Promise<void> { async dblclick(options: channels.ElementHandleDblclickOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.dblclick(options);
return await channel.dblclick(options);
});
} }
async tap(options: channels.ElementHandleTapOptions = {}): Promise<void> { async tap(options: channels.ElementHandleTapOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.tap(options);
return await channel.tap(options);
});
} }
async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> { async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options });
const result = await channel.selectOption({ ...convertSelectOptionValues(values), ...options }); return result.values;
return result.values;
});
} }
async fill(value: string, options: channels.ElementHandleFillOptions = {}): Promise<void> { async fill(value: string, options: channels.ElementHandleFillOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.fill({ value, ...options });
return await channel.fill({ value, ...options });
});
} }
async selectText(options: channels.ElementHandleSelectTextOptions = {}): Promise<void> { async selectText(options: channels.ElementHandleSelectTextOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.selectText(options);
await channel.selectText(options);
});
} }
async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions = {}) { async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions = {}) {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.setInputFiles({ files: await convertInputFiles(files), ...options });
await channel.setInputFiles({ files: await convertInputFiles(files), ...options });
});
} }
async focus(): Promise<void> { async focus(): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.focus();
await channel.focus();
});
} }
async type(text: string, options: channels.ElementHandleTypeOptions = {}): Promise<void> { async type(text: string, options: channels.ElementHandleTypeOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.type({ text, ...options });
await channel.type({ text, ...options });
});
} }
async press(key: string, options: channels.ElementHandlePressOptions = {}): Promise<void> { async press(key: string, options: channels.ElementHandlePressOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { await this._elementChannel.press({ key, ...options });
await channel.press({ key, ...options });
});
} }
async check(options: channels.ElementHandleCheckOptions = {}) { async check(options: channels.ElementHandleCheckOptions = {}) {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.check(options);
return await channel.check(options);
});
} }
async uncheck(options: channels.ElementHandleUncheckOptions = {}) { async uncheck(options: channels.ElementHandleUncheckOptions = {}) {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.uncheck(options);
return await channel.uncheck(options);
});
} }
async setChecked(checked: boolean, options?: channels.ElementHandleCheckOptions) { async setChecked(checked: boolean, options?: channels.ElementHandleCheckOptions) {
@ -225,67 +169,51 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
} }
async boundingBox(): Promise<Rect | null> { async boundingBox(): Promise<Rect | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const value = (await this._elementChannel.boundingBox()).value;
const value = (await channel.boundingBox()).value; return value === undefined ? null : value;
return value === undefined ? null : value;
});
} }
async screenshot(options: channels.ElementHandleScreenshotOptions & { path?: string } = {}): Promise<Buffer> { async screenshot(options: channels.ElementHandleScreenshotOptions & { path?: string } = {}): Promise<Buffer> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const copy = { ...options };
const copy = { ...options }; if (!copy.type)
if (!copy.type) copy.type = determineScreenshotType(options);
copy.type = determineScreenshotType(options); const result = await this._elementChannel.screenshot(copy);
const result = await channel.screenshot(copy); const buffer = Buffer.from(result.binary, 'base64');
const buffer = Buffer.from(result.binary, 'base64'); if (options.path) {
if (options.path) { await mkdirIfNeeded(options.path);
await mkdirIfNeeded(options.path); await fs.promises.writeFile(options.path, buffer);
await fs.promises.writeFile(options.path, buffer); }
} return buffer;
return buffer;
});
} }
async $(selector: string): Promise<ElementHandle<SVGElement | HTMLElement> | null> { async $(selector: string): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element) as ElementHandle<SVGElement | HTMLElement> | null;
return ElementHandle.fromNullable((await channel.querySelector({ selector })).element) as ElementHandle<SVGElement | HTMLElement> | null;
});
} }
async $$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]> { async $$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const result = await this._elementChannel.querySelectorAll({ selector });
const result = await channel.querySelectorAll({ selector }); return result.elements.map(h => ElementHandle.from(h) as ElementHandle<SVGElement | HTMLElement>);
return result.elements.map(h => ElementHandle.from(h) as ElementHandle<SVGElement | HTMLElement>);
});
} }
async $eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element, Arg, R>, arg?: Arg): Promise<R> { async $eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element, Arg, R>, arg?: Arg): Promise<R> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const result = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return parseResult(result.value);
return parseResult(result.value);
});
} }
async $$eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element[], Arg, R>, arg?: Arg): Promise<R> { async $$eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element[], Arg, R>, arg?: Arg): Promise<R> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const result = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return parseResult(result.value);
return parseResult(result.value);
});
} }
async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled', options: channels.ElementHandleWaitForElementStateOptions = {}): Promise<void> { async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled', options: channels.ElementHandleWaitForElementStateOptions = {}): Promise<void> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { return await this._elementChannel.waitForElementState({ state, ...options });
return await channel.waitForElementState({ state, ...options });
});
} }
waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<ElementHandle<SVGElement | HTMLElement>>; waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<ElementHandle<SVGElement | HTMLElement>>;
waitForSelector(selector: string, options?: channels.ElementHandleWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement> | null>; waitForSelector(selector: string, options?: channels.ElementHandleWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement> | null>;
async waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions = {}): Promise<ElementHandle<SVGElement | HTMLElement> | null> { async waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions = {}): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._wrapApiCall(async (channel: channels.ElementHandleChannel) => { const result = await this._elementChannel.waitForSelector({ selector, ...options });
const result = await channel.waitForSelector({ selector, ...options }); return ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
return ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
});
} }
} }

View file

@ -56,16 +56,14 @@ export class APIRequest implements api.APIRequest {
} }
async newContext(options: NewContextOptions = {}): Promise<APIRequestContext> { async newContext(options: NewContextOptions = {}): Promise<APIRequestContext> {
return await this._playwright._wrapApiCall(async (channel: channels.PlaywrightChannel) => { const storageState = typeof options.storageState === 'string' ?
const storageState = typeof options.storageState === 'string' ? JSON.parse(await fs.promises.readFile(options.storageState, 'utf8')) :
JSON.parse(await fs.promises.readFile(options.storageState, 'utf8')) : options.storageState;
options.storageState; return APIRequestContext.from((await this._playwright._channel.newRequest({
return APIRequestContext.from((await channel.newRequest({ ...options,
...options, extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined, storageState,
storageState, })).request);
})).request);
});
} }
} }
@ -78,10 +76,8 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
} }
dispose(): Promise<void> { async dispose(): Promise<void> {
return this._wrapApiCall(async (channel: channels.APIRequestContextChannel) => { await this._channel.dispose();
await channel.dispose();
});
} }
async delete(url: string, options?: RequestWithBodyOptions): Promise<APIResponse> { async delete(url: string, options?: RequestWithBodyOptions): Promise<APIResponse> {
@ -127,7 +123,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
} }
async fetch(urlOrRequest: string | api.Request, options: FetchOptions = {}): Promise<APIResponse> { async fetch(urlOrRequest: string | api.Request, options: FetchOptions = {}): Promise<APIResponse> {
return this._wrapApiCall(async (channel: channels.APIRequestContextChannel) => { return this._wrapApiCall(async () => {
const request: network.Request | undefined = (urlOrRequest instanceof network.Request) ? urlOrRequest as network.Request : undefined; const request: network.Request | undefined = (urlOrRequest instanceof network.Request) ? urlOrRequest as network.Request : undefined;
assert(request || typeof urlOrRequest === 'string', 'First argument must be either URL string or Request'); assert(request || typeof urlOrRequest === 'string', 'First argument must be either URL string or Request');
assert((options.data === undefined ? 0 : 1) + (options.form === undefined ? 0 : 1) + (options.multipart === undefined ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`); assert((options.data === undefined ? 0 : 1) + (options.form === undefined ? 0 : 1) + (options.multipart === undefined ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
@ -175,7 +171,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
if (postDataBuffer === undefined && jsonData === undefined && formData === undefined && multipartData === undefined) if (postDataBuffer === undefined && jsonData === undefined && formData === undefined && multipartData === undefined)
postDataBuffer = request?.postDataBuffer() || undefined; postDataBuffer = request?.postDataBuffer() || undefined;
const postData = (postDataBuffer ? postDataBuffer.toString('base64') : undefined); const postData = (postDataBuffer ? postDataBuffer.toString('base64') : undefined);
const result = await channel.fetch({ const result = await this._channel.fetch({
url, url,
params, params,
method, method,
@ -195,14 +191,12 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
} }
async storageState(options: { path?: string } = {}): Promise<StorageState> { async storageState(options: { path?: string } = {}): Promise<StorageState> {
return await this._wrapApiCall(async (channel: channels.APIRequestContextChannel) => { const state = await this._channel.storageState();
const state = await channel.storageState(); if (options.path) {
if (options.path) { await mkdirIfNeeded(options.path);
await mkdirIfNeeded(options.path); await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8'); }
} return state;
return state;
});
} }
} }
@ -242,18 +236,16 @@ export class APIResponse implements api.APIResponse {
} }
async body(): Promise<Buffer> { async body(): Promise<Buffer> {
return this._request._wrapApiCall(async (channel: channels.APIRequestContextChannel) => { try {
try { const result = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
const result = await channel.fetchResponseBody({ fetchUid: this._fetchUid() }); if (result.binary === undefined)
if (result.binary === undefined) throw new Error('Response has been disposed');
throw new Error('Response has been disposed'); return Buffer.from(result.binary!, 'base64');
return Buffer.from(result.binary!, 'base64'); } catch (e) {
} catch (e) { if (e.message.includes(kBrowserOrContextClosedError))
if (e.message === kBrowserOrContextClosedError) throw new Error('Response has been disposed');
throw new Error('Response has been disposed'); throw e;
throw e; }
}
});
} }
async text(): Promise<string> { async text(): Promise<string> {
@ -267,9 +259,7 @@ export class APIResponse implements api.APIResponse {
} }
async dispose(): Promise<void> { async dispose(): Promise<void> {
return this._request._wrapApiCall(async (channel: channels.APIRequestContextChannel) => { await this._request._channel.disposeAPIResponse({ fetchUid: this._fetchUid() });
await channel.disposeAPIResponse({ fetchUid: this._fetchUid() });
});
} }
[util.inspect.custom]() { [util.inspect.custom]() {

View file

@ -44,8 +44,6 @@ export class FileChooser implements api.FileChooser {
} }
async setFiles(files: string | FilePayload | string[] | FilePayload[], options?: channels.ElementHandleSetInputFilesOptions) { async setFiles(files: string | FilePayload | string[] | FilePayload[], options?: channels.ElementHandleSetInputFilesOptions) {
return this._page._wrapApiCall(async () => { return this._elementHandle.setInputFiles(files, options);
return this._elementHandle.setInputFiles(files, options);
});
} }
} }

View file

@ -105,7 +105,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
} }
async waitForNavigation(options: WaitForNavigationOptions = {}): Promise<network.Response | null> { async waitForNavigation(options: WaitForNavigationOptions = {}): Promise<network.Response | null> {
return this._page!._wrapApiCall(async (channel: channels.PageChannel) => { return this._page!._wrapApiCall(async () => {
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
const waiter = this._setupNavigationWaiter(options); const waiter = this._setupNavigationWaiter(options);
@ -143,7 +143,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
state = verifyLoadState('state', state); state = verifyLoadState('state', state);
if (this._loadStates.has(state)) if (this._loadStates.has(state))
return; return;
return this._page!._wrapApiCall(async (channel: channels.PageChannel) => { return this._page!._wrapApiCall(async () => {
const waiter = this._setupNavigationWaiter(options); const waiter = this._setupNavigationWaiter(options);
await waiter.waitForEvent<LifecycleEvent>(this._eventEmitter, 'loadstate', s => { await waiter.waitForEvent<LifecycleEvent>(this._eventEmitter, 'loadstate', s => {
waiter.log(` "${s}" event fired`); waiter.log(` "${s}" event fired`);

View file

@ -27,33 +27,23 @@ export class Keyboard implements api.Keyboard {
} }
async down(key: string) { async down(key: string) {
await this._page._wrapApiCall(async channel => { await this._page._channel.keyboardDown({ key });
await channel.keyboardDown({ key });
});
} }
async up(key: string) { async up(key: string) {
await this._page._wrapApiCall(async channel => { await this._page._channel.keyboardUp({ key });
await channel.keyboardUp({ key });
});
} }
async insertText(text: string) { async insertText(text: string) {
await this._page._wrapApiCall(async channel => { await this._page._channel.keyboardInsertText({ text });
await channel.keyboardInsertText({ text });
});
} }
async type(text: string, options: channels.PageKeyboardTypeOptions = {}) { async type(text: string, options: channels.PageKeyboardTypeOptions = {}) {
await this._page._wrapApiCall(async channel => { await this._page._channel.keyboardType({ text, ...options });
await channel.keyboardType({ text, ...options });
});
} }
async press(key: string, options: channels.PageKeyboardPressOptions = {}) { async press(key: string, options: channels.PageKeyboardPressOptions = {}) {
await this._page._wrapApiCall(async channel => { await this._page._channel.keyboardPress({ key, ...options });
await channel.keyboardPress({ key, ...options });
});
} }
} }
@ -65,27 +55,19 @@ export class Mouse implements api.Mouse {
} }
async move(x: number, y: number, options: { steps?: number } = {}) { async move(x: number, y: number, options: { steps?: number } = {}) {
await this._page._wrapApiCall(async channel => { await this._page._channel.mouseMove({ x, y, ...options });
await channel.mouseMove({ x, y, ...options });
});
} }
async down(options: channels.PageMouseDownOptions = {}) { async down(options: channels.PageMouseDownOptions = {}) {
await this._page._wrapApiCall(async channel => { await this._page._channel.mouseDown({ ...options });
await channel.mouseDown({ ...options });
});
} }
async up(options: channels.PageMouseUpOptions = {}) { async up(options: channels.PageMouseUpOptions = {}) {
await this._page._wrapApiCall(async channel => { await this._page._channel.mouseUp(options);
await channel.mouseUp(options);
});
} }
async click(x: number, y: number, options: channels.PageMouseClickOptions = {}) { async click(x: number, y: number, options: channels.PageMouseClickOptions = {}) {
await this._page._wrapApiCall(async channel => { await this._page._channel.mouseClick({ x, y, ...options });
await channel.mouseClick({ x, y, ...options });
});
} }
async dblclick(x: number, y: number, options: Omit<channels.PageMouseClickOptions, 'clickCount'> = {}) { async dblclick(x: number, y: number, options: Omit<channels.PageMouseClickOptions, 'clickCount'> = {}) {
@ -93,9 +75,7 @@ export class Mouse implements api.Mouse {
} }
async wheel(deltaX: number, deltaY: number) { async wheel(deltaX: number, deltaY: number) {
await this._page._wrapApiCall(async channel => { await this._page._channel.mouseWheel({ deltaX, deltaY });
await channel.mouseWheel({ deltaX, deltaY });
});
} }
} }
@ -107,8 +87,6 @@ export class Touchscreen implements api.Touchscreen {
} }
async tap(x: number, y: number) { async tap(x: number, y: number) {
await this._page._wrapApiCall(async channel => { await this._page._channel.touchscreenTap({ x, y });
await channel.touchscreenTap({ x, y });
});
} }
} }

View file

@ -34,39 +34,29 @@ export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel> im
} }
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<R> { async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<R> {
return this._wrapApiCall(async (channel: channels.JSHandleChannel) => { const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return parseResult(result.value);
return parseResult(result.value);
});
} }
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> { async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
return this._wrapApiCall(async (channel: channels.JSHandleChannel) => { const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
} }
async getProperty(propertyName: string): Promise<JSHandle> { async getProperty(propertyName: string): Promise<JSHandle> {
return this._wrapApiCall(async (channel: channels.JSHandleChannel) => { const result = await this._channel.getProperty({ name: propertyName });
const result = await channel.getProperty({ name: propertyName }); return JSHandle.from(result.handle);
return JSHandle.from(result.handle);
});
} }
async getProperties(): Promise<Map<string, JSHandle>> { async getProperties(): Promise<Map<string, JSHandle>> {
return this._wrapApiCall(async (channel: channels.JSHandleChannel) => { const map = new Map<string, JSHandle>();
const map = new Map<string, JSHandle>(); for (const { name, value } of (await this._channel.getPropertyList()).properties)
for (const { name, value } of (await channel.getPropertyList()).properties) map.set(name, JSHandle.from(value));
map.set(name, JSHandle.from(value)); return map;
return map;
});
} }
async jsonValue(): Promise<T> { async jsonValue(): Promise<T> {
return this._wrapApiCall(async (channel: channels.JSHandleChannel) => { return parseResult((await this._channel.jsonValue()).value);
return parseResult((await channel.jsonValue()).value);
});
} }
asElement(): T extends Node ? api.ElementHandle<T> : null { asElement(): T extends Node ? api.ElementHandle<T> : null {
@ -74,9 +64,7 @@ export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel> im
} }
async dispose() { async dispose() {
return this._wrapApiCall(async (channel: channels.JSHandleChannel) => { return await this._channel.dispose();
return await channel.dispose();
});
} }
override toString(): string { override toString(): string {

View file

@ -37,8 +37,8 @@ export class Locator implements api.Locator {
timeout = this._frame.page()._timeoutSettings.timeout({ timeout }); timeout = this._frame.page()._timeoutSettings.timeout({ timeout });
const deadline = timeout ? monotonicTime() + timeout : 0; const deadline = timeout ? monotonicTime() + timeout : 0;
return this._frame._wrapApiCall<R>(async (channel: channels.FrameChannel) => { return this._frame._wrapApiCall<R>(async () => {
const result = await channel.waitForSelector({ selector: this._selector, strict: true, state: 'attached', timeout }); const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: 'attached', timeout });
const handle = ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null; const handle = ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
if (!handle) if (!handle)
throw new Error(`Could not resolve ${this._selector} to DOM Element`); throw new Error(`Could not resolve ${this._selector} to DOM Element`);
@ -224,21 +224,17 @@ export class Locator implements api.Locator {
waitFor(options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<void>; waitFor(options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<void>;
waitFor(options?: channels.FrameWaitForSelectorOptions): Promise<void>; waitFor(options?: channels.FrameWaitForSelectorOptions): Promise<void>;
async waitFor(options?: channels.FrameWaitForSelectorOptions): Promise<void> { async waitFor(options?: channels.FrameWaitForSelectorOptions): Promise<void> {
return this._frame._wrapApiCall(async (channel: channels.FrameChannel) => { await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options });
await channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options });
});
} }
async _expect(expression: string, options: FrameExpectOptions): Promise<{ matches: boolean, received?: any, log?: string[] }> { async _expect(expression: string, options: FrameExpectOptions): Promise<{ matches: boolean, received?: any, log?: string[] }> {
return this._frame._wrapApiCall(async (channel: channels.FrameChannel) => { const params: channels.FrameExpectParams = { selector: this._selector, expression, ...options, isNot: !!options.isNot };
const params: channels.FrameExpectParams = { selector: this._selector, expression, ...options, isNot: !!options.isNot }; if (options.expectedValue)
if (options.expectedValue) params.expectedValue = serializeArgument(options.expectedValue);
params.expectedValue = serializeArgument(options.expectedValue); const result = (await this._frame._channel.expect(params));
const result = (await channel.expect(params)); if (result.received !== undefined)
if (result.received !== undefined) result.received = parseResult(result.received);
result.received = parseResult(result.received); return result;
return result;
});
} }
[util.inspect.custom]() { [util.inspect.custom]() {

View file

@ -142,8 +142,8 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
_actualHeaders(): Promise<RawHeaders> { _actualHeaders(): Promise<RawHeaders> {
if (!this._actualHeadersPromise) { if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this._wrapApiCall(async (channel: channels.RequestChannel) => { this._actualHeadersPromise = this._wrapApiCall(async () => {
return new RawHeaders((await channel.rawRequestHeaders()).headers); return new RawHeaders((await this._channel.rawRequestHeaders()).headers);
}); });
} }
return this._actualHeadersPromise; return this._actualHeadersPromise;
@ -162,14 +162,12 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
} }
async response(): Promise<Response | null> { async response(): Promise<Response | null> {
return this._wrapApiCall(async (channel: channels.RequestChannel) => { return Response.fromNullable((await this._channel.response()).response);
return Response.fromNullable((await channel.response()).response);
});
} }
async _internalResponse(): Promise<Response | null> { async _internalResponse(): Promise<Response | null> {
return this._wrapApiCall(async (channel: channels.RequestChannel) => { return this._wrapApiCall(async () => {
return Response.fromNullable((await channel.response()).response); return Response.fromNullable((await this._channel.response()).response);
}, true); }, true);
} }
@ -205,9 +203,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
const response = await this.response(); const response = await this.response();
if (!response) if (!response)
throw new Error('Unable to fetch sizes for failed request'); throw new Error('Unable to fetch sizes for failed request');
return response._wrapApiCall(async (channel: channels.ResponseChannel) => { return (await response._channel.sizes()).sizes;
return (await channel.sizes()).sizes;
});
} }
_finalRequest(): Request { _finalRequest(): Request {
@ -240,56 +236,52 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
} }
async abort(errorCode?: string) { async abort(errorCode?: string) {
return this._wrapApiCall(async (channel: channels.RouteChannel) => { await this._raceWithPageClose(this._channel.abort({ errorCode }));
await this._raceWithPageClose(channel.abort({ errorCode }));
});
} }
async fulfill(options: { response?: api.APIResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) { async fulfill(options: { response?: api.APIResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
return this._wrapApiCall(async (channel: channels.RouteChannel) => { let fetchResponseUid;
let fetchResponseUid; let { status: statusOption, headers: headersOption, body } = options;
let { status: statusOption, headers: headersOption, body } = options; if (options.response) {
if (options.response) { statusOption ||= options.response.status();
statusOption ||= options.response.status(); headersOption ||= options.response.headers();
headersOption ||= options.response.headers(); if (options.body === undefined && options.path === undefined && options.response instanceof APIResponse)
if (options.body === undefined && options.path === undefined && options.response instanceof APIResponse) fetchResponseUid = (options.response as APIResponse)._fetchUid();
fetchResponseUid = (options.response as APIResponse)._fetchUid(); }
}
let isBase64 = false; let isBase64 = false;
let length = 0; let length = 0;
if (options.path) { if (options.path) {
const buffer = await fs.promises.readFile(options.path); const buffer = await fs.promises.readFile(options.path);
body = buffer.toString('base64'); body = buffer.toString('base64');
isBase64 = true; isBase64 = true;
length = buffer.length; length = buffer.length;
} else if (isString(body)) { } else if (isString(body)) {
isBase64 = false; isBase64 = false;
length = Buffer.byteLength(body); length = Buffer.byteLength(body);
} else if (body) { } else if (body) {
length = body.length; length = body.length;
body = body.toString('base64'); body = body.toString('base64');
isBase64 = true; isBase64 = true;
} }
const headers: Headers = {}; const headers: Headers = {};
for (const header of Object.keys(headersOption || {})) for (const header of Object.keys(headersOption || {}))
headers[header.toLowerCase()] = String(headersOption![header]); headers[header.toLowerCase()] = String(headersOption![header]);
if (options.contentType) if (options.contentType)
headers['content-type'] = String(options.contentType); headers['content-type'] = String(options.contentType);
else if (options.path) else if (options.path)
headers['content-type'] = mime.getType(options.path) || 'application/octet-stream'; headers['content-type'] = mime.getType(options.path) || 'application/octet-stream';
if (length && !('content-length' in headers)) if (length && !('content-length' in headers))
headers['content-length'] = String(length); headers['content-length'] = String(length);
await this._raceWithPageClose(channel.fulfill({ await this._raceWithPageClose(this._channel.fulfill({
status: statusOption || 200, status: statusOption || 200,
headers: headersObjectToArray(headers), headers: headersObjectToArray(headers),
body, body,
isBase64, isBase64,
fetchResponseUid fetchResponseUid
})); }));
});
} }
async continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) { async continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
@ -301,9 +293,9 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
} }
private async _continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer }, isInternal?: boolean) { private async _continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer }, isInternal?: boolean) {
return await this._wrapApiCall(async (channel: channels.RouteChannel) => { return await this._wrapApiCall(async () => {
const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData; const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
await this._raceWithPageClose(channel.continue({ await this._raceWithPageClose(this._channel.continue({
url: options.url, url: options.url,
method: options.method, method: options.method,
headers: options.headers ? headersObjectToArray(options.headers) : undefined, headers: options.headers ? headersObjectToArray(options.headers) : undefined,
@ -381,9 +373,9 @@ export class Response extends ChannelOwner<channels.ResponseChannel> implements
async _actualHeaders(): Promise<RawHeaders> { async _actualHeaders(): Promise<RawHeaders> {
if (!this._actualHeadersPromise) { if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this._wrapApiCall(async (channel: channels.ResponseChannel) => { this._actualHeadersPromise = (async () => {
return new RawHeaders((await channel.rawResponseHeaders()).headers); return new RawHeaders((await this._channel.rawResponseHeaders()).headers);
}); })();
} }
return this._actualHeadersPromise; return this._actualHeadersPromise;
} }
@ -409,9 +401,7 @@ export class Response extends ChannelOwner<channels.ResponseChannel> implements
} }
async body(): Promise<Buffer> { async body(): Promise<Buffer> {
return this._wrapApiCall(async (channel: channels.ResponseChannel) => { return Buffer.from((await this._channel.body()).binary, 'base64');
return Buffer.from((await channel.body()).binary, 'base64');
});
} }
async text(): Promise<string> { async text(): Promise<string> {
@ -433,15 +423,11 @@ export class Response extends ChannelOwner<channels.ResponseChannel> implements
} }
async serverAddr(): Promise<RemoteAddr|null> { async serverAddr(): Promise<RemoteAddr|null> {
return this._wrapApiCall(async (channel: channels.ResponseChannel) => { return (await this._channel.serverAddr()).value || null;
return (await channel.serverAddr()).value || null;
});
} }
async securityDetails(): Promise<SecurityDetails|null> { async securityDetails(): Promise<SecurityDetails|null> {
return this._wrapApiCall(async (channel: channels.ResponseChannel) => { return (await this._channel.securityDetails()).value || null;
return (await channel.securityDetails()).value || null;
});
} }
} }
@ -485,10 +471,10 @@ export class WebSocket extends ChannelOwner<channels.WebSocketChannel> implement
} }
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> { async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
return this._wrapApiCall(async (channel: channels.WebSocketChannel) => { return this._wrapApiCall(async () => {
const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(channel, event); const waiter = Waiter.createForEvent(this._channel, event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.WebSocket.Error) if (event !== Events.WebSocket.Error)
waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error('Socket error')); waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error('Socket error'));

View file

@ -173,7 +173,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
if (routeHandler.handle(route, request)) { if (routeHandler.handle(route, request)) {
this._routes.splice(this._routes.indexOf(routeHandler), 1); this._routes.splice(this._routes.indexOf(routeHandler), 1);
if (!this._routes.length) if (!this._routes.length)
this._wrapApiCall(channel => this._disableInterception(channel), true).catch(() => {}); this._wrapApiCall(() => this._disableInterception(), true).catch(() => {});
} }
return; return;
} }
@ -440,11 +440,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
async unroute(url: URLMatch, handler?: RouteHandlerCallback): Promise<void> { async unroute(url: URLMatch, handler?: RouteHandlerCallback): Promise<void> {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler)); this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
if (!this._routes.length) if (!this._routes.length)
await this._disableInterception(this._channel); await this._disableInterception();
} }
private async _disableInterception(channel: channels.PageChannel) { private async _disableInterception() {
await channel.setNetworkInterceptionEnabled({ enabled: false }); await this._channel.setNetworkInterceptionEnabled({ enabled: false });
} }
async screenshot(options: channels.PageScreenshotOptions & { path?: string } = {}): Promise<Buffer> { async screenshot(options: channels.PageScreenshotOptions & { path?: string } = {}): Promise<Buffer> {
@ -470,12 +470,10 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
async close(options: { runBeforeUnload?: boolean } = { runBeforeUnload: undefined }) { async close(options: { runBeforeUnload?: boolean } = { runBeforeUnload: undefined }) {
try { try {
await this._wrapApiCall(async (channel: channels.PageChannel) => { if (this._ownedContext)
if (this._ownedContext) await this._ownedContext.close();
await this._ownedContext.close(); else
else await this._channel.close(options);
await channel.close(options);
});
} catch (e) { } catch (e) {
if (isSafeCloseError(e)) if (isSafeCloseError(e))
return; return;

View file

@ -46,29 +46,25 @@ export class Tracing implements api.Tracing {
async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) { async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) {
if (options.sources) if (options.sources)
this._context._instrumentation!.addListener(this._instrumentationListener); this._context._instrumentation!.addListener(this._instrumentationListener);
await this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._context._wrapApiCall(async () => {
await channel.tracingStart(options); await this._context._channel.tracingStart(options);
await channel.tracingStartChunk({ title: options.title }); await this._context._channel.tracingStartChunk({ title: options.title });
}); });
} }
async startChunk(options: { title?: string } = {}) { async startChunk(options: { title?: string } = {}) {
this._sources = new Set(); this._sources = new Set();
await this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._context._channel.tracingStartChunk(options);
await channel.tracingStartChunk(options);
});
} }
async stopChunk(options: { path?: string } = {}) { async stopChunk(options: { path?: string } = {}) {
await this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._doStopChunk(this._context._channel, options.path);
await this._doStopChunk(channel, options.path);
});
} }
async stop(options: { path?: string } = {}) { async stop(options: { path?: string } = {}) {
await this._context._wrapApiCall(async (channel: channels.BrowserContextChannel) => { await this._context._wrapApiCall(async () => {
await this._doStopChunk(channel, options.path); await this._doStopChunk(this._context._channel, options.path);
await channel.tracingStop(); await this._context._channel.tracingStop();
}); });
} }

View file

@ -48,17 +48,13 @@ export class Worker extends ChannelOwner<channels.WorkerChannel> implements api.
async evaluate<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<R> { async evaluate<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2); assertMaxArguments(arguments.length, 2);
return this._wrapApiCall(async (channel: channels.WorkerChannel) => { const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return parseResult(result.value);
return parseResult(result.value);
});
} }
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> { async evaluateHandle<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2); assertMaxArguments(arguments.length, 2);
return this._wrapApiCall(async (channel: channels.WorkerChannel) => { const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
const result = await channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }); return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
} }
} }