Skip to content

Commit

Permalink
feat(logs): add wrapApiCall for logging to many api methods (#5093)
Browse files Browse the repository at this point in the history
Some methods (quite a few!) were missing the wrapper that produces the log.
  • Loading branch information
dgozman committed Jan 22, 2021
1 parent 5464540 commit a9b7536
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 107 deletions.
20 changes: 13 additions & 7 deletions src/client/download.ts
Expand Up @@ -69,18 +69,24 @@ export class Download extends ChannelOwner<channels.DownloadChannel, channels.Do
}

async failure(): Promise<string | null> {
return (await this._channel.failure()).error || null;
return this._wrapApiCall('download.failure', async () => {
return (await this._channel.failure()).error || null;
});
}

async createReadStream(): Promise<Readable | null> {
const result = await this._channel.stream();
if (!result.stream)
return null;
const stream = Stream.from(result.stream);
return stream.stream();
return this._wrapApiCall('download.createReadStream', async () => {
const result = await this._channel.stream();
if (!result.stream)
return null;
const stream = Stream.from(result.stream);
return stream.stream();
});
}

async delete(): Promise<void> {
return this._channel.delete();
return this._wrapApiCall('download.delete', async () => {
return this._channel.delete();
});
}
}
26 changes: 17 additions & 9 deletions src/client/electron.ts
Expand Up @@ -86,14 +86,18 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
}

async firstWindow(): Promise<electronApi.ElectronPage> {
if (this._windows.size)
return this._windows.values().next().value;
return this.waitForEvent('window');
return this._wrapApiCall('electronApplication.firstWindow', async () => {
if (this._windows.size)
return this._windows.values().next().value;
return this.waitForEvent('window');
});
}

async newBrowserWindow(options: any): Promise<electronApi.ElectronPage> {
const result = await this._channel.newBrowserWindow({ arg: serializeArgument(options) });
return Page.from(result.page) as any as electronApi.ElectronPage;
return this._wrapApiCall('electronApplication.newBrowserWindow', async () => {
const result = await this._channel.newBrowserWindow({ arg: serializeArgument(options) });
return Page.from(result.page) as any as electronApi.ElectronPage;
});
}

context(): ChromiumBrowserContext {
Expand All @@ -117,12 +121,16 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
}

async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<ElectronAppType, Arg, R>, arg: Arg): Promise<R> {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
return this._wrapApiCall('electronApplication.evaluate', async () => {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
});
}

async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<ElectronAppType, Arg, R>, arg: Arg): Promise<structs.SmartHandle<R>> {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
return this._wrapApiCall('electronApplication.evaluateHandle', async () => {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
}
}
4 changes: 3 additions & 1 deletion src/client/fileChooser.ts
Expand Up @@ -44,6 +44,8 @@ export class FileChooser implements api.FileChooser {
}

async setFiles(files: string | FilePayload | string[] | FilePayload[], options?: channels.ElementHandleSetInputFilesOptions) {
return this._elementHandle.setInputFiles(files, options);
return this._page._wrapApiCall('fileChooser.setFiles', async () => {
return this._elementHandle.setInputFiles(files, options);
});
}
}
4 changes: 3 additions & 1 deletion src/client/frame.ts
Expand Up @@ -441,7 +441,9 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
}

async waitForTimeout(timeout: number) {
await new Promise(fulfill => setTimeout(fulfill, timeout));
return this._wrapApiCall(this._apiName('waitForTimeout'), async () => {
await new Promise(fulfill => setTimeout(fulfill, timeout));
});
}

async waitForFunction<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg, options: WaitForFunctionOptions = {}): Promise<structs.SmartHandle<R>> {
Expand Down
32 changes: 21 additions & 11 deletions src/client/jsHandle.ts
Expand Up @@ -34,29 +34,39 @@ export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel, ch
}

async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<R> {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
return this._wrapApiCall('jsHandle.evaluate', async () => {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
});
}

async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
return this._wrapApiCall('jsHandle.evaluateHandle', async () => {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
}

async getProperty(propertyName: string): Promise<JSHandle> {
const result = await this._channel.getProperty({ name: propertyName });
return JSHandle.from(result.handle);
return this._wrapApiCall('jsHandle.getProperty', async () => {
const result = await this._channel.getProperty({ name: propertyName });
return JSHandle.from(result.handle);
});
}

async getProperties(): Promise<Map<string, JSHandle>> {
const map = new Map<string, JSHandle>();
for (const { name, value } of (await this._channel.getPropertyList()).properties)
map.set(name, JSHandle.from(value));
return map;
return this._wrapApiCall('jsHandle.getProperties', async () => {
const map = new Map<string, JSHandle>();
for (const { name, value } of (await this._channel.getPropertyList()).properties)
map.set(name, JSHandle.from(value));
return map;
});
}

async jsonValue(): Promise<T> {
return parseResult((await this._channel.jsonValue()).value);
return this._wrapApiCall('jsHandle.jsonValue', async () => {
return parseResult((await this._channel.jsonValue()).value);
});
}

asElement(): T extends Node ? api.ElementHandle<T> : null {
Expand Down
94 changes: 52 additions & 42 deletions src/client/network.ts
Expand Up @@ -132,7 +132,9 @@ export class Request extends ChannelOwner<channels.RequestChannel, channels.Requ
}

async response(): Promise<Response | null> {
return Response.fromNullable((await this._channel.response()).response);
return this._wrapApiCall('request.response', async () => {
return Response.fromNullable((await this._channel.response()).response);
});
}

frame(): Frame {
Expand Down Expand Up @@ -182,53 +184,59 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
}

async abort(errorCode?: string) {
await this._channel.abort({ errorCode });
return this._wrapApiCall('route.abort', async () => {
await this._channel.abort({ errorCode });
});
}

async fulfill(options: { status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
let body = '';
let isBase64 = false;
let length = 0;
if (options.path) {
const buffer = await util.promisify(fs.readFile)(options.path);
body = buffer.toString('base64');
isBase64 = true;
length = buffer.length;
} else if (isString(options.body)) {
body = options.body;
isBase64 = false;
length = Buffer.byteLength(body);
} else if (options.body) {
body = options.body.toString('base64');
isBase64 = true;
length = options.body.length;
}

const headers: Headers = {};
for (const header of Object.keys(options.headers || {}))
headers[header.toLowerCase()] = String(options.headers![header]);
if (options.contentType)
headers['content-type'] = String(options.contentType);
else if (options.path)
headers['content-type'] = mime.getType(options.path) || 'application/octet-stream';
if (length && !('content-length' in headers))
headers['content-length'] = String(length);

await this._channel.fulfill({
status: options.status || 200,
headers: headersObjectToArray(headers),
body,
isBase64
return this._wrapApiCall('route.fulfill', async () => {
let body = '';
let isBase64 = false;
let length = 0;
if (options.path) {
const buffer = await util.promisify(fs.readFile)(options.path);
body = buffer.toString('base64');
isBase64 = true;
length = buffer.length;
} else if (isString(options.body)) {
body = options.body;
isBase64 = false;
length = Buffer.byteLength(body);
} else if (options.body) {
body = options.body.toString('base64');
isBase64 = true;
length = options.body.length;
}

const headers: Headers = {};
for (const header of Object.keys(options.headers || {}))
headers[header.toLowerCase()] = String(options.headers![header]);
if (options.contentType)
headers['content-type'] = String(options.contentType);
else if (options.path)
headers['content-type'] = mime.getType(options.path) || 'application/octet-stream';
if (length && !('content-length' in headers))
headers['content-length'] = String(length);

await this._channel.fulfill({
status: options.status || 200,
headers: headersObjectToArray(headers),
body,
isBase64
});
});
}

async continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
await this._channel.continue({
url: options.url,
method: options.method,
headers: options.headers ? headersObjectToArray(options.headers) : undefined,
postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined,
return this._wrapApiCall('route.continue', async () => {
const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
await this._channel.continue({
url: options.url,
method: options.method,
headers: options.headers ? headersObjectToArray(options.headers) : undefined,
postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined,
});
});
}
}
Expand Down Expand Up @@ -295,7 +303,9 @@ export class Response extends ChannelOwner<channels.ResponseChannel, channels.Re
}

async body(): Promise<Buffer> {
return Buffer.from((await this._channel.body()).binary, 'base64');
return this._wrapApiCall('response.body', async () => {
return Buffer.from((await this._channel.body()).binary, 'base64');
});
}

async text(): Promise<string> {
Expand Down
90 changes: 58 additions & 32 deletions src/client/page.ts
Expand Up @@ -359,27 +359,43 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
}

async waitForRequest(urlOrPredicate: string | RegExp | ((r: Request) => boolean), options: { timeout?: number } = {}): Promise<Request> {
const predicate = (request: Request) => {
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
return urlMatches(request.url(), urlOrPredicate);
return urlOrPredicate(request);
};
return this.waitForEvent(Events.Page.Request, { predicate, timeout: options.timeout });
return this._wrapApiCall('page.waitForRequest', async () => {
const predicate = (request: Request) => {
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
return urlMatches(request.url(), urlOrPredicate);
return urlOrPredicate(request);
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for request "${trimmedUrl}"` : undefined;
return this._waitForEvent(Events.Page.Request, { predicate, timeout: options.timeout }, logLine);
});
}

async waitForResponse(urlOrPredicate: string | RegExp | ((r: Response) => boolean), options: { timeout?: number } = {}): Promise<Response> {
const predicate = (response: Response) => {
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
return urlMatches(response.url(), urlOrPredicate);
return urlOrPredicate(response);
};
return this.waitForEvent(Events.Page.Response, { predicate, timeout: options.timeout });
return this._wrapApiCall('page.waitForResponse', async () => {
const predicate = (response: Response) => {
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
return urlMatches(response.url(), urlOrPredicate);
return urlOrPredicate(response);
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for response "${trimmedUrl}"` : undefined;
return this._waitForEvent(Events.Page.Response, { predicate, timeout: options.timeout }, logLine);
});
}

async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
return this._wrapApiCall('page.waitForEvent', async () => {
return this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`);
});
}

private async _waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions, logLine?: string): Promise<any> {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = new Waiter();
if (logLine)
waiter.log(logLine);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.Page.Crash)
waiter.rejectOnEvent(this, Events.Page.Crash, new Error('Page crashed'));
Expand Down Expand Up @@ -584,7 +600,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
}

async waitForTimeout(timeout: number) {
await this._mainFrame.waitForTimeout(timeout);
return this._attributeToPage(() => this._mainFrame.waitForTimeout(timeout));
}

async waitForFunction<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg, options?: WaitForFunctionOptions): Promise<structs.SmartHandle<R>> {
Expand Down Expand Up @@ -624,25 +640,27 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
}

async _pdf(options: PDFOptions = {}): Promise<Buffer> {
const transportOptions: channels.PagePdfParams = { ...options } as channels.PagePdfParams;
if (transportOptions.margin)
transportOptions.margin = { ...transportOptions.margin };
if (typeof options.width === 'number')
transportOptions.width = options.width + 'px';
if (typeof options.height === 'number')
transportOptions.height = options.height + 'px';
for (const margin of ['top', 'right', 'bottom', 'left']) {
const index = margin as 'top' | 'right' | 'bottom' | 'left';
if (options.margin && typeof options.margin[index] === 'number')
transportOptions.margin![index] = transportOptions.margin![index] + 'px';
}
const result = await this._channel.pdf(transportOptions);
const buffer = Buffer.from(result.pdf, 'base64');
if (options.path) {
await mkdirAsync(path.dirname(options.path), { recursive: true });
await fsWriteFileAsync(options.path, buffer);
}
return buffer;
return this._wrapApiCall('page.pdf', async () => {
const transportOptions: channels.PagePdfParams = { ...options } as channels.PagePdfParams;
if (transportOptions.margin)
transportOptions.margin = { ...transportOptions.margin };
if (typeof options.width === 'number')
transportOptions.width = options.width + 'px';
if (typeof options.height === 'number')
transportOptions.height = options.height + 'px';
for (const margin of ['top', 'right', 'bottom', 'left']) {
const index = margin as 'top' | 'right' | 'bottom' | 'left';
if (options.margin && typeof options.margin[index] === 'number')
transportOptions.margin![index] = transportOptions.margin![index] + 'px';
}
const result = await this._channel.pdf(transportOptions);
const buffer = Buffer.from(result.pdf, 'base64');
if (options.path) {
await mkdirAsync(path.dirname(options.path), { recursive: true });
await fsWriteFileAsync(options.path, buffer);
}
return buffer;
});
}
}

Expand Down Expand Up @@ -674,3 +692,11 @@ export class BindingCall extends ChannelOwner<channels.BindingCallChannel, chann
}
}
}

function trimUrl(param: any): string | undefined {
if (isString(param)) {
if (param.length > 50)
param = param.substring(0, 50) + '\u2026';
return param;
}
}

0 comments on commit a9b7536

Please sign in to comment.