chore: simplify logging a bit (#2512)
This merges a few classes and stops inheriting from InnerLogger.
This commit is contained in:
parent
492a65f9d3
commit
80705ff5e9
|
|
@ -20,7 +20,7 @@ import { EventEmitter } from 'events';
|
|||
import { Download } from './download';
|
||||
import type { BrowserServer } from './server/browserServer';
|
||||
import { Events } from './events';
|
||||
import { InnerLogger, Log } from './logger';
|
||||
import { InnerLogger } from './logger';
|
||||
import { ProxySettings } from './types';
|
||||
|
||||
export type BrowserOptions = {
|
||||
|
|
@ -41,7 +41,7 @@ export interface Browser extends EventEmitter {
|
|||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class BrowserBase extends EventEmitter implements Browser, InnerLogger {
|
||||
export abstract class BrowserBase extends EventEmitter implements Browser {
|
||||
readonly _options: BrowserOptions;
|
||||
private _downloads = new Map<string, Download>();
|
||||
_defaultContext: BrowserContextBase | null = null;
|
||||
|
|
@ -93,13 +93,5 @@ export abstract class BrowserBase extends EventEmitter implements Browser, Inner
|
|||
if (this.isConnected())
|
||||
await new Promise(x => this.once(Events.Browser.Disconnected, x));
|
||||
}
|
||||
|
||||
_isLogEnabled(log: Log): boolean {
|
||||
return this._options.logger._isLogEnabled(log);
|
||||
}
|
||||
|
||||
_log(log: Log, message: string | Error, ...args: any[]) {
|
||||
return this._options.logger._log(log, message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { Events } from './events';
|
|||
import { ExtendedEventEmitter } from './extendedEventEmitter';
|
||||
import { Download } from './download';
|
||||
import { BrowserBase } from './browser';
|
||||
import { Log, InnerLogger, Logger, RootLogger } from './logger';
|
||||
import { InnerLogger, Logger } from './logger';
|
||||
import { FunctionWithSource } from './frames';
|
||||
import * as debugSupport from './debug/debugSupport';
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export type BrowserContextOptions = CommonContextOptions & {
|
|||
logger?: Logger,
|
||||
};
|
||||
|
||||
export interface BrowserContext extends InnerLogger {
|
||||
export interface BrowserContext {
|
||||
setDefaultNavigationTimeout(timeout: number): void;
|
||||
setDefaultTimeout(timeout: number): void;
|
||||
pages(): Page[];
|
||||
|
|
@ -87,13 +87,13 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements
|
|||
readonly _permissions = new Map<string, string[]>();
|
||||
readonly _downloads = new Set<Download>();
|
||||
readonly _browserBase: BrowserBase;
|
||||
private _logger: InnerLogger;
|
||||
readonly _logger: InnerLogger;
|
||||
|
||||
constructor(browserBase: BrowserBase, options: BrowserContextOptions) {
|
||||
super();
|
||||
this._browserBase = browserBase;
|
||||
this._options = options;
|
||||
this._logger = options.logger ? new RootLogger(options.logger) : browserBase;
|
||||
this._logger = options.logger ? new InnerLogger(options.logger) : browserBase._options.logger;
|
||||
this._closePromise = new Promise(fulfill => this._closePromiseFulfill = fulfill);
|
||||
}
|
||||
|
||||
|
|
@ -188,14 +188,6 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements
|
|||
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
|
||||
_isLogEnabled(log: Log): boolean {
|
||||
return this._logger._isLogEnabled(log);
|
||||
}
|
||||
|
||||
_log(log: Log, message: string | Error, ...args: any[]) {
|
||||
return this._logger._log(log, message, ...args);
|
||||
}
|
||||
|
||||
async _loadDefaultContext() {
|
||||
if (!this.pages().length)
|
||||
await this.waitForEvent('page');
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ class CRServiceWorker extends Worker {
|
|||
readonly _browserContext: CRBrowserContext;
|
||||
|
||||
constructor(browserContext: CRBrowserContext, session: CRSession, url: string) {
|
||||
super(browserContext, url);
|
||||
super(url);
|
||||
this._browserContext = browserContext;
|
||||
session.once('Runtime.executionContextCreated', event => {
|
||||
this._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||
|
|
|
|||
|
|
@ -62,15 +62,15 @@ export class CRConnection extends EventEmitter {
|
|||
const message: ProtocolRequest = { id, method, params };
|
||||
if (sessionId)
|
||||
message.sessionId = sessionId;
|
||||
if (this._logger._isLogEnabled(protocolLog))
|
||||
this._logger._log(protocolLog, 'SEND ► ' + rewriteInjectedScriptEvaluationLog(message));
|
||||
if (this._logger.isLogEnabled(protocolLog))
|
||||
this._logger.log(protocolLog, 'SEND ► ' + rewriteInjectedScriptEvaluationLog(message));
|
||||
this._transport.send(message);
|
||||
return id;
|
||||
}
|
||||
|
||||
async _onMessage(message: ProtocolResponse) {
|
||||
if (this._logger._isLogEnabled(protocolLog))
|
||||
this._logger._log(protocolLog, '◀ RECV ' + JSON.stringify(message));
|
||||
if (this._logger.isLogEnabled(protocolLog))
|
||||
this._logger.log(protocolLog, '◀ RECV ' + JSON.stringify(message));
|
||||
if (message.id === kBrowserCloseMessageId)
|
||||
return;
|
||||
if (message.method === 'Target.attachedToTarget') {
|
||||
|
|
@ -168,7 +168,7 @@ export class CRSession extends EventEmitter {
|
|||
_sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
|
||||
return this.send(method, params).catch(error => {
|
||||
if (this._connection)
|
||||
this._connection._logger._log(errorLog, error, []);
|
||||
this._connection._logger.log(errorLog, error, []);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -566,7 +566,7 @@ class FrameSession {
|
|||
}
|
||||
|
||||
const url = event.targetInfo.url;
|
||||
const worker = new Worker(this._page, url);
|
||||
const worker = new Worker(url);
|
||||
this._page._addWorker(event.sessionId, worker);
|
||||
session.once('Runtime.executionContextCreated', async event => {
|
||||
worker._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||
|
|
|
|||
42
src/dom.ts
42
src/dom.ts
|
|
@ -28,7 +28,7 @@ import { Page } from './page';
|
|||
import { selectors } from './selectors';
|
||||
import * as types from './types';
|
||||
import { NotConnectedError } from './errors';
|
||||
import { logError, apiLog } from './logger';
|
||||
import { apiLog } from './logger';
|
||||
import { Progress, runAbortableTask } from './progress';
|
||||
|
||||
export type PointerActionOptions = {
|
||||
|
|
@ -45,7 +45,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||
private _injectedPromise?: Promise<js.JSHandle>;
|
||||
|
||||
constructor(delegate: js.ExecutionContextDelegate, frame: frames.Frame) {
|
||||
super(delegate, frame._page);
|
||||
super(delegate);
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
private async _offsetPoint(offset: types.Point): Promise<types.Point | 'invisible'> {
|
||||
const [box, border] = await Promise.all([
|
||||
this.boundingBox(),
|
||||
this._evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch(logError(this._context._logger)),
|
||||
this._evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch(e => {}),
|
||||
]);
|
||||
if (!box || !border)
|
||||
return 'invisible';
|
||||
|
|
@ -317,7 +317,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
hover(options: PointerActionOptions & types.PointerActionWaitOptions = {}): Promise<void> {
|
||||
return runAbortableTask(progress => this._hover(progress, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._hover(progress, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
_hover(progress: Progress, options: PointerActionOptions & types.PointerActionWaitOptions): Promise<void> {
|
||||
|
|
@ -325,7 +325,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
click(options: ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}): Promise<void> {
|
||||
return runAbortableTask(progress => this._click(progress, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._click(progress, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
_click(progress: Progress, options: ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
|
||||
|
|
@ -333,7 +333,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
dblclick(options: MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}): Promise<void> {
|
||||
return runAbortableTask(progress => this._dblclick(progress, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._dblclick(progress, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
_dblclick(progress: Progress, options: MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
|
||||
|
|
@ -341,7 +341,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
async selectOption(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
|
||||
return runAbortableTask(progress => this._selectOption(progress, values, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._selectOption(progress, values, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _selectOption(progress: Progress, values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options: types.NavigatingActionWaitOptions): Promise<string[]> {
|
||||
|
|
@ -368,7 +368,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
async fill(value: string, options: types.NavigatingActionWaitOptions = {}): Promise<void> {
|
||||
return runAbortableTask(progress => this._fill(progress, value, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._fill(progress, value, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions): Promise<void> {
|
||||
|
|
@ -391,13 +391,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
async selectText(): Promise<void> {
|
||||
this._page._log(apiLog, `elementHandle.selectText()`);
|
||||
const injectedResult = await this._evaluateInUtility(([injected, node]) => injected.selectText(node), {});
|
||||
handleInjectedResult(injectedResult);
|
||||
return runAbortableTask(async progress => {
|
||||
const injectedResult = await this._evaluateInUtility(([injected, node]) => injected.selectText(node), {});
|
||||
handleInjectedResult(injectedResult);
|
||||
}, this._page._logger, 0);
|
||||
}
|
||||
|
||||
async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}) {
|
||||
return runAbortableTask(async progress => this._setInputFiles(progress, files, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(async progress => this._setInputFiles(progress, files, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _setInputFiles(progress: Progress, files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions) {
|
||||
|
|
@ -435,31 +436,34 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
async focus() {
|
||||
this._page._log(apiLog, `elementHandle.focus()`);
|
||||
return runAbortableTask(progress => this._focus(progress), this._page._logger, 0);
|
||||
}
|
||||
|
||||
async _focus(progress: Progress) {
|
||||
const injectedResult = await this._evaluateInUtility(([injected, node]) => injected.focusNode(node), {});
|
||||
handleInjectedResult(injectedResult);
|
||||
}
|
||||
|
||||
async type(text: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) {
|
||||
return runAbortableTask(progress => this._type(progress, text, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._type(progress, text, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _type(progress: Progress, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions) {
|
||||
progress.log(apiLog, `elementHandle.type("${text}")`);
|
||||
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
|
||||
await this.focus();
|
||||
await this._focus(progress);
|
||||
await this._page.keyboard.type(text, options);
|
||||
}, 'input');
|
||||
}
|
||||
|
||||
async press(key: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) {
|
||||
return runAbortableTask(progress => this._press(progress, key, options), this._page, this._page._timeoutSettings.timeout(options));
|
||||
return runAbortableTask(progress => this._press(progress, key, options), this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _press(progress: Progress, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions) {
|
||||
progress.log(apiLog, `elementHandle.press("${key}")`);
|
||||
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
|
||||
await this.focus();
|
||||
await this._focus(progress);
|
||||
await this._page.keyboard.press(key, options);
|
||||
}, 'input');
|
||||
}
|
||||
|
|
@ -468,14 +472,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
return runAbortableTask(async progress => {
|
||||
progress.log(apiLog, `elementHandle.check()`);
|
||||
await this._setChecked(progress, true, options);
|
||||
}, this._page, this._page._timeoutSettings.timeout(options));
|
||||
}, this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async uncheck(options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
|
||||
return runAbortableTask(async progress => {
|
||||
progress.log(apiLog, `elementHandle.uncheck()`);
|
||||
await this._setChecked(progress, false, options);
|
||||
}, this._page, this._page._timeoutSettings.timeout(options));
|
||||
}, this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async _setChecked(progress: Progress, state: boolean, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
|
||||
|
|
|
|||
|
|
@ -79,14 +79,14 @@ export class FFConnection extends EventEmitter {
|
|||
}
|
||||
|
||||
_rawSend(message: ProtocolRequest) {
|
||||
if (this._logger._isLogEnabled(protocolLog))
|
||||
this._logger._log(protocolLog, 'SEND ► ' + rewriteInjectedScriptEvaluationLog(message));
|
||||
if (this._logger.isLogEnabled(protocolLog))
|
||||
this._logger.log(protocolLog, 'SEND ► ' + rewriteInjectedScriptEvaluationLog(message));
|
||||
this._transport.send(message);
|
||||
}
|
||||
|
||||
async _onMessage(message: ProtocolResponse) {
|
||||
if (this._logger._isLogEnabled(protocolLog))
|
||||
this._logger._log(protocolLog, '◀ RECV ' + JSON.stringify(message));
|
||||
if (this._logger.isLogEnabled(protocolLog))
|
||||
this._logger.log(protocolLog, '◀ RECV ' + JSON.stringify(message));
|
||||
if (message.id === kBrowserCloseMessageId)
|
||||
return;
|
||||
if (message.sessionId) {
|
||||
|
|
@ -187,7 +187,7 @@ export class FFSession extends EventEmitter {
|
|||
|
||||
sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
|
||||
return this.send(method, params).catch(error => {
|
||||
this._connection._logger._log(errorLog, error, []);
|
||||
this._connection._logger.log(errorLog, error, []);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ export class FFPage implements PageDelegate {
|
|||
|
||||
async _onWorkerCreated(event: Protocol.Page.workerCreatedPayload) {
|
||||
const workerId = event.workerId;
|
||||
const worker = new Worker(this._page, event.url);
|
||||
const worker = new Worker(event.url);
|
||||
const workerSession = new FFSession(this._session._connection, 'worker', workerId, (message: any) => {
|
||||
this._session.send('Page.sendMessageToWorker', {
|
||||
frameId: event.frameId,
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
async goto(url: string, options: GotoOptions = {}): Promise<network.Response | null> {
|
||||
const progressController = new ProgressController(this._page, this._page._timeoutSettings.navigationTimeout(options));
|
||||
const progressController = new ProgressController(this._page._logger, this._page._timeoutSettings.navigationTimeout(options));
|
||||
abortProgressOnFrameDetach(progressController, this);
|
||||
return progressController.run(async progress => {
|
||||
progress.log(apiLog, `navigating to "${url}", waiting until "${options.waitUntil || 'load'}"`);
|
||||
|
|
@ -377,7 +377,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
async waitForNavigation(options: types.WaitForNavigationOptions = {}): Promise<network.Response | null> {
|
||||
const progressController = new ProgressController(this._page, this._page._timeoutSettings.navigationTimeout(options));
|
||||
const progressController = new ProgressController(this._page._logger, this._page._timeoutSettings.navigationTimeout(options));
|
||||
abortProgressOnFrameDetach(progressController, this);
|
||||
return progressController.run(async progress => {
|
||||
const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
|
||||
|
|
@ -396,7 +396,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
async waitForLoadState(state: types.LifecycleEvent = 'load', options: types.TimeoutOptions = {}): Promise<void> {
|
||||
const progressController = new ProgressController(this._page, this._page._timeoutSettings.navigationTimeout(options));
|
||||
const progressController = new ProgressController(this._page._logger, this._page._timeoutSettings.navigationTimeout(options));
|
||||
abortProgressOnFrameDetach(progressController, this);
|
||||
return progressController.run(progress => this._waitForLoadState(progress, state));
|
||||
}
|
||||
|
|
@ -469,7 +469,7 @@ export class Frame {
|
|||
return adopted;
|
||||
}
|
||||
return handle;
|
||||
}, this._page, this._page._timeoutSettings.timeout(options));
|
||||
}, this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async dispatchEvent(selector: string, type: string, eventInit?: Object, options: types.TimeoutOptions = {}): Promise<void> {
|
||||
|
|
@ -478,7 +478,7 @@ export class Frame {
|
|||
progress.log(apiLog, `Dispatching "${type}" event on selector "${selector}"...`);
|
||||
const result = await this._scheduleRerunnableTask(progress, 'main', task);
|
||||
result.dispose();
|
||||
}, this._page, this._page._timeoutSettings.timeout(options));
|
||||
}, this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async $eval<R, Arg>(selector: string, pageFunction: types.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
|
||||
|
|
@ -520,7 +520,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
async setContent(html: string, options: types.NavigateOptions = {}): Promise<void> {
|
||||
const progressController = new ProgressController(this._page, this._page._timeoutSettings.navigationTimeout(options));
|
||||
const progressController = new ProgressController(this._page._logger, this._page._timeoutSettings.navigationTimeout(options));
|
||||
abortProgressOnFrameDetach(progressController, this);
|
||||
return progressController.run(async progress => {
|
||||
const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;
|
||||
|
|
@ -726,7 +726,7 @@ export class Frame {
|
|||
}
|
||||
}
|
||||
return undefined as any;
|
||||
}, this._page, this._page._timeoutSettings.timeout(options));
|
||||
}, this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async click(selector: string, options: dom.ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
|
||||
|
|
@ -742,7 +742,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
async focus(selector: string, options: types.TimeoutOptions = {}) {
|
||||
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle.focus());
|
||||
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._focus(progress));
|
||||
}
|
||||
|
||||
async textContent(selector: string, options: types.TimeoutOptions = {}): Promise<null|string> {
|
||||
|
|
@ -814,7 +814,7 @@ export class Frame {
|
|||
};
|
||||
return runAbortableTask(
|
||||
progress => this._scheduleRerunnableTask(progress, 'main', task),
|
||||
this._page, this._page._timeoutSettings.timeout(options));
|
||||
this._page._logger, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async title(): Promise<string> {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export function waitForTimeoutWasUsed(page: Page) {
|
|||
if (waitForTimeoutWasUsedReported)
|
||||
return;
|
||||
waitForTimeoutWasUsedReported = true;
|
||||
page._log(hintsLog, `WARNING: page.waitForTimeout(timeout) should only be used for debugging.
|
||||
page._logger.log(hintsLog, `WARNING: page.waitForTimeout(timeout) should only be used for debugging.
|
||||
Tests using the timer in production are going to be flaky.
|
||||
Use signals such as network events, selectors becoming visible, etc. instead.`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import * as types from './types';
|
||||
import * as dom from './dom';
|
||||
import * as utilityScriptSource from './generated/utilityScriptSource';
|
||||
import { InnerLogger } from './logger';
|
||||
import * as debugSupport from './debug/debugSupport';
|
||||
import { serializeAsCallArgument } from './utilityScriptSerializers';
|
||||
import { helper } from './helper';
|
||||
|
|
@ -38,12 +37,10 @@ export interface ExecutionContextDelegate {
|
|||
|
||||
export class ExecutionContext {
|
||||
readonly _delegate: ExecutionContextDelegate;
|
||||
readonly _logger: InnerLogger;
|
||||
private _utilityScriptPromise: Promise<JSHandle> | undefined;
|
||||
|
||||
constructor(delegate: ExecutionContextDelegate, logger: InnerLogger) {
|
||||
constructor(delegate: ExecutionContextDelegate) {
|
||||
this._delegate = delegate;
|
||||
this._logger = logger;
|
||||
}
|
||||
|
||||
adoptIfNeeded(handle: JSHandle): Promise<JSHandle> | null {
|
||||
|
|
|
|||
|
|
@ -29,34 +29,35 @@ export interface Logger {
|
|||
log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }): void;
|
||||
}
|
||||
|
||||
export interface InnerLogger {
|
||||
_isLogEnabled(log: Log): boolean;
|
||||
_log(log: Log, message: string | Error, ...args: any[]): void;
|
||||
}
|
||||
|
||||
export const errorLog: Log = { name: 'generic', severity: 'error' };
|
||||
export const apiLog: Log = { name: 'api', color: 'cyan' };
|
||||
|
||||
export function logError(logger: InnerLogger): (error: Error) => void {
|
||||
return error => logger._log(errorLog, error, []);
|
||||
return error => logger.log(errorLog, error, []);
|
||||
}
|
||||
|
||||
export class RootLogger implements InnerLogger {
|
||||
private _logger = new MultiplexingLogger();
|
||||
export class InnerLogger {
|
||||
private _userSink: Logger | undefined;
|
||||
private _debugSink: DebugLogger;
|
||||
|
||||
constructor(userSink: Logger | undefined) {
|
||||
if (userSink)
|
||||
this._logger.add('user', userSink);
|
||||
this._logger.add('debug', new DebugLogger());
|
||||
this._userSink = userSink;
|
||||
this._debugSink = new DebugLogger();
|
||||
}
|
||||
|
||||
_isLogEnabled(log: Log): boolean {
|
||||
return this._logger.isEnabled(log.name, log.severity || 'info');
|
||||
isLogEnabled(log: Log): boolean {
|
||||
const severity = log.severity || 'info';
|
||||
if (this._userSink && this._userSink.isEnabled(log.name, severity))
|
||||
return true;
|
||||
return this._debugSink.isEnabled(log.name, severity);
|
||||
}
|
||||
|
||||
_log(log: Log, message: string | Error, ...args: any[]) {
|
||||
if (this._logger.isEnabled(log.name, log.severity || 'info'))
|
||||
this._logger.log(log.name, log.severity || 'info', message, args, log.color ? { color: log.color } : {});
|
||||
log(log: Log, message: string | Error, ...args: any[]) {
|
||||
const severity = log.severity || 'info';
|
||||
const hints = log.color ? { color: log.color } : {};
|
||||
if (this._userSink && this._userSink.isEnabled(log.name, severity))
|
||||
this._userSink.log(log.name, severity, message, args, hints);
|
||||
this._debugSink.log(log.name, severity, message, args, hints);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,36 +71,7 @@ const colorMap = new Map<string, number>([
|
|||
['reset', 0],
|
||||
]);
|
||||
|
||||
class MultiplexingLogger implements Logger {
|
||||
private _loggers = new Map<string, Logger>();
|
||||
|
||||
add(id: string, logger: Logger) {
|
||||
this._loggers.set(id, logger);
|
||||
}
|
||||
|
||||
get(id: string): Logger | undefined {
|
||||
return this._loggers.get(id);
|
||||
}
|
||||
|
||||
remove(id: string) {
|
||||
this._loggers.delete(id);
|
||||
}
|
||||
|
||||
isEnabled(name: string, severity: LoggerSeverity): boolean {
|
||||
for (const logger of this._loggers.values()) {
|
||||
if (logger.isEnabled(name, severity))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }) {
|
||||
for (const logger of this._loggers.values())
|
||||
logger.log(name, severity, message, args, hints);
|
||||
}
|
||||
}
|
||||
|
||||
class DebugLogger implements Logger {
|
||||
class DebugLogger {
|
||||
private _debuggers = new Map<string, debug.IDebugger>();
|
||||
|
||||
isEnabled(name: string, severity: LoggerSeverity): boolean {
|
||||
|
|
|
|||
24
src/page.ts
24
src/page.ts
|
|
@ -31,7 +31,7 @@ import * as accessibility from './accessibility';
|
|||
import { ExtendedEventEmitter } from './extendedEventEmitter';
|
||||
import { EventEmitter } from 'events';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { logError, InnerLogger, Log } from './logger';
|
||||
import { logError, InnerLogger } from './logger';
|
||||
|
||||
export interface PageDelegate {
|
||||
readonly rawMouse: input.RawMouse;
|
||||
|
|
@ -88,7 +88,7 @@ type PageState = {
|
|||
extraHTTPHeaders: network.Headers | null;
|
||||
};
|
||||
|
||||
export class Page extends ExtendedEventEmitter implements InnerLogger {
|
||||
export class Page extends ExtendedEventEmitter {
|
||||
private _closed = false;
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
|
|
@ -100,6 +100,7 @@ export class Page extends ExtendedEventEmitter implements InnerLogger {
|
|||
readonly mouse: input.Mouse;
|
||||
readonly _timeoutSettings: TimeoutSettings;
|
||||
readonly _delegate: PageDelegate;
|
||||
readonly _logger: InnerLogger;
|
||||
readonly _state: PageState;
|
||||
readonly _pageBindings = new Map<string, PageBinding>();
|
||||
readonly _screenshotter: Screenshotter;
|
||||
|
|
@ -114,6 +115,7 @@ export class Page extends ExtendedEventEmitter implements InnerLogger {
|
|||
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
|
||||
super();
|
||||
this._delegate = delegate;
|
||||
this._logger = browserContext._logger;
|
||||
this._closedCallback = () => {};
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
this._disconnectedCallback = () => {};
|
||||
|
|
@ -141,7 +143,7 @@ export class Page extends ExtendedEventEmitter implements InnerLogger {
|
|||
}
|
||||
|
||||
protected _getLogger(): InnerLogger {
|
||||
return this;
|
||||
return this._logger;
|
||||
}
|
||||
|
||||
protected _getTimeoutSettings(): TimeoutSettings {
|
||||
|
|
@ -552,33 +554,23 @@ export class Page extends ExtendedEventEmitter implements InnerLogger {
|
|||
this._delegate.setFileChooserIntercepted(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
_isLogEnabled(log: Log): boolean {
|
||||
return this._browserContext._isLogEnabled(log);
|
||||
}
|
||||
|
||||
_log(log: Log, message: string | Error, ...args: any[]) {
|
||||
return this._browserContext._log(log, message, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
export class Worker extends EventEmitter {
|
||||
private _logger: InnerLogger;
|
||||
private _url: string;
|
||||
private _executionContextPromise: Promise<js.ExecutionContext>;
|
||||
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
||||
_existingExecutionContext: js.ExecutionContext | null = null;
|
||||
|
||||
constructor(logger: InnerLogger, url: string) {
|
||||
constructor(url: string) {
|
||||
super();
|
||||
this._logger = logger;
|
||||
this._url = url;
|
||||
this._executionContextCallback = () => {};
|
||||
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
||||
}
|
||||
|
||||
_createExecutionContext(delegate: js.ExecutionContextDelegate) {
|
||||
this._existingExecutionContext = new js.ExecutionContext(delegate, this._logger);
|
||||
this._existingExecutionContext = new js.ExecutionContext(delegate);
|
||||
this._executionContextCallback(this._existingExecutionContext);
|
||||
}
|
||||
|
||||
|
|
@ -627,7 +619,7 @@ export class PageBinding {
|
|||
else
|
||||
expression = helper.evaluationString(deliverErrorValue, name, seq, error);
|
||||
}
|
||||
context.evaluateInternal(expression).catch(logError(page));
|
||||
context.evaluateInternal(expression).catch(logError(page._logger));
|
||||
|
||||
function deliverResult(name: string, seq: number, result: any) {
|
||||
(window as any)[name]['callbacks'].get(seq).resolve(result);
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ export class ProgressController {
|
|||
log: (log: Log, message: string | Error) => {
|
||||
if (this._state === 'running') {
|
||||
this._logRecording.push(message.toString());
|
||||
this._logger._log(log, ' ' + message);
|
||||
this._logger.log(log, ' ' + message);
|
||||
} else {
|
||||
this._logger._log(log, message);
|
||||
this._logger.log(log, message);
|
||||
}
|
||||
},
|
||||
};
|
||||
this._logger._log(apiLog, `=> ${this._apiName} started`);
|
||||
this._logger.log(apiLog, `=> ${this._apiName} started`);
|
||||
|
||||
const timeoutError = new TimeoutError(`Timeout ${this._timeout}ms exceeded during ${this._apiName}.`);
|
||||
const timer = setTimeout(() => this._forceAbort(timeoutError), progress.timeUntilDeadline());
|
||||
|
|
@ -100,7 +100,7 @@ export class ProgressController {
|
|||
clearTimeout(timer);
|
||||
this._state = 'finished';
|
||||
this._logRecording = [];
|
||||
this._logger._log(apiLog, `<= ${this._apiName} succeeded`);
|
||||
this._logger.log(apiLog, `<= ${this._apiName} succeeded`);
|
||||
return result;
|
||||
} catch (e) {
|
||||
this._aborted();
|
||||
|
|
@ -108,7 +108,7 @@ export class ProgressController {
|
|||
clearTimeout(timer);
|
||||
this._state = 'aborted';
|
||||
this._logRecording = [];
|
||||
this._logger._log(apiLog, `<= ${this._apiName} failed`);
|
||||
this._logger.log(apiLog, `<= ${this._apiName} failed`);
|
||||
await Promise.all(this._cleanups.splice(0).map(cleanup => runCleanup(cleanup)));
|
||||
throw e;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import * as util from 'util';
|
|||
import { BrowserContext, PersistentContextOptions, verifyProxySettings, validateBrowserContextOptions } from '../browserContext';
|
||||
import { BrowserServer, WebSocketWrapper } from './browserServer';
|
||||
import * as browserPaths from '../install/browserPaths';
|
||||
import { Logger, RootLogger, InnerLogger } from '../logger';
|
||||
import { Logger, InnerLogger } from '../logger';
|
||||
import { ConnectionTransport, WebSocketTransport } from '../transport';
|
||||
import { BrowserBase, BrowserOptions, Browser } from '../browser';
|
||||
import { assert } from '../helper';
|
||||
|
|
@ -108,7 +108,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
async launch(options: LaunchOptions = {}): Promise<Browser> {
|
||||
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.');
|
||||
const logger = new RootLogger(options.logger);
|
||||
const logger = new InnerLogger(options.logger);
|
||||
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, logger, undefined), logger, TimeoutSettings.timeout(options));
|
||||
return browser;
|
||||
}
|
||||
|
|
@ -116,12 +116,12 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
async launchPersistentContext(userDataDir: string, options: LaunchOptions & PersistentContextOptions = {}): Promise<BrowserContext> {
|
||||
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
||||
const persistent = validateBrowserContextOptions(options);
|
||||
const logger = new RootLogger(options.logger);
|
||||
const logger = new InnerLogger(options.logger);
|
||||
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, logger, persistent, userDataDir), logger, TimeoutSettings.timeout(options));
|
||||
return browser._defaultContext!;
|
||||
}
|
||||
|
||||
async _innerLaunch(progress: Progress, options: LaunchOptions, logger: RootLogger, persistent: PersistentContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
||||
async _innerLaunch(progress: Progress, options: LaunchOptions, logger: InnerLogger, persistent: PersistentContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
||||
options.proxy = options.proxy ? verifyProxySettings(options.proxy) : undefined;
|
||||
const { browserServer, downloadsPath, transport } = await this._launchServer(progress, options, !!persistent, logger, userDataDir);
|
||||
if ((options as any).__testHookBeforeCreateBrowser)
|
||||
|
|
@ -147,7 +147,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
|
||||
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launchServer`. Use `browserType.launchPersistentContext` instead');
|
||||
const { port = 0 } = options;
|
||||
const logger = new RootLogger(options.logger);
|
||||
const logger = new InnerLogger(options.logger);
|
||||
return runAbortableTask(async progress => {
|
||||
const { browserServer, transport } = await this._launchServer(progress, options, false, logger);
|
||||
browserServer._webSocketWrapper = this._wrapTransportWithWebSocket(transport, logger, port);
|
||||
|
|
@ -156,7 +156,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
}
|
||||
|
||||
async connect(options: ConnectOptions): Promise<Browser> {
|
||||
const logger = new RootLogger(options.logger);
|
||||
const logger = new InnerLogger(options.logger);
|
||||
return runAbortableTask(async progress => {
|
||||
const transport = await WebSocketTransport.connect(progress, options.wsEndpoint);
|
||||
progress.cleanupWhenAborted(() => transport.closeAndWait());
|
||||
|
|
@ -167,7 +167,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
}, logger, TimeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
private async _launchServer(progress: Progress, options: LaunchServerOptions, isPersistent: boolean, logger: RootLogger, userDataDir?: string): Promise<{ browserServer: BrowserServer, downloadsPath: string, transport: ConnectionTransport }> {
|
||||
private async _launchServer(progress: Progress, options: LaunchServerOptions, isPersistent: boolean, logger: InnerLogger, userDataDir?: string): Promise<{ browserServer: BrowserServer, downloadsPath: string, transport: ConnectionTransport }> {
|
||||
const {
|
||||
ignoreDefaultArgs = false,
|
||||
args = [],
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { CRExecutionContext } from '../chromium/crExecutionContext';
|
|||
import { Events } from '../events';
|
||||
import { ExtendedEventEmitter } from '../extendedEventEmitter';
|
||||
import * as js from '../javascript';
|
||||
import { InnerLogger, Logger, RootLogger } from '../logger';
|
||||
import { InnerLogger, Logger } from '../logger';
|
||||
import { Page } from '../page';
|
||||
import { TimeoutSettings } from '../timeoutSettings';
|
||||
import { WebSocketTransport } from '../transport';
|
||||
|
|
@ -130,7 +130,7 @@ export class ElectronApplication extends ExtendedEventEmitter {
|
|||
|
||||
async _init() {
|
||||
this._nodeSession.once('Runtime.executionContextCreated', event => {
|
||||
this._nodeExecutionContext = new js.ExecutionContext(new CRExecutionContext(this._nodeSession, event.context), this._logger);
|
||||
this._nodeExecutionContext = new js.ExecutionContext(new CRExecutionContext(this._nodeSession, event.context));
|
||||
});
|
||||
await this._nodeSession.send('Runtime.enable', {}).catch(e => {});
|
||||
this._nodeElectronHandle = await js.evaluate(this._nodeExecutionContext!, false /* returnByValue */, () => {
|
||||
|
|
@ -171,7 +171,7 @@ export class Electron {
|
|||
handleSIGTERM = true,
|
||||
handleSIGHUP = true,
|
||||
} = options;
|
||||
const logger = new RootLogger(options.logger);
|
||||
const logger = new InnerLogger(options.logger);
|
||||
return runAbortableTask(async progress => {
|
||||
let app: ElectronApplication | undefined = undefined;
|
||||
const electronArguments = ['--inspect=0', '--remote-debugging-port=0', '--require', path.join(__dirname, 'electronLoader.js'), ...args];
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ export class WKConnection {
|
|||
}
|
||||
|
||||
rawSend(message: ProtocolRequest) {
|
||||
if (this._logger._isLogEnabled(protocolLog))
|
||||
this._logger._log(protocolLog, 'SEND ► ' + rewriteInjectedScriptEvaluationLog(message));
|
||||
if (this._logger.isLogEnabled(protocolLog))
|
||||
this._logger.log(protocolLog, 'SEND ► ' + rewriteInjectedScriptEvaluationLog(message));
|
||||
this._transport.send(message);
|
||||
}
|
||||
|
||||
private _dispatchMessage(message: ProtocolResponse) {
|
||||
if (this._logger._isLogEnabled(protocolLog))
|
||||
this._logger._log(protocolLog, '◀ RECV ' + JSON.stringify(message));
|
||||
if (this._logger.isLogEnabled(protocolLog))
|
||||
this._logger.log(protocolLog, '◀ RECV ' + JSON.stringify(message));
|
||||
if (message.id === kBrowserCloseMessageId)
|
||||
return;
|
||||
if (message.pageProxyId) {
|
||||
|
|
@ -139,7 +139,7 @@ export class WKSession extends EventEmitter {
|
|||
|
||||
sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
|
||||
return this.send(method, params).catch(error => {
|
||||
this.connection._logger._log(errorLog, error, []);
|
||||
this.connection._logger.log(errorLog, error, []);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import { selectors } from '../selectors';
|
|||
import * as jpeg from 'jpeg-js';
|
||||
import * as png from 'pngjs';
|
||||
import { NotConnectedError } from '../errors';
|
||||
import { logError } from '../logger';
|
||||
import { ConsoleMessageLocation } from '../console';
|
||||
import { JSHandle } from '../javascript';
|
||||
|
||||
|
|
@ -364,7 +363,7 @@ export class WKPage implements PageDelegate {
|
|||
// as well to always be in sync with the backend.
|
||||
if (this._provisionalPage)
|
||||
sessions.push(this._provisionalPage._session);
|
||||
await Promise.all(sessions.map(session => callback(session).catch(logError(this._page))));
|
||||
await Promise.all(sessions.map(session => callback(session).catch(e => {})));
|
||||
}
|
||||
|
||||
private _onFrameScheduledNavigation(frameId: string) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export class WKWorkers {
|
|||
this.clear();
|
||||
this._sessionListeners = [
|
||||
helper.addEventListener(session, 'Worker.workerCreated', (event: Protocol.Worker.workerCreatedPayload) => {
|
||||
const worker = new Worker(this._page, event.url);
|
||||
const worker = new Worker(event.url);
|
||||
const workerSession = new WKSession(session.connection, event.workerId, 'Most likely the worker has been closed.', (message: any) => {
|
||||
session.send('Worker.sendMessageToWorker', {
|
||||
workerId: event.workerId,
|
||||
|
|
|
|||
|
|
@ -426,3 +426,13 @@ describe('ElementHandle.selectOption', function() {
|
|||
expect(await page.evaluate(() => result.onChange)).toEqual(['blue']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.focus', function() {
|
||||
it('should focus a button', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
expect(await button.evaluate(button => document.activeElement === button)).toBe(false);
|
||||
await button.focus();
|
||||
expect(await button.evaluate(button => document.activeElement === button)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue