From 8b28e637c8ca117aa225889f2afd2bf8c0d3cb7c Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 14 Feb 2025 15:10:50 -0800 Subject: [PATCH] chrome: remove state from isomorphic utils (#34795) --- packages/playwright-core/src/DEPS.list | 15 +- .../playwright-core/src/browserServerImpl.ts | 12 +- packages/playwright-core/src/cli/DEPS.list | 2 - packages/playwright-core/src/cli/program.ts | 3 +- packages/playwright-core/src/client/DEPS.list | 1 - .../playwright-core/src/client/android.ts | 8 +- .../src/client/browserContext.ts | 7 +- .../playwright-core/src/client/browserType.ts | 2 +- .../src/client/channelOwner.ts | 10 +- .../src/client/clientBundle.ts | 29 ++ .../src/client/clientHelper.ts | 2 +- .../src/client/clientStackTrace.ts | 78 ++++++ .../playwright-core/src/client/connection.ts | 16 +- .../src/client/consoleMessage.ts | 2 +- .../playwright-core/src/client/electron.ts | 6 +- .../src/client/elementHandle.ts | 4 +- .../src/client/eventEmitter.ts | 13 +- packages/playwright-core/src/client/fetch.ts | 6 +- .../playwright-core/src/client/fileUtils.ts | 2 +- packages/playwright-core/src/client/frame.ts | 2 +- .../playwright-core/src/client/network.ts | 6 +- packages/playwright-core/src/client/page.ts | 8 +- .../src/{common => client}/platform.ts | 29 +- .../playwright-core/src/client/selectors.ts | 4 +- .../src/client/timeoutSettings.ts | 76 ++++++ packages/playwright-core/src/client/types.ts | 4 +- packages/playwright-core/src/client/waiter.ts | 2 +- .../playwright-core/src/client/webSocket.ts | 2 +- .../playwright-core/src/inProcessFactory.ts | 21 +- packages/playwright-core/src/inprocess.ts | 3 +- packages/playwright-core/src/outofprocess.ts | 6 +- .../playwright-core/src/protocol/DEPS.list | 1 - .../src/protocol/validatorPrimitives.ts | 6 +- packages/playwright-core/src/remote/DEPS.list | 2 - .../src/remote/playwrightConnection.ts | 3 +- packages/playwright-core/src/server/DEPS.list | 3 +- .../src/server/android/DEPS.list | 2 - .../src/server/android/android.ts | 4 +- .../src/server/android/backendAdb.ts | 2 +- .../src/server/browserContext.ts | 4 +- .../playwright-core/src/server/browserType.ts | 6 +- .../sequence.ts => server/callLog.ts} | 26 +- .../src/server/chromium/chromium.ts | 2 +- .../src/server/chromium/crBrowser.ts | 2 +- .../src/server/chromium/crPage.ts | 2 +- .../src/server/dispatchers/DEPS.list | 1 - .../src/server/dispatchers/dispatcher.ts | 4 +- .../src/server/dispatchers/frameDispatcher.ts | 2 - packages/playwright-core/src/server/dom.ts | 2 +- .../src/server/electron/DEPS.list | 1 - .../src/server/electron/electron.ts | 2 +- packages/playwright-core/src/server/fetch.ts | 2 +- .../src/server/fileUploadUtils.ts | 2 +- packages/playwright-core/src/server/frames.ts | 3 +- .../playwright-core/src/server/harBackend.ts | 2 +- .../src/server/injected/recorder/recorder.ts | 2 +- .../playwright-core/src/server/localUtils.ts | 2 +- .../playwright-core/src/server/network.ts | 2 +- packages/playwright-core/src/server/page.ts | 7 +- .../playwright-core/src/server/recorder.ts | 2 +- .../src/server/recorder/DEPS.list | 2 +- .../src/server/recorder/recorderApp.ts | 2 +- .../src/server/recorder/recorderCollection.ts | 2 +- .../src/server/screenshotter.ts | 2 +- .../isomorphic => server}/timeoutSettings.ts | 3 +- .../src/server/trace/recorder/DEPS.list | 2 - .../src/server/trace/recorder/tracing.ts | 4 +- packages/playwright-core/src/server/types.ts | 4 +- .../src/server/utils/DEPS.list | 1 - .../src/server/utils/crypto.ts | 2 +- .../isomorphic => server/utils}/debug.ts | 16 +- .../src/server/utils/happyEyeballs.ts | 2 +- .../src/server/utils/httpServer.ts | 2 +- .../src/server/utils/nodePlatform.ts | 20 +- .../src/server/utils/socksProxy.ts | 2 +- .../src/server/webkit/wkPage.ts | 3 +- packages/playwright-core/src/utils.ts | 10 +- .../src/utils/isomorphic/assert.ts | 20 ++ .../src/utils/isomorphic/manualPromise.ts | 11 +- .../src/utils/isomorphic/stackTrace.ts | 249 +++++++++--------- .../src/utils/isomorphic/stackUtils.ts | 158 ----------- .../src/{common => utils/isomorphic}/types.ts | 0 packages/playwright/src/index.ts | 4 +- packages/playwright/src/reporters/base.ts | 4 +- packages/playwright/src/util.ts | 16 +- .../events/check-listener-leaks.spec.ts | 2 +- tests/library/unit/sequence.spec.ts | 2 +- 87 files changed, 560 insertions(+), 467 deletions(-) create mode 100644 packages/playwright-core/src/client/clientBundle.ts create mode 100644 packages/playwright-core/src/client/clientStackTrace.ts rename packages/playwright-core/src/{common => client}/platform.ts (81%) create mode 100644 packages/playwright-core/src/client/timeoutSettings.ts rename packages/playwright-core/src/{utils/isomorphic/sequence.ts => server/callLog.ts} (66%) rename packages/playwright-core/src/{utils/isomorphic => server}/timeoutSettings.ts (97%) rename packages/playwright-core/src/{utils/isomorphic => server/utils}/debug.ts (71%) create mode 100644 packages/playwright-core/src/utils/isomorphic/assert.ts delete mode 100644 packages/playwright-core/src/utils/isomorphic/stackUtils.ts rename packages/playwright-core/src/{common => utils/isomorphic}/types.ts (100%) diff --git a/packages/playwright-core/src/DEPS.list b/packages/playwright-core/src/DEPS.list index 2ffa077b4e7cb..d21eb103c4ad9 100644 --- a/packages/playwright-core/src/DEPS.list +++ b/packages/playwright-core/src/DEPS.list @@ -1,22 +1,27 @@ [browserServerImpl.ts] -** +remote/ +server/ +server/utils +utils/isomorphic/ +utilsBundle.ts [androidServerImpl.ts] -** +remote/ +server/ +server/utils +utils/isomorphic/ +utilsBundle.ts [inProcessFactory.ts] ** [inprocess.ts] -common/ utils/ server/utils [outofprocess.ts] client/ -common/ protocol/ utils/ utils/isomorphic server/utils -common/ diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts index 77fb9a984443a..7e6b90741b529 100644 --- a/packages/playwright-core/src/browserServerImpl.ts +++ b/packages/playwright-core/src/browserServerImpl.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { envObjectToArray } from './client/clientHelper'; import { SocksProxy } from './server/utils/socksProxy'; import { PlaywrightServer } from './remote/playwrightServer'; import { helper } from './server/helper'; @@ -25,7 +24,7 @@ import { rewriteErrorMessage } from './utils/isomorphic/stackTrace'; import { ws } from './utilsBundle'; 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 { 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), [], {}); } : 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; +} diff --git a/packages/playwright-core/src/cli/DEPS.list b/packages/playwright-core/src/cli/DEPS.list index 6d8d6a15695c7..06a2e3baae9ad 100644 --- a/packages/playwright-core/src/cli/DEPS.list +++ b/packages/playwright-core/src/cli/DEPS.list @@ -1,7 +1,5 @@ [*] ../../ -../client -../common ../debug/injected ../generated/ ../server/ diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index 360ab4726fbcc..6c0ccd230c822 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -22,7 +22,6 @@ import * as path from 'path'; import * as playwright from '../..'; import { launchBrowserServer, printApiJson, runDriver, runServer } from './driver'; -import { isTargetClosedError } from '../client/errors'; import { registry, writeDockerVersion } from '../server'; import { gracefullyProcessExitDoNotHang } from '../utils'; 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:')) url = 'http://' + url; 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 // in a stray navigation aborted error. We should ignore it. } else { diff --git a/packages/playwright-core/src/client/DEPS.list b/packages/playwright-core/src/client/DEPS.list index e886cdbe634e8..1c44a4a48b98d 100644 --- a/packages/playwright-core/src/client/DEPS.list +++ b/packages/playwright-core/src/client/DEPS.list @@ -1,4 +1,3 @@ [*] -../common/ ../protocol/ ../utils/isomorphic diff --git a/packages/playwright-core/src/client/android.ts b/packages/playwright-core/src/client/android.ts index 134be7c7bba13..c6eb4225b4322 100644 --- a/packages/playwright-core/src/client/android.ts +++ b/packages/playwright-core/src/client/android.ts @@ -20,7 +20,7 @@ import { ChannelOwner } from './channelOwner'; import { TargetClosedError, isTargetClosedError } from './errors'; import { Events } from './events'; import { Waiter } from './waiter'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from './timeoutSettings'; import { isRegExp, isString } from '../utils/isomorphic/rtti'; import { monotonicTime } from '../utils/isomorphic/time'; import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner'; @@ -30,7 +30,7 @@ import type { Page } from './page'; import type * as types from './types'; import type * as api from '../../types/types'; import type { AndroidServerLauncherImpl } from '../androidServerImpl'; -import type { Platform } from '../common/platform'; +import type { Platform } from './platform'; import type * as channels from '@protocol/channels'; type Direction = 'down' | 'up' | 'left' | 'right'; @@ -46,7 +46,7 @@ export class Android extends ChannelOwner implements ap constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidInitializer) { super(parent, type, guid, initializer); - this._timeoutSettings = new TimeoutSettings(); + this._timeoutSettings = new TimeoutSettings(this._platform); } setDefaultTimeout(timeout: number) { @@ -112,7 +112,7 @@ export class AndroidDevice extends ChannelOwner i constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidDeviceInitializer) { super(parent, type, guid, initializer); 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('webViewRemoved', ({ socketName }) => this._onWebViewRemoved(socketName)); this._channel.on('close', () => this._didClose()); diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 21f5a200b79f7..5ed9262b02ca8 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -34,7 +34,7 @@ import { Tracing } from './tracing'; import { Waiter } from './waiter'; import { WebError } from './webError'; import { Worker } from './worker'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from './timeoutSettings'; import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; 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 api from '../../types/types'; 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'; export class BrowserContext extends ChannelOwner implements api.BrowserContext { @@ -56,7 +56,7 @@ export class BrowserContext extends ChannelOwner readonly _browser: Browser | null = null; _browserType: BrowserType | undefined; readonly _bindings = new Map any>(); - _timeoutSettings = new TimeoutSettings(); + _timeoutSettings: TimeoutSettings; _ownerPage: Page | undefined; private _closedPromise: Promise; _options: channels.BrowserNewContextParams = { }; @@ -83,6 +83,7 @@ export class BrowserContext extends ChannelOwner constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.BrowserContextInitializer) { super(parent, type, guid, initializer); + this._timeoutSettings = new TimeoutSettings(this._platform); if (parent instanceof Browser) this._browser = parent; this._browser?._contexts.add(this); diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index e4942bd7f00de..f67c97ced866c 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -19,7 +19,7 @@ import { BrowserContext, prepareBrowserContextParams } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { envObjectToArray } from './clientHelper'; import { Events } from './events'; -import { assert } from '../utils/isomorphic/debug'; +import { assert } from '../utils/isomorphic/assert'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { monotonicTime } from '../utils/isomorphic/time'; import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner'; diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts index 92deaecabd59d..3597afd730589 100644 --- a/packages/playwright-core/src/client/channelOwner.ts +++ b/packages/playwright-core/src/client/channelOwner.ts @@ -16,14 +16,14 @@ import { EventEmitter } from './eventEmitter'; import { ValidationError, maybeFindValidator } from '../protocol/validator'; -import { isUnderTest } from '../utils/isomorphic/debug'; -import { captureLibraryStackTrace, stringifyStackFrames } from '../utils/isomorphic/stackTrace'; +import { captureLibraryStackTrace } from './clientStackTrace'; +import { stringifyStackFrames } from '../utils/isomorphic/stackTrace'; import type { ClientInstrumentation } from './clientInstrumentation'; import type { Connection } from './connection'; import type { Logger } from './types'; import type { ValidatorContext } from '../protocol/validator'; -import type { Platform } from '../common/platform'; +import type { Platform } from './platform'; import type * as channels from '@protocol/channels'; type Listener = (...args: any[]) => void; @@ -181,7 +181,7 @@ export abstract class ChannelOwner\n' + e.stack : ''; + const innerError = ((process.env.PWDEBUGIMPL || this._platform.isUnderTest()) && e.stack) ? '\n\n' + e.stack : ''; if (apiZone.apiName && !apiZone.apiName.includes('')) e.message = apiZone.apiName + ': ' + e.message; const stackFrames = '\n' + stringifyStackFrames(stackTrace.frames).join('\n') + innerError; diff --git a/packages/playwright-core/src/client/clientBundle.ts b/packages/playwright-core/src/client/clientBundle.ts new file mode 100644 index 0000000000000..a81e13daa9020 --- /dev/null +++ b/packages/playwright-core/src/client/clientBundle.ts @@ -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); +} diff --git a/packages/playwright-core/src/client/clientHelper.ts b/packages/playwright-core/src/client/clientHelper.ts index afb6077c13b8e..aecc92a156380 100644 --- a/packages/playwright-core/src/client/clientHelper.ts +++ b/packages/playwright-core/src/client/clientHelper.ts @@ -18,7 +18,7 @@ import { isString } from '../utils/isomorphic/rtti'; 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 }[] { const result: { name: string, value: string }[] = []; diff --git a/packages/playwright-core/src/client/clientStackTrace.ts b/packages/playwright-core/src/client/clientStackTrace.ts new file mode 100644 index 0000000000000..8a18787517329 --- /dev/null +++ b/packages/playwright-core/src/client/clientStackTrace.ts @@ -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 + }; +} diff --git a/packages/playwright-core/src/client/connection.ts b/packages/playwright-core/src/client/connection.ts index 9f2745a8ec26c..ad5e16f866763 100644 --- a/packages/playwright-core/src/client/connection.ts +++ b/packages/playwright-core/src/client/connection.ts @@ -14,7 +14,6 @@ * limitations under the License. */ - import { EventEmitter } from './eventEmitter'; import { Android, AndroidDevice, AndroidSocket } from './android'; import { Artifact } from './artifact'; @@ -42,12 +41,12 @@ import { Tracing } from './tracing'; import { Worker } from './worker'; import { WritableStream } from './writableStream'; 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 { HeadersArray } from './types'; import type { ValidatorContext } from '../protocol/validator'; -import type { Platform } from '../common/platform'; +import type { Platform } from './platform'; import type * as channels from '@protocol/channels'; class Root extends ChannelOwner { @@ -83,7 +82,7 @@ export class Connection extends EventEmitter { // Used from @playwright/test fixtures -> TODO remove? readonly headers: HeadersArray; - constructor(localUtils: LocalUtils | undefined, platform: Platform, instrumentation: ClientInstrumentation | undefined, headers: HeadersArray) { + constructor(platform: Platform, localUtils?: LocalUtils, instrumentation?: ClientInstrumentation, headers: HeadersArray = []) { super(); this._instrumentation = instrumentation || createInstrumentation(); this._localUtils = localUtils; @@ -333,3 +332,12 @@ export class Connection extends EventEmitter { 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'))} +`; +} diff --git a/packages/playwright-core/src/client/consoleMessage.ts b/packages/playwright-core/src/client/consoleMessage.ts index 5d215cf2ad8b4..9cf9acd0ba399 100644 --- a/packages/playwright-core/src/client/consoleMessage.ts +++ b/packages/playwright-core/src/client/consoleMessage.ts @@ -18,7 +18,7 @@ import { JSHandle } from './jsHandle'; import { Page } from './page'; 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'; type ConsoleMessageLocation = channels.BrowserContextConsoleEvent['location']; diff --git a/packages/playwright-core/src/client/electron.ts b/packages/playwright-core/src/client/electron.ts index 90ae6dc4d42de..f721d89f07a89 100644 --- a/packages/playwright-core/src/client/electron.ts +++ b/packages/playwright-core/src/client/electron.ts @@ -22,7 +22,7 @@ import { TargetClosedError, isTargetClosedError } from './errors'; import { Events } from './events'; import { JSHandle, parseResult, serializeArgument } from './jsHandle'; import { Waiter } from './waiter'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from './timeoutSettings'; import type { Page } from './page'; import type { BrowserContextOptions, Env, Headers, WaitForEventOptions } from './types'; @@ -66,7 +66,7 @@ export class Electron extends ChannelOwner implements export class ElectronApplication extends ChannelOwner implements api.ElectronApplication { readonly _context: BrowserContext; private _windows = new Set(); - private _timeoutSettings = new TimeoutSettings(); + private _timeoutSettings: TimeoutSettings; static from(electronApplication: channels.ElectronApplicationChannel): ElectronApplication { return (electronApplication as any)._object; @@ -74,6 +74,8 @@ export class ElectronApplication extends ChannelOwner extends JSHandle implements api.ElementHandle { diff --git a/packages/playwright-core/src/client/eventEmitter.ts b/packages/playwright-core/src/client/eventEmitter.ts index a0781534e3540..73d048b27413b 100644 --- a/packages/playwright-core/src/client/eventEmitter.ts +++ b/packages/playwright-core/src/client/eventEmitter.ts @@ -22,18 +22,19 @@ * 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 { Platform } from './platform'; type EventType = string | symbol; type Listener = (...args: any[]) => any; type EventMap = Record; -let defaultMaxListenersProvider = () => 10; +let platform = emptyPlatform; -export function setDefaultMaxListenersProvider(provider: () => number) { - defaultMaxListenersProvider = provider; +export function setPlatformForEventEmitter(p: Platform) { + platform = p; } export class EventEmitter implements EventEmitterType { @@ -62,7 +63,7 @@ export class EventEmitter implements EventEmitterType { } getMaxListeners(): number { - return this._maxListeners === undefined ? defaultMaxListenersProvider() : this._maxListeners; + return this._maxListeners === undefined ? platform.defaultMaxListeners() : this._maxListeners; } emit(type: EventType, ...args: any[]): boolean { @@ -160,7 +161,7 @@ export class EventEmitter implements EventEmitterType { w.emitter = this; w.type = type; w.count = existing.length; - if (!isUnderTest()) { + if (!platform.isUnderTest()) { // eslint-disable-next-line no-console console.warn(w); } diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index 41070b86654e5..8bdf38631deb5 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -19,7 +19,7 @@ import { ChannelOwner } from './channelOwner'; import { TargetClosedError, isTargetClosedError } from './errors'; import { RawHeaders } from './network'; import { Tracing } from './tracing'; -import { assert } from '../utils/isomorphic/debug'; +import { assert } from '../utils/isomorphic/assert'; import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; 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 { Serializable } from '../../types/structs'; import type * as api from '../../types/types'; -import type { HeadersArray, NameValue } from '../common/types'; -import type { Platform } from '../common/platform'; +import type { HeadersArray, NameValue } from '../utils/isomorphic/types'; +import type { Platform } from './platform'; import type * as channels from '@protocol/channels'; import type * as fs from 'fs'; diff --git a/packages/playwright-core/src/client/fileUtils.ts b/packages/playwright-core/src/client/fileUtils.ts index 0d21b195c09ea..1eb483394655c 100644 --- a/packages/playwright-core/src/client/fileUtils.ts +++ b/packages/playwright-core/src/client/fileUtils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Platform } from '../common/platform'; +import type { Platform } from './platform'; // Keep in sync with the server. export const fileUploadSizeLimit = 50 * 1024 * 1024; diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts index ecbfe3cb1188c..69d7e2b73c9ba 100644 --- a/packages/playwright-core/src/client/frame.ts +++ b/packages/playwright-core/src/client/frame.ts @@ -25,7 +25,7 @@ import { FrameLocator, Locator, testIdAttributeName } from './locator'; import * as network from './network'; import { kLifecycleEvents } from './types'; 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 { urlMatches } from '../utils/isomorphic/urlMatch'; diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index a4f5ff68d0c75..01525b2939ff2 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -21,7 +21,7 @@ import { APIResponse } from './fetch'; import { Frame } from './frame'; import { Waiter } from './waiter'; import { Worker } from './worker'; -import { assert } from '../utils/isomorphic/debug'; +import { assert } from '../utils/isomorphic/assert'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { urlMatches } from '../utils/isomorphic/urlMatch'; 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 { Serializable } from '../../types/structs'; 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 * as channels from '@protocol/channels'; -import type { Platform, Zone } from '../common/platform'; +import type { Platform, Zone } from './platform'; export type NetworkCookie = { name: string, diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index cb6d0656b852d..0aa5aeb66795e 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -33,8 +33,8 @@ import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRou import { Video } from './video'; import { Waiter } from './waiter'; import { Worker } from './worker'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; -import { assert } from '../utils/isomorphic/debug'; +import { TimeoutSettings } from './timeoutSettings'; +import { assert } from '../utils/isomorphic/assert'; import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils'; @@ -118,7 +118,7 @@ export class Page extends ChannelOwner implements api.Page constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.PageInitializer) { super(parent, type, guid, initializer); 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.keyboard = new Keyboard(this); @@ -799,7 +799,7 @@ export class Page extends ChannelOwner implements api.Page } async pause(_options?: { __testHookKeepTestTimeout: boolean }) { - if (this._platform.isDebuggerAttached()) + if (this._platform.isJSDebuggerAttached()) return; const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout(); const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout(); diff --git a/packages/playwright-core/src/common/platform.ts b/packages/playwright-core/src/client/platform.ts similarity index 81% rename from packages/playwright-core/src/common/platform.ts rename to packages/playwright-core/src/client/platform.ts index 25819b9a6f93e..31d67053783bb 100644 --- a/packages/playwright-core/src/common/platform.ts +++ b/packages/playwright-core/src/client/platform.ts @@ -18,8 +18,8 @@ import { webColors, noColors } from '../utils/isomorphic/colors'; import type * as fs from 'fs'; import type * as path from 'path'; -import type { Colors } from '../utils/isomorphic/colors'; import type { Readable, Writable } from 'stream'; +import type { Colors } from '@isomorphic/colors'; import type * as channels from '@protocol/channels'; export type Zone = { @@ -39,17 +39,22 @@ const noopZone: Zone = { export type Platform = { name: 'node' | 'web' | 'empty'; - calculateSha1(text: string): Promise; + boxedStackPrefixes: () => string[]; + calculateSha1: (text: string) => Promise; colors: Colors; + coreDir?: string; createGuid: () => string; + defaultMaxListeners: () => number; fs: () => typeof fs; inspectCustom: symbol | undefined; - isDebuggerAttached(): boolean; - isLogEnabled(name: 'api' | 'channel'): boolean; - log(name: 'api' | 'channel', message: string | Error | object): void; + isDebugMode: () => boolean; + isJSDebuggerAttached: () => boolean; + isLogEnabled: (name: 'api' | 'channel') => boolean; + isUnderTest: () => boolean, + log: (name: 'api' | 'channel', message: string | Error | object) => void; path: () => typeof path; pathSeparator: string; - streamFile(path: string, writable: Writable): Promise, + streamFile: (path: string, writable: Writable) => Promise, streamReadable: (channel: channels.StreamChannel) => Readable, streamWritable: (channel: channels.WritableStreamChannel) => Writable, zones: { empty: Zone, current: () => Zone; }; @@ -58,6 +63,8 @@ export type Platform = { export const emptyPlatform: Platform = { name: 'empty', + boxedStackPrefixes: () => [], + calculateSha1: async () => { throw new Error('Not implemented'); }, @@ -68,18 +75,24 @@ export const emptyPlatform: Platform = { throw new Error('Not implemented'); }, + defaultMaxListeners: () => 10, + fs: () => { throw new Error('Not implemented'); }, inspectCustom: undefined, - isDebuggerAttached: () => false, + isDebugMode: () => false, + + isJSDebuggerAttached: () => false, isLogEnabled(name: 'api' | 'channel') { return false; }, + isUnderTest: () => false, + log(name: 'api' | 'channel', message: string | Error | object) { }, path: () => { @@ -108,6 +121,8 @@ export const webPlatform: Platform = { name: 'web', + boxedStackPrefixes: () => [], + calculateSha1: async (text: string) => { const bytes = new TextEncoder().encode(text); const hashBuffer = await window.crypto.subtle.digest('SHA-1', bytes); diff --git a/packages/playwright-core/src/client/selectors.ts b/packages/playwright-core/src/client/selectors.ts index 3eca51bf36d60..d93723ab9a797 100644 --- a/packages/playwright-core/src/client/selectors.ts +++ b/packages/playwright-core/src/client/selectors.ts @@ -17,12 +17,12 @@ import { ChannelOwner } from './channelOwner'; import { evaluationScript } from './clientHelper'; import { setTestIdAttribute, testIdAttributeName } from './locator'; -import { emptyPlatform } from '../common/platform'; +import { emptyPlatform } from './platform'; import type { SelectorEngine } from './types'; import type * as api from '../../types/types'; import type * as channels from '@protocol/channels'; -import type { Platform } from '../common/platform'; +import type { Platform } from './platform'; let platform = emptyPlatform; diff --git a/packages/playwright-core/src/client/timeoutSettings.ts b/packages/playwright-core/src/client/timeoutSettings.ts new file mode 100644 index 0000000000000..90cb7e7558670 --- /dev/null +++ b/packages/playwright-core/src/client/timeoutSettings.ts @@ -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; + } +} diff --git a/packages/playwright-core/src/client/types.ts b/packages/playwright-core/src/client/types.ts index 2cad7757120e3..29dcd5112cb25 100644 --- a/packages/playwright-core/src/client/types.ts +++ b/packages/playwright-core/src/client/types.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import type { Size } from '../common/types'; +import type { Size } from '../utils/isomorphic/types'; 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'; export interface Logger { diff --git a/packages/playwright-core/src/client/waiter.ts b/packages/playwright-core/src/client/waiter.ts index 7d07d2128ed25..8c01193d7a3a6 100644 --- a/packages/playwright-core/src/client/waiter.ts +++ b/packages/playwright-core/src/client/waiter.ts @@ -20,7 +20,7 @@ import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace'; import type { ChannelOwner } from './channelOwner'; import type * as channels from '@protocol/channels'; import type { EventEmitter } from 'events'; -import type { Zone } from '../common/platform'; +import type { Zone } from './platform'; export class Waiter { private _dispose: (() => void)[]; diff --git a/packages/playwright-core/src/client/webSocket.ts b/packages/playwright-core/src/client/webSocket.ts index cef07a4dfa3e1..6fdd56aa4ad31 100644 --- a/packages/playwright-core/src/client/webSocket.ts +++ b/packages/playwright-core/src/client/webSocket.ts @@ -24,7 +24,7 @@ export async function connectOverWebSocket(parentConnection: Connection, params: const localUtils = parentConnection.localUtils(); const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport(); 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.on('close', () => transport.close()); diff --git a/packages/playwright-core/src/inProcessFactory.ts b/packages/playwright-core/src/inProcessFactory.ts index a53aa8c2e3ce3..5e5bad19e971f 100644 --- a/packages/playwright-core/src/inProcessFactory.ts +++ b/packages/playwright-core/src/inProcessFactory.ts @@ -14,34 +14,21 @@ * limitations under the License. */ -import * as path from 'path'; -import { EventEmitter } from 'events'; - import { AndroidServerLauncherImpl } from './androidServerImpl'; import { BrowserServerLauncherImpl } from './browserServerImpl'; -import { Connection } from './client/connection'; +import { createConnectionFactory } from './client/clientBundle'; 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 { setPlatformForSelectors } from './client/selectors'; -import { setDefaultMaxListenersProvider } from './client/eventEmitter'; import type { Playwright as PlaywrightAPI } from './client/playwright'; 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' }); - setDebugMode(getFromENV('PWDEBUG') || ''); - setPlatformForSelectors(nodePlatform); - setDefaultMaxListenersProvider(() => EventEmitter.defaultMaxListeners); - - setLibraryStackPrefix(path.join(__dirname, '..')); - const clientConnection = new Connection(undefined, platform, undefined, []); + const clientConnection = connectionFactory(); clientConnection.useRawBuffers(); const dispatcherConnection = new DispatcherConnection(true /* local */); diff --git a/packages/playwright-core/src/inprocess.ts b/packages/playwright-core/src/inprocess.ts index fc0550e9249a9..90b1bf499d401 100644 --- a/packages/playwright-core/src/inprocess.ts +++ b/packages/playwright-core/src/inprocess.ts @@ -15,6 +15,5 @@ */ import { createInProcessPlaywright } from './inProcessFactory'; -import { nodePlatform } from './server/utils/nodePlatform'; -module.exports = createInProcessPlaywright(nodePlatform); +module.exports = createInProcessPlaywright(); diff --git a/packages/playwright-core/src/outofprocess.ts b/packages/playwright-core/src/outofprocess.ts index c2427b818d156..6906b7ea1ca66 100644 --- a/packages/playwright-core/src/outofprocess.ts +++ b/packages/playwright-core/src/outofprocess.ts @@ -17,13 +17,15 @@ import * as childProcess from 'child_process'; import * as path from 'path'; -import { Connection } from './client/connection'; +import { createConnectionFactory } from './client/clientBundle'; import { PipeTransport } from './server/utils/pipeTransport'; import { ManualPromise } from './utils/isomorphic/manualPromise'; import { nodePlatform } from './server/utils/nodePlatform'; import type { Playwright } from './client/playwright'; +const connectionFactory = createConnectionFactory(nodePlatform); + export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise }> { const client = new PlaywrightClient(env); const playwright = await client._playwright; @@ -48,7 +50,7 @@ class PlaywrightClient { this._driverProcess.unref(); 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!); connection.onmessage = message => transport.send(JSON.stringify(message)); transport.onmessage = message => connection.dispatch(JSON.parse(message)); diff --git a/packages/playwright-core/src/protocol/DEPS.list b/packages/playwright-core/src/protocol/DEPS.list index dbdeafe86cc2a..d4e5c2cb00b8a 100644 --- a/packages/playwright-core/src/protocol/DEPS.list +++ b/packages/playwright-core/src/protocol/DEPS.list @@ -1,3 +1,2 @@ [*] -../common/ ../utils/isomorphic diff --git a/packages/playwright-core/src/protocol/validatorPrimitives.ts b/packages/playwright-core/src/protocol/validatorPrimitives.ts index f57a63aceac1c..2c1a097da5e52 100644 --- a/packages/playwright-core/src/protocol/validatorPrimitives.ts +++ b/packages/playwright-core/src/protocol/validatorPrimitives.ts @@ -14,7 +14,11 @@ * 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 type Validator = (arg: any, path: string, context: ValidatorContext) => any; diff --git a/packages/playwright-core/src/remote/DEPS.list b/packages/playwright-core/src/remote/DEPS.list index bf3843dca07f5..f9c5148cac6fa 100644 --- a/packages/playwright-core/src/remote/DEPS.list +++ b/packages/playwright-core/src/remote/DEPS.list @@ -1,6 +1,4 @@ [*] -../client/ -../common/ ../server/ ../server/android/ ../server/dispatchers/ diff --git a/packages/playwright-core/src/remote/playwrightConnection.ts b/packages/playwright-core/src/remote/playwrightConnection.ts index 00ca33b35351b..c5c3c36b02134 100644 --- a/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/packages/playwright-core/src/remote/playwrightConnection.ts @@ -20,7 +20,8 @@ import { AndroidDevice } from '../server/android/android'; import { Browser } from '../server/browser'; import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher'; 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 { monotonicTime } from '../utils'; import { debugLogger } from '../server/utils/debugLogger'; diff --git a/packages/playwright-core/src/server/DEPS.list b/packages/playwright-core/src/server/DEPS.list index 6b389baead029..fa67d86bede87 100644 --- a/packages/playwright-core/src/server/DEPS.list +++ b/packages/playwright-core/src/server/DEPS.list @@ -1,8 +1,7 @@ [*] -../common/ ../generated/ ../protocol/ -../utils/ +../utils ../utils/isomorphic/ ../utilsBundle.ts ../zipBundle.ts diff --git a/packages/playwright-core/src/server/android/DEPS.list b/packages/playwright-core/src/server/android/DEPS.list index b852561b95bcd..1ecc153814c31 100644 --- a/packages/playwright-core/src/server/android/DEPS.list +++ b/packages/playwright-core/src/server/android/DEPS.list @@ -1,8 +1,6 @@ [*] ../ -../../common/ ../../protocol/ -../../utils/ ../../utils/isomorphic/ ../../utilsBundle.ts ../chromium/ diff --git a/packages/playwright-core/src/server/android/android.ts b/packages/playwright-core/src/server/android/android.ts index a4bf8f8ded8ad..5215cc6d0136a 100644 --- a/packages/playwright-core/src/server/android/android.ts +++ b/packages/playwright-core/src/server/android/android.ts @@ -19,10 +19,10 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from '../timeoutSettings'; import { PipeTransport } from '../utils/pipeTransport'; import { createGuid } from '../utils/crypto'; -import { isUnderTest } from '../../utils/isomorphic/debug'; +import { isUnderTest } from '../utils/debug'; import { getPackageManagerExecCommand } from '../utils/env'; import { makeWaitForNextTask } from '../utils/task'; import { RecentLogsCollector } from '../utils/debugLogger'; diff --git a/packages/playwright-core/src/server/android/backendAdb.ts b/packages/playwright-core/src/server/android/backendAdb.ts index 036c74703e110..4b1787a78ba83 100644 --- a/packages/playwright-core/src/server/android/backendAdb.ts +++ b/packages/playwright-core/src/server/android/backendAdb.ts @@ -17,7 +17,7 @@ import { EventEmitter } from 'events'; import * as net from 'net'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; import { createGuid } from '../utils/crypto'; import { debug } from '../../utilsBundle'; diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index fd50994343994..3fdb08577b712 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -18,9 +18,9 @@ import * as fs from 'fs'; import * as path from 'path'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from './timeoutSettings'; import { createGuid } from './utils/crypto'; -import { debugMode } from '../utils/isomorphic/debug'; +import { debugMode } from './utils/debug'; import { Clock } from './clock'; import { Debugger } from './debugger'; import { BrowserContextAPIRequestContext } from './fetch'; diff --git a/packages/playwright-core/src/server/browserType.ts b/packages/playwright-core/src/server/browserType.ts index 7fe3844c7d05f..0ecfccc510a56 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -19,8 +19,10 @@ import * as os from 'os'; import * as path from 'path'; import { normalizeProxySettings, validateBrowserContextOptions } from './browserContext'; -import { DEFAULT_TIMEOUT, TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; -import { ManualPromise, assert, debugMode } from '../utils'; +import { DEFAULT_TIMEOUT, TimeoutSettings } from './timeoutSettings'; +import { debugMode } from './utils/debug'; +import { assert } from '../utils/isomorphic/assert'; +import { ManualPromise } from '../utils/isomorphic/manualPromise'; import { existsAsync } from './utils/fileUtils'; import { helper } from './helper'; import { SdkObject } from './instrumentation'; diff --git a/packages/playwright-core/src/utils/isomorphic/sequence.ts b/packages/playwright-core/src/server/callLog.ts similarity index 66% rename from packages/playwright-core/src/utils/isomorphic/sequence.ts rename to packages/playwright-core/src/server/callLog.ts index b063e5c488d71..4de5a907d6029 100644 --- a/packages/playwright-core/src/utils/isomorphic/sequence.ts +++ b/packages/playwright-core/src/server/callLog.ts @@ -1,7 +1,7 @@ /** * 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 obtain a copy of the License at * @@ -14,7 +14,27 @@ * 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 result = []; let i = 0; @@ -64,3 +84,5 @@ export function findRepeatedSubsequences(s: string[]): { sequence: string[]; cou return result; } + +export const findRepeatedSubsequencesForTest = findRepeatedSubsequences; diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 3f06e135cd7d8..9cbb84b2acf81 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -22,7 +22,7 @@ import * as path from 'path'; import { chromiumSwitches } from './chromiumSwitches'; import { CRBrowser } from './crBrowser'; import { kBrowserCloseMessageId } from './crConnection'; -import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from '../timeoutSettings'; import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; import { RecentLogsCollector } from '../utils/debugLogger'; diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index 7cc6030486cfe..fe76876a92d55 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -17,7 +17,7 @@ import * as path from 'path'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; import { createGuid } from '../utils/crypto'; import { Artifact } from '../artifact'; import { Browser } from '../browser'; diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index 34470470dc9c3..caa95a80c9023 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -17,7 +17,7 @@ import * as path from 'path'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; import { createGuid } from '../utils/crypto'; import { eventsHelper } from '../utils/eventsHelper'; import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace'; diff --git a/packages/playwright-core/src/server/dispatchers/DEPS.list b/packages/playwright-core/src/server/dispatchers/DEPS.list index cefc3fa04cf3c..b91f84e960ecb 100644 --- a/packages/playwright-core/src/server/dispatchers/DEPS.list +++ b/packages/playwright-core/src/server/dispatchers/DEPS.list @@ -1,5 +1,4 @@ [*] -../../common/ ../../generated/ ../../protocol/ ../../utils/ diff --git a/packages/playwright-core/src/server/dispatchers/dispatcher.ts b/packages/playwright-core/src/server/dispatchers/dispatcher.ts index 9912de9849094..c6eb6f786890b 100644 --- a/packages/playwright-core/src/server/dispatchers/dispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/dispatcher.ts @@ -18,10 +18,12 @@ import { EventEmitter } from 'events'; import { eventsHelper } from '../utils/eventsHelper'; 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 { SdkObject } from '../instrumentation'; import { isProtocolError } from '../protocolError'; +import { compressCallLog } from '../callLog'; import type { CallMetadata } from '../instrumentation'; import type { PlaywrightDispatcher } from './playwrightDispatcher'; diff --git a/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts b/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts index 7e99511d3325a..ef8b891d2f788 100644 --- a/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts @@ -20,7 +20,6 @@ import { ElementHandleDispatcher } from './elementHandlerDispatcher'; import { parseArgument, serializeResult } from './jsHandleDispatcher'; import { ResponseDispatcher } from './networkDispatchers'; import { RequestDispatcher } from './networkDispatchers'; -import { debugAssert } from '../../utils'; import { parseAriaSnapshotUnsafe } from '../../utils/isomorphic/ariaSnapshot'; import { yaml } from '../../utilsBundle'; @@ -50,7 +49,6 @@ export class FrameDispatcher extends Dispatcher(frame._page); super(pageDispatcher || scope, frame, 'Frame', { diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index 7bb1862645f1f..53462df6275fe 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -30,7 +30,7 @@ import type { Page } from './page'; import type { Progress } from './progress'; import type { ScreenshotOptions } from './screenshotter'; 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'; diff --git a/packages/playwright-core/src/server/electron/DEPS.list b/packages/playwright-core/src/server/electron/DEPS.list index 1acb5c5a0e03c..193adeb6d633c 100644 --- a/packages/playwright-core/src/server/electron/DEPS.list +++ b/packages/playwright-core/src/server/electron/DEPS.list @@ -1,6 +1,5 @@ [*] ../ -../../common/ ../../utils/ ../../utils/isomorphic/ ../chromium/ diff --git a/packages/playwright-core/src/server/electron/electron.ts b/packages/playwright-core/src/server/electron/electron.ts index 4713324ed1b99..d6fe4b01dd089 100644 --- a/packages/playwright-core/src/server/electron/electron.ts +++ b/packages/playwright-core/src/server/electron/electron.ts @@ -19,7 +19,7 @@ import * as os from 'os'; import * as path from 'path'; import * as readline from 'readline'; -import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from '../timeoutSettings'; import { ManualPromise } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; import { RecentLogsCollector } from '../utils/debugLogger'; diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 6e1d6d02f11b3..23f0c87a5140d 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -21,7 +21,7 @@ import { TLSSocket } from 'tls'; import * as url from 'url'; import * as zlib from 'zlib'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { TimeoutSettings } from './timeoutSettings'; import { assert, constructURLBasedOnBaseURL, eventsHelper, monotonicTime } from '../utils'; import { createGuid } from './utils/crypto'; import { getUserAgent } from './utils/userAgent'; diff --git a/packages/playwright-core/src/server/fileUploadUtils.ts b/packages/playwright-core/src/server/fileUploadUtils.ts index 5a15617c8b99b..d2e18572b9d22 100644 --- a/packages/playwright-core/src/server/fileUploadUtils.ts +++ b/packages/playwright-core/src/server/fileUploadUtils.ts @@ -17,7 +17,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { assert } from '../utils/isomorphic/debug'; +import { assert } from '../utils/isomorphic/assert'; import { mime } from '../utilsBundle'; import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher'; diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index dee2ad7f08010..69ef1ecba5d74 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -27,12 +27,13 @@ import * as network from './network'; import { Page } from './page'; import { ProgressController } from './progress'; 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 { debugLogger } from './utils/debugLogger'; import { eventsHelper } from './utils/eventsHelper'; import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser'; import { ManualPromise } from '../utils/isomorphic/manualPromise'; +import { compressCallLog } from './callLog'; import type { ConsoleMessage } from './console'; import type { Dialog } from './dialog'; diff --git a/packages/playwright-core/src/server/harBackend.ts b/packages/playwright-core/src/server/harBackend.ts index 0814be9c5f587..4a59b9ab609a8 100644 --- a/packages/playwright-core/src/server/harBackend.ts +++ b/packages/playwright-core/src/server/harBackend.ts @@ -20,7 +20,7 @@ import * as path from 'path'; import { createGuid } from './utils/crypto'; 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'; const redirectStatus = [301, 302, 303, 307, 308]; diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index 27e4f35a10c0e..68fa96e77f8c4 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -16,7 +16,7 @@ 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 { InjectedScript } from '../injectedScript'; import type { ElementText } from '../selectorUtils'; diff --git a/packages/playwright-core/src/server/localUtils.ts b/packages/playwright-core/src/server/localUtils.ts index 1f5f0064b5ceb..7751d20fdbfd1 100644 --- a/packages/playwright-core/src/server/localUtils.ts +++ b/packages/playwright-core/src/server/localUtils.ts @@ -24,7 +24,7 @@ import { ManualPromise } from '../utils/isomorphic/manualPromise'; import { ZipFile } from './utils/zipFile'; import { yauzl, yazl } from '../zipBundle'; import { serializeClientSideCallMetadata } from '../utils/isomorphic/traceUtils'; -import { assert } from '../utils/isomorphic/debug'; +import { assert } from '../utils/isomorphic/assert'; import { removeFolders } from './utils/fileUtils'; import type * as channels from '@protocol/channels'; diff --git a/packages/playwright-core/src/server/network.ts b/packages/playwright-core/src/server/network.ts index dfee496fc2cd9..e40e0e99a9a20 100644 --- a/packages/playwright-core/src/server/network.ts +++ b/packages/playwright-core/src/server/network.ts @@ -25,7 +25,7 @@ import type * as frames from './frames'; import type * as pages from './page'; import type * as types 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'; diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index d2db2e4211418..e15e2516070da 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -28,14 +28,15 @@ import { parseEvaluationResultValue, source } from './isomorphic/utilityScriptSe import * as js from './javascript'; import { ProgressController } from './progress'; import { Screenshotter, validateScreenshotOptions } from './screenshotter'; -import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; -import { LongStandingScope, assert, compressCallLog, trimStringWithEllipsis } from '../utils'; +import { TimeoutSettings } from './timeoutSettings'; +import { LongStandingScope, assert, trimStringWithEllipsis } from '../utils'; import { createGuid } from './utils/crypto'; import { asLocator } from '../utils'; import { getComparator } from './utils/comparators'; import { debugLogger } from './utils/debugLogger'; import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser'; import { ManualPromise } from '../utils/isomorphic/manualPromise'; +import { compressCallLog } from './callLog'; import type { Artifact } from './artifact'; import type * as dom from './dom'; @@ -45,7 +46,7 @@ import type * as network from './network'; import type { Progress } from './progress'; import type { ScreenshotOptions } from './screenshotter'; 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 * as channels from '@protocol/channels'; diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index 79165df8f27c6..f8fc1402194d6 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -30,7 +30,7 @@ import type { Frame } from './frames'; import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation'; import type { Page } from './page'; 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 * as channels from '@protocol/channels'; import type * as actions from '@recorder/actions'; diff --git a/packages/playwright-core/src/server/recorder/DEPS.list b/packages/playwright-core/src/server/recorder/DEPS.list index b130c181dc1ec..6151537ccb4af 100644 --- a/packages/playwright-core/src/server/recorder/DEPS.list +++ b/packages/playwright-core/src/server/recorder/DEPS.list @@ -4,7 +4,7 @@ ../codegen/languages.ts ../isomorphic/** ../registry/** -../../common/ +../utils/** ../../generated/pollingRecorderSource.ts ../../protocol/ ../../utils/** diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index 79c7d2d4d0746..5233edaeddda9 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -18,7 +18,7 @@ import { EventEmitter } from 'events'; import * as fs from 'fs'; import * as path from 'path'; -import { isUnderTest } from '../../utils'; +import { isUnderTest } from '../utils/debug'; import { mime } from '../../utilsBundle'; import { serverSideCallMetadata } from '../instrumentation'; import { syncLocalStorageWithSettings } from '../launchApp'; diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts index 8c7b344b8e997..b8802a920acf9 100644 --- a/packages/playwright-core/src/server/recorder/recorderCollection.ts +++ b/packages/playwright-core/src/server/recorder/recorderCollection.ts @@ -18,7 +18,7 @@ import { EventEmitter } from 'events'; import { performAction } from './recorderRunner'; import { collapseActions } from './recorderUtils'; -import { isUnderTest } from '../../utils/isomorphic/debug'; +import { isUnderTest } from '../utils/debug'; import { monotonicTime } from '../../utils/isomorphic/time'; import type { Signal } from '../../../../recorder/src/actions'; diff --git a/packages/playwright-core/src/server/screenshotter.ts b/packages/playwright-core/src/server/screenshotter.ts index b32f51a6dea27..77c83b8325f19 100644 --- a/packages/playwright-core/src/server/screenshotter.ts +++ b/packages/playwright-core/src/server/screenshotter.ts @@ -24,7 +24,7 @@ import type { Frame } from './frames'; import type { Page } from './page'; import type { Progress } from './progress'; 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'; diff --git a/packages/playwright-core/src/utils/isomorphic/timeoutSettings.ts b/packages/playwright-core/src/server/timeoutSettings.ts similarity index 97% rename from packages/playwright-core/src/utils/isomorphic/timeoutSettings.ts rename to packages/playwright-core/src/server/timeoutSettings.ts index 02c02a33b53bb..1c71437e2a382 100644 --- a/packages/playwright-core/src/utils/isomorphic/timeoutSettings.ts +++ b/packages/playwright-core/src/server/timeoutSettings.ts @@ -15,8 +15,9 @@ * 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_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes diff --git a/packages/playwright-core/src/server/trace/recorder/DEPS.list b/packages/playwright-core/src/server/trace/recorder/DEPS.list index bf35323ecce4e..195bdb5eb866b 100644 --- a/packages/playwright-core/src/server/trace/recorder/DEPS.list +++ b/packages/playwright-core/src/server/trace/recorder/DEPS.list @@ -1,9 +1,7 @@ [*] ../../ ../../har/ -../../../common/ ../../../protocol/ -../../../utils/ ../../../utilsBundle.ts ../../../utils/isomorphic/ ../../../zipBundle.ts diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 309867f3b017a..41d2680757157 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -20,7 +20,7 @@ import * as path from 'path'; import { Snapshotter } from './snapshotter'; import { commandsWithTracingSnapshots } from '../../../protocol/debug'; -import { assert } from '../../../utils/isomorphic/debug'; +import { assert } from '../../../utils/isomorphic/assert'; import { monotonicTime } from '../../../utils/isomorphic/time'; import { eventsHelper } from '../../utils/eventsHelper'; import { createGuid } from '../../utils/crypto'; @@ -34,7 +34,7 @@ import { SdkObject } from '../../instrumentation'; import { Page } from '../../page'; 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 { ConsoleMessage } from '../../console'; import type { Dialog } from '../../dialog'; diff --git a/packages/playwright-core/src/server/types.ts b/packages/playwright-core/src/server/types.ts index e7bf2c11f36a8..a8681fd6cf902 100644 --- a/packages/playwright-core/src/server/types.ts +++ b/packages/playwright-core/src/server/types.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import type { HeadersArray, Point, Size, TimeoutOptions } from '../common/types'; -export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../common/types'; +import type { HeadersArray, Point, Size, TimeoutOptions } from '../utils/isomorphic/types'; +export type { HeadersArray, Point, Quad, Rect, Size, TimeoutOptions } from '../utils/isomorphic/types'; import type * as channels from '@protocol/channels'; export type StrictOptions = { diff --git a/packages/playwright-core/src/server/utils/DEPS.list b/packages/playwright-core/src/server/utils/DEPS.list index 685e00df46501..8d1b7a30f27b9 100644 --- a/packages/playwright-core/src/server/utils/DEPS.list +++ b/packages/playwright-core/src/server/utils/DEPS.list @@ -1,5 +1,4 @@ [*] -../../common ../../utils ../../utils/isomorphic ../../utilsBundle.ts diff --git a/packages/playwright-core/src/server/utils/crypto.ts b/packages/playwright-core/src/server/utils/crypto.ts index 7189f38b0041c..c3ca75efd7dbb 100644 --- a/packages/playwright-core/src/server/utils/crypto.ts +++ b/packages/playwright-core/src/server/utils/crypto.ts @@ -16,7 +16,7 @@ import * as crypto from 'crypto'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; export function createGuid(): string { return crypto.randomBytes(16).toString('hex'); diff --git a/packages/playwright-core/src/utils/isomorphic/debug.ts b/packages/playwright-core/src/server/utils/debug.ts similarity index 71% rename from packages/playwright-core/src/utils/isomorphic/debug.ts rename to packages/playwright-core/src/server/utils/debug.ts index 1eec988aa9e73..489c772b70fc7 100644 --- a/packages/playwright-core/src/utils/isomorphic/debug.ts +++ b/packages/playwright-core/src/server/utils/debug.ts @@ -14,21 +14,9 @@ * limitations under the License. */ -export function assert(value: any, message?: string): asserts value { - if (!value) - throw new Error(message || 'Assertion error'); -} - -export function debugAssert(value: any, message?: string): asserts value { - if (isUnderTest() && !value) - throw new Error(message); -} +import { getFromENV } from './env'; -let _debugMode: string | undefined; - -export function setDebugMode(mode: string) { - _debugMode = mode; -} +const _debugMode = getFromENV('PWDEBUG') || ''; export function debugMode() { if (_debugMode === 'console') diff --git a/packages/playwright-core/src/server/utils/happyEyeballs.ts b/packages/playwright-core/src/server/utils/happyEyeballs.ts index da3481f4c10a8..c8c0f4182c933 100644 --- a/packages/playwright-core/src/server/utils/happyEyeballs.ts +++ b/packages/playwright-core/src/server/utils/happyEyeballs.ts @@ -20,7 +20,7 @@ import * as https from 'https'; import * as net from 'net'; import * as tls from 'tls'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; import { monotonicTime } from '../../utils/isomorphic/time'; diff --git a/packages/playwright-core/src/server/utils/httpServer.ts b/packages/playwright-core/src/server/utils/httpServer.ts index 0c9f0fe25aaf9..7c8a67e10cc33 100644 --- a/packages/playwright-core/src/server/utils/httpServer.ts +++ b/packages/playwright-core/src/server/utils/httpServer.ts @@ -19,7 +19,7 @@ import * as path from 'path'; import { mime, wsServer } from '../../utilsBundle'; import { createGuid } from './crypto'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; import { createHttpServer } from './network'; diff --git a/packages/playwright-core/src/server/utils/nodePlatform.ts b/packages/playwright-core/src/server/utils/nodePlatform.ts index fcdfebde2a55e..4a363220368da 100644 --- a/packages/playwright-core/src/server/utils/nodePlatform.ts +++ b/packages/playwright-core/src/server/utils/nodePlatform.ts @@ -19,12 +19,14 @@ import * as fs from 'fs'; import * as path from 'path'; import * as util from 'util'; import { Readable, Writable, pipeline } from 'stream'; +import { EventEmitter } from 'events'; import { colors } from '../../utilsBundle'; import { debugLogger } from './debugLogger'; 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 * 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 = { name: 'node', + boxedStackPrefixes: () => boxedStackPrefixes, + calculateSha1: (text: string) => { const sha1 = crypto.createHash('sha1'); sha1.update(text); @@ -65,18 +74,25 @@ export const nodePlatform: Platform = { colors, + coreDir: path.dirname(require.resolve('../../../package.json')), + createGuid: () => crypto.randomBytes(16).toString('hex'), + defaultMaxListeners: () => EventEmitter.defaultMaxListeners, fs: () => fs, inspectCustom: util.inspect.custom, - isDebuggerAttached: () => !!require('inspector').url(), + isDebugMode: () => !!debugMode(), + + isJSDebuggerAttached: () => !!require('inspector').url(), isLogEnabled(name: 'api' | 'channel') { return debugLogger.isEnabled(name); }, + isUnderTest: () => isUnderTest(), + log(name: 'api' | 'channel', message: string | Error | object) { debugLogger.log(name, message); }, diff --git a/packages/playwright-core/src/server/utils/socksProxy.ts b/packages/playwright-core/src/server/utils/socksProxy.ts index c0fe0f126a8c1..bd36ed58f5fd0 100644 --- a/packages/playwright-core/src/server/utils/socksProxy.ts +++ b/packages/playwright-core/src/server/utils/socksProxy.ts @@ -17,7 +17,7 @@ import EventEmitter from 'events'; import * as net from 'net'; -import { assert } from '../../utils/isomorphic/debug'; +import { assert } from '../../utils/isomorphic/assert'; import { createGuid } from './crypto'; import { debugLogger } from './debugLogger'; import { createSocket } from './happyEyeballs'; diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index f9e9517fb210f..c6d0afc9d1209 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -17,7 +17,7 @@ import * as path from 'path'; -import { assert, debugAssert } from '../../utils'; +import { assert } from '../../utils'; import { headersArrayToObject } from '../../utils/isomorphic/headers'; import { createGuid } from '../utils/crypto'; import { eventsHelper } from '../utils/eventsHelper'; @@ -294,7 +294,6 @@ export class WKPage implements PageDelegate { } handleWindowOpen(event: Protocol.Playwright.windowOpenPayload) { - debugAssert(!this._nextWindowOpenPopupFeatures); this._nextWindowOpenPopupFeatures = event.windowFeatures; } diff --git a/packages/playwright-core/src/utils.ts b/packages/playwright-core/src/utils.ts index dc5f28495531e..4f2f04fc110fb 100644 --- a/packages/playwright-core/src/utils.ts +++ b/packages/playwright-core/src/utils.ts @@ -15,24 +15,24 @@ */ 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/manualPromise'; export * from './utils/isomorphic/mimeType'; export * from './utils/isomorphic/multimap'; export * from './utils/isomorphic/rtti'; +export * from './utils/isomorphic/semaphore'; +export * from './utils/isomorphic/stackTrace'; export * from './utils/isomorphic/stringUtils'; export * from './utils/isomorphic/time'; export * from './utils/isomorphic/timeoutRunner'; 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/comparators'; export * from './server/utils/crypto'; +export * from './server/utils/debug'; export * from './server/utils/debugLogger'; export * from './server/utils/env'; export * from './server/utils/eventsHelper'; diff --git a/packages/playwright-core/src/utils/isomorphic/assert.ts b/packages/playwright-core/src/utils/isomorphic/assert.ts new file mode 100644 index 0000000000000..bd0c38d413c3e --- /dev/null +++ b/packages/playwright-core/src/utils/isomorphic/assert.ts @@ -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'); +} diff --git a/packages/playwright-core/src/utils/isomorphic/manualPromise.ts b/packages/playwright-core/src/utils/isomorphic/manualPromise.ts index a5034e05ece5f..467b8de0dba2e 100644 --- a/packages/playwright-core/src/utils/isomorphic/manualPromise.ts +++ b/packages/playwright-core/src/utils/isomorphic/manualPromise.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { captureRawStack } from './stackTrace'; + export class ManualPromise extends Promise { private _resolve!: (t: T) => 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'); 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'); -} diff --git a/packages/playwright-core/src/utils/isomorphic/stackTrace.ts b/packages/playwright-core/src/utils/isomorphic/stackTrace.ts index 26b79c5c27e2f..87d9e84eaab5c 100644 --- a/packages/playwright-core/src/utils/isomorphic/stackTrace.ts +++ b/packages/playwright-core/src/utils/isomorphic/stackTrace.ts @@ -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"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Copyright (c) 2016-2023 Isaac Z. Schlueter i@izs.me, James Talmage james@talmage.io (github.com/jamestalmage), and + * Contributors * - * 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 - * 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. + * 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. */ -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: 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 type RawStack = string[]; -export const setLibraryStackPrefix = (prefix: string) => { - coreDir = prefix; - playwrightStackPrefixes.push(prefix); +export type StackFrame = { + file: string, + line: number, + column: number, + function?: string, }; -export type RawStack = string[]; - export function captureRawStack(): RawStack { const stackTraceLimit = Error.stackTraceLimit; Error.stackTraceLimit = 50; @@ -66,61 +37,82 @@ export function captureRawStack(): RawStack { return stack.split('\n'); } -export function captureLibraryStackTrace(pathSeparator: string): { frames: StackFrame[], apiName: string } { - const stack = captureRawStack(); +export function parseStackFrame(text: string, pathSeparator: string): StackFrame | null { + const match = text && text.match(re); + if (!match) + return null; + + let fname = match[2]; + let file = match[7]; + if (!file) + 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] === ')'; - type ParsedFrame = { - frame: StackFrame; - frameText: string; - isPlaywrightLibrary: boolean; + 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 = ''; - - // 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; + + if (line) + frame.line = Number(line); + + if (column) + frame.column = Number(column); + + 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; + } + } } } - 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]; + if (fname) { + const methodMatch = fname.match(methodRe); + if (methodMatch) + fname = methodMatch[1]; } - // This is for the inspector so that it did not include the test runner stack frames. - parsedFrames = parsedFrames.filter(f => { - if (process.env.PWDEBUGIMPL) - return true; - if (playwrightStackPrefixes.some(prefix => f.frame.file.startsWith(prefix))) - return false; - return true; - }); + if (file) { + if (file.startsWith('file://')) + file = fileURLToPath(file, pathSeparator); + frame.file = file; + } - return { - frames: parsedFrames.map(p => p.frame), - apiName - }; + if (fname) + frame.function = fname; + + return frame; +} + +export function rewriteErrorMessage(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[] { @@ -142,31 +134,40 @@ export function splitErrorMessage(message: string): { name: string, message: str }; } -export function formatCallLog(platform: Platform, log: string[] | undefined): string { - if (!log || !log.some(l => !!l)) - return ''; - return ` -Call log: -${platform.colors.dim(log.join('\n'))} -`; -} - -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; +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 (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); } diff --git a/packages/playwright-core/src/utils/isomorphic/stackUtils.ts b/packages/playwright-core/src/utils/isomorphic/stackUtils.ts deleted file mode 100644 index 20be8081afc9d..0000000000000 --- a/packages/playwright-core/src/utils/isomorphic/stackUtils.ts +++ /dev/null @@ -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 (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); -} diff --git a/packages/playwright-core/src/common/types.ts b/packages/playwright-core/src/utils/isomorphic/types.ts similarity index 100% rename from packages/playwright-core/src/common/types.ts rename to packages/playwright-core/src/utils/isomorphic/types.ts diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index 9d6089614c9cc..bb0ebd9513273 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -18,7 +18,7 @@ import * as fs from 'fs'; import * as path from 'path'; 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 { rootTestType } from './common/testType'; @@ -32,7 +32,7 @@ import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, export { expect } from './matchers/expect'; 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__']) { const originalStackTraceLimit = Error.stackTraceLimit; diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index 1748cac9b994a..79d0d254a196b 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -17,7 +17,7 @@ import * as path from 'path'; 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 { colors as realColors, noColors } from 'playwright-core/lib/utils'; @@ -522,7 +522,7 @@ export function prepareErrorStack(stack: string): { const stackLines = lines.slice(firstStackLine); let location: Location | undefined; for (const line of stackLines) { - const frame = parseStackTraceLine(line, path.sep); + const frame = parseStackFrame(line, path.sep); if (!frame || !frame.file) continue; if (belongsToNodeModules(frame.file)) diff --git a/packages/playwright/src/util.ts b/packages/playwright/src/util.ts index b80a3f387df02..d10babff563e9 100644 --- a/packages/playwright/src/util.ts +++ b/packages/playwright/src/util.ts @@ -19,9 +19,8 @@ import * as path from 'path'; import * as url from 'url'; import util from 'util'; -import { parseStackTraceLine, sanitizeForFilePath, calculateSha1, formatCallLog, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils'; -import { debug, mime, minimatch } from 'playwright-core/lib/utilsBundle'; -import { nodePlatform } from 'playwright-core/lib/utils'; +import { parseStackFrame, sanitizeForFilePath, calculateSha1, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils'; +import { colors, debug, mime, minimatch } from 'playwright-core/lib/utilsBundle'; import type { Location } from './../types/testReporter'; import type { TestInfoErrorImpl } from './common/ipc'; @@ -56,7 +55,7 @@ export function filterStackFile(file: string) { export function filteredStackTrace(rawStack: RawStack): StackFrame[] { const frames: StackFrame[] = []; for (const line of rawStack) { - const frame = parseStackTraceLine(line, path.sep); + const frame = parseStackFrame(line, path.sep); if (!frame || !frame.file) continue; if (!filterStackFile(frame.file)) @@ -225,7 +224,14 @@ export function getContainedPath(parentPath: string, subPath: string = ''): stri 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(); diff --git a/tests/library/events/check-listener-leaks.spec.ts b/tests/library/events/check-listener-leaks.spec.ts index 5a328d720f27b..916c5678f2c74 100644 --- a/tests/library/events/check-listener-leaks.spec.ts +++ b/tests/library/events/check-listener-leaks.spec.ts @@ -22,7 +22,7 @@ import events from 'events'; 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 * as common from './utils'; diff --git a/tests/library/unit/sequence.spec.ts b/tests/library/unit/sequence.spec.ts index e0decd4d8479c..7fdb20bd1ab43 100644 --- a/tests/library/unit/sequence.spec.ts +++ b/tests/library/unit/sequence.spec.ts @@ -16,7 +16,7 @@ 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', () => { const input = [];