chrome: remove state from isomorphic utils (#34795)
This commit is contained in:
parent
be95a08c4d
commit
8b28e637c8
|
@ -1,22 +1,27 @@
|
||||||
[browserServerImpl.ts]
|
[browserServerImpl.ts]
|
||||||
**
|
remote/
|
||||||
|
server/
|
||||||
|
server/utils
|
||||||
|
utils/isomorphic/
|
||||||
|
utilsBundle.ts
|
||||||
|
|
||||||
[androidServerImpl.ts]
|
[androidServerImpl.ts]
|
||||||
**
|
remote/
|
||||||
|
server/
|
||||||
|
server/utils
|
||||||
|
utils/isomorphic/
|
||||||
|
utilsBundle.ts
|
||||||
|
|
||||||
[inProcessFactory.ts]
|
[inProcessFactory.ts]
|
||||||
**
|
**
|
||||||
|
|
||||||
[inprocess.ts]
|
[inprocess.ts]
|
||||||
common/
|
|
||||||
utils/
|
utils/
|
||||||
server/utils
|
server/utils
|
||||||
|
|
||||||
[outofprocess.ts]
|
[outofprocess.ts]
|
||||||
client/
|
client/
|
||||||
common/
|
|
||||||
protocol/
|
protocol/
|
||||||
utils/
|
utils/
|
||||||
utils/isomorphic
|
utils/isomorphic
|
||||||
server/utils
|
server/utils
|
||||||
common/
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { envObjectToArray } from './client/clientHelper';
|
|
||||||
import { SocksProxy } from './server/utils/socksProxy';
|
import { SocksProxy } from './server/utils/socksProxy';
|
||||||
import { PlaywrightServer } from './remote/playwrightServer';
|
import { PlaywrightServer } from './remote/playwrightServer';
|
||||||
import { helper } from './server/helper';
|
import { helper } from './server/helper';
|
||||||
|
@ -25,7 +24,7 @@ import { rewriteErrorMessage } from './utils/isomorphic/stackTrace';
|
||||||
import { ws } from './utilsBundle';
|
import { ws } from './utilsBundle';
|
||||||
|
|
||||||
import type { BrowserServer, BrowserServerLauncher } from './client/browserType';
|
import type { BrowserServer, BrowserServerLauncher } from './client/browserType';
|
||||||
import type { LaunchServerOptions, Logger } from './client/types';
|
import type { LaunchServerOptions, Logger, Env } from './client/types';
|
||||||
import type { ProtocolLogger } from './server/types';
|
import type { ProtocolLogger } from './server/types';
|
||||||
import type { WebSocketEventEmitter } from './utilsBundle';
|
import type { WebSocketEventEmitter } from './utilsBundle';
|
||||||
|
|
||||||
|
@ -85,3 +84,12 @@ function toProtocolLogger(logger: Logger | undefined): ProtocolLogger | undefine
|
||||||
logger.log('protocol', 'verbose', (direction === 'send' ? 'SEND ► ' : '◀ RECV ') + JSON.stringify(message), [], {});
|
logger.log('protocol', 'verbose', (direction === 'send' ? 'SEND ► ' : '◀ RECV ') + JSON.stringify(message), [], {});
|
||||||
} : undefined;
|
} : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function envObjectToArray(env: Env): { name: string, value: string }[] {
|
||||||
|
const result: { name: string, value: string }[] = [];
|
||||||
|
for (const name in env) {
|
||||||
|
if (!Object.is(env[name], undefined))
|
||||||
|
result.push({ name, value: String(env[name]) });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
[*]
|
[*]
|
||||||
../../
|
../../
|
||||||
../client
|
|
||||||
../common
|
|
||||||
../debug/injected
|
../debug/injected
|
||||||
../generated/
|
../generated/
|
||||||
../server/
|
../server/
|
||||||
|
|
|
@ -22,7 +22,6 @@ import * as path from 'path';
|
||||||
|
|
||||||
import * as playwright from '../..';
|
import * as playwright from '../..';
|
||||||
import { launchBrowserServer, printApiJson, runDriver, runServer } from './driver';
|
import { launchBrowserServer, printApiJson, runDriver, runServer } from './driver';
|
||||||
import { isTargetClosedError } from '../client/errors';
|
|
||||||
import { registry, writeDockerVersion } from '../server';
|
import { registry, writeDockerVersion } from '../server';
|
||||||
import { gracefullyProcessExitDoNotHang } from '../utils';
|
import { gracefullyProcessExitDoNotHang } from '../utils';
|
||||||
import { runTraceInBrowser, runTraceViewerApp } from '../server/trace/viewer/traceViewer';
|
import { runTraceInBrowser, runTraceViewerApp } from '../server/trace/viewer/traceViewer';
|
||||||
|
@ -553,7 +552,7 @@ async function openPage(context: BrowserContext, url: string | undefined): Promi
|
||||||
else if (!url.startsWith('http') && !url.startsWith('file://') && !url.startsWith('about:') && !url.startsWith('data:'))
|
else if (!url.startsWith('http') && !url.startsWith('file://') && !url.startsWith('about:') && !url.startsWith('data:'))
|
||||||
url = 'http://' + url;
|
url = 'http://' + url;
|
||||||
await page.goto(url).catch(error => {
|
await page.goto(url).catch(error => {
|
||||||
if (process.env.PWTEST_CLI_AUTO_EXIT_WHEN && isTargetClosedError(error)) {
|
if (process.env.PWTEST_CLI_AUTO_EXIT_WHEN) {
|
||||||
// Tests with PWTEST_CLI_AUTO_EXIT_WHEN might close page too fast, resulting
|
// Tests with PWTEST_CLI_AUTO_EXIT_WHEN might close page too fast, resulting
|
||||||
// in a stray navigation aborted error. We should ignore it.
|
// in a stray navigation aborted error. We should ignore it.
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
[*]
|
[*]
|
||||||
../common/
|
|
||||||
../protocol/
|
../protocol/
|
||||||
../utils/isomorphic
|
../utils/isomorphic
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { ChannelOwner } from './channelOwner';
|
||||||
import { TargetClosedError, isTargetClosedError } from './errors';
|
import { TargetClosedError, isTargetClosedError } from './errors';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
import { isRegExp, isString } from '../utils/isomorphic/rtti';
|
import { isRegExp, isString } from '../utils/isomorphic/rtti';
|
||||||
import { monotonicTime } from '../utils/isomorphic/time';
|
import { monotonicTime } from '../utils/isomorphic/time';
|
||||||
import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner';
|
import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner';
|
||||||
|
@ -30,7 +30,7 @@ import type { Page } from './page';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type { AndroidServerLauncherImpl } from '../androidServerImpl';
|
import type { AndroidServerLauncherImpl } from '../androidServerImpl';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
type Direction = 'down' | 'up' | 'left' | 'right';
|
type Direction = 'down' | 'up' | 'left' | 'right';
|
||||||
|
@ -46,7 +46,7 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
|
||||||
|
|
||||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidInitializer) {
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidInitializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
this._timeoutSettings = new TimeoutSettings();
|
this._timeoutSettings = new TimeoutSettings(this._platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultTimeout(timeout: number) {
|
setDefaultTimeout(timeout: number) {
|
||||||
|
@ -112,7 +112,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
|
||||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidDeviceInitializer) {
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidDeviceInitializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
this.input = new AndroidInput(this);
|
this.input = new AndroidInput(this);
|
||||||
this._timeoutSettings = new TimeoutSettings((parent as Android)._timeoutSettings);
|
this._timeoutSettings = new TimeoutSettings(this._platform, (parent as Android)._timeoutSettings);
|
||||||
this._channel.on('webViewAdded', ({ webView }) => this._onWebViewAdded(webView));
|
this._channel.on('webViewAdded', ({ webView }) => this._onWebViewAdded(webView));
|
||||||
this._channel.on('webViewRemoved', ({ socketName }) => this._onWebViewRemoved(socketName));
|
this._channel.on('webViewRemoved', ({ socketName }) => this._onWebViewRemoved(socketName));
|
||||||
this._channel.on('close', () => this._didClose());
|
this._channel.on('close', () => this._didClose());
|
||||||
|
|
|
@ -34,7 +34,7 @@ import { Tracing } from './tracing';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { WebError } from './webError';
|
import { WebError } from './webError';
|
||||||
import { Worker } from './worker';
|
import { Worker } from './worker';
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
import { mkdirIfNeeded } from './fileUtils';
|
import { mkdirIfNeeded } from './fileUtils';
|
||||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
||||||
import { urlMatchesEqual } from '../utils/isomorphic/urlMatch';
|
import { urlMatchesEqual } from '../utils/isomorphic/urlMatch';
|
||||||
|
@ -46,7 +46,7 @@ import type { BrowserContextOptions, Headers, LaunchOptions, StorageState, WaitF
|
||||||
import type * as structs from '../../types/structs';
|
import type * as structs from '../../types/structs';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
||||||
|
@ -56,7 +56,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
readonly _browser: Browser | null = null;
|
readonly _browser: Browser | null = null;
|
||||||
_browserType: BrowserType | undefined;
|
_browserType: BrowserType | undefined;
|
||||||
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
|
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
|
||||||
_timeoutSettings = new TimeoutSettings();
|
_timeoutSettings: TimeoutSettings;
|
||||||
_ownerPage: Page | undefined;
|
_ownerPage: Page | undefined;
|
||||||
private _closedPromise: Promise<void>;
|
private _closedPromise: Promise<void>;
|
||||||
_options: channels.BrowserNewContextParams = { };
|
_options: channels.BrowserNewContextParams = { };
|
||||||
|
@ -83,6 +83,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
|
|
||||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.BrowserContextInitializer) {
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.BrowserContextInitializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
|
this._timeoutSettings = new TimeoutSettings(this._platform);
|
||||||
if (parent instanceof Browser)
|
if (parent instanceof Browser)
|
||||||
this._browser = parent;
|
this._browser = parent;
|
||||||
this._browser?._contexts.add(this);
|
this._browser?._contexts.add(this);
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { BrowserContext, prepareBrowserContextParams } from './browserContext';
|
||||||
import { ChannelOwner } from './channelOwner';
|
import { ChannelOwner } from './channelOwner';
|
||||||
import { envObjectToArray } from './clientHelper';
|
import { envObjectToArray } from './clientHelper';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
||||||
import { monotonicTime } from '../utils/isomorphic/time';
|
import { monotonicTime } from '../utils/isomorphic/time';
|
||||||
import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner';
|
import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner';
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
|
|
||||||
import { EventEmitter } from './eventEmitter';
|
import { EventEmitter } from './eventEmitter';
|
||||||
import { ValidationError, maybeFindValidator } from '../protocol/validator';
|
import { ValidationError, maybeFindValidator } from '../protocol/validator';
|
||||||
import { isUnderTest } from '../utils/isomorphic/debug';
|
import { captureLibraryStackTrace } from './clientStackTrace';
|
||||||
import { captureLibraryStackTrace, stringifyStackFrames } from '../utils/isomorphic/stackTrace';
|
import { stringifyStackFrames } from '../utils/isomorphic/stackTrace';
|
||||||
|
|
||||||
import type { ClientInstrumentation } from './clientInstrumentation';
|
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||||
import type { Connection } from './connection';
|
import type { Connection } from './connection';
|
||||||
import type { Logger } from './types';
|
import type { Logger } from './types';
|
||||||
import type { ValidatorContext } from '../protocol/validator';
|
import type { ValidatorContext } from '../protocol/validator';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
type Listener = (...args: any[]) => void;
|
type Listener = (...args: any[]) => void;
|
||||||
|
@ -181,7 +181,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
|
|
||||||
if (isInternal === undefined)
|
if (isInternal === undefined)
|
||||||
isInternal = this._isInternalType;
|
isInternal = this._isInternalType;
|
||||||
const stackTrace = captureLibraryStackTrace(this._platform.pathSeparator);
|
const stackTrace = captureLibraryStackTrace(this._platform);
|
||||||
const apiZone: ApiZone = { apiName: stackTrace.apiName, frames: stackTrace.frames, isInternal, reported: false, userData: undefined, stepId: undefined };
|
const apiZone: ApiZone = { apiName: stackTrace.apiName, frames: stackTrace.frames, isInternal, reported: false, userData: undefined, stepId: undefined };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -192,7 +192,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const innerError = ((process.env.PWDEBUGIMPL || isUnderTest()) && e.stack) ? '\n<inner error>\n' + e.stack : '';
|
const innerError = ((process.env.PWDEBUGIMPL || this._platform.isUnderTest()) && e.stack) ? '\n<inner error>\n' + e.stack : '';
|
||||||
if (apiZone.apiName && !apiZone.apiName.includes('<anonymous>'))
|
if (apiZone.apiName && !apiZone.apiName.includes('<anonymous>'))
|
||||||
e.message = apiZone.apiName + ': ' + e.message;
|
e.message = apiZone.apiName + ': ' + e.message;
|
||||||
const stackFrames = '\n' + stringifyStackFrames(stackTrace.frames).join('\n') + innerError;
|
const stackFrames = '\n' + stringifyStackFrames(stackTrace.frames).join('\n') + innerError;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Connection } from './connection';
|
||||||
|
import { setPlatformForSelectors } from './selectors';
|
||||||
|
import { setPlatformForEventEmitter } from './eventEmitter';
|
||||||
|
import { setIsUnderTestForValidator } from '../protocol/validatorPrimitives';
|
||||||
|
|
||||||
|
import type { Platform } from './platform';
|
||||||
|
|
||||||
|
export function createConnectionFactory(platform: Platform): () => Connection {
|
||||||
|
setPlatformForSelectors(platform);
|
||||||
|
setPlatformForEventEmitter(platform);
|
||||||
|
setIsUnderTestForValidator(() => platform.isUnderTest());
|
||||||
|
return () => new Connection(platform);
|
||||||
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
import { isString } from '../utils/isomorphic/rtti';
|
import { isString } from '../utils/isomorphic/rtti';
|
||||||
|
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
|
|
||||||
export function envObjectToArray(env: types.Env): { name: string, value: string }[] {
|
export function envObjectToArray(env: types.Env): { name: string, value: string }[] {
|
||||||
const result: { name: string, value: string }[] = [];
|
const result: { name: string, value: string }[] = [];
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { captureRawStack, parseStackFrame } from '../utils/isomorphic/stackTrace';
|
||||||
|
|
||||||
|
import type { Platform } from './platform';
|
||||||
|
import type { StackFrame } from '@isomorphic/stackTrace';
|
||||||
|
|
||||||
|
export function captureLibraryStackTrace(platform: Platform): { frames: StackFrame[], apiName: string } {
|
||||||
|
const stack = captureRawStack();
|
||||||
|
|
||||||
|
type ParsedFrame = {
|
||||||
|
frame: StackFrame;
|
||||||
|
frameText: string;
|
||||||
|
isPlaywrightLibrary: boolean;
|
||||||
|
};
|
||||||
|
let parsedFrames = stack.map(line => {
|
||||||
|
const frame = parseStackFrame(line, platform.pathSeparator);
|
||||||
|
if (!frame || !frame.file)
|
||||||
|
return null;
|
||||||
|
const isPlaywrightLibrary = !!platform.coreDir && frame.file.startsWith(platform.coreDir);
|
||||||
|
const parsed: ParsedFrame = {
|
||||||
|
frame,
|
||||||
|
frameText: line,
|
||||||
|
isPlaywrightLibrary
|
||||||
|
};
|
||||||
|
return parsed;
|
||||||
|
}).filter(Boolean) as ParsedFrame[];
|
||||||
|
|
||||||
|
let apiName = '';
|
||||||
|
|
||||||
|
// Deepest transition between non-client code calling into client
|
||||||
|
// code is the api entry.
|
||||||
|
for (let i = 0; i < parsedFrames.length - 1; i++) {
|
||||||
|
const parsedFrame = parsedFrames[i];
|
||||||
|
if (parsedFrame.isPlaywrightLibrary && !parsedFrames[i + 1].isPlaywrightLibrary) {
|
||||||
|
apiName = apiName || normalizeAPIName(parsedFrame.frame.function);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAPIName(name?: string): string {
|
||||||
|
if (!name)
|
||||||
|
return '';
|
||||||
|
const match = name.match(/(API|JS|CDP|[A-Z])(.*)/);
|
||||||
|
if (!match)
|
||||||
|
return name;
|
||||||
|
return match[1].toLowerCase() + match[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is for the inspector so that it did not include the test runner stack frames.
|
||||||
|
const filterPrefixes = platform.coreDir ? [platform.coreDir, ...platform.boxedStackPrefixes()] : platform.boxedStackPrefixes();
|
||||||
|
parsedFrames = parsedFrames.filter(f => {
|
||||||
|
if (process.env.PWDEBUGIMPL)
|
||||||
|
return true;
|
||||||
|
if (filterPrefixes.some(prefix => f.frame.file.startsWith(prefix)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
frames: parsedFrames.map(p => p.frame),
|
||||||
|
apiName
|
||||||
|
};
|
||||||
|
}
|
|
@ -14,7 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import { EventEmitter } from './eventEmitter';
|
import { EventEmitter } from './eventEmitter';
|
||||||
import { Android, AndroidDevice, AndroidSocket } from './android';
|
import { Android, AndroidDevice, AndroidSocket } from './android';
|
||||||
import { Artifact } from './artifact';
|
import { Artifact } from './artifact';
|
||||||
|
@ -42,12 +41,12 @@ import { Tracing } from './tracing';
|
||||||
import { Worker } from './worker';
|
import { Worker } from './worker';
|
||||||
import { WritableStream } from './writableStream';
|
import { WritableStream } from './writableStream';
|
||||||
import { ValidationError, findValidator } from '../protocol/validator';
|
import { ValidationError, findValidator } from '../protocol/validator';
|
||||||
import { formatCallLog, rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
|
import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
|
||||||
|
|
||||||
import type { ClientInstrumentation } from './clientInstrumentation';
|
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||||
import type { HeadersArray } from './types';
|
import type { HeadersArray } from './types';
|
||||||
import type { ValidatorContext } from '../protocol/validator';
|
import type { ValidatorContext } from '../protocol/validator';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
class Root extends ChannelOwner<channels.RootChannel> {
|
class Root extends ChannelOwner<channels.RootChannel> {
|
||||||
|
@ -83,7 +82,7 @@ export class Connection extends EventEmitter {
|
||||||
// Used from @playwright/test fixtures -> TODO remove?
|
// Used from @playwright/test fixtures -> TODO remove?
|
||||||
readonly headers: HeadersArray;
|
readonly headers: HeadersArray;
|
||||||
|
|
||||||
constructor(localUtils: LocalUtils | undefined, platform: Platform, instrumentation: ClientInstrumentation | undefined, headers: HeadersArray) {
|
constructor(platform: Platform, localUtils?: LocalUtils, instrumentation?: ClientInstrumentation, headers: HeadersArray = []) {
|
||||||
super();
|
super();
|
||||||
this._instrumentation = instrumentation || createInstrumentation();
|
this._instrumentation = instrumentation || createInstrumentation();
|
||||||
this._localUtils = localUtils;
|
this._localUtils = localUtils;
|
||||||
|
@ -333,3 +332,12 @@ export class Connection extends EventEmitter {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCallLog(platform: Platform, log: string[] | undefined): string {
|
||||||
|
if (!log || !log.some(l => !!l))
|
||||||
|
return '';
|
||||||
|
return `
|
||||||
|
Call log:
|
||||||
|
${platform.colors.dim(log.join('\n'))}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { JSHandle } from './jsHandle';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
|
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
type ConsoleMessageLocation = channels.BrowserContextConsoleEvent['location'];
|
type ConsoleMessageLocation = channels.BrowserContextConsoleEvent['location'];
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { TargetClosedError, isTargetClosedError } from './errors';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
|
|
||||||
import type { Page } from './page';
|
import type { Page } from './page';
|
||||||
import type { BrowserContextOptions, Env, Headers, WaitForEventOptions } from './types';
|
import type { BrowserContextOptions, Env, Headers, WaitForEventOptions } from './types';
|
||||||
|
@ -66,7 +66,7 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
|
||||||
export class ElectronApplication extends ChannelOwner<channels.ElectronApplicationChannel> implements api.ElectronApplication {
|
export class ElectronApplication extends ChannelOwner<channels.ElectronApplicationChannel> implements api.ElectronApplication {
|
||||||
readonly _context: BrowserContext;
|
readonly _context: BrowserContext;
|
||||||
private _windows = new Set<Page>();
|
private _windows = new Set<Page>();
|
||||||
private _timeoutSettings = new TimeoutSettings();
|
private _timeoutSettings: TimeoutSettings;
|
||||||
|
|
||||||
static from(electronApplication: channels.ElectronApplicationChannel): ElectronApplication {
|
static from(electronApplication: channels.ElectronApplicationChannel): ElectronApplication {
|
||||||
return (electronApplication as any)._object;
|
return (electronApplication as any)._object;
|
||||||
|
@ -74,6 +74,8 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
|
||||||
|
|
||||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.ElectronApplicationInitializer) {
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.ElectronApplicationInitializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
|
|
||||||
|
this._timeoutSettings = new TimeoutSettings(this._platform);
|
||||||
this._context = BrowserContext.from(initializer.context);
|
this._context = BrowserContext.from(initializer.context);
|
||||||
for (const page of this._context._pages)
|
for (const page of this._context._pages)
|
||||||
this._onPage(page);
|
this._onPage(page);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import { Frame } from './frame';
|
import { Frame } from './frame';
|
||||||
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { fileUploadSizeLimit, mkdirIfNeeded } from './fileUtils';
|
import { fileUploadSizeLimit, mkdirIfNeeded } from './fileUtils';
|
||||||
import { isString } from '../utils/isomorphic/rtti';
|
import { isString } from '../utils/isomorphic/rtti';
|
||||||
import { WritableStream } from './writableStream';
|
import { WritableStream } from './writableStream';
|
||||||
|
@ -28,7 +28,7 @@ import type { Locator } from './locator';
|
||||||
import type { FilePayload, Rect, SelectOption, SelectOptionOptions } from './types';
|
import type { FilePayload, Rect, SelectOption, SelectOptionOptions } from './types';
|
||||||
import type * as structs from '../../types/structs';
|
import type * as structs from '../../types/structs';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements api.ElementHandle {
|
export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements api.ElementHandle {
|
||||||
|
|
|
@ -22,18 +22,19 @@
|
||||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { isUnderTest } from '../utils/isomorphic/debug';
|
import { emptyPlatform } from './platform';
|
||||||
|
|
||||||
import type { EventEmitter as EventEmitterType } from 'events';
|
import type { EventEmitter as EventEmitterType } from 'events';
|
||||||
|
import type { Platform } from './platform';
|
||||||
|
|
||||||
type EventType = string | symbol;
|
type EventType = string | symbol;
|
||||||
type Listener = (...args: any[]) => any;
|
type Listener = (...args: any[]) => any;
|
||||||
type EventMap = Record<EventType, Listener | Listener[]>;
|
type EventMap = Record<EventType, Listener | Listener[]>;
|
||||||
|
|
||||||
let defaultMaxListenersProvider = () => 10;
|
let platform = emptyPlatform;
|
||||||
|
|
||||||
export function setDefaultMaxListenersProvider(provider: () => number) {
|
export function setPlatformForEventEmitter(p: Platform) {
|
||||||
defaultMaxListenersProvider = provider;
|
platform = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventEmitter implements EventEmitterType {
|
export class EventEmitter implements EventEmitterType {
|
||||||
|
@ -62,7 +63,7 @@ export class EventEmitter implements EventEmitterType {
|
||||||
}
|
}
|
||||||
|
|
||||||
getMaxListeners(): number {
|
getMaxListeners(): number {
|
||||||
return this._maxListeners === undefined ? defaultMaxListenersProvider() : this._maxListeners;
|
return this._maxListeners === undefined ? platform.defaultMaxListeners() : this._maxListeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(type: EventType, ...args: any[]): boolean {
|
emit(type: EventType, ...args: any[]): boolean {
|
||||||
|
@ -160,7 +161,7 @@ export class EventEmitter implements EventEmitterType {
|
||||||
w.emitter = this;
|
w.emitter = this;
|
||||||
w.type = type;
|
w.type = type;
|
||||||
w.count = existing.length;
|
w.count = existing.length;
|
||||||
if (!isUnderTest()) {
|
if (!platform.isUnderTest()) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(w);
|
console.warn(w);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { ChannelOwner } from './channelOwner';
|
||||||
import { TargetClosedError, isTargetClosedError } from './errors';
|
import { TargetClosedError, isTargetClosedError } from './errors';
|
||||||
import { RawHeaders } from './network';
|
import { RawHeaders } from './network';
|
||||||
import { Tracing } from './tracing';
|
import { Tracing } from './tracing';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { mkdirIfNeeded } from './fileUtils';
|
import { mkdirIfNeeded } from './fileUtils';
|
||||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
||||||
import { isString } from '../utils/isomorphic/rtti';
|
import { isString } from '../utils/isomorphic/rtti';
|
||||||
|
@ -28,8 +28,8 @@ import type { Playwright } from './playwright';
|
||||||
import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types';
|
import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types';
|
||||||
import type { Serializable } from '../../types/structs';
|
import type { Serializable } from '../../types/structs';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type { HeadersArray, NameValue } from '../common/types';
|
import type { HeadersArray, NameValue } from '../utils/isomorphic/types';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import type * as fs from 'fs';
|
import type * as fs from 'fs';
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
|
|
||||||
// Keep in sync with the server.
|
// Keep in sync with the server.
|
||||||
export const fileUploadSizeLimit = 50 * 1024 * 1024;
|
export const fileUploadSizeLimit = 50 * 1024 * 1024;
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { FrameLocator, Locator, testIdAttributeName } from './locator';
|
||||||
import * as network from './network';
|
import * as network from './network';
|
||||||
import { kLifecycleEvents } from './types';
|
import { kLifecycleEvents } from './types';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector } from '../utils/isomorphic/locatorUtils';
|
import { getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector } from '../utils/isomorphic/locatorUtils';
|
||||||
import { urlMatches } from '../utils/isomorphic/urlMatch';
|
import { urlMatches } from '../utils/isomorphic/urlMatch';
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { APIResponse } from './fetch';
|
||||||
import { Frame } from './frame';
|
import { Frame } from './frame';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { Worker } from './worker';
|
import { Worker } from './worker';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
||||||
import { urlMatches } from '../utils/isomorphic/urlMatch';
|
import { urlMatches } from '../utils/isomorphic/urlMatch';
|
||||||
import { LongStandingScope, ManualPromise } from '../utils/isomorphic/manualPromise';
|
import { LongStandingScope, ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||||
|
@ -35,10 +35,10 @@ import type { Page } from './page';
|
||||||
import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
|
import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
|
||||||
import type { Serializable } from '../../types/structs';
|
import type { Serializable } from '../../types/structs';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type { HeadersArray } from '../common/types';
|
import type { HeadersArray } from '../utils/isomorphic/types';
|
||||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import type { Platform, Zone } from '../common/platform';
|
import type { Platform, Zone } from './platform';
|
||||||
|
|
||||||
export type NetworkCookie = {
|
export type NetworkCookie = {
|
||||||
name: string,
|
name: string,
|
||||||
|
|
|
@ -33,8 +33,8 @@ import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRou
|
||||||
import { Video } from './video';
|
import { Video } from './video';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { Worker } from './worker';
|
import { Worker } from './worker';
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { mkdirIfNeeded } from './fileUtils';
|
import { mkdirIfNeeded } from './fileUtils';
|
||||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
import { headersObjectToArray } from '../utils/isomorphic/headers';
|
||||||
import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils';
|
import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils';
|
||||||
|
@ -118,7 +118,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.PageInitializer) {
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.PageInitializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
this._browserContext = parent as unknown as BrowserContext;
|
this._browserContext = parent as unknown as BrowserContext;
|
||||||
this._timeoutSettings = new TimeoutSettings(this._browserContext._timeoutSettings);
|
this._timeoutSettings = new TimeoutSettings(this._platform, this._browserContext._timeoutSettings);
|
||||||
|
|
||||||
this.accessibility = new Accessibility(this._channel);
|
this.accessibility = new Accessibility(this._channel);
|
||||||
this.keyboard = new Keyboard(this);
|
this.keyboard = new Keyboard(this);
|
||||||
|
@ -799,7 +799,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||||
}
|
}
|
||||||
|
|
||||||
async pause(_options?: { __testHookKeepTestTimeout: boolean }) {
|
async pause(_options?: { __testHookKeepTestTimeout: boolean }) {
|
||||||
if (this._platform.isDebuggerAttached())
|
if (this._platform.isJSDebuggerAttached())
|
||||||
return;
|
return;
|
||||||
const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout();
|
const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout();
|
||||||
const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout();
|
const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout();
|
||||||
|
|
|
@ -18,8 +18,8 @@ import { webColors, noColors } from '../utils/isomorphic/colors';
|
||||||
|
|
||||||
import type * as fs from 'fs';
|
import type * as fs from 'fs';
|
||||||
import type * as path from 'path';
|
import type * as path from 'path';
|
||||||
import type { Colors } from '../utils/isomorphic/colors';
|
|
||||||
import type { Readable, Writable } from 'stream';
|
import type { Readable, Writable } from 'stream';
|
||||||
|
import type { Colors } from '@isomorphic/colors';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
export type Zone = {
|
export type Zone = {
|
||||||
|
@ -39,17 +39,22 @@ const noopZone: Zone = {
|
||||||
export type Platform = {
|
export type Platform = {
|
||||||
name: 'node' | 'web' | 'empty';
|
name: 'node' | 'web' | 'empty';
|
||||||
|
|
||||||
calculateSha1(text: string): Promise<string>;
|
boxedStackPrefixes: () => string[];
|
||||||
|
calculateSha1: (text: string) => Promise<string>;
|
||||||
colors: Colors;
|
colors: Colors;
|
||||||
|
coreDir?: string;
|
||||||
createGuid: () => string;
|
createGuid: () => string;
|
||||||
|
defaultMaxListeners: () => number;
|
||||||
fs: () => typeof fs;
|
fs: () => typeof fs;
|
||||||
inspectCustom: symbol | undefined;
|
inspectCustom: symbol | undefined;
|
||||||
isDebuggerAttached(): boolean;
|
isDebugMode: () => boolean;
|
||||||
isLogEnabled(name: 'api' | 'channel'): boolean;
|
isJSDebuggerAttached: () => boolean;
|
||||||
log(name: 'api' | 'channel', message: string | Error | object): void;
|
isLogEnabled: (name: 'api' | 'channel') => boolean;
|
||||||
|
isUnderTest: () => boolean,
|
||||||
|
log: (name: 'api' | 'channel', message: string | Error | object) => void;
|
||||||
path: () => typeof path;
|
path: () => typeof path;
|
||||||
pathSeparator: string;
|
pathSeparator: string;
|
||||||
streamFile(path: string, writable: Writable): Promise<void>,
|
streamFile: (path: string, writable: Writable) => Promise<void>,
|
||||||
streamReadable: (channel: channels.StreamChannel) => Readable,
|
streamReadable: (channel: channels.StreamChannel) => Readable,
|
||||||
streamWritable: (channel: channels.WritableStreamChannel) => Writable,
|
streamWritable: (channel: channels.WritableStreamChannel) => Writable,
|
||||||
zones: { empty: Zone, current: () => Zone; };
|
zones: { empty: Zone, current: () => Zone; };
|
||||||
|
@ -58,6 +63,8 @@ export type Platform = {
|
||||||
export const emptyPlatform: Platform = {
|
export const emptyPlatform: Platform = {
|
||||||
name: 'empty',
|
name: 'empty',
|
||||||
|
|
||||||
|
boxedStackPrefixes: () => [],
|
||||||
|
|
||||||
calculateSha1: async () => {
|
calculateSha1: async () => {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
@ -68,18 +75,24 @@ export const emptyPlatform: Platform = {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
defaultMaxListeners: () => 10,
|
||||||
|
|
||||||
fs: () => {
|
fs: () => {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
inspectCustom: undefined,
|
inspectCustom: undefined,
|
||||||
|
|
||||||
isDebuggerAttached: () => false,
|
isDebugMode: () => false,
|
||||||
|
|
||||||
|
isJSDebuggerAttached: () => false,
|
||||||
|
|
||||||
isLogEnabled(name: 'api' | 'channel') {
|
isLogEnabled(name: 'api' | 'channel') {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isUnderTest: () => false,
|
||||||
|
|
||||||
log(name: 'api' | 'channel', message: string | Error | object) { },
|
log(name: 'api' | 'channel', message: string | Error | object) { },
|
||||||
|
|
||||||
path: () => {
|
path: () => {
|
||||||
|
@ -108,6 +121,8 @@ export const webPlatform: Platform = {
|
||||||
|
|
||||||
name: 'web',
|
name: 'web',
|
||||||
|
|
||||||
|
boxedStackPrefixes: () => [],
|
||||||
|
|
||||||
calculateSha1: async (text: string) => {
|
calculateSha1: async (text: string) => {
|
||||||
const bytes = new TextEncoder().encode(text);
|
const bytes = new TextEncoder().encode(text);
|
||||||
const hashBuffer = await window.crypto.subtle.digest('SHA-1', bytes);
|
const hashBuffer = await window.crypto.subtle.digest('SHA-1', bytes);
|
|
@ -17,12 +17,12 @@
|
||||||
import { ChannelOwner } from './channelOwner';
|
import { ChannelOwner } from './channelOwner';
|
||||||
import { evaluationScript } from './clientHelper';
|
import { evaluationScript } from './clientHelper';
|
||||||
import { setTestIdAttribute, testIdAttributeName } from './locator';
|
import { setTestIdAttribute, testIdAttributeName } from './locator';
|
||||||
import { emptyPlatform } from '../common/platform';
|
import { emptyPlatform } from './platform';
|
||||||
|
|
||||||
import type { SelectorEngine } from './types';
|
import type { SelectorEngine } from './types';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import type { Platform } from '../common/platform';
|
import type { Platform } from './platform';
|
||||||
|
|
||||||
let platform = emptyPlatform;
|
let platform = emptyPlatform;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Platform } from './platform';
|
||||||
|
|
||||||
|
// Keep in sync with server.
|
||||||
|
export const DEFAULT_TIMEOUT = 30000;
|
||||||
|
export const DEFAULT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes
|
||||||
|
|
||||||
|
export class TimeoutSettings {
|
||||||
|
private _parent: TimeoutSettings | undefined;
|
||||||
|
private _defaultTimeout: number | undefined;
|
||||||
|
private _defaultNavigationTimeout: number | undefined;
|
||||||
|
private _platform: Platform;
|
||||||
|
|
||||||
|
constructor(platform: Platform, parent?: TimeoutSettings) {
|
||||||
|
this._parent = parent;
|
||||||
|
this._platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultTimeout(timeout: number | undefined) {
|
||||||
|
this._defaultTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultNavigationTimeout(timeout: number | undefined) {
|
||||||
|
this._defaultNavigationTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultNavigationTimeout() {
|
||||||
|
return this._defaultNavigationTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTimeout() {
|
||||||
|
return this._defaultTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationTimeout(options: { timeout?: number }): number {
|
||||||
|
if (typeof options.timeout === 'number')
|
||||||
|
return options.timeout;
|
||||||
|
if (this._defaultNavigationTimeout !== undefined)
|
||||||
|
return this._defaultNavigationTimeout;
|
||||||
|
if (this._platform.isDebugMode())
|
||||||
|
return 0;
|
||||||
|
if (this._defaultTimeout !== undefined)
|
||||||
|
return this._defaultTimeout;
|
||||||
|
if (this._parent)
|
||||||
|
return this._parent.navigationTimeout(options);
|
||||||
|
return DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout(options: { timeout?: number }): number {
|
||||||
|
if (typeof options.timeout === 'number')
|
||||||
|
return options.timeout;
|
||||||
|
if (this._platform.isDebugMode())
|
||||||
|
return 0;
|
||||||
|
if (this._defaultTimeout !== undefined)
|
||||||
|
return this._defaultTimeout;
|
||||||
|
if (this._parent)
|
||||||
|
return this._parent.timeout(options);
|
||||||
|
return DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,9 +15,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Size } from '../common/types';
|
import type { Size } from '../utils/isomorphic/types';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../common/types';
|
export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../utils/isomorphic/types';
|
||||||
|
|
||||||
type LoggerSeverity = 'verbose' | 'info' | 'warning' | 'error';
|
type LoggerSeverity = 'verbose' | 'info' | 'warning' | 'error';
|
||||||
export interface Logger {
|
export interface Logger {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
|
||||||
import type { ChannelOwner } from './channelOwner';
|
import type { ChannelOwner } from './channelOwner';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import type { EventEmitter } from 'events';
|
import type { EventEmitter } from 'events';
|
||||||
import type { Zone } from '../common/platform';
|
import type { Zone } from './platform';
|
||||||
|
|
||||||
export class Waiter {
|
export class Waiter {
|
||||||
private _dispose: (() => void)[];
|
private _dispose: (() => void)[];
|
||||||
|
|
|
@ -24,7 +24,7 @@ export async function connectOverWebSocket(parentConnection: Connection, params:
|
||||||
const localUtils = parentConnection.localUtils();
|
const localUtils = parentConnection.localUtils();
|
||||||
const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport();
|
const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport();
|
||||||
const connectHeaders = await transport.connect(params);
|
const connectHeaders = await transport.connect(params);
|
||||||
const connection = new Connection(localUtils, parentConnection.platform, parentConnection._instrumentation, connectHeaders);
|
const connection = new Connection(parentConnection.platform, localUtils, parentConnection._instrumentation, connectHeaders);
|
||||||
connection.markAsRemote();
|
connection.markAsRemote();
|
||||||
connection.on('close', () => transport.close());
|
connection.on('close', () => transport.close());
|
||||||
|
|
||||||
|
|
|
@ -14,34 +14,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as path from 'path';
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
|
|
||||||
import { AndroidServerLauncherImpl } from './androidServerImpl';
|
import { AndroidServerLauncherImpl } from './androidServerImpl';
|
||||||
import { BrowserServerLauncherImpl } from './browserServerImpl';
|
import { BrowserServerLauncherImpl } from './browserServerImpl';
|
||||||
import { Connection } from './client/connection';
|
import { createConnectionFactory } from './client/clientBundle';
|
||||||
import { DispatcherConnection, PlaywrightDispatcher, RootDispatcher, createPlaywright } from './server';
|
import { DispatcherConnection, PlaywrightDispatcher, RootDispatcher, createPlaywright } from './server';
|
||||||
import { setLibraryStackPrefix } from './utils/isomorphic/stackTrace';
|
|
||||||
import { setDebugMode } from './utils/isomorphic/debug';
|
|
||||||
import { getFromENV } from './server/utils/env';
|
|
||||||
import { nodePlatform } from './server/utils/nodePlatform';
|
import { nodePlatform } from './server/utils/nodePlatform';
|
||||||
import { setPlatformForSelectors } from './client/selectors';
|
|
||||||
import { setDefaultMaxListenersProvider } from './client/eventEmitter';
|
|
||||||
|
|
||||||
import type { Playwright as PlaywrightAPI } from './client/playwright';
|
import type { Playwright as PlaywrightAPI } from './client/playwright';
|
||||||
import type { Language } from './utils';
|
import type { Language } from './utils';
|
||||||
import type { Platform } from './common/platform';
|
|
||||||
|
|
||||||
|
const connectionFactory = createConnectionFactory(nodePlatform);
|
||||||
|
|
||||||
export function createInProcessPlaywright(platform: Platform): PlaywrightAPI {
|
export function createInProcessPlaywright(): PlaywrightAPI {
|
||||||
const playwright = createPlaywright({ sdkLanguage: (process.env.PW_LANG_NAME as Language | undefined) || 'javascript' });
|
const playwright = createPlaywright({ sdkLanguage: (process.env.PW_LANG_NAME as Language | undefined) || 'javascript' });
|
||||||
setDebugMode(getFromENV('PWDEBUG') || '');
|
|
||||||
setPlatformForSelectors(nodePlatform);
|
|
||||||
setDefaultMaxListenersProvider(() => EventEmitter.defaultMaxListeners);
|
|
||||||
|
|
||||||
setLibraryStackPrefix(path.join(__dirname, '..'));
|
const clientConnection = connectionFactory();
|
||||||
|
|
||||||
const clientConnection = new Connection(undefined, platform, undefined, []);
|
|
||||||
clientConnection.useRawBuffers();
|
clientConnection.useRawBuffers();
|
||||||
const dispatcherConnection = new DispatcherConnection(true /* local */);
|
const dispatcherConnection = new DispatcherConnection(true /* local */);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,5 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createInProcessPlaywright } from './inProcessFactory';
|
import { createInProcessPlaywright } from './inProcessFactory';
|
||||||
import { nodePlatform } from './server/utils/nodePlatform';
|
|
||||||
|
|
||||||
module.exports = createInProcessPlaywright(nodePlatform);
|
module.exports = createInProcessPlaywright();
|
||||||
|
|
|
@ -17,13 +17,15 @@
|
||||||
import * as childProcess from 'child_process';
|
import * as childProcess from 'child_process';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { Connection } from './client/connection';
|
import { createConnectionFactory } from './client/clientBundle';
|
||||||
import { PipeTransport } from './server/utils/pipeTransport';
|
import { PipeTransport } from './server/utils/pipeTransport';
|
||||||
import { ManualPromise } from './utils/isomorphic/manualPromise';
|
import { ManualPromise } from './utils/isomorphic/manualPromise';
|
||||||
import { nodePlatform } from './server/utils/nodePlatform';
|
import { nodePlatform } from './server/utils/nodePlatform';
|
||||||
|
|
||||||
import type { Playwright } from './client/playwright';
|
import type { Playwright } from './client/playwright';
|
||||||
|
|
||||||
|
const connectionFactory = createConnectionFactory(nodePlatform);
|
||||||
|
|
||||||
export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise<void> }> {
|
export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise<void> }> {
|
||||||
const client = new PlaywrightClient(env);
|
const client = new PlaywrightClient(env);
|
||||||
const playwright = await client._playwright;
|
const playwright = await client._playwright;
|
||||||
|
@ -48,7 +50,7 @@ class PlaywrightClient {
|
||||||
this._driverProcess.unref();
|
this._driverProcess.unref();
|
||||||
this._driverProcess.stderr!.on('data', data => process.stderr.write(data));
|
this._driverProcess.stderr!.on('data', data => process.stderr.write(data));
|
||||||
|
|
||||||
const connection = new Connection(undefined, nodePlatform, undefined, []);
|
const connection = connectionFactory();
|
||||||
const transport = new PipeTransport(this._driverProcess.stdin!, this._driverProcess.stdout!);
|
const transport = new PipeTransport(this._driverProcess.stdin!, this._driverProcess.stdout!);
|
||||||
connection.onmessage = message => transport.send(JSON.stringify(message));
|
connection.onmessage = message => transport.send(JSON.stringify(message));
|
||||||
transport.onmessage = message => connection.dispatch(JSON.parse(message));
|
transport.onmessage = message => connection.dispatch(JSON.parse(message));
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
[*]
|
[*]
|
||||||
../common/
|
|
||||||
../utils/isomorphic
|
../utils/isomorphic
|
||||||
|
|
|
@ -14,7 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { isUnderTest } from '../utils/isomorphic/debug';
|
let isUnderTest = () => false;
|
||||||
|
|
||||||
|
export function setIsUnderTestForValidator(getter: () => boolean) {
|
||||||
|
isUnderTest = getter;
|
||||||
|
}
|
||||||
|
|
||||||
export class ValidationError extends Error {}
|
export class ValidationError extends Error {}
|
||||||
export type Validator = (arg: any, path: string, context: ValidatorContext) => any;
|
export type Validator = (arg: any, path: string, context: ValidatorContext) => any;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
[*]
|
[*]
|
||||||
../client/
|
|
||||||
../common/
|
|
||||||
../server/
|
../server/
|
||||||
../server/android/
|
../server/android/
|
||||||
../server/dispatchers/
|
../server/dispatchers/
|
||||||
|
|
|
@ -20,7 +20,8 @@ import { AndroidDevice } from '../server/android/android';
|
||||||
import { Browser } from '../server/browser';
|
import { Browser } from '../server/browser';
|
||||||
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
|
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
|
||||||
import { serverSideCallMetadata } from '../server/instrumentation';
|
import { serverSideCallMetadata } from '../server/instrumentation';
|
||||||
import { assert, isUnderTest } from '../utils';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
|
import { isUnderTest } from '../server/utils/debug';
|
||||||
import { startProfiling, stopProfiling } from '../server/utils/profiler';
|
import { startProfiling, stopProfiling } from '../server/utils/profiler';
|
||||||
import { monotonicTime } from '../utils';
|
import { monotonicTime } from '../utils';
|
||||||
import { debugLogger } from '../server/utils/debugLogger';
|
import { debugLogger } from '../server/utils/debugLogger';
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
[*]
|
[*]
|
||||||
../common/
|
|
||||||
../generated/
|
../generated/
|
||||||
../protocol/
|
../protocol/
|
||||||
../utils/
|
../utils
|
||||||
../utils/isomorphic/
|
../utils/isomorphic/
|
||||||
../utilsBundle.ts
|
../utilsBundle.ts
|
||||||
../zipBundle.ts
|
../zipBundle.ts
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
[*]
|
[*]
|
||||||
../
|
../
|
||||||
../../common/
|
|
||||||
../../protocol/
|
../../protocol/
|
||||||
../../utils/
|
|
||||||
../../utils/isomorphic/
|
../../utils/isomorphic/
|
||||||
../../utilsBundle.ts
|
../../utilsBundle.ts
|
||||||
../chromium/
|
../chromium/
|
||||||
|
|
|
@ -19,10 +19,10 @@ import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from '../timeoutSettings';
|
||||||
import { PipeTransport } from '../utils/pipeTransport';
|
import { PipeTransport } from '../utils/pipeTransport';
|
||||||
import { createGuid } from '../utils/crypto';
|
import { createGuid } from '../utils/crypto';
|
||||||
import { isUnderTest } from '../../utils/isomorphic/debug';
|
import { isUnderTest } from '../utils/debug';
|
||||||
import { getPackageManagerExecCommand } from '../utils/env';
|
import { getPackageManagerExecCommand } from '../utils/env';
|
||||||
import { makeWaitForNextTask } from '../utils/task';
|
import { makeWaitForNextTask } from '../utils/task';
|
||||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
|
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
import { createGuid } from '../utils/crypto';
|
import { createGuid } from '../utils/crypto';
|
||||||
import { debug } from '../../utilsBundle';
|
import { debug } from '../../utilsBundle';
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
import { createGuid } from './utils/crypto';
|
import { createGuid } from './utils/crypto';
|
||||||
import { debugMode } from '../utils/isomorphic/debug';
|
import { debugMode } from './utils/debug';
|
||||||
import { Clock } from './clock';
|
import { Clock } from './clock';
|
||||||
import { Debugger } from './debugger';
|
import { Debugger } from './debugger';
|
||||||
import { BrowserContextAPIRequestContext } from './fetch';
|
import { BrowserContextAPIRequestContext } from './fetch';
|
||||||
|
|
|
@ -19,8 +19,10 @@ import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { normalizeProxySettings, validateBrowserContextOptions } from './browserContext';
|
import { normalizeProxySettings, validateBrowserContextOptions } from './browserContext';
|
||||||
import { DEFAULT_TIMEOUT, TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { DEFAULT_TIMEOUT, TimeoutSettings } from './timeoutSettings';
|
||||||
import { ManualPromise, assert, debugMode } from '../utils';
|
import { debugMode } from './utils/debug';
|
||||||
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
|
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||||
import { existsAsync } from './utils/fileUtils';
|
import { existsAsync } from './utils/fileUtils';
|
||||||
import { helper } from './helper';
|
import { helper } from './helper';
|
||||||
import { SdkObject } from './instrumentation';
|
import { SdkObject } from './instrumentation';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Copyright (c) Microsoft Corporation.
|
* Copyright (c) Microsoft Corporation.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
|
@ -14,7 +14,27 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function findRepeatedSubsequences(s: string[]): { sequence: string[]; count: number }[] {
|
export function compressCallLog(log: string[]): string[] {
|
||||||
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
for (const block of findRepeatedSubsequences(log)) {
|
||||||
|
for (let i = 0; i < block.sequence.length; i++) {
|
||||||
|
const line = block.sequence[i];
|
||||||
|
const leadingWhitespace = line.match(/^\s*/);
|
||||||
|
const whitespacePrefix = ' ' + leadingWhitespace?.[0] || '';
|
||||||
|
const countPrefix = `${block.count} × `;
|
||||||
|
if (block.count > 1 && i === 0)
|
||||||
|
lines.push(whitespacePrefix + countPrefix + line.trim());
|
||||||
|
else if (block.count > 1)
|
||||||
|
lines.push(whitespacePrefix + ' '.repeat(countPrefix.length - 2) + '- ' + line.trim());
|
||||||
|
else
|
||||||
|
lines.push(whitespacePrefix + '- ' + line.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRepeatedSubsequences(s: string[]): { sequence: string[]; count: number }[] {
|
||||||
const n = s.length;
|
const n = s.length;
|
||||||
const result = [];
|
const result = [];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -64,3 +84,5 @@ export function findRepeatedSubsequences(s: string[]): { sequence: string[]; cou
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const findRepeatedSubsequencesForTest = findRepeatedSubsequences;
|
|
@ -22,7 +22,7 @@ import * as path from 'path';
|
||||||
import { chromiumSwitches } from './chromiumSwitches';
|
import { chromiumSwitches } from './chromiumSwitches';
|
||||||
import { CRBrowser } from './crBrowser';
|
import { CRBrowser } from './crBrowser';
|
||||||
import { kBrowserCloseMessageId } from './crConnection';
|
import { kBrowserCloseMessageId } from './crConnection';
|
||||||
import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from '../timeoutSettings';
|
||||||
import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils';
|
import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils';
|
||||||
import { wrapInASCIIBox } from '../utils/ascii';
|
import { wrapInASCIIBox } from '../utils/ascii';
|
||||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
import { createGuid } from '../utils/crypto';
|
import { createGuid } from '../utils/crypto';
|
||||||
import { Artifact } from '../artifact';
|
import { Artifact } from '../artifact';
|
||||||
import { Browser } from '../browser';
|
import { Browser } from '../browser';
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
import { createGuid } from '../utils/crypto';
|
import { createGuid } from '../utils/crypto';
|
||||||
import { eventsHelper } from '../utils/eventsHelper';
|
import { eventsHelper } from '../utils/eventsHelper';
|
||||||
import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace';
|
import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace';
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
[*]
|
[*]
|
||||||
../../common/
|
|
||||||
../../generated/
|
../../generated/
|
||||||
../../protocol/
|
../../protocol/
|
||||||
../../utils/
|
../../utils/
|
||||||
|
|
|
@ -18,10 +18,12 @@ import { EventEmitter } from 'events';
|
||||||
|
|
||||||
import { eventsHelper } from '../utils/eventsHelper';
|
import { eventsHelper } from '../utils/eventsHelper';
|
||||||
import { ValidationError, createMetadataValidator, findValidator } from '../../protocol/validator';
|
import { ValidationError, createMetadataValidator, findValidator } from '../../protocol/validator';
|
||||||
import { LongStandingScope, assert, compressCallLog, isUnderTest, monotonicTime, rewriteErrorMessage } from '../../utils';
|
import { LongStandingScope, assert, monotonicTime, rewriteErrorMessage } from '../../utils';
|
||||||
|
import { isUnderTest } from '../utils/debug';
|
||||||
import { TargetClosedError, isTargetClosedError, serializeError } from '../errors';
|
import { TargetClosedError, isTargetClosedError, serializeError } from '../errors';
|
||||||
import { SdkObject } from '../instrumentation';
|
import { SdkObject } from '../instrumentation';
|
||||||
import { isProtocolError } from '../protocolError';
|
import { isProtocolError } from '../protocolError';
|
||||||
|
import { compressCallLog } from '../callLog';
|
||||||
|
|
||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||||
import { parseArgument, serializeResult } from './jsHandleDispatcher';
|
import { parseArgument, serializeResult } from './jsHandleDispatcher';
|
||||||
import { ResponseDispatcher } from './networkDispatchers';
|
import { ResponseDispatcher } from './networkDispatchers';
|
||||||
import { RequestDispatcher } from './networkDispatchers';
|
import { RequestDispatcher } from './networkDispatchers';
|
||||||
import { debugAssert } from '../../utils';
|
|
||||||
import { parseAriaSnapshotUnsafe } from '../../utils/isomorphic/ariaSnapshot';
|
import { parseAriaSnapshotUnsafe } from '../../utils/isomorphic/ariaSnapshot';
|
||||||
import { yaml } from '../../utilsBundle';
|
import { yaml } from '../../utilsBundle';
|
||||||
|
|
||||||
|
@ -50,7 +49,6 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Br
|
||||||
// Main frames are gc'ed separately from any other frames, so that
|
// Main frames are gc'ed separately from any other frames, so that
|
||||||
// methods on Page that redirect to the main frame remain operational.
|
// methods on Page that redirect to the main frame remain operational.
|
||||||
// Note: we cannot check parentFrame() here because it may be null after the frame has been detached.
|
// Note: we cannot check parentFrame() here because it may be null after the frame has been detached.
|
||||||
debugAssert(frame._page.mainFrame(), 'Cannot determine whether the frame is a main frame');
|
|
||||||
const gcBucket = frame._page.mainFrame() === frame ? 'MainFrame' : 'Frame';
|
const gcBucket = frame._page.mainFrame() === frame ? 'MainFrame' : 'Frame';
|
||||||
const pageDispatcher = existingDispatcher<PageDispatcher>(frame._page);
|
const pageDispatcher = existingDispatcher<PageDispatcher>(frame._page);
|
||||||
super(pageDispatcher || scope, frame, 'Frame', {
|
super(pageDispatcher || scope, frame, 'Frame', {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import type { Page } from './page';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import type { ScreenshotOptions } from './screenshotter';
|
import type { ScreenshotOptions } from './screenshotter';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { TimeoutOptions } from '../common/types';
|
import type { TimeoutOptions } from '../utils/isomorphic/types';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
[*]
|
[*]
|
||||||
../
|
../
|
||||||
../../common/
|
|
||||||
../../utils/
|
../../utils/
|
||||||
../../utils/isomorphic/
|
../../utils/isomorphic/
|
||||||
../chromium/
|
../chromium/
|
||||||
|
|
|
@ -19,7 +19,7 @@ import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as readline from 'readline';
|
import * as readline from 'readline';
|
||||||
|
|
||||||
import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from '../timeoutSettings';
|
||||||
import { ManualPromise } from '../../utils';
|
import { ManualPromise } from '../../utils';
|
||||||
import { wrapInASCIIBox } from '../utils/ascii';
|
import { wrapInASCIIBox } from '../utils/ascii';
|
||||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { TLSSocket } from 'tls';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import * as zlib from 'zlib';
|
import * as zlib from 'zlib';
|
||||||
|
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
import { assert, constructURLBasedOnBaseURL, eventsHelper, monotonicTime } from '../utils';
|
import { assert, constructURLBasedOnBaseURL, eventsHelper, monotonicTime } from '../utils';
|
||||||
import { createGuid } from './utils/crypto';
|
import { createGuid } from './utils/crypto';
|
||||||
import { getUserAgent } from './utils/userAgent';
|
import { getUserAgent } from './utils/userAgent';
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { mime } from '../utilsBundle';
|
import { mime } from '../utilsBundle';
|
||||||
|
|
||||||
import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher';
|
import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher';
|
||||||
|
|
|
@ -27,12 +27,13 @@ import * as network from './network';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import { ProgressController } from './progress';
|
import { ProgressController } from './progress';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { LongStandingScope, asLocator, assert, compressCallLog, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils';
|
import { LongStandingScope, asLocator, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils';
|
||||||
import { isSessionClosedError } from './protocolError';
|
import { isSessionClosedError } from './protocolError';
|
||||||
import { debugLogger } from './utils/debugLogger';
|
import { debugLogger } from './utils/debugLogger';
|
||||||
import { eventsHelper } from './utils/eventsHelper';
|
import { eventsHelper } from './utils/eventsHelper';
|
||||||
import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
|
import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
|
||||||
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||||
|
import { compressCallLog } from './callLog';
|
||||||
|
|
||||||
import type { ConsoleMessage } from './console';
|
import type { ConsoleMessage } from './console';
|
||||||
import type { Dialog } from './dialog';
|
import type { Dialog } from './dialog';
|
||||||
|
|
|
@ -20,7 +20,7 @@ import * as path from 'path';
|
||||||
import { createGuid } from './utils/crypto';
|
import { createGuid } from './utils/crypto';
|
||||||
import { ZipFile } from './utils/zipFile';
|
import { ZipFile } from './utils/zipFile';
|
||||||
|
|
||||||
import type { HeadersArray } from '../common/types';
|
import type { HeadersArray } from '../utils/isomorphic/types';
|
||||||
import type * as har from '@trace/har';
|
import type * as har from '@trace/har';
|
||||||
|
|
||||||
const redirectStatus = [301, 302, 303, 307, 308];
|
const redirectStatus = [301, 302, 303, 307, 308];
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import clipPaths from './clipPaths';
|
import clipPaths from './clipPaths';
|
||||||
|
|
||||||
import type { Point } from '../../../common/types';
|
import type { Point } from '../../../utils/isomorphic/types';
|
||||||
import type { Highlight, HighlightOptions } from '../highlight';
|
import type { Highlight, HighlightOptions } from '../highlight';
|
||||||
import type { InjectedScript } from '../injectedScript';
|
import type { InjectedScript } from '../injectedScript';
|
||||||
import type { ElementText } from '../selectorUtils';
|
import type { ElementText } from '../selectorUtils';
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||||
import { ZipFile } from './utils/zipFile';
|
import { ZipFile } from './utils/zipFile';
|
||||||
import { yauzl, yazl } from '../zipBundle';
|
import { yauzl, yazl } from '../zipBundle';
|
||||||
import { serializeClientSideCallMetadata } from '../utils/isomorphic/traceUtils';
|
import { serializeClientSideCallMetadata } from '../utils/isomorphic/traceUtils';
|
||||||
import { assert } from '../utils/isomorphic/debug';
|
import { assert } from '../utils/isomorphic/assert';
|
||||||
import { removeFolders } from './utils/fileUtils';
|
import { removeFolders } from './utils/fileUtils';
|
||||||
|
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
|
@ -25,7 +25,7 @@ import type * as frames from './frames';
|
||||||
import type * as pages from './page';
|
import type * as pages from './page';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { NormalizedContinueOverrides } from './types';
|
import type { NormalizedContinueOverrides } from './types';
|
||||||
import type { HeadersArray, NameValue } from '../common/types';
|
import type { HeadersArray, NameValue } from '../utils/isomorphic/types';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,15 @@ import { parseEvaluationResultValue, source } from './isomorphic/utilityScriptSe
|
||||||
import * as js from './javascript';
|
import * as js from './javascript';
|
||||||
import { ProgressController } from './progress';
|
import { ProgressController } from './progress';
|
||||||
import { Screenshotter, validateScreenshotOptions } from './screenshotter';
|
import { Screenshotter, validateScreenshotOptions } from './screenshotter';
|
||||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
|
import { TimeoutSettings } from './timeoutSettings';
|
||||||
import { LongStandingScope, assert, compressCallLog, trimStringWithEllipsis } from '../utils';
|
import { LongStandingScope, assert, trimStringWithEllipsis } from '../utils';
|
||||||
import { createGuid } from './utils/crypto';
|
import { createGuid } from './utils/crypto';
|
||||||
import { asLocator } from '../utils';
|
import { asLocator } from '../utils';
|
||||||
import { getComparator } from './utils/comparators';
|
import { getComparator } from './utils/comparators';
|
||||||
import { debugLogger } from './utils/debugLogger';
|
import { debugLogger } from './utils/debugLogger';
|
||||||
import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
|
import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
|
||||||
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||||
|
import { compressCallLog } from './callLog';
|
||||||
|
|
||||||
import type { Artifact } from './artifact';
|
import type { Artifact } from './artifact';
|
||||||
import type * as dom from './dom';
|
import type * as dom from './dom';
|
||||||
|
@ -45,7 +46,7 @@ import type * as network from './network';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import type { ScreenshotOptions } from './screenshotter';
|
import type { ScreenshotOptions } from './screenshotter';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { TimeoutOptions } from '../common/types';
|
import type { TimeoutOptions } from '../utils/isomorphic/types';
|
||||||
import type { ImageComparatorOptions } from './utils/comparators';
|
import type { ImageComparatorOptions } from './utils/comparators';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import type { Frame } from './frames';
|
||||||
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
|
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
|
||||||
import type { Page } from './page';
|
import type { Page } from './page';
|
||||||
import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorder/recorderFrontend';
|
import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorder/recorderFrontend';
|
||||||
import type { Point } from '../common/types';
|
import type { Point } from '../utils/isomorphic/types';
|
||||||
import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot';
|
import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import type * as actions from '@recorder/actions';
|
import type * as actions from '@recorder/actions';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
../codegen/languages.ts
|
../codegen/languages.ts
|
||||||
../isomorphic/**
|
../isomorphic/**
|
||||||
../registry/**
|
../registry/**
|
||||||
../../common/
|
../utils/**
|
||||||
../../generated/pollingRecorderSource.ts
|
../../generated/pollingRecorderSource.ts
|
||||||
../../protocol/
|
../../protocol/
|
||||||
../../utils/**
|
../../utils/**
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { isUnderTest } from '../../utils';
|
import { isUnderTest } from '../utils/debug';
|
||||||
import { mime } from '../../utilsBundle';
|
import { mime } from '../../utilsBundle';
|
||||||
import { serverSideCallMetadata } from '../instrumentation';
|
import { serverSideCallMetadata } from '../instrumentation';
|
||||||
import { syncLocalStorageWithSettings } from '../launchApp';
|
import { syncLocalStorageWithSettings } from '../launchApp';
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
|
||||||
|
|
||||||
import { performAction } from './recorderRunner';
|
import { performAction } from './recorderRunner';
|
||||||
import { collapseActions } from './recorderUtils';
|
import { collapseActions } from './recorderUtils';
|
||||||
import { isUnderTest } from '../../utils/isomorphic/debug';
|
import { isUnderTest } from '../utils/debug';
|
||||||
import { monotonicTime } from '../../utils/isomorphic/time';
|
import { monotonicTime } from '../../utils/isomorphic/time';
|
||||||
|
|
||||||
import type { Signal } from '../../../../recorder/src/actions';
|
import type { Signal } from '../../../../recorder/src/actions';
|
||||||
|
|
|
@ -24,7 +24,7 @@ import type { Frame } from './frames';
|
||||||
import type { Page } from './page';
|
import type { Page } from './page';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { Rect } from '../common/types';
|
import type { Rect } from '../utils/isomorphic/types';
|
||||||
import type { ParsedSelector } from '../utils/isomorphic/selectorParser';
|
import type { ParsedSelector } from '../utils/isomorphic/selectorParser';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { debugMode } from './debug';
|
import { debugMode } from './utils/debug';
|
||||||
|
|
||||||
|
// Keep in sync with client.
|
||||||
export const DEFAULT_TIMEOUT = 30000;
|
export const DEFAULT_TIMEOUT = 30000;
|
||||||
export const DEFAULT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes
|
export const DEFAULT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
[*]
|
[*]
|
||||||
../../
|
../../
|
||||||
../../har/
|
../../har/
|
||||||
../../../common/
|
|
||||||
../../../protocol/
|
../../../protocol/
|
||||||
../../../utils/
|
|
||||||
../../../utilsBundle.ts
|
../../../utilsBundle.ts
|
||||||
../../../utils/isomorphic/
|
../../../utils/isomorphic/
|
||||||
../../../zipBundle.ts
|
../../../zipBundle.ts
|
||||||
|
|
|
@ -20,7 +20,7 @@ import * as path from 'path';
|
||||||
|
|
||||||
import { Snapshotter } from './snapshotter';
|
import { Snapshotter } from './snapshotter';
|
||||||
import { commandsWithTracingSnapshots } from '../../../protocol/debug';
|
import { commandsWithTracingSnapshots } from '../../../protocol/debug';
|
||||||
import { assert } from '../../../utils/isomorphic/debug';
|
import { assert } from '../../../utils/isomorphic/assert';
|
||||||
import { monotonicTime } from '../../../utils/isomorphic/time';
|
import { monotonicTime } from '../../../utils/isomorphic/time';
|
||||||
import { eventsHelper } from '../../utils/eventsHelper';
|
import { eventsHelper } from '../../utils/eventsHelper';
|
||||||
import { createGuid } from '../../utils/crypto';
|
import { createGuid } from '../../utils/crypto';
|
||||||
|
@ -34,7 +34,7 @@ import { SdkObject } from '../../instrumentation';
|
||||||
import { Page } from '../../page';
|
import { Page } from '../../page';
|
||||||
|
|
||||||
import type { SnapshotterBlob, SnapshotterDelegate } from './snapshotter';
|
import type { SnapshotterBlob, SnapshotterDelegate } from './snapshotter';
|
||||||
import type { NameValue } from '../../../common/types';
|
import type { NameValue } from '../../../utils/isomorphic/types';
|
||||||
import type { RegisteredListener } from '../../../utils';
|
import type { RegisteredListener } from '../../../utils';
|
||||||
import type { ConsoleMessage } from '../../console';
|
import type { ConsoleMessage } from '../../console';
|
||||||
import type { Dialog } from '../../dialog';
|
import type { Dialog } from '../../dialog';
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { HeadersArray, Point, Size, TimeoutOptions } from '../common/types';
|
import type { HeadersArray, Point, Size, TimeoutOptions } from '../utils/isomorphic/types';
|
||||||
export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../common/types';
|
export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../utils/isomorphic/types';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
export type StrictOptions = {
|
export type StrictOptions = {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
[*]
|
[*]
|
||||||
../../common
|
|
||||||
../../utils
|
../../utils
|
||||||
../../utils/isomorphic
|
../../utils/isomorphic
|
||||||
../../utilsBundle.ts
|
../../utilsBundle.ts
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
|
|
||||||
export function createGuid(): string {
|
export function createGuid(): string {
|
||||||
return crypto.randomBytes(16).toString('hex');
|
return crypto.randomBytes(16).toString('hex');
|
||||||
|
|
|
@ -14,21 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function assert(value: any, message?: string): asserts value {
|
import { getFromENV } from './env';
|
||||||
if (!value)
|
|
||||||
throw new Error(message || 'Assertion error');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function debugAssert(value: any, message?: string): asserts value {
|
const _debugMode = getFromENV('PWDEBUG') || '';
|
||||||
if (isUnderTest() && !value)
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _debugMode: string | undefined;
|
|
||||||
|
|
||||||
export function setDebugMode(mode: string) {
|
|
||||||
_debugMode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function debugMode() {
|
export function debugMode() {
|
||||||
if (_debugMode === 'console')
|
if (_debugMode === 'console')
|
|
@ -20,7 +20,7 @@ import * as https from 'https';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
import * as tls from 'tls';
|
import * as tls from 'tls';
|
||||||
|
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
import { ManualPromise } from '../../utils/isomorphic/manualPromise';
|
import { ManualPromise } from '../../utils/isomorphic/manualPromise';
|
||||||
import { monotonicTime } from '../../utils/isomorphic/time';
|
import { monotonicTime } from '../../utils/isomorphic/time';
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import * as path from 'path';
|
||||||
|
|
||||||
import { mime, wsServer } from '../../utilsBundle';
|
import { mime, wsServer } from '../../utilsBundle';
|
||||||
import { createGuid } from './crypto';
|
import { createGuid } from './crypto';
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
import { ManualPromise } from '../../utils/isomorphic/manualPromise';
|
import { ManualPromise } from '../../utils/isomorphic/manualPromise';
|
||||||
import { createHttpServer } from './network';
|
import { createHttpServer } from './network';
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,14 @@ import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import { Readable, Writable, pipeline } from 'stream';
|
import { Readable, Writable, pipeline } from 'stream';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
import { colors } from '../../utilsBundle';
|
import { colors } from '../../utilsBundle';
|
||||||
import { debugLogger } from './debugLogger';
|
import { debugLogger } from './debugLogger';
|
||||||
import { currentZone, emptyZone } from './zones';
|
import { currentZone, emptyZone } from './zones';
|
||||||
|
import { debugMode, isUnderTest } from './debug';
|
||||||
|
|
||||||
import type { Platform, Zone } from '../../common/platform';
|
import type { Platform, Zone } from '../../client/platform';
|
||||||
import type { Zone as ZoneImpl } from './zones';
|
import type { Zone as ZoneImpl } from './zones';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
|
|
||||||
|
@ -54,9 +56,16 @@ class NodeZone implements Zone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let boxedStackPrefixes: string[] = [];
|
||||||
|
export function setBoxedStackPrefixes(prefixes: string[]) {
|
||||||
|
boxedStackPrefixes = prefixes;
|
||||||
|
}
|
||||||
|
|
||||||
export const nodePlatform: Platform = {
|
export const nodePlatform: Platform = {
|
||||||
name: 'node',
|
name: 'node',
|
||||||
|
|
||||||
|
boxedStackPrefixes: () => boxedStackPrefixes,
|
||||||
|
|
||||||
calculateSha1: (text: string) => {
|
calculateSha1: (text: string) => {
|
||||||
const sha1 = crypto.createHash('sha1');
|
const sha1 = crypto.createHash('sha1');
|
||||||
sha1.update(text);
|
sha1.update(text);
|
||||||
|
@ -65,18 +74,25 @@ export const nodePlatform: Platform = {
|
||||||
|
|
||||||
colors,
|
colors,
|
||||||
|
|
||||||
|
coreDir: path.dirname(require.resolve('../../../package.json')),
|
||||||
|
|
||||||
createGuid: () => crypto.randomBytes(16).toString('hex'),
|
createGuid: () => crypto.randomBytes(16).toString('hex'),
|
||||||
|
|
||||||
|
defaultMaxListeners: () => EventEmitter.defaultMaxListeners,
|
||||||
fs: () => fs,
|
fs: () => fs,
|
||||||
|
|
||||||
inspectCustom: util.inspect.custom,
|
inspectCustom: util.inspect.custom,
|
||||||
|
|
||||||
isDebuggerAttached: () => !!require('inspector').url(),
|
isDebugMode: () => !!debugMode(),
|
||||||
|
|
||||||
|
isJSDebuggerAttached: () => !!require('inspector').url(),
|
||||||
|
|
||||||
isLogEnabled(name: 'api' | 'channel') {
|
isLogEnabled(name: 'api' | 'channel') {
|
||||||
return debugLogger.isEnabled(name);
|
return debugLogger.isEnabled(name);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isUnderTest: () => isUnderTest(),
|
||||||
|
|
||||||
log(name: 'api' | 'channel', message: string | Error | object) {
|
log(name: 'api' | 'channel', message: string | Error | object) {
|
||||||
debugLogger.log(name, message);
|
debugLogger.log(name, message);
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
|
|
||||||
import { assert } from '../../utils/isomorphic/debug';
|
import { assert } from '../../utils/isomorphic/assert';
|
||||||
import { createGuid } from './crypto';
|
import { createGuid } from './crypto';
|
||||||
import { debugLogger } from './debugLogger';
|
import { debugLogger } from './debugLogger';
|
||||||
import { createSocket } from './happyEyeballs';
|
import { createSocket } from './happyEyeballs';
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { assert, debugAssert } from '../../utils';
|
import { assert } from '../../utils';
|
||||||
import { headersArrayToObject } from '../../utils/isomorphic/headers';
|
import { headersArrayToObject } from '../../utils/isomorphic/headers';
|
||||||
import { createGuid } from '../utils/crypto';
|
import { createGuid } from '../utils/crypto';
|
||||||
import { eventsHelper } from '../utils/eventsHelper';
|
import { eventsHelper } from '../utils/eventsHelper';
|
||||||
|
@ -294,7 +294,6 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowOpen(event: Protocol.Playwright.windowOpenPayload) {
|
handleWindowOpen(event: Protocol.Playwright.windowOpenPayload) {
|
||||||
debugAssert(!this._nextWindowOpenPopupFeatures);
|
|
||||||
this._nextWindowOpenPopupFeatures = event.windowFeatures;
|
this._nextWindowOpenPopupFeatures = event.windowFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,24 +15,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './utils/isomorphic/colors';
|
export * from './utils/isomorphic/colors';
|
||||||
export * from './utils/isomorphic/debug';
|
export * from './utils/isomorphic/assert';
|
||||||
|
export * from './utils/isomorphic/headers';
|
||||||
export * from './utils/isomorphic/locatorGenerators';
|
export * from './utils/isomorphic/locatorGenerators';
|
||||||
export * from './utils/isomorphic/manualPromise';
|
export * from './utils/isomorphic/manualPromise';
|
||||||
export * from './utils/isomorphic/mimeType';
|
export * from './utils/isomorphic/mimeType';
|
||||||
export * from './utils/isomorphic/multimap';
|
export * from './utils/isomorphic/multimap';
|
||||||
export * from './utils/isomorphic/rtti';
|
export * from './utils/isomorphic/rtti';
|
||||||
|
export * from './utils/isomorphic/semaphore';
|
||||||
|
export * from './utils/isomorphic/stackTrace';
|
||||||
export * from './utils/isomorphic/stringUtils';
|
export * from './utils/isomorphic/stringUtils';
|
||||||
export * from './utils/isomorphic/time';
|
export * from './utils/isomorphic/time';
|
||||||
export * from './utils/isomorphic/timeoutRunner';
|
export * from './utils/isomorphic/timeoutRunner';
|
||||||
export * from './utils/isomorphic/urlMatch';
|
export * from './utils/isomorphic/urlMatch';
|
||||||
|
|
||||||
export * from './utils/isomorphic/headers';
|
|
||||||
export * from './utils/isomorphic/semaphore';
|
|
||||||
export * from './utils/isomorphic/stackTrace';
|
|
||||||
|
|
||||||
export * from './server/utils/ascii';
|
export * from './server/utils/ascii';
|
||||||
export * from './server/utils/comparators';
|
export * from './server/utils/comparators';
|
||||||
export * from './server/utils/crypto';
|
export * from './server/utils/crypto';
|
||||||
|
export * from './server/utils/debug';
|
||||||
export * from './server/utils/debugLogger';
|
export * from './server/utils/debugLogger';
|
||||||
export * from './server/utils/env';
|
export * from './server/utils/env';
|
||||||
export * from './server/utils/eventsHelper';
|
export * from './server/utils/eventsHelper';
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function assert(value: any, message?: string): asserts value {
|
||||||
|
if (!value)
|
||||||
|
throw new Error(message || 'Assertion error');
|
||||||
|
}
|
|
@ -14,6 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { captureRawStack } from './stackTrace';
|
||||||
|
|
||||||
export class ManualPromise<T = void> extends Promise<T> {
|
export class ManualPromise<T = void> extends Promise<T> {
|
||||||
private _resolve!: (t: T) => void;
|
private _resolve!: (t: T) => void;
|
||||||
private _reject!: (e: Error) => void;
|
private _reject!: (e: Error) => void;
|
||||||
|
@ -116,12 +118,3 @@ function cloneError(error: Error, frames: string[]) {
|
||||||
clone.stack = [error.name + ':' + error.message, ...frames].join('\n');
|
clone.stack = [error.name + ':' + error.message, ...frames].join('\n');
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
function captureRawStack(): string[] {
|
|
||||||
const stackTraceLimit = Error.stackTraceLimit;
|
|
||||||
Error.stackTraceLimit = 50;
|
|
||||||
const error = new Error();
|
|
||||||
const stack = error.stack || '';
|
|
||||||
Error.stackTraceLimit = stackTraceLimit;
|
|
||||||
return stack.split('\n');
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,62 +1,33 @@
|
||||||
/**
|
/**
|
||||||
* Copyright (c) Microsoft Corporation.
|
* The MIT License (MIT)
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Copyright (c) 2016-2023 Isaac Z. Schlueter i@izs.me, James Talmage james@talmage.io (github.com/jamestalmage), and
|
||||||
* you may not use this file except in compliance with the License.
|
* Contributors
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* Software.
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
*
|
||||||
* See the License for the specific language governing permissions and
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
* limitations under the License.
|
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { findRepeatedSubsequences } from './sequence';
|
|
||||||
import { parseStackFrame } from './stackUtils';
|
|
||||||
|
|
||||||
import type { StackFrame } from '@protocol/channels';
|
|
||||||
import type { Platform } from '../../common/platform';
|
|
||||||
|
|
||||||
export function parseStackTraceLine(line: string, pathSeparator: string): StackFrame | null {
|
|
||||||
const frame = parseStackFrame(line, pathSeparator);
|
|
||||||
if (!frame)
|
|
||||||
return null;
|
|
||||||
if (!process.env.PWDEBUGIMPL && (frame.file?.startsWith('internal') || frame.file?.startsWith('node:')))
|
|
||||||
return null;
|
|
||||||
if (!frame.file)
|
|
||||||
return null;
|
|
||||||
return {
|
|
||||||
file: frame.file,
|
|
||||||
line: frame.line || 0,
|
|
||||||
column: frame.column || 0,
|
|
||||||
function: frame.function,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rewriteErrorMessage<E extends Error>(e: E, newMessage: string): E {
|
|
||||||
const lines: string[] = (e.stack?.split('\n') || []).filter(l => l.startsWith(' at '));
|
|
||||||
e.message = newMessage;
|
|
||||||
const errorTitle = `${e.name}: ${e.message}`;
|
|
||||||
if (lines.length)
|
|
||||||
e.stack = `${errorTitle}\n${lines.join('\n')}`;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
let coreDir: string | undefined;
|
|
||||||
|
|
||||||
const playwrightStackPrefixes: string[] = [];
|
|
||||||
export const addInternalStackPrefix = (prefix: string) => playwrightStackPrefixes.push(prefix);
|
|
||||||
|
|
||||||
export const setLibraryStackPrefix = (prefix: string) => {
|
|
||||||
coreDir = prefix;
|
|
||||||
playwrightStackPrefixes.push(prefix);
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RawStack = string[];
|
export type RawStack = string[];
|
||||||
|
|
||||||
|
export type StackFrame = {
|
||||||
|
file: string,
|
||||||
|
line: number,
|
||||||
|
column: number,
|
||||||
|
function?: string,
|
||||||
|
};
|
||||||
|
|
||||||
export function captureRawStack(): RawStack {
|
export function captureRawStack(): RawStack {
|
||||||
const stackTraceLimit = Error.stackTraceLimit;
|
const stackTraceLimit = Error.stackTraceLimit;
|
||||||
Error.stackTraceLimit = 50;
|
Error.stackTraceLimit = 50;
|
||||||
|
@ -66,61 +37,82 @@ export function captureRawStack(): RawStack {
|
||||||
return stack.split('\n');
|
return stack.split('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function captureLibraryStackTrace(pathSeparator: string): { frames: StackFrame[], apiName: string } {
|
export function parseStackFrame(text: string, pathSeparator: string): StackFrame | null {
|
||||||
const stack = captureRawStack();
|
const match = text && text.match(re);
|
||||||
|
if (!match)
|
||||||
|
return null;
|
||||||
|
|
||||||
type ParsedFrame = {
|
let fname = match[2];
|
||||||
frame: StackFrame;
|
let file = match[7];
|
||||||
frameText: string;
|
if (!file)
|
||||||
isPlaywrightLibrary: boolean;
|
return null;
|
||||||
|
if (!process.env.PWDEBUGIMPL && (file.startsWith('internal') || file.startsWith('node:')))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const line = match[8];
|
||||||
|
const column = match[9];
|
||||||
|
const closeParen = match[11] === ')';
|
||||||
|
|
||||||
|
const frame: StackFrame = {
|
||||||
|
file: '',
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
};
|
};
|
||||||
let parsedFrames = stack.map(line => {
|
|
||||||
const frame = parseStackTraceLine(line, pathSeparator);
|
|
||||||
if (!frame || !frame.file)
|
|
||||||
return null;
|
|
||||||
const isPlaywrightLibrary = !!coreDir && frame.file.startsWith(coreDir);
|
|
||||||
const parsed: ParsedFrame = {
|
|
||||||
frame,
|
|
||||||
frameText: line,
|
|
||||||
isPlaywrightLibrary
|
|
||||||
};
|
|
||||||
return parsed;
|
|
||||||
}).filter(Boolean) as ParsedFrame[];
|
|
||||||
|
|
||||||
let apiName = '';
|
if (line)
|
||||||
|
frame.line = Number(line);
|
||||||
|
|
||||||
// Deepest transition between non-client code calling into client
|
if (column)
|
||||||
// code is the api entry.
|
frame.column = Number(column);
|
||||||
for (let i = 0; i < parsedFrames.length - 1; i++) {
|
|
||||||
const parsedFrame = parsedFrames[i];
|
if (closeParen && file) {
|
||||||
if (parsedFrame.isPlaywrightLibrary && !parsedFrames[i + 1].isPlaywrightLibrary) {
|
// make sure parens are balanced
|
||||||
apiName = apiName || normalizeAPIName(parsedFrame.frame.function);
|
// if we have a file like "asdf) [as foo] (xyz.js", then odds are
|
||||||
break;
|
// that the fname should be += " (asdf) [as foo]" and the file
|
||||||
|
// should be just "xyz.js"
|
||||||
|
// walk backwards from the end to find the last unbalanced (
|
||||||
|
let closes = 0;
|
||||||
|
for (let i = file.length - 1; i > 0; i--) {
|
||||||
|
if (file.charAt(i) === ')') {
|
||||||
|
closes++;
|
||||||
|
} else if (file.charAt(i) === '(' && file.charAt(i - 1) === ' ') {
|
||||||
|
closes--;
|
||||||
|
if (closes === -1 && file.charAt(i - 1) === ' ') {
|
||||||
|
const before = file.slice(0, i - 1);
|
||||||
|
const after = file.slice(i + 1);
|
||||||
|
file = after;
|
||||||
|
fname += ` (${before}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeAPIName(name?: string): string {
|
if (fname) {
|
||||||
if (!name)
|
const methodMatch = fname.match(methodRe);
|
||||||
return '';
|
if (methodMatch)
|
||||||
const match = name.match(/(API|JS|CDP|[A-Z])(.*)/);
|
fname = methodMatch[1];
|
||||||
if (!match)
|
|
||||||
return name;
|
|
||||||
return match[1].toLowerCase() + match[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for the inspector so that it did not include the test runner stack frames.
|
if (file) {
|
||||||
parsedFrames = parsedFrames.filter(f => {
|
if (file.startsWith('file://'))
|
||||||
if (process.env.PWDEBUGIMPL)
|
file = fileURLToPath(file, pathSeparator);
|
||||||
return true;
|
frame.file = file;
|
||||||
if (playwrightStackPrefixes.some(prefix => f.frame.file.startsWith(prefix)))
|
}
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
if (fname)
|
||||||
frames: parsedFrames.map(p => p.frame),
|
frame.function = fname;
|
||||||
apiName
|
|
||||||
};
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rewriteErrorMessage<E extends Error>(e: E, newMessage: string): E {
|
||||||
|
const lines: string[] = (e.stack?.split('\n') || []).filter(l => l.startsWith(' at '));
|
||||||
|
e.message = newMessage;
|
||||||
|
const errorTitle = `${e.name}: ${e.message}`;
|
||||||
|
if (lines.length)
|
||||||
|
e.stack = `${errorTitle}\n${lines.join('\n')}`;
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringifyStackFrames(frames: StackFrame[]): string[] {
|
export function stringifyStackFrames(frames: StackFrame[]): string[] {
|
||||||
|
@ -142,31 +134,40 @@ export function splitErrorMessage(message: string): { name: string, message: str
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatCallLog(platform: Platform, log: string[] | undefined): string {
|
const re = new RegExp('^' +
|
||||||
if (!log || !log.some(l => !!l))
|
// Sometimes we strip out the ' at' because it's noisy
|
||||||
return '';
|
'(?:\\s*at )?' +
|
||||||
return `
|
// $1 = ctor if 'new'
|
||||||
Call log:
|
'(?:(new) )?' +
|
||||||
${platform.colors.dim(log.join('\n'))}
|
// $2 = function name (can be literally anything)
|
||||||
`;
|
// May contain method at the end as [as xyz]
|
||||||
}
|
'(?:(.*?) \\()?' +
|
||||||
|
// (eval at <anonymous> (file.js:1:1),
|
||||||
|
// $3 = eval origin
|
||||||
|
// $4:$5:$6 are eval file/line/col, but not normally reported
|
||||||
|
'(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?' +
|
||||||
|
// file:line:col
|
||||||
|
// $7:$8:$9
|
||||||
|
// $10 = 'native' if native
|
||||||
|
'(?:(.+?):(\\d+):(\\d+)|(native))' +
|
||||||
|
// maybe close the paren, then end
|
||||||
|
// if $11 is ), then we only allow balanced parens in the filename
|
||||||
|
// any imbalance is placed on the fname. This is a heuristic, and
|
||||||
|
// bound to be incorrect in some edge cases. The bet is that
|
||||||
|
// having weird characters in method names is more common than
|
||||||
|
// having weird characters in filenames, which seems reasonable.
|
||||||
|
'(\\)?)$'
|
||||||
|
);
|
||||||
|
|
||||||
export function compressCallLog(log: string[]): string[] {
|
const methodRe = /^(.*?) \[as (.*?)\]$/;
|
||||||
const lines: string[] = [];
|
|
||||||
|
|
||||||
for (const block of findRepeatedSubsequences(log)) {
|
function fileURLToPath(fileUrl: string, pathSeparator: string): string {
|
||||||
for (let i = 0; i < block.sequence.length; i++) {
|
if (!fileUrl.startsWith('file://'))
|
||||||
const line = block.sequence[i];
|
return fileUrl;
|
||||||
const leadingWhitespace = line.match(/^\s*/);
|
|
||||||
const whitespacePrefix = ' ' + leadingWhitespace?.[0] || '';
|
let path = decodeURIComponent(fileUrl.slice(7));
|
||||||
const countPrefix = `${block.count} × `;
|
if (path.startsWith('/') && /^[a-zA-Z]:/.test(path.slice(1)))
|
||||||
if (block.count > 1 && i === 0)
|
path = path.slice(1);
|
||||||
lines.push(whitespacePrefix + countPrefix + line.trim());
|
|
||||||
else if (block.count > 1)
|
return path.replace(/\//g, pathSeparator);
|
||||||
lines.push(whitespacePrefix + ' '.repeat(countPrefix.length - 2) + '- ' + line.trim());
|
|
||||||
else
|
|
||||||
lines.push(whitespacePrefix + '- ' + line.trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
/**
|
|
||||||
* The MIT License (MIT)
|
|
||||||
* Modifications copyright (c) Microsoft Corporation.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2016-2023 Isaac Z. Schlueter i@izs.me, James Talmage james@talmage.io (github.com/jamestalmage), and
|
|
||||||
* Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
||||||
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
||||||
* Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
||||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
type StackData = {
|
|
||||||
line?: number;
|
|
||||||
column?: number;
|
|
||||||
file?: string;
|
|
||||||
isConstructor?: boolean;
|
|
||||||
evalOrigin?: string;
|
|
||||||
native?: boolean;
|
|
||||||
function?: string;
|
|
||||||
method?: string;
|
|
||||||
evalLine?: number | undefined;
|
|
||||||
evalColumn?: number | undefined;
|
|
||||||
evalFile?: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function parseStackFrame(line: string, pathSeparator: string): StackData | null {
|
|
||||||
const match = line && line.match(re);
|
|
||||||
if (!match)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const ctor = match[1] === 'new';
|
|
||||||
let fname = match[2];
|
|
||||||
const evalOrigin = match[3];
|
|
||||||
const evalFile = match[4];
|
|
||||||
const evalLine = Number(match[5]);
|
|
||||||
const evalCol = Number(match[6]);
|
|
||||||
let file = match[7];
|
|
||||||
const lnum = match[8];
|
|
||||||
const col = match[9];
|
|
||||||
const native = match[10] === 'native';
|
|
||||||
const closeParen = match[11] === ')';
|
|
||||||
let method;
|
|
||||||
|
|
||||||
const res: StackData = {};
|
|
||||||
|
|
||||||
if (lnum)
|
|
||||||
res.line = Number(lnum);
|
|
||||||
|
|
||||||
if (col)
|
|
||||||
res.column = Number(col);
|
|
||||||
|
|
||||||
if (closeParen && file) {
|
|
||||||
// make sure parens are balanced
|
|
||||||
// if we have a file like "asdf) [as foo] (xyz.js", then odds are
|
|
||||||
// that the fname should be += " (asdf) [as foo]" and the file
|
|
||||||
// should be just "xyz.js"
|
|
||||||
// walk backwards from the end to find the last unbalanced (
|
|
||||||
let closes = 0;
|
|
||||||
for (let i = file.length - 1; i > 0; i--) {
|
|
||||||
if (file.charAt(i) === ')') {
|
|
||||||
closes++;
|
|
||||||
} else if (file.charAt(i) === '(' && file.charAt(i - 1) === ' ') {
|
|
||||||
closes--;
|
|
||||||
if (closes === -1 && file.charAt(i - 1) === ' ') {
|
|
||||||
const before = file.slice(0, i - 1);
|
|
||||||
const after = file.slice(i + 1);
|
|
||||||
file = after;
|
|
||||||
fname += ` (${before}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fname) {
|
|
||||||
const methodMatch = fname.match(methodRe);
|
|
||||||
if (methodMatch) {
|
|
||||||
fname = methodMatch[1];
|
|
||||||
method = methodMatch[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setFile(res, file, pathSeparator);
|
|
||||||
|
|
||||||
if (ctor)
|
|
||||||
res.isConstructor = true;
|
|
||||||
|
|
||||||
if (evalOrigin) {
|
|
||||||
res.evalOrigin = evalOrigin;
|
|
||||||
res.evalLine = evalLine;
|
|
||||||
res.evalColumn = evalCol;
|
|
||||||
res.evalFile = evalFile && evalFile.replace(/\\/g, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (native)
|
|
||||||
res.native = true;
|
|
||||||
if (fname)
|
|
||||||
res.function = fname;
|
|
||||||
if (method && fname !== method)
|
|
||||||
res.method = method;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setFile(result: StackData, filename: string, pathSeparator: string) {
|
|
||||||
if (filename) {
|
|
||||||
if (filename.startsWith('file://'))
|
|
||||||
filename = fileURLToPath(filename, pathSeparator);
|
|
||||||
result.file = filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const re = new RegExp('^' +
|
|
||||||
// Sometimes we strip out the ' at' because it's noisy
|
|
||||||
'(?:\\s*at )?' +
|
|
||||||
// $1 = ctor if 'new'
|
|
||||||
'(?:(new) )?' +
|
|
||||||
// $2 = function name (can be literally anything)
|
|
||||||
// May contain method at the end as [as xyz]
|
|
||||||
'(?:(.*?) \\()?' +
|
|
||||||
// (eval at <anonymous> (file.js:1:1),
|
|
||||||
// $3 = eval origin
|
|
||||||
// $4:$5:$6 are eval file/line/col, but not normally reported
|
|
||||||
'(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?' +
|
|
||||||
// file:line:col
|
|
||||||
// $7:$8:$9
|
|
||||||
// $10 = 'native' if native
|
|
||||||
'(?:(.+?):(\\d+):(\\d+)|(native))' +
|
|
||||||
// maybe close the paren, then end
|
|
||||||
// if $11 is ), then we only allow balanced parens in the filename
|
|
||||||
// any imbalance is placed on the fname. This is a heuristic, and
|
|
||||||
// bound to be incorrect in some edge cases. The bet is that
|
|
||||||
// having weird characters in method names is more common than
|
|
||||||
// having weird characters in filenames, which seems reasonable.
|
|
||||||
'(\\)?)$'
|
|
||||||
);
|
|
||||||
|
|
||||||
const methodRe = /^(.*?) \[as (.*?)\]$/;
|
|
||||||
|
|
||||||
function fileURLToPath(fileUrl: string, pathSeparator: string): string {
|
|
||||||
if (!fileUrl.startsWith('file://'))
|
|
||||||
return fileUrl;
|
|
||||||
|
|
||||||
let path = decodeURIComponent(fileUrl.slice(7));
|
|
||||||
if (path.startsWith('/') && /^[a-zA-Z]:/.test(path.slice(1)))
|
|
||||||
path = path.slice(1);
|
|
||||||
|
|
||||||
return path.replace(/\//g, pathSeparator);
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import * as playwrightLibrary from 'playwright-core';
|
import * as playwrightLibrary from 'playwright-core';
|
||||||
import { addInternalStackPrefix, asLocator, createGuid, currentZone, debugMode, isString, jsonStringifyForceASCII } from 'playwright-core/lib/utils';
|
import { setBoxedStackPrefixes, asLocator, createGuid, currentZone, debugMode, isString, jsonStringifyForceASCII } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
import { currentTestInfo } from './common/globals';
|
import { currentTestInfo } from './common/globals';
|
||||||
import { rootTestType } from './common/testType';
|
import { rootTestType } from './common/testType';
|
||||||
|
@ -32,7 +32,7 @@ import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions,
|
||||||
export { expect } from './matchers/expect';
|
export { expect } from './matchers/expect';
|
||||||
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
||||||
|
|
||||||
addInternalStackPrefix(path.dirname(require.resolve('../package.json')));
|
setBoxedStackPrefixes([path.dirname(require.resolve('../package.json'))]);
|
||||||
|
|
||||||
if ((process as any)['__pw_initiator__']) {
|
if ((process as any)['__pw_initiator__']) {
|
||||||
const originalStackTraceLimit = Error.stackTraceLimit;
|
const originalStackTraceLimit = Error.stackTraceLimit;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { getPackageManagerExecCommand } from 'playwright-core/lib/utils';
|
import { getPackageManagerExecCommand } from 'playwright-core/lib/utils';
|
||||||
import { parseStackTraceLine } from 'playwright-core/lib/utils';
|
import { parseStackFrame } from 'playwright-core/lib/utils';
|
||||||
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
||||||
import { colors as realColors, noColors } from 'playwright-core/lib/utils';
|
import { colors as realColors, noColors } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
|
@ -522,7 +522,7 @@ export function prepareErrorStack(stack: string): {
|
||||||
const stackLines = lines.slice(firstStackLine);
|
const stackLines = lines.slice(firstStackLine);
|
||||||
let location: Location | undefined;
|
let location: Location | undefined;
|
||||||
for (const line of stackLines) {
|
for (const line of stackLines) {
|
||||||
const frame = parseStackTraceLine(line, path.sep);
|
const frame = parseStackFrame(line, path.sep);
|
||||||
if (!frame || !frame.file)
|
if (!frame || !frame.file)
|
||||||
continue;
|
continue;
|
||||||
if (belongsToNodeModules(frame.file))
|
if (belongsToNodeModules(frame.file))
|
||||||
|
|
|
@ -19,9 +19,8 @@ import * as path from 'path';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
|
|
||||||
import { parseStackTraceLine, sanitizeForFilePath, calculateSha1, formatCallLog, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
|
import { parseStackFrame, sanitizeForFilePath, calculateSha1, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
|
||||||
import { debug, mime, minimatch } from 'playwright-core/lib/utilsBundle';
|
import { colors, debug, mime, minimatch } from 'playwright-core/lib/utilsBundle';
|
||||||
import { nodePlatform } from 'playwright-core/lib/utils';
|
|
||||||
|
|
||||||
import type { Location } from './../types/testReporter';
|
import type { Location } from './../types/testReporter';
|
||||||
import type { TestInfoErrorImpl } from './common/ipc';
|
import type { TestInfoErrorImpl } from './common/ipc';
|
||||||
|
@ -56,7 +55,7 @@ export function filterStackFile(file: string) {
|
||||||
export function filteredStackTrace(rawStack: RawStack): StackFrame[] {
|
export function filteredStackTrace(rawStack: RawStack): StackFrame[] {
|
||||||
const frames: StackFrame[] = [];
|
const frames: StackFrame[] = [];
|
||||||
for (const line of rawStack) {
|
for (const line of rawStack) {
|
||||||
const frame = parseStackTraceLine(line, path.sep);
|
const frame = parseStackFrame(line, path.sep);
|
||||||
if (!frame || !frame.file)
|
if (!frame || !frame.file)
|
||||||
continue;
|
continue;
|
||||||
if (!filterStackFile(frame.file))
|
if (!filterStackFile(frame.file))
|
||||||
|
@ -225,7 +224,14 @@ export function getContainedPath(parentPath: string, subPath: string = ''): stri
|
||||||
|
|
||||||
export const debugTest = debug('pw:test');
|
export const debugTest = debug('pw:test');
|
||||||
|
|
||||||
export const callLogText = (log: string[] | undefined) => formatCallLog(nodePlatform, log);
|
export const callLogText = (log: string[] | undefined) => {
|
||||||
|
if (!log || !log.some(l => !!l))
|
||||||
|
return '';
|
||||||
|
return `
|
||||||
|
Call log:
|
||||||
|
${colors.dim(log.join('\n'))}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
const folderToPackageJsonPath = new Map<string, string>();
|
const folderToPackageJsonPath = new Map<string, string>();
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
import events from 'events';
|
import events from 'events';
|
||||||
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
|
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
|
||||||
import { setUnderTest } from '../../../packages/playwright-core/lib/utils/isomorphic/debug';
|
import { setUnderTest } from '../../../packages/playwright-core/lib/server/utils/debug';
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
import * as common from './utils';
|
import * as common from './utils';
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
|
|
||||||
import { test as it, expect } from '@playwright/test';
|
import { test as it, expect } from '@playwright/test';
|
||||||
import { findRepeatedSubsequences } from '../../../packages/playwright-core/lib/utils/isomorphic/sequence';
|
import { findRepeatedSubsequencesForTest as findRepeatedSubsequences } from '../../../packages/playwright-core/lib/server/callLog';
|
||||||
|
|
||||||
it('should return an empty array when the input is empty', () => {
|
it('should return an empty array when the input is empty', () => {
|
||||||
const input = [];
|
const input = [];
|
||||||
|
|
Loading…
Reference in New Issue