diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 0f0d5f643b..38c5d71834 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -39,7 +39,7 @@ }, { "name": "webkit", - "revision": "2132", + "revision": "2134", "installByDefault": true, "revisionOverrides": { "debian11-x64": "2105", diff --git a/packages/playwright-core/bundles/utils/package-lock.json b/packages/playwright-core/bundles/utils/package-lock.json index 49e99ef5c3..bc58353c99 100644 --- a/packages/playwright-core/bundles/utils/package-lock.json +++ b/packages/playwright-core/bundles/utils/package-lock.json @@ -167,6 +167,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", "engines": { "node": ">=0.1.90" } diff --git a/packages/playwright-core/src/DEPS.list b/packages/playwright-core/src/DEPS.list index ec14e89070..2ffa077b4e 100644 --- a/packages/playwright-core/src/DEPS.list +++ b/packages/playwright-core/src/DEPS.list @@ -8,10 +8,13 @@ ** [inprocess.ts] +common/ utils/ +server/utils [outofprocess.ts] client/ +common/ protocol/ utils/ utils/isomorphic diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts index e8d66bc4c4..77fb9a9844 100644 --- a/packages/playwright-core/src/browserServerImpl.ts +++ b/packages/playwright-core/src/browserServerImpl.ts @@ -21,7 +21,7 @@ import { helper } from './server/helper'; import { serverSideCallMetadata } from './server/instrumentation'; import { createPlaywright } from './server/playwright'; import { createGuid } from './server/utils/crypto'; -import { rewriteErrorMessage } from './utils/stackTrace'; +import { rewriteErrorMessage } from './utils/isomorphic/stackTrace'; import { ws } from './utilsBundle'; import type { BrowserServer, BrowserServerLauncher } from './client/browserType'; diff --git a/packages/playwright-core/src/cli/driver.ts b/packages/playwright-core/src/cli/driver.ts index 1e16de00bf..104db006cc 100644 --- a/packages/playwright-core/src/cli/driver.ts +++ b/packages/playwright-core/src/cli/driver.ts @@ -19,7 +19,7 @@ import * as fs from 'fs'; import * as playwright from '../..'; -import { PipeTransport } from '../utils/pipeTransport'; +import { PipeTransport } from '../server/utils/pipeTransport'; import { PlaywrightServer } from '../remote/playwrightServer'; import { DispatcherConnection, PlaywrightDispatcher, RootDispatcher, createPlaywright } from '../server'; import { gracefullyProcessExitDoNotHang } from '../server/utils/processLauncher'; diff --git a/packages/playwright-core/src/client/DEPS.list b/packages/playwright-core/src/client/DEPS.list index 4be2917927..e886cdbe63 100644 --- a/packages/playwright-core/src/client/DEPS.list +++ b/packages/playwright-core/src/client/DEPS.list @@ -1,5 +1,4 @@ [*] ../common/ ../protocol/ -../utils/** -../utilsBundle.ts +../utils/isomorphic diff --git a/packages/playwright-core/src/client/android.ts b/packages/playwright-core/src/client/android.ts index f6e0419c2c..134be7c7bb 100644 --- a/packages/playwright-core/src/client/android.ts +++ b/packages/playwright-core/src/client/android.ts @@ -14,23 +14,23 @@ * limitations under the License. */ -import { EventEmitter } from 'events'; - +import { EventEmitter } from './eventEmitter'; import { BrowserContext, prepareBrowserContextParams } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { TargetClosedError, isTargetClosedError } from './errors'; import { Events } from './events'; import { Waiter } from './waiter'; -import { TimeoutSettings } from '../common/timeoutSettings'; +import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; import { isRegExp, isString } from '../utils/isomorphic/rtti'; import { monotonicTime } from '../utils/isomorphic/time'; import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner'; +import { connectOverWebSocket } from './webSocket'; 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 '../utils/platform'; +import type { Platform } from '../common/platform'; import type * as channels from '@protocol/channels'; type Direction = 'down' | 'up' | 'left' | 'right'; @@ -69,9 +69,8 @@ export class Android extends ChannelOwner implements ap return await this._wrapApiCall(async () => { const deadline = options.timeout ? monotonicTime() + options.timeout : 0; const headers = { 'x-playwright-browser': 'android', ...options.headers }; - const localUtils = this._connection.localUtils(); const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout }; - const connection = await localUtils.connect(connectParams); + const connection = await connectOverWebSocket(this._connection, connectParams); let device: AndroidDevice; connection.on('close', () => { diff --git a/packages/playwright-core/src/client/artifact.ts b/packages/playwright-core/src/client/artifact.ts index fc1e9bca12..7c6fd947cf 100644 --- a/packages/playwright-core/src/client/artifact.ts +++ b/packages/playwright-core/src/client/artifact.ts @@ -16,7 +16,7 @@ import { ChannelOwner } from './channelOwner'; import { Stream } from './stream'; -import { mkdirIfNeeded } from '../utils/fileUtils'; +import { mkdirIfNeeded } from './fileUtils'; import type * as channels from '@protocol/channels'; import type { Readable } from 'stream'; diff --git a/packages/playwright-core/src/client/browser.ts b/packages/playwright-core/src/client/browser.ts index 9d2ca1fab0..a12bf108a8 100644 --- a/packages/playwright-core/src/client/browser.ts +++ b/packages/playwright-core/src/client/browser.ts @@ -20,7 +20,7 @@ import { CDPSession } from './cdpSession'; import { ChannelOwner } from './channelOwner'; import { isTargetClosedError } from './errors'; import { Events } from './events'; -import { mkdirIfNeeded } from '../utils/fileUtils'; +import { mkdirIfNeeded } from './fileUtils'; import type { BrowserType } from './browserType'; import type { Page } from './page'; diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 7c0210ceaf..21f5a200b7 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -34,19 +34,19 @@ import { Tracing } from './tracing'; import { Waiter } from './waiter'; import { WebError } from './webError'; import { Worker } from './worker'; -import { TimeoutSettings } from '../common/timeoutSettings'; -import { mkdirIfNeeded } from '../utils/fileUtils'; +import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { urlMatchesEqual } from '../utils/isomorphic/urlMatch'; import { isRegExp, isString } from '../utils/isomorphic/rtti'; -import { rewriteErrorMessage } from '../utils/stackTrace'; +import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace'; import type { BrowserType } from './browserType'; import type { BrowserContextOptions, Headers, LaunchOptions, StorageState, WaitForEventOptions } from './types'; 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 '../utils/platform'; +import type { Platform } from '../common/platform'; import type * as channels from '@protocol/channels'; export class BrowserContext extends ChannelOwner implements api.BrowserContext { @@ -338,7 +338,7 @@ export class BrowserContext extends ChannelOwner } async route(url: URLMatch, handler: network.RouteHandlerCallback, options: { times?: number } = {}): Promise { - this._routes.unshift(new network.RouteHandler(this._options.baseURL, url, handler, options.times)); + this._routes.unshift(new network.RouteHandler(this._platform, this._options.baseURL, url, handler, options.times)); await this._updateInterceptionPatterns(); } @@ -361,11 +361,14 @@ export class BrowserContext extends ChannelOwner } async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full' } = {}): Promise { + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error('Route from har is not supported in thin clients'); if (options.update) { await this._recordIntoHAR(har, null, options); return; } - const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url }); + const harRouter = await HarRouter.create(localUtils, har, options.notFound || 'abort', { urlMatch: options.url }); this._harRouters.push(harRouter); await harRouter.addContextRoute(this); } @@ -484,8 +487,11 @@ export class BrowserContext extends ChannelOwner const isCompressed = harParams.content === 'attach' || harParams.path.endsWith('.zip'); const needCompressed = harParams.path.endsWith('.zip'); if (isCompressed && !needCompressed) { + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error('Uncompressed har is not supported in thin clients'); await artifact.saveAs(harParams.path + '.tmp'); - await this._connection.localUtils().harUnzip({ zipFile: harParams.path + '.tmp', harFile: harParams.path }); + await localUtils.harUnzip({ zipFile: harParams.path + '.tmp', harFile: harParams.path }); } else { await artifact.saveAs(harParams.path); } diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index 79d2fbd521..e4942bd7f0 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -14,17 +14,16 @@ * limitations under the License. */ -import path from 'path'; - import { Browser } from './browser'; import { BrowserContext, prepareBrowserContextParams } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { envObjectToArray } from './clientHelper'; import { Events } from './events'; -import { assert } from '../utils/debug'; +import { assert } from '../utils/isomorphic/debug'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { monotonicTime } from '../utils/isomorphic/time'; import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner'; +import { connectOverWebSocket } from './webSocket'; import type { Playwright } from './playwright'; import type { ConnectOptions, LaunchOptions, LaunchPersistentContextOptions, LaunchServerOptions, Logger } from './types'; @@ -100,7 +99,7 @@ export class BrowserType extends ChannelOwner imple ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), env: options.env ? envObjectToArray(options.env) : undefined, channel: options.channel, - userDataDir: (path.isAbsolute(userDataDir) || !userDataDir) ? userDataDir : path.resolve(userDataDir), + userDataDir: (this._platform.path().isAbsolute(userDataDir) || !userDataDir) ? userDataDir : this._platform.path().resolve(userDataDir), }; return await this._wrapApiCall(async () => { const result = await this._channel.launchPersistentContext(persistentParams); @@ -124,7 +123,6 @@ export class BrowserType extends ChannelOwner imple return await this._wrapApiCall(async () => { const deadline = params.timeout ? monotonicTime() + params.timeout : 0; const headers = { 'x-playwright-browser': this.name(), ...params.headers }; - const localUtils = this._connection.localUtils(); const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint: params.wsEndpoint, headers, @@ -134,7 +132,7 @@ export class BrowserType extends ChannelOwner imple }; if ((params as any).__testHookRedirectPortForwarding) connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding; - const connection = await localUtils.connect(connectParams); + const connection = await connectOverWebSocket(this._connection, connectParams); let browser: Browser; connection.on('close', () => { // Emulate all pages, contexts and the browser closing upon disconnect. diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts index 70a5c51777..92deaecabd 100644 --- a/packages/playwright-core/src/client/channelOwner.ts +++ b/packages/playwright-core/src/client/channelOwner.ts @@ -16,16 +16,14 @@ import { EventEmitter } from './eventEmitter'; import { ValidationError, maybeFindValidator } from '../protocol/validator'; -import { isUnderTest } from '../utils/debug'; -import { debugLogger } from '../utils/debugLogger'; -import { captureLibraryStackTrace, stringifyStackFrames } from '../utils/stackTrace'; -import { zones } from '../utils/zones'; +import { isUnderTest } from '../utils/isomorphic/debug'; +import { captureLibraryStackTrace, 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 '../utils/platform'; +import type { Platform } from '../common/platform'; import type * as channels from '@protocol/channels'; type Listener = (...args: any[]) => void; @@ -158,7 +156,7 @@ export abstract class ChannelOwner ${apiZone.apiName} started`); + logApiCall(this._platform, this._logger, `=> ${apiZone.apiName} started`); return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone.apiName, apiZone.frames, apiZone.stepId); } // Since this api call is either internal, or has already been reported/traced once, @@ -177,19 +175,19 @@ export abstract class ChannelOwner(func: (apiZone: ApiZone) => Promise, isInternal?: boolean): Promise { const logger = this._logger; - const existingApiZone = zones.zoneData('apiZone'); + const existingApiZone = this._platform.zones.current().data(); if (existingApiZone) return await func(existingApiZone); if (isInternal === undefined) isInternal = this._isInternalType; - const stackTrace = captureLibraryStackTrace(); + const stackTrace = captureLibraryStackTrace(this._platform.pathSeparator); const apiZone: ApiZone = { apiName: stackTrace.apiName, frames: stackTrace.frames, isInternal, reported: false, userData: undefined, stepId: undefined }; try { - const result = await zones.run('apiZone', apiZone, async () => await func(apiZone)); + const result = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone)); if (!isInternal) { - logApiCall(logger, `<= ${apiZone.apiName} succeeded`); + logApiCall(this._platform, logger, `<= ${apiZone.apiName} succeeded`); this._instrumentation.onApiCallEnd(apiZone); } return result; @@ -204,7 +202,7 @@ export abstract class ChannelOwner { @@ -110,8 +108,8 @@ export class Connection extends EventEmitter { return this._rawBuffers; } - localUtils(): LocalUtils { - return this._localUtils!; + localUtils(): LocalUtils | undefined { + return this._localUtils; } async initializePlaywright(): Promise { @@ -139,9 +137,9 @@ export class Connection extends EventEmitter { const type = object._type; const id = ++this._lastId; const message = { id, guid, method, params }; - if (debugLogger.isEnabled('channel')) { + if (this.platform.isLogEnabled('channel')) { // Do not include metadata in debug logs to avoid noise. - debugLogger.log('channel', 'SEND> ' + JSON.stringify(message)); + this.platform.log('channel', 'SEND> ' + JSON.stringify(message)); } const location = frames[0] ? { file: frames[0].file, line: frames[0].line, column: frames[0].column } : undefined; const metadata: channels.Metadata = { apiName, location, internal: !apiName, stepId }; @@ -149,7 +147,7 @@ export class Connection extends EventEmitter { this._localUtils?.addStackToTracingNoReply({ callData: { stack: frames, id } }).catch(() => {}); // We need to exit zones before calling into the server, otherwise // when we receive events from the server, we would be in an API zone. - zones.empty().run(() => this.onmessage({ ...message, metadata })); + this.platform.zones.empty.run(() => this.onmessage({ ...message, metadata })); return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, apiName, type, method })); } @@ -159,15 +157,15 @@ export class Connection extends EventEmitter { const { id, guid, method, params, result, error, log } = message as any; if (id) { - if (debugLogger.isEnabled('channel')) - debugLogger.log('channel', ' any; type EventMap = Record; +let defaultMaxListenersProvider = () => 10; + +export function setDefaultMaxListenersProvider(provider: () => number) { + defaultMaxListenersProvider = provider; +} + export class EventEmitter implements EventEmitterType { private _events: EventMap | undefined = undefined; @@ -58,7 +62,7 @@ export class EventEmitter implements EventEmitterType { } getMaxListeners(): number { - return this._maxListeners === undefined ? OriginalEventEmitter.defaultMaxListeners : this._maxListeners; + return this._maxListeners === undefined ? defaultMaxListenersProvider() : this._maxListeners; } emit(type: EventType, ...args: any[]): boolean { diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index 11c28e7c20..41070b8665 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -19,8 +19,8 @@ import { ChannelOwner } from './channelOwner'; import { TargetClosedError, isTargetClosedError } from './errors'; import { RawHeaders } from './network'; import { Tracing } from './tracing'; -import { assert } from '../utils/debug'; -import { mkdirIfNeeded } from '../utils/fileUtils'; +import { assert } from '../utils/isomorphic/debug'; +import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { isString } from '../utils/isomorphic/rtti'; @@ -29,7 +29,7 @@ import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageS import type { Serializable } from '../../types/structs'; import type * as api from '../../types/types'; import type { HeadersArray, NameValue } from '../common/types'; -import type { Platform } from '../utils/platform'; +import type { Platform } from '../common/platform'; import type * as channels from '@protocol/channels'; import type * as fs from 'fs'; diff --git a/packages/playwright-core/src/utils/fileUtils.ts b/packages/playwright-core/src/client/fileUtils.ts similarity index 75% rename from packages/playwright-core/src/utils/fileUtils.ts rename to packages/playwright-core/src/client/fileUtils.ts index 261b72d84a..0d21b195c0 100644 --- a/packages/playwright-core/src/utils/fileUtils.ts +++ b/packages/playwright-core/src/client/fileUtils.ts @@ -14,17 +14,12 @@ * limitations under the License. */ -import type { Platform } from './platform'; +import type { Platform } from '../common/platform'; +// Keep in sync with the server. export const fileUploadSizeLimit = 50 * 1024 * 1024; export async function mkdirIfNeeded(platform: Platform, filePath: string) { // This will harmlessly throw on windows if the dirname is the root directory. await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => {}); } - -export async function removeFolders(platform: Platform, dirs: string[]): Promise { - return await Promise.all(dirs.map((dir: string) => - platform.fs().promises.rm(dir, { recursive: true, force: true, maxRetries: 10 }).catch(e => e) - )); -} diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts index 2155ab0f87..ecbfe3cb11 100644 --- a/packages/playwright-core/src/client/frame.ts +++ b/packages/playwright-core/src/client/frame.ts @@ -15,8 +15,7 @@ * limitations under the License. */ -import { EventEmitter } from 'events'; - +import { EventEmitter } from './eventEmitter'; import { ChannelOwner } from './channelOwner'; import { addSourceUrlToScript } from './clientHelper'; import { ElementHandle, convertInputFiles, convertSelectOptionValues } from './elementHandle'; @@ -26,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/debug'; +import { assert } from '../utils/isomorphic/debug'; 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/harRouter.ts b/packages/playwright-core/src/client/harRouter.ts index 33767e11a0..a9ab966b2a 100644 --- a/packages/playwright-core/src/client/harRouter.ts +++ b/packages/playwright-core/src/client/harRouter.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -import { debugLogger } from '../utils/debugLogger'; - import type { BrowserContext } from './browserContext'; import type { LocalUtils } from './localUtils'; import type { Route } from './network'; @@ -57,7 +55,7 @@ export class HarRouter { }); if (response.action === 'redirect') { - debugLogger.log('api', `HAR: ${route.request().url()} redirected to ${response.redirectURL}`); + route._platform.log('api', `HAR: ${route.request().url()} redirected to ${response.redirectURL}`); await route._redirectNavigationRequest(response.redirectURL!); return; } @@ -79,7 +77,7 @@ export class HarRouter { } if (response.action === 'error') - debugLogger.log('api', 'HAR: ' + response.message!); + route._platform.log('api', 'HAR: ' + response.message!); // Report the error, but fall through to the default handler. if (this._notFoundAction === 'abort') { diff --git a/packages/playwright-core/src/client/localUtils.ts b/packages/playwright-core/src/client/localUtils.ts index 29f2cc8312..5ba982251f 100644 --- a/packages/playwright-core/src/client/localUtils.ts +++ b/packages/playwright-core/src/client/localUtils.ts @@ -15,12 +15,8 @@ */ import { ChannelOwner } from './channelOwner'; -import { Connection } from './connection'; -import * as localUtils from '../utils/localUtils'; -import type { HeadersArray, Size } from './types'; -import type { HarBackend } from '../utils/harBackend'; -import type { Platform } from '../utils/platform'; +import type { Size } from './types'; import type * as channels from '@protocol/channels'; type DeviceDescriptor = { @@ -35,8 +31,6 @@ type Devices = { [name: string]: DeviceDescriptor }; export class LocalUtils extends ChannelOwner { readonly devices: Devices; - private _harBackends = new Map(); - private _stackSessions = new Map(); constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.LocalUtilsInitializer) { super(parent, type, guid, initializer); @@ -47,132 +41,34 @@ export class LocalUtils extends ChannelOwner { } async zip(params: channels.LocalUtilsZipParams): Promise { - return await localUtils.zip(this._platform, this._stackSessions, params); + return await this._channel.zip(params); } async harOpen(params: channels.LocalUtilsHarOpenParams): Promise { - return await localUtils.harOpen(this._platform, this._harBackends, params); + return await this._channel.harOpen(params); } async harLookup(params: channels.LocalUtilsHarLookupParams): Promise { - return await localUtils.harLookup(this._harBackends, params); + return await this._channel.harLookup(params); } async harClose(params: channels.LocalUtilsHarCloseParams): Promise { - return await localUtils.harClose(this._harBackends, params); + return await this._channel.harClose(params); } async harUnzip(params: channels.LocalUtilsHarUnzipParams): Promise { - return await localUtils.harUnzip(params); + return await this._channel.harUnzip(params); } async tracingStarted(params: channels.LocalUtilsTracingStartedParams): Promise { - return await localUtils.tracingStarted(this._stackSessions, params); + return await this._channel.tracingStarted(params); } async traceDiscarded(params: channels.LocalUtilsTraceDiscardedParams): Promise { - return await localUtils.traceDiscarded(this._platform, this._stackSessions, params); + return await this._channel.traceDiscarded(params); } async addStackToTracingNoReply(params: channels.LocalUtilsAddStackToTracingNoReplyParams): Promise { - return await localUtils.addStackToTracingNoReply(this._stackSessions, params); - } - - async connect(params: channels.LocalUtilsConnectParams): Promise { - const transport = this._platform.ws ? new WebSocketTransport(this._platform) : new JsonPipeTransport(this); - const connectHeaders = await transport.connect(params); - const connection = new Connection(this, this._platform, this._instrumentation, connectHeaders); - connection.markAsRemote(); - connection.on('close', () => transport.close()); - - let closeError: string | undefined; - const onTransportClosed = (reason?: string) => { - connection.close(reason || closeError); - }; - transport.onClose(reason => onTransportClosed(reason)); - connection.onmessage = message => transport.send(message).catch(() => onTransportClosed()); - transport.onMessage(message => { - try { - connection!.dispatch(message); - } catch (e) { - closeError = String(e); - transport.close(); - } - }); - return connection; - } -} -interface Transport { - connect(params: channels.LocalUtilsConnectParams): Promise; - send(message: any): Promise; - onMessage(callback: (message: object) => void): void; - onClose(callback: (reason?: string) => void): void; - close(): Promise; -} - -class JsonPipeTransport implements Transport { - private _pipe: channels.JsonPipeChannel | undefined; - private _owner: ChannelOwner; - - constructor(owner: ChannelOwner) { - this._owner = owner; - } - - async connect(params: channels.LocalUtilsConnectParams) { - const { pipe, headers: connectHeaders } = await this._owner._wrapApiCall(async () => { - return await this._owner._channel.connect(params); - }, /* isInternal */ true); - this._pipe = pipe; - return connectHeaders; - } - - async send(message: object) { - this._owner._wrapApiCall(async () => { - await this._pipe!.send({ message }); - }, /* isInternal */ true); - } - - onMessage(callback: (message: object) => void) { - this._pipe!.on('message', ({ message }) => callback(message)); - } - - onClose(callback: (reason?: string) => void) { - this._pipe!.on('closed', ({ reason }) => callback(reason)); - } - - async close() { - await this._owner._wrapApiCall(async () => { - await this._pipe!.close().catch(() => {}); - }, /* isInternal */ true); - } -} - -class WebSocketTransport implements Transport { - private _platform: Platform; - private _ws: WebSocket | undefined; - - constructor(platform: Platform) { - this._platform = platform; - } - - async connect(params: channels.LocalUtilsConnectParams) { - this._ws = this._platform.ws!(params.wsEndpoint); - return []; - } - - async send(message: object) { - this._ws!.send(JSON.stringify(message)); - } - - onMessage(callback: (message: object) => void) { - this._ws!.addEventListener('message', event => callback(JSON.parse(event.data))); - } - - onClose(callback: (reason?: string) => void) { - this._ws!.addEventListener('close', () => callback()); - } - - async close() { - this._ws!.close(); + return await this._channel.addStackToTracingNoReply(params); } } diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 20675492eb..a4f5ff68d0 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -import { URLSearchParams } from 'url'; - import { ChannelOwner } from './channelOwner'; import { isTargetClosedError } from './errors'; import { Events } from './events'; @@ -23,15 +21,14 @@ import { APIResponse } from './fetch'; import { Frame } from './frame'; import { Waiter } from './waiter'; import { Worker } from './worker'; -import { assert } from '../utils/debug'; +import { assert } from '../utils/isomorphic/debug'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { urlMatches } from '../utils/isomorphic/urlMatch'; import { LongStandingScope, ManualPromise } from '../utils/isomorphic/manualPromise'; import { MultiMap } from '../utils/isomorphic/multimap'; import { isRegExp, isString } from '../utils/isomorphic/rtti'; -import { rewriteErrorMessage } from '../utils/stackTrace'; -import { zones } from '../utils/zones'; -import { mime } from '../utilsBundle'; +import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace'; +import { getMimeTypeForPath } from '../utils/isomorphic/mimeType'; import type { BrowserContext } from './browserContext'; import type { Page } from './page'; @@ -40,8 +37,8 @@ import type { Serializable } from '../../types/structs'; import type * as api from '../../types/types'; import type { HeadersArray } from '../common/types'; import type { URLMatch } from '../utils/isomorphic/urlMatch'; -import type { Zone } from '../utils/zones'; import type * as channels from '@protocol/channels'; +import type { Platform, Zone } from '../common/platform'; export type NetworkCookie = { name: string, @@ -414,7 +411,7 @@ export class Route extends ChannelOwner implements api.Ro else if (options.json) headers['content-type'] = 'application/json'; else if (options.path) - headers['content-type'] = mime.getType(options.path) || 'application/octet-stream'; + headers['content-type'] = getMimeTypeForPath(options.path) || 'application/octet-stream'; if (length && !('content-length' in headers)) headers['content-length'] = String(length); @@ -821,14 +818,14 @@ export class RouteHandler { readonly handler: RouteHandlerCallback; private _ignoreException: boolean = false; private _activeInvocations: Set<{ complete: Promise, route: Route }> = new Set(); - private _svedZone: Zone; + private _savedZone: Zone; - constructor(baseURL: string | undefined, url: URLMatch, handler: RouteHandlerCallback, times: number = Number.MAX_SAFE_INTEGER) { + constructor(platform: Platform, baseURL: string | undefined, url: URLMatch, handler: RouteHandlerCallback, times: number = Number.MAX_SAFE_INTEGER) { this._baseURL = baseURL; this._times = times; this.url = url; this.handler = handler; - this._svedZone = zones.current().without('apiZone'); + this._savedZone = platform.zones.current().pop(); } static prepareInterceptionPatterns(handlers: RouteHandler[]) { @@ -852,7 +849,7 @@ export class RouteHandler { } public async handle(route: Route): Promise { - return await this._svedZone.run(async () => this._handleImpl(route)); + return await this._savedZone.run(async () => this._handleImpl(route)); } private async _handleImpl(route: Route): Promise { diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index d71afce060..cb6d0656b8 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -33,9 +33,9 @@ import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRou import { Video } from './video'; import { Waiter } from './waiter'; import { Worker } from './worker'; -import { TimeoutSettings } from '../common/timeoutSettings'; -import { assert } from '../utils/debug'; -import { mkdirIfNeeded } from '../utils/fileUtils'; +import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; +import { assert } from '../utils/isomorphic/debug'; +import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils'; import { urlMatches, urlMatchesEqual } from '../utils/isomorphic/urlMatch'; @@ -520,16 +520,19 @@ export class Page extends ChannelOwner implements api.Page } async route(url: URLMatch, handler: RouteHandlerCallback, options: { times?: number } = {}): Promise { - this._routes.unshift(new RouteHandler(this._browserContext._options.baseURL, url, handler, options.times)); + this._routes.unshift(new RouteHandler(this._platform, this._browserContext._options.baseURL, url, handler, options.times)); await this._updateInterceptionPatterns(); } async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full'} = {}): Promise { + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error('Route from har is not supported in thin clients'); if (options.update) { await this._browserContext._recordIntoHAR(har, this, options); return; } - const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url }); + const harRouter = await HarRouter.create(localUtils, har, options.notFound || 'abort', { urlMatch: options.url }); this._harRouters.push(harRouter); await harRouter.addPageRoute(this); } @@ -796,7 +799,7 @@ export class Page extends ChannelOwner implements api.Page } async pause(_options?: { __testHookKeepTestTimeout: boolean }) { - if (require('inspector').url()) + if (this._platform.isDebuggerAttached()) return; const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout(); const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout(); diff --git a/packages/playwright-core/src/client/selectors.ts b/packages/playwright-core/src/client/selectors.ts index 2a1097f7ec..3eca51bf36 100644 --- a/packages/playwright-core/src/client/selectors.ts +++ b/packages/playwright-core/src/client/selectors.ts @@ -17,19 +17,25 @@ import { ChannelOwner } from './channelOwner'; import { evaluationScript } from './clientHelper'; import { setTestIdAttribute, testIdAttributeName } from './locator'; -import { nodePlatform } from '../utils/platform'; +import { emptyPlatform } from '../common/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'; +let platform = emptyPlatform; + +export function setPlatformForSelectors(p: Platform) { + platform = p; +} export class Selectors implements api.Selectors { private _channels = new Set(); private _registrations: channels.SelectorsRegisterParams[] = []; async register(name: string, script: string | (() => SelectorEngine) | { path?: string, content?: string }, options: { contentScript?: boolean } = {}): Promise { - const source = await evaluationScript(nodePlatform, script, undefined, false); + const source = await evaluationScript(platform, script, undefined, false); const params = { ...options, name, source }; for (const channel of this._channels) await channel._channel.register(params); diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index 61124107fa..5b80f30a0a 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -69,8 +69,8 @@ export class Tracing extends ChannelOwner implements ap this._isTracing = true; this._connection.setIsTracing(true); } - const result = await this._connection.localUtils().tracingStarted({ tracesDir: this._tracesDir, traceName }); - this._stacksId = result.stacksId; + const result = await this._connection.localUtils()?.tracingStarted({ tracesDir: this._tracesDir, traceName }); + this._stacksId = result?.stacksId; } async stopChunk(options: { path?: string } = {}) { @@ -89,15 +89,19 @@ export class Tracing extends ChannelOwner implements ap // Not interested in artifacts. await this._channel.tracingStopChunk({ mode: 'discard' }); if (this._stacksId) - await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId }); + await this._connection.localUtils()!.traceDiscarded({ stacksId: this._stacksId }); return; } + const localUtils = this._connection.localUtils(); + if (!localUtils) + throw new Error('Cannot save trace in thin clients'); + const isLocal = !this._connection.isRemote(); if (isLocal) { const result = await this._channel.tracingStopChunk({ mode: 'entries' }); - await this._connection.localUtils().zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources }); + await localUtils.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources }); return; } @@ -106,7 +110,7 @@ export class Tracing extends ChannelOwner implements ap // The artifact may be missing if the browser closed while stopping tracing. if (!result.artifact) { if (this._stacksId) - await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId }); + await localUtils.traceDiscarded({ stacksId: this._stacksId }); return; } @@ -115,7 +119,7 @@ export class Tracing extends ChannelOwner implements ap await artifact.saveAs(filePath); await artifact.delete(); - await this._connection.localUtils().zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources }); + await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources }); } _resetStackCounter() { diff --git a/packages/playwright-core/src/client/waiter.ts b/packages/playwright-core/src/client/waiter.ts index b125ed034a..7d07d2128e 100644 --- a/packages/playwright-core/src/client/waiter.ts +++ b/packages/playwright-core/src/client/waiter.ts @@ -15,13 +15,12 @@ */ import { TimeoutError } from './errors'; -import { rewriteErrorMessage } from '../utils/stackTrace'; -import { zones } from '../utils/zones'; +import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace'; import type { ChannelOwner } from './channelOwner'; -import type { Zone } from '../utils/zones'; import type * as channels from '@protocol/channels'; import type { EventEmitter } from 'events'; +import type { Zone } from '../common/platform'; export class Waiter { private _dispose: (() => void)[]; @@ -36,7 +35,7 @@ export class Waiter { constructor(channelOwner: ChannelOwner, event: string) { this._waitId = channelOwner._platform.createGuid(); this._channelOwner = channelOwner; - this._savedZone = zones.current().without('apiZone'); + this._savedZone = channelOwner._platform.zones.current().pop(); this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {}); this._dispose = [ diff --git a/packages/playwright-core/src/client/webSocket.ts b/packages/playwright-core/src/client/webSocket.ts new file mode 100644 index 0000000000..cef07a4dfa --- /dev/null +++ b/packages/playwright-core/src/client/webSocket.ts @@ -0,0 +1,116 @@ +/** + * 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 { ChannelOwner } from './channelOwner'; +import { Connection } from './connection'; + +import type { HeadersArray } from './types'; +import type * as channels from '@protocol/channels'; + +export async function connectOverWebSocket(parentConnection: Connection, params: channels.LocalUtilsConnectParams): Promise { + 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); + connection.markAsRemote(); + connection.on('close', () => transport.close()); + + let closeError: string | undefined; + const onTransportClosed = (reason?: string) => { + connection.close(reason || closeError); + }; + transport.onClose(reason => onTransportClosed(reason)); + connection.onmessage = message => transport.send(message).catch(() => onTransportClosed()); + transport.onMessage(message => { + try { + connection!.dispatch(message); + } catch (e) { + closeError = String(e); + transport.close(); + } + }); + return connection; +} + +interface Transport { + connect(params: channels.LocalUtilsConnectParams): Promise; + send(message: any): Promise; + onMessage(callback: (message: object) => void): void; + onClose(callback: (reason?: string) => void): void; + close(): Promise; +} + +class JsonPipeTransport implements Transport { + private _pipe: channels.JsonPipeChannel | undefined; + private _owner: ChannelOwner; + + constructor(owner: ChannelOwner) { + this._owner = owner; + } + + async connect(params: channels.LocalUtilsConnectParams) { + const { pipe, headers: connectHeaders } = await this._owner._wrapApiCall(async () => { + return await this._owner._channel.connect(params); + }, /* isInternal */ true); + this._pipe = pipe; + return connectHeaders; + } + + async send(message: object) { + this._owner._wrapApiCall(async () => { + await this._pipe!.send({ message }); + }, /* isInternal */ true); + } + + onMessage(callback: (message: object) => void) { + this._pipe!.on('message', ({ message }) => callback(message)); + } + + onClose(callback: (reason?: string) => void) { + this._pipe!.on('closed', ({ reason }) => callback(reason)); + } + + async close() { + await this._owner._wrapApiCall(async () => { + await this._pipe!.close().catch(() => {}); + }, /* isInternal */ true); + } +} + +class WebSocketTransport implements Transport { + private _ws: WebSocket | undefined; + + async connect(params: channels.LocalUtilsConnectParams) { + this._ws = new window.WebSocket(params.wsEndpoint); + return []; + } + + async send(message: object) { + this._ws!.send(JSON.stringify(message)); + } + + onMessage(callback: (message: object) => void) { + this._ws!.addEventListener('message', event => callback(JSON.parse(event.data))); + } + + onClose(callback: (reason?: string) => void) { + this._ws!.addEventListener('close', () => callback()); + } + + async close() { + this._ws!.close(); + } +} diff --git a/packages/playwright-core/src/common/DEPS.list b/packages/playwright-core/src/common/DEPS.list index 43bff9dba4..686b88087b 100644 --- a/packages/playwright-core/src/common/DEPS.list +++ b/packages/playwright-core/src/common/DEPS.list @@ -1,4 +1,2 @@ [*] -../utils/ -../utilsBundle.ts -../zipBundle.ts +../utils/isomorphic/ diff --git a/packages/playwright-core/src/common/platform.ts b/packages/playwright-core/src/common/platform.ts new file mode 100644 index 0000000000..10bdc35922 --- /dev/null +++ b/packages/playwright-core/src/common/platform.ts @@ -0,0 +1,125 @@ +/** + * 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 { 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'; + +export type Zone = { + push(data: unknown): Zone; + pop(): Zone; + run(func: () => R): R; + data(): T | undefined; +}; + +const noopZone: Zone = { + push: () => noopZone, + pop: () => noopZone, + run: func => func(), + data: () => undefined, +}; + +export type Platform = { + name: 'node' | 'web' | 'empty'; + + calculateSha1(text: string): Promise; + colors: Colors; + createGuid: () => string; + fs: () => typeof fs; + inspectCustom: symbol | undefined; + isDebuggerAttached(): boolean; + isLogEnabled(name: 'api' | 'channel'): boolean; + log(name: 'api' | 'channel', message: string | Error | object): void; + path: () => typeof path; + pathSeparator: string; + zones: { empty: Zone, current: () => Zone; }; +}; + +export const webPlatform: Platform = { + name: 'web', + + calculateSha1: async (text: string) => { + const bytes = new TextEncoder().encode(text); + const hashBuffer = await window.crypto.subtle.digest('SHA-1', bytes); + return Array.from(new Uint8Array(hashBuffer), b => b.toString(16).padStart(2, '0')).join(''); + }, + + colors: webColors, + + createGuid: () => { + return Array.from(window.crypto.getRandomValues(new Uint8Array(16)), b => b.toString(16).padStart(2, '0')).join(''); + }, + + fs: () => { + throw new Error('File system is not available'); + }, + + inspectCustom: undefined, + + isDebuggerAttached: () => false, + + isLogEnabled(name: 'api' | 'channel') { + return false; + }, + + log(name: 'api' | 'channel', message: string | Error | object) {}, + + path: () => { + throw new Error('Path module is not available'); + }, + + pathSeparator: '/', + + zones: { empty: noopZone, current: () => noopZone }, +}; + +export const emptyPlatform: Platform = { + name: 'empty', + + calculateSha1: async () => { + throw new Error('Not implemented'); + }, + + colors: noColors, + + createGuid: () => { + throw new Error('Not implemented'); + }, + + fs: () => { + throw new Error('Not implemented'); + }, + + inspectCustom: undefined, + + isDebuggerAttached: () => false, + + isLogEnabled(name: 'api' | 'channel') { + return false; + }, + + log(name: 'api' | 'channel', message: string | Error | object) { }, + + path: () => { + throw new Error('Function not implemented.'); + }, + + pathSeparator: '/', + + zones: { empty: noopZone, current: () => noopZone }, +}; diff --git a/packages/playwright-core/src/common/progress.ts b/packages/playwright-core/src/common/progress.ts deleted file mode 100644 index f09670e823..0000000000 --- a/packages/playwright-core/src/common/progress.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 interface Progress { - log(message: string): void; - timeUntilDeadline(): number; - isRunning(): boolean; - cleanupWhenAborted(cleanup: () => any): void; - throwIfAborted(): void; -} diff --git a/packages/playwright-core/src/inProcessFactory.ts b/packages/playwright-core/src/inProcessFactory.ts index 296c742127..a53aa8c2e3 100644 --- a/packages/playwright-core/src/inProcessFactory.ts +++ b/packages/playwright-core/src/inProcessFactory.ts @@ -14,17 +14,32 @@ * 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 { 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 './utils/platform'; +import type { Platform } from './common/platform'; + export function createInProcessPlaywright(platform: Platform): 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, []); clientConnection.useRawBuffers(); diff --git a/packages/playwright-core/src/inprocess.ts b/packages/playwright-core/src/inprocess.ts index c057f7b5c0..fc0550e924 100644 --- a/packages/playwright-core/src/inprocess.ts +++ b/packages/playwright-core/src/inprocess.ts @@ -15,6 +15,6 @@ */ import { createInProcessPlaywright } from './inProcessFactory'; -import { nodePlatform } from './utils/platform'; +import { nodePlatform } from './server/utils/nodePlatform'; module.exports = createInProcessPlaywright(nodePlatform); diff --git a/packages/playwright-core/src/outofprocess.ts b/packages/playwright-core/src/outofprocess.ts index 8dcb8fa45a..c2427b818d 100644 --- a/packages/playwright-core/src/outofprocess.ts +++ b/packages/playwright-core/src/outofprocess.ts @@ -18,9 +18,9 @@ import * as childProcess from 'child_process'; import * as path from 'path'; import { Connection } from './client/connection'; -import { PipeTransport } from './utils/pipeTransport'; +import { PipeTransport } from './server/utils/pipeTransport'; import { ManualPromise } from './utils/isomorphic/manualPromise'; -import { nodePlatform } from './utils/platform'; +import { nodePlatform } from './server/utils/nodePlatform'; import type { Playwright } from './client/playwright'; diff --git a/packages/playwright-core/src/protocol/DEPS.list b/packages/playwright-core/src/protocol/DEPS.list index bf64b324f1..dbdeafe86c 100644 --- a/packages/playwright-core/src/protocol/DEPS.list +++ b/packages/playwright-core/src/protocol/DEPS.list @@ -1,4 +1,3 @@ [*] ../common/ -../utils/ - +../utils/isomorphic diff --git a/packages/playwright-core/src/protocol/validatorPrimitives.ts b/packages/playwright-core/src/protocol/validatorPrimitives.ts index 9d4614512b..f57a63acea 100644 --- a/packages/playwright-core/src/protocol/validatorPrimitives.ts +++ b/packages/playwright-core/src/protocol/validatorPrimitives.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { isUnderTest } from '../utils'; +import { isUnderTest } from '../utils/isomorphic/debug'; export class ValidationError extends Error {} export type Validator = (arg: any, path: string, context: ValidatorContext) => any; diff --git a/packages/playwright-core/src/remote/playwrightConnection.ts b/packages/playwright-core/src/remote/playwrightConnection.ts index e276873668..00ca33b353 100644 --- a/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/packages/playwright-core/src/remote/playwrightConnection.ts @@ -23,7 +23,7 @@ import { serverSideCallMetadata } from '../server/instrumentation'; import { assert, isUnderTest } from '../utils'; import { startProfiling, stopProfiling } from '../server/utils/profiler'; import { monotonicTime } from '../utils'; -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from '../server/utils/debugLogger'; import type { DispatcherScope, Playwright } from '../server'; import type { LaunchOptions } from '../server/types'; diff --git a/packages/playwright-core/src/remote/playwrightServer.ts b/packages/playwright-core/src/remote/playwrightServer.ts index 911fa1b97c..7f6a58a0a5 100644 --- a/packages/playwright-core/src/remote/playwrightServer.ts +++ b/packages/playwright-core/src/remote/playwrightServer.ts @@ -16,11 +16,11 @@ import { PlaywrightConnection } from './playwrightConnection'; import { createPlaywright } from '../server/playwright'; -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from '../server/utils/debugLogger'; import { Semaphore } from '../utils/isomorphic/semaphore'; import { WSServer } from '../server/utils/wsServer'; import { wrapInASCIIBox } from '../server/utils/ascii'; -import { getPlaywrightVersion } from '../utils/userAgent'; +import { getPlaywrightVersion } from '../server/utils/userAgent'; import type { ClientType } from './playwrightConnection'; import type { SocksProxy } from '../server/utils/socksProxy'; diff --git a/packages/playwright-core/src/server/android/DEPS.list b/packages/playwright-core/src/server/android/DEPS.list index b45c3a16a7..b852561b95 100644 --- a/packages/playwright-core/src/server/android/DEPS.list +++ b/packages/playwright-core/src/server/android/DEPS.list @@ -3,6 +3,7 @@ ../../common/ ../../protocol/ ../../utils/ +../../utils/isomorphic/ ../../utilsBundle.ts ../chromium/ ../utils diff --git a/packages/playwright-core/src/server/android/android.ts b/packages/playwright-core/src/server/android/android.ts index 68db8ab77c..a4bf8f8ded 100644 --- a/packages/playwright-core/src/server/android/android.ts +++ b/packages/playwright-core/src/server/android/android.ts @@ -19,13 +19,13 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { TimeoutSettings } from '../../common/timeoutSettings'; -import { PipeTransport } from '../../utils/pipeTransport'; +import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings'; +import { PipeTransport } from '../utils/pipeTransport'; import { createGuid } from '../utils/crypto'; -import { isUnderTest } from '../../utils/debug'; -import { getPackageManagerExecCommand } from '../../utils/env'; -import { makeWaitForNextTask } from '../../utils/task'; -import { RecentLogsCollector } from '../../utils/debugLogger'; +import { isUnderTest } from '../../utils/isomorphic/debug'; +import { getPackageManagerExecCommand } from '../utils/env'; +import { makeWaitForNextTask } from '../utils/task'; +import { RecentLogsCollector } from '../utils/debugLogger'; import { debug } from '../../utilsBundle'; import { wsReceiver, wsSender } from '../../utilsBundle'; import { validateBrowserContextOptions } from '../browserContext'; diff --git a/packages/playwright-core/src/server/android/backendAdb.ts b/packages/playwright-core/src/server/android/backendAdb.ts index 737d415050..036c74703e 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/debug'; +import { assert } from '../../utils/isomorphic/debug'; import { createGuid } from '../utils/crypto'; import { debug } from '../../utilsBundle'; diff --git a/packages/playwright-core/src/server/bidi/bidiConnection.ts b/packages/playwright-core/src/server/bidi/bidiConnection.ts index 87b3f59c9b..0882836c38 100644 --- a/packages/playwright-core/src/server/bidi/bidiConnection.ts +++ b/packages/playwright-core/src/server/bidi/bidiConnection.ts @@ -16,11 +16,11 @@ import { EventEmitter } from 'events'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; -import type { RecentLogsCollector } from '../../utils/debugLogger'; +import type { RecentLogsCollector } from '../utils/debugLogger'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { ProtocolLogger } from '../types'; import type * as bidiCommands from './third_party/bidiCommands'; diff --git a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts index ec0cb681a0..aea4672196 100644 --- a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +++ b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts @@ -16,6 +16,7 @@ import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers'; import * as js from '../javascript'; +import * as dom from '../dom'; import { BidiDeserializer } from './third_party/bidiDeserializer'; import * as bidi from './third_party/bidiProtocol'; import { BidiSerializer } from './third_party/bidiSerializer'; @@ -137,7 +138,45 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { }); } - async rawCallFunction(functionDeclaration: string, arg: bidi.Script.LocalValue): Promise { + + async nodeIdForElementHandle(handle: dom.ElementHandle): Promise { + const shared = await this._remoteValueForReference({ handle: handle._objectId }); + // TODO: store sharedId in the handle. + if (!('sharedId' in shared)) + throw new Error('Element is not a node'); + return { + sharedId: shared.sharedId!, + }; + } + + async remoteObjectForNodeId(nodeId: bidi.Script.SharedReference): Promise { + const result = await this._remoteValueForReference(nodeId); + if ('handle' in result) + return { objectId: result.handle!, ...result }; + throw new Error('Can\'t get remote object for nodeId'); + } + + async contentFrameIdForFrame(handle: dom.ElementHandle) { + const contentWindow = await this._rawCallFunction('e => e.contentWindow', { handle: handle._objectId }); + if (contentWindow?.type === 'window') + return contentWindow.value.context; + return null; + } + + async frameIdForWindowHandle(handle: js.JSHandle): Promise { + if (!handle._objectId) + throw new Error('JSHandle is not a DOM node handle'); + const contentWindow = await this._remoteValueForReference({ handle: handle._objectId }); + if (contentWindow.type === 'window') + return contentWindow.value.context; + return null; + } + + private async _remoteValueForReference(reference: bidi.Script.RemoteReference) { + return await this._rawCallFunction('e => e', reference); + } + + private async _rawCallFunction(functionDeclaration: string, arg: bidi.Script.LocalValue): Promise { const response = await this._session.send('script.callFunction', { functionDeclaration, target: this._target, diff --git a/packages/playwright-core/src/server/bidi/bidiOverCdp.ts b/packages/playwright-core/src/server/bidi/bidiOverCdp.ts index adf7c9afbf..8acd45563d 100644 --- a/packages/playwright-core/src/server/bidi/bidiOverCdp.ts +++ b/packages/playwright-core/src/server/bidi/bidiOverCdp.ts @@ -17,7 +17,7 @@ import * as bidiMapper from 'chromium-bidi/lib/cjs/bidiMapper/BidiMapper'; import * as bidiCdpConnection from 'chromium-bidi/lib/cjs/cdp/CdpConnection'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { ChromiumBidi } from 'chromium-bidi/lib/cjs/protocol/protocol'; diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index 0ee5822f93..153bb830de 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -413,13 +413,10 @@ export class BidiPage implements PageDelegate { async getContentFrame(handle: dom.ElementHandle): Promise { const executionContext = toBidiExecutionContext(handle._context); - const contentWindow = await executionContext.rawCallFunction('e => e.contentWindow', { handle: handle._objectId }); - if (contentWindow.type === 'window') { - const frameId = contentWindow.value.context; - const result = this._page._frameManager.frame(frameId); - return result; - } - return null; + const frameId = await executionContext.contentFrameIdForFrame(handle); + if (!frameId) + return null; + return this._page._frameManager.frame(frameId); } async getOwnerFrame(handle: dom.ElementHandle): Promise { @@ -430,15 +427,8 @@ export class BidiPage implements PageDelegate { }); if (!windowHandle) return null; - if (!windowHandle._objectId) - return null; - const executionContext = toBidiExecutionContext(windowHandle._context as dom.FrameExecutionContext); - const contentWindow = await executionContext.rawCallFunction('e => e', { handle: windowHandle._objectId }); - if (contentWindow.type === 'window') { - const frameId = contentWindow.value.context; - return frameId; - } - return null; + const executionContext = toBidiExecutionContext(handle._context); + return executionContext.frameIdForWindowHandle(windowHandle); } isElementHandle(remoteObject: bidi.Script.RemoteValue): boolean { @@ -530,34 +520,26 @@ export class BidiPage implements PageDelegate { } async setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise { - throw new Error('Setting FilePayloads is not supported in Bidi.'); + await handle.evaluateInUtility(([injected, node, files]) => + injected.setInputFiles(node, files), files); } async setInputFilePaths(handle: dom.ElementHandle, paths: string[]): Promise { const fromContext = toBidiExecutionContext(handle._context); - const shared = await fromContext.rawCallFunction('x => x', { handle: handle._objectId }); - // TODO: store sharedId in the handle. - if (!('sharedId' in shared)) - throw new Error('Element is not a node'); - const sharedId = shared.sharedId!; await this._session.send('input.setFiles', { context: this._session.sessionId, - element: { sharedId }, + element: await fromContext.nodeIdForElementHandle(handle), files: paths, }); } async adoptElementHandle(handle: dom.ElementHandle, to: dom.FrameExecutionContext): Promise> { const fromContext = toBidiExecutionContext(handle._context); - const shared = await fromContext.rawCallFunction('x => x', { handle: handle._objectId }); - // TODO: store sharedId in the handle. - if (!('sharedId' in shared)) - throw new Error('Element is not a node'); - const sharedId = shared.sharedId!; + const nodeId = await fromContext.nodeIdForElementHandle(handle); const executionContext = toBidiExecutionContext(to); - const result = await executionContext.rawCallFunction('x => x', { sharedId }); - if ('handle' in result) - return to.createHandle({ objectId: result.handle!, ...result }) as dom.ElementHandle; + const objectId = await executionContext.remoteObjectForNodeId(nodeId); + if (objectId) + return to.createHandle(objectId) as dom.ElementHandle; throw new Error('Failed to adopt element handle.'); } diff --git a/packages/playwright-core/src/server/browser.ts b/packages/playwright-core/src/server/browser.ts index d99a3f8f8a..7cf00f73c8 100644 --- a/packages/playwright-core/src/server/browser.ts +++ b/packages/playwright-core/src/server/browser.ts @@ -24,7 +24,7 @@ import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor'; import type { CallMetadata } from './instrumentation'; import type * as types from './types'; import type { ProxySettings } from './types'; -import type { RecentLogsCollector } from '../utils/debugLogger'; +import type { RecentLogsCollector } from './utils/debugLogger'; import type * as channels from '@protocol/channels'; import type { ChildProcess } from 'child_process'; diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 48c87f7dc4..fd50994343 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 '../common/timeoutSettings'; +import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; import { createGuid } from './utils/crypto'; -import { debugMode } from '../utils/debug'; +import { debugMode } from '../utils/isomorphic/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 9952aef2f4..7fe3844c7d 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -19,7 +19,7 @@ import * as os from 'os'; import * as path from 'path'; import { normalizeProxySettings, validateBrowserContextOptions } from './browserContext'; -import { DEFAULT_TIMEOUT, TimeoutSettings } from '../common/timeoutSettings'; +import { DEFAULT_TIMEOUT, TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; import { ManualPromise, assert, debugMode } from '../utils'; import { existsAsync } from './utils/fileUtils'; import { helper } from './helper'; @@ -31,7 +31,7 @@ import { isProtocolError } from './protocolError'; import { registry } from './registry'; import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor'; import { WebSocketTransport } from './transport'; -import { RecentLogsCollector } from '../utils/debugLogger'; +import { RecentLogsCollector } from './utils/debugLogger'; import type { Browser, BrowserOptions, BrowserProcess } from './browser'; import type { BrowserContext } from './browserContext'; diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 7c520aa213..3f06e135cd 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -22,13 +22,13 @@ import * as path from 'path'; import { chromiumSwitches } from './chromiumSwitches'; import { CRBrowser } from './crBrowser'; import { kBrowserCloseMessageId } from './crConnection'; -import { TimeoutSettings } from '../../common/timeoutSettings'; +import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings'; import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; -import { RecentLogsCollector } from '../../utils/debugLogger'; +import { RecentLogsCollector } from '../utils/debugLogger'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; import { fetchData } from '../utils/network'; -import { getUserAgent } from '../../utils/userAgent'; +import { getUserAgent } from '../utils/userAgent'; import { validateBrowserContextOptions } from '../browserContext'; import { BrowserType, kNoXServerRunningError } from '../browserType'; import { BrowserReadyState } from '../browserType'; diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index abd4aee685..7cc6030486 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/debug'; +import { assert } from '../../utils/isomorphic/debug'; import { createGuid } from '../utils/crypto'; import { Artifact } from '../artifact'; import { Browser } from '../browser'; diff --git a/packages/playwright-core/src/server/chromium/crConnection.ts b/packages/playwright-core/src/server/chromium/crConnection.ts index 424d64a500..98c566c82a 100644 --- a/packages/playwright-core/src/server/chromium/crConnection.ts +++ b/packages/playwright-core/src/server/chromium/crConnection.ts @@ -18,14 +18,14 @@ import { EventEmitter } from 'events'; import { assert, eventsHelper } from '../../utils'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; import type { RegisteredListener } from '../../utils'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { Protocol } from './protocol'; -import type { RecentLogsCollector } from '../../utils/debugLogger'; +import type { RecentLogsCollector } from '../utils/debugLogger'; import type { ProtocolLogger } from '../types'; diff --git a/packages/playwright-core/src/server/chromium/crExecutionContext.ts b/packages/playwright-core/src/server/chromium/crExecutionContext.ts index bd1b64099b..910932caf3 100644 --- a/packages/playwright-core/src/server/chromium/crExecutionContext.ts +++ b/packages/playwright-core/src/server/chromium/crExecutionContext.ts @@ -16,7 +16,7 @@ */ import { getExceptionMessage, releaseObject } from './crProtocolHelper'; -import { rewriteErrorMessage } from '../../utils/stackTrace'; +import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace'; import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers'; import * as js from '../javascript'; import { isSessionClosedError } from '../protocolError'; diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index 0a12f9fcd8..34470470dc 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -17,10 +17,10 @@ import * as path from 'path'; -import { assert } from '../../utils/debug'; +import { assert } from '../../utils/isomorphic/debug'; import { createGuid } from '../utils/crypto'; import { eventsHelper } from '../utils/eventsHelper'; -import { rewriteErrorMessage } from '../../utils/stackTrace'; +import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace'; import * as dialog from '../dialog'; import * as dom from '../dom'; import * as frames from '../frames'; diff --git a/packages/playwright-core/src/server/chromium/crProtocolHelper.ts b/packages/playwright-core/src/server/chromium/crProtocolHelper.ts index 570b404d9b..f9e1409a79 100644 --- a/packages/playwright-core/src/server/chromium/crProtocolHelper.ts +++ b/packages/playwright-core/src/server/chromium/crProtocolHelper.ts @@ -17,7 +17,7 @@ import * as fs from 'fs'; -import { splitErrorMessage } from '../../utils/stackTrace'; +import { splitErrorMessage } from '../../utils/isomorphic/stackTrace'; import { mkdirIfNeeded } from '../utils/fileUtils'; import type { CRSession } from './crConnection'; diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 37ff8c8b81..1c74b7258c 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -16,9 +16,8 @@ import { Dispatcher } from './dispatcher'; import { SdkObject } from '../../server/instrumentation'; -import * as localUtils from '../../utils/localUtils'; -import { nodePlatform } from '../../utils/platform'; -import { getUserAgent } from '../../utils/userAgent'; +import * as localUtils from '../localUtils'; +import { getUserAgent } from '../utils/userAgent'; import { deviceDescriptors as descriptors } from '../deviceDescriptors'; import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher'; import { Progress, ProgressController } from '../progress'; @@ -26,7 +25,7 @@ import { SocksInterceptor } from '../socksInterceptor'; import { WebSocketTransport } from '../transport'; import { fetchData } from '../utils/network'; -import type { HarBackend } from '../../utils/harBackend'; +import type { HarBackend } from '../harBackend'; import type { CallMetadata } from '../instrumentation'; import type { Playwright } from '../playwright'; import type { RootDispatcher } from './dispatcher'; @@ -50,11 +49,11 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. } async zip(params: channels.LocalUtilsZipParams): Promise { - return await localUtils.zip(nodePlatform, this._stackSessions, params); + return await localUtils.zip(this._stackSessions, params); } async harOpen(params: channels.LocalUtilsHarOpenParams, metadata: CallMetadata): Promise { - return await localUtils.harOpen(nodePlatform, this._harBackends, params); + return await localUtils.harOpen(this._harBackends, params); } async harLookup(params: channels.LocalUtilsHarLookupParams, metadata: CallMetadata): Promise { @@ -74,7 +73,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. } async traceDiscarded(params: channels.LocalUtilsTraceDiscardedParams, metadata?: CallMetadata | undefined): Promise { - return await localUtils.traceDiscarded(nodePlatform, this._stackSessions, params); + return await localUtils.traceDiscarded(this._stackSessions, params); } async addStackToTracingNoReply(params: channels.LocalUtilsAddStackToTracingNoReplyParams, metadata?: CallMetadata | undefined): Promise { diff --git a/packages/playwright-core/src/server/electron/DEPS.list b/packages/playwright-core/src/server/electron/DEPS.list index 1d51da425e..1acb5c5a0e 100644 --- a/packages/playwright-core/src/server/electron/DEPS.list +++ b/packages/playwright-core/src/server/electron/DEPS.list @@ -2,5 +2,6 @@ ../ ../../common/ ../../utils/ +../../utils/isomorphic/ ../chromium/ ../utils \ No newline at end of file diff --git a/packages/playwright-core/src/server/electron/electron.ts b/packages/playwright-core/src/server/electron/electron.ts index 1728a317a8..4713324ed1 100644 --- a/packages/playwright-core/src/server/electron/electron.ts +++ b/packages/playwright-core/src/server/electron/electron.ts @@ -19,10 +19,10 @@ import * as os from 'os'; import * as path from 'path'; import * as readline from 'readline'; -import { TimeoutSettings } from '../../common/timeoutSettings'; +import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings'; import { ManualPromise } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; -import { RecentLogsCollector } from '../../utils/debugLogger'; +import { RecentLogsCollector } from '../utils/debugLogger'; import { eventsHelper } from '../utils/eventsHelper'; import { validateBrowserContextOptions } from '../browserContext'; import { CRBrowser } from '../chromium/crBrowser'; diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 08e63bb83b..6e1d6d02f1 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -21,10 +21,10 @@ import { TLSSocket } from 'tls'; import * as url from 'url'; import * as zlib from 'zlib'; -import { TimeoutSettings } from '../common/timeoutSettings'; +import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; import { assert, constructURLBasedOnBaseURL, eventsHelper, monotonicTime } from '../utils'; import { createGuid } from './utils/crypto'; -import { getUserAgent } from '../utils/userAgent'; +import { getUserAgent } from './utils/userAgent'; import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle'; import { BrowserContext, verifyClientCertificates } from './browserContext'; import { CookieStore, domainMatches, parseRawCookie } from './cookieStore'; diff --git a/packages/playwright-core/src/server/fileUploadUtils.ts b/packages/playwright-core/src/server/fileUploadUtils.ts index 1c53812a0a..5a15617c8b 100644 --- a/packages/playwright-core/src/server/fileUploadUtils.ts +++ b/packages/playwright-core/src/server/fileUploadUtils.ts @@ -17,8 +17,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { assert } from '../utils/debug'; -import { fileUploadSizeLimit } from '../utils/fileUtils'; +import { assert } from '../utils/isomorphic/debug'; import { mime } from '../utilsBundle'; import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher'; @@ -27,6 +26,9 @@ import type { Frame } from './frames'; import type * as types from './types'; import type * as channels from '@protocol/channels'; +// Keep in sync with the client. +export const fileUploadSizeLimit = 50 * 1024 * 1024; + async function filesExceedUploadLimit(files: string[]) { const sizes = await Promise.all(files.map(async file => (await fs.promises.stat(file)).size)); return sizes.reduce((total, size) => total + size, 0) >= fileUploadSizeLimit; diff --git a/packages/playwright-core/src/server/firefox/ffConnection.ts b/packages/playwright-core/src/server/firefox/ffConnection.ts index 6001754219..1477ace249 100644 --- a/packages/playwright-core/src/server/firefox/ffConnection.ts +++ b/packages/playwright-core/src/server/firefox/ffConnection.ts @@ -17,13 +17,13 @@ import { EventEmitter } from 'events'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { Protocol } from './protocol'; -import type { RecentLogsCollector } from '../../utils/debugLogger'; +import type { RecentLogsCollector } from '../utils/debugLogger'; import type { ProtocolLogger } from '../types'; diff --git a/packages/playwright-core/src/server/firefox/ffExecutionContext.ts b/packages/playwright-core/src/server/firefox/ffExecutionContext.ts index 7de27727cd..5de8cddae3 100644 --- a/packages/playwright-core/src/server/firefox/ffExecutionContext.ts +++ b/packages/playwright-core/src/server/firefox/ffExecutionContext.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { rewriteErrorMessage } from '../../utils/stackTrace'; +import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace'; import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers'; import * as js from '../javascript'; import { isSessionClosedError } from '../protocolError'; diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts index 305a9638cb..22229dff3e 100644 --- a/packages/playwright-core/src/server/firefox/ffPage.ts +++ b/packages/playwright-core/src/server/firefox/ffPage.ts @@ -25,8 +25,8 @@ import { FFSession } from './ffConnection'; import { FFExecutionContext } from './ffExecutionContext'; import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './ffInput'; import { FFNetworkManager } from './ffNetworkManager'; -import { debugLogger } from '../../utils/debugLogger'; -import { splitErrorMessage } from '../../utils/stackTrace'; +import { debugLogger } from '../utils/debugLogger'; +import { splitErrorMessage } from '../../utils/isomorphic/stackTrace'; import { BrowserContext } from '../browserContext'; import { TargetClosedError } from '../errors'; diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 87bd4f9623..dee2ad7f08 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -29,7 +29,7 @@ import { ProgressController } from './progress'; import * as types from './types'; import { LongStandingScope, asLocator, assert, compressCallLog, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils'; import { isSessionClosedError } from './protocolError'; -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from './utils/debugLogger'; import { eventsHelper } from './utils/eventsHelper'; import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser'; import { ManualPromise } from '../utils/isomorphic/manualPromise'; diff --git a/packages/playwright-core/src/utils/harBackend.ts b/packages/playwright-core/src/server/harBackend.ts similarity index 93% rename from packages/playwright-core/src/utils/harBackend.ts rename to packages/playwright-core/src/server/harBackend.ts index 304fe4b150..0814be9c5f 100644 --- a/packages/playwright-core/src/utils/harBackend.ts +++ b/packages/playwright-core/src/server/harBackend.ts @@ -14,11 +14,14 @@ * limitations under the License. */ -import { ZipFile } from './zipFile'; +import * as fs from 'fs'; +import * as path from 'path'; + +import { createGuid } from './utils/crypto'; +import { ZipFile } from './utils/zipFile'; import type { HeadersArray } from '../common/types'; import type * as har from '@trace/har'; -import type { Platform } from './platform'; const redirectStatus = [301, 302, 303, 307, 308]; @@ -27,11 +30,9 @@ export class HarBackend { private _harFile: har.HARFile; private _zipFile: ZipFile | null; private _baseDir: string | null; - private _platform: Platform; - constructor(platform: Platform, harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) { - this._platform = platform; - this.id = platform.createGuid(); + constructor(harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) { + this.id = createGuid(); this._harFile = harFile; this._baseDir = baseDir; this._zipFile = zipFile; @@ -79,7 +80,7 @@ export class HarBackend { if (this._zipFile) buffer = await this._zipFile.read(file); else - buffer = await this._platform.fs().promises.readFile(this._platform.path().resolve(this._baseDir!, file)); + buffer = await fs.promises.readFile(path.resolve(this._baseDir!, file)); } else { buffer = Buffer.from(content.text || '', content.encoding === 'base64' ? 'base64' : 'utf-8'); } diff --git a/packages/playwright-core/src/server/helper.ts b/packages/playwright-core/src/server/helper.ts index da049e2be6..e9fec62419 100644 --- a/packages/playwright-core/src/server/helper.ts +++ b/packages/playwright-core/src/server/helper.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from './utils/debugLogger'; import { eventsHelper } from './utils/eventsHelper'; import type { Progress } from './progress'; diff --git a/packages/playwright-core/src/utils/localUtils.ts b/packages/playwright-core/src/server/localUtils.ts similarity index 83% rename from packages/playwright-core/src/utils/localUtils.ts rename to packages/playwright-core/src/server/localUtils.ts index 49a3c43175..1f5f0064b5 100644 --- a/packages/playwright-core/src/utils/localUtils.ts +++ b/packages/playwright-core/src/server/localUtils.ts @@ -18,15 +18,15 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { removeFolders } from './fileUtils'; +import { calculateSha1 } from './utils/crypto'; import { HarBackend } from './harBackend'; -import { ManualPromise } from './isomorphic/manualPromise'; -import { ZipFile } from './zipFile'; +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/debug'; +import { assert } from '../utils/isomorphic/debug'; +import { removeFolders } from './utils/fileUtils'; -import type { Platform } from './platform'; import type * as channels from '@protocol/channels'; import type * as har from '@trace/har'; import type EventEmitter from 'events'; @@ -39,7 +39,7 @@ export type StackSession = { callStacks: channels.ClientSideCallMetadata[]; }; -export async function zip(platform: Platform, stackSessions: Map, params: channels.LocalUtilsZipParams): Promise { +export async function zip(stackSessions: Map, params: channels.LocalUtilsZipParams): Promise { const promise = new ManualPromise(); const zipFile = new yazl.ZipFile(); (zipFile as any as EventEmitter).on('error', error => promise.reject(error)); @@ -77,7 +77,7 @@ export async function zip(platform: Platform, stackSessions: Map promise.reject(error)); }); await promise; - await deleteStackSession(platform, stackSessions, params.stacksId); + await deleteStackSession(stackSessions, params.stacksId); return; } @@ -124,20 +124,20 @@ export async function zip(platform: Platform, stackSessions: Map, stacksId?: string) { +async function deleteStackSession(stackSessions: Map, stacksId?: string) { const session = stacksId ? stackSessions.get(stacksId) : undefined; if (!session) return; await session.writer; if (session.tmpDir) - await removeFolders(platform, [session.tmpDir]); + await removeFolders([session.tmpDir]); stackSessions.delete(stacksId!); } -export async function harOpen(platform: Platform, harBackends: Map, params: channels.LocalUtilsHarOpenParams): Promise { +export async function harOpen(harBackends: Map, params: channels.LocalUtilsHarOpenParams): Promise { let harBackend: HarBackend; if (params.file.endsWith('.zip')) { const zipFile = new ZipFile(params.file); @@ -147,10 +147,10 @@ export async function harOpen(platform: Platform, harBackends: Map, p return { stacksId: traceStacksFile }; } -export async function traceDiscarded(platform: Platform, stackSessions: Map, params: channels.LocalUtilsTraceDiscardedParams): Promise { - await deleteStackSession(platform, stackSessions, params.stacksId); +export async function traceDiscarded(stackSessions: Map, params: channels.LocalUtilsTraceDiscardedParams): Promise { + await deleteStackSession(stackSessions, params.stacksId); } export async function addStackToTracingNoReply(stackSessions: Map, params: channels.LocalUtilsAddStackToTracingNoReplyParams): Promise { diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 300cf05e50..d2db2e4211 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -28,12 +28,12 @@ import { parseEvaluationResultValue, source } from './isomorphic/utilityScriptSe import * as js from './javascript'; import { ProgressController } from './progress'; import { Screenshotter, validateScreenshotOptions } from './screenshotter'; -import { TimeoutSettings } from '../common/timeoutSettings'; +import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings'; import { LongStandingScope, assert, compressCallLog, trimStringWithEllipsis } from '../utils'; import { createGuid } from './utils/crypto'; import { asLocator } from '../utils'; import { getComparator } from './utils/comparators'; -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from './utils/debugLogger'; import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser'; import { ManualPromise } from '../utils/isomorphic/manualPromise'; diff --git a/packages/playwright-core/src/server/pipeTransport.ts b/packages/playwright-core/src/server/pipeTransport.ts index 67bf126324..ba811e606e 100644 --- a/packages/playwright-core/src/server/pipeTransport.ts +++ b/packages/playwright-core/src/server/pipeTransport.ts @@ -16,7 +16,7 @@ */ import { makeWaitForNextTask } from '../utils'; -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from './utils/debugLogger'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from './transport'; diff --git a/packages/playwright-core/src/server/progress.ts b/packages/playwright-core/src/server/progress.ts index 9766c66ebd..16dcc6fb1f 100644 --- a/packages/playwright-core/src/server/progress.ts +++ b/packages/playwright-core/src/server/progress.ts @@ -19,10 +19,14 @@ import { assert, monotonicTime } from '../utils'; import { ManualPromise } from '../utils/isomorphic/manualPromise'; import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation'; -import type { Progress as CommonProgress } from '../common/progress'; -import type { LogName } from '../utils/debugLogger'; +import type { LogName } from './utils/debugLogger'; -export interface Progress extends CommonProgress { +export interface Progress { + log(message: string): void; + timeUntilDeadline(): number; + isRunning(): boolean; + cleanupWhenAborted(cleanup: () => any): void; + throwIfAborted(): void; metadata: CallMetadata; } diff --git a/packages/playwright-core/src/server/protocolError.ts b/packages/playwright-core/src/server/protocolError.ts index 1f999b7c64..9ef856c2eb 100644 --- a/packages/playwright-core/src/server/protocolError.ts +++ b/packages/playwright-core/src/server/protocolError.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { rewriteErrorMessage } from '../utils/stackTrace'; +import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace'; export class ProtocolError extends Error { type: 'error' | 'closed' | 'crashed'; diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts index 454fa05b90..8c7b344b8e 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/debug'; +import { isUnderTest } from '../../utils/isomorphic/debug'; import { monotonicTime } from '../../utils/isomorphic/time'; import type { Signal } from '../../../../recorder/src/actions'; diff --git a/packages/playwright-core/src/server/registry/browserFetcher.ts b/packages/playwright-core/src/server/registry/browserFetcher.ts index 16cab16db1..6a84f2573a 100644 --- a/packages/playwright-core/src/server/registry/browserFetcher.ts +++ b/packages/playwright-core/src/server/registry/browserFetcher.ts @@ -20,10 +20,10 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; -import { getUserAgent } from '../../utils/userAgent'; -import { colors, progress as ProgressBar } from '../../utilsBundle'; +import { getUserAgent } from '../utils/userAgent'; +import { progress as ProgressBar, colors } from '../../utilsBundle'; import { existsAsync } from '../utils/fileUtils'; import { browserDirectoryToMarkerFilePath } from '.'; diff --git a/packages/playwright-core/src/server/registry/dependencies.ts b/packages/playwright-core/src/server/registry/dependencies.ts index e405d75c6f..5cf4a097b1 100644 --- a/packages/playwright-core/src/server/registry/dependencies.ts +++ b/packages/playwright-core/src/server/registry/dependencies.ts @@ -21,9 +21,9 @@ import * as path from 'path'; import { deps } from './nativeDeps'; import { wrapInASCIIBox } from '../utils/ascii'; -import { hostPlatform, isOfficiallySupportedPlatform } from '../../utils/hostPlatform'; +import { hostPlatform, isOfficiallySupportedPlatform } from '../utils/hostPlatform'; import { spawnAsync } from '../utils/spawnAsync'; -import { getPlaywrightVersion } from '../../utils/userAgent'; +import { getPlaywrightVersion } from '../utils/userAgent'; import { buildPlaywrightCLICommand, registry } from '.'; diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 87804f06dd..e0282120fb 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -25,16 +25,16 @@ import { dockerVersion, readDockerVersionSync, transformCommandsForRoot } from ' import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies'; import { calculateSha1, getAsBooleanFromENV, getFromENV, getPackageManagerExecCommand } from '../../utils'; import { wrapInASCIIBox } from '../utils/ascii'; -import { debugLogger } from '../../utils/debugLogger'; -import { hostPlatform, isOfficiallySupportedPlatform } from '../../utils/hostPlatform'; +import { debugLogger } from '../utils/debugLogger'; +import { hostPlatform, isOfficiallySupportedPlatform } from '../utils/hostPlatform'; import { fetchData } from '../utils/network'; import { spawnAsync } from '../utils/spawnAsync'; -import { getEmbedderName } from '../../utils/userAgent'; +import { getEmbedderName } from '../utils/userAgent'; import { lockfile } from '../../utilsBundle'; import { canAccessFile, existsAsync, removeFolders } from '../utils/fileUtils'; import type { DependencyGroup } from './dependencies'; -import type { HostPlatform } from '../../utils/hostPlatform'; +import type { HostPlatform } from '../utils/hostPlatform'; export { writeDockerVersion } from './dependencies'; diff --git a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts index b1943ca123..f769f60d74 100644 --- a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts +++ b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts @@ -24,7 +24,7 @@ import { SocksProxy } from './utils/socksProxy'; import { ManualPromise, escapeHTML, generateSelfSignedCertificate, rewriteErrorMessage } from '../utils'; import { verifyClientCertificates } from './browserContext'; import { createProxyAgent } from './fetch'; -import { debugLogger } from '../utils/debugLogger'; +import { debugLogger } from './utils/debugLogger'; import { createSocket, createTLSSocket } from './utils/happyEyeballs'; import type * as types from './types'; diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts index 9a914e1ad4..f4e288d077 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts @@ -17,7 +17,7 @@ import { frameSnapshotStreamer } from './snapshotterInjected'; import { monotonicTime } from '../../../utils/isomorphic/time'; import { calculateSha1, createGuid } from '../../utils/crypto'; -import { debugLogger } from '../../../utils/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import { eventsHelper } from '../../utils/eventsHelper'; import { mime } from '../../../utilsBundle'; import { BrowserContext } from '../../browserContext'; diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 26e3075a29..309867f3b0 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/debug'; +import { assert } from '../../../utils/isomorphic/debug'; import { monotonicTime } from '../../../utils/isomorphic/time'; import { eventsHelper } from '../../utils/eventsHelper'; import { createGuid } from '../../utils/crypto'; diff --git a/packages/playwright-core/src/server/utils/DEPS.list b/packages/playwright-core/src/server/utils/DEPS.list index 8d1b7a30f2..685e00df46 100644 --- a/packages/playwright-core/src/server/utils/DEPS.list +++ b/packages/playwright-core/src/server/utils/DEPS.list @@ -1,4 +1,5 @@ [*] +../../common ../../utils ../../utils/isomorphic ../../utilsBundle.ts diff --git a/packages/playwright-core/src/server/utils/comparators.ts b/packages/playwright-core/src/server/utils/comparators.ts index 072133d833..06b6bdc6ff 100644 --- a/packages/playwright-core/src/server/utils/comparators.ts +++ b/packages/playwright-core/src/server/utils/comparators.ts @@ -18,8 +18,8 @@ import { compare } from './image_tools/compare'; // @ts-ignore import pixelmatch from '../../third_party/pixelmatch'; -import { colors, jpegjs } from '../../utilsBundle'; -import { diff } from '../../utilsBundle'; +import { jpegjs } from '../../utilsBundle'; +import { colors, diff } from '../../utilsBundle'; import { PNG } from '../../utilsBundle'; export type ImageComparatorOptions = { threshold?: number, maxDiffPixels?: number, maxDiffPixelRatio?: number, comparator?: string }; diff --git a/packages/playwright-core/src/server/utils/crypto.ts b/packages/playwright-core/src/server/utils/crypto.ts index 0a35c3c8c9..7189f38b00 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/debug'; +import { assert } from '../../utils/isomorphic/debug'; export function createGuid(): string { return crypto.randomBytes(16).toString('hex'); diff --git a/packages/playwright-core/src/utils/debugLogger.ts b/packages/playwright-core/src/server/utils/debugLogger.ts similarity index 98% rename from packages/playwright-core/src/utils/debugLogger.ts rename to packages/playwright-core/src/server/utils/debugLogger.ts index 24b0a1dbf8..8713b3cf7e 100644 --- a/packages/playwright-core/src/utils/debugLogger.ts +++ b/packages/playwright-core/src/server/utils/debugLogger.ts @@ -16,7 +16,7 @@ import * as fs from 'fs'; -import { debug } from '../utilsBundle'; +import { debug } from '../../utilsBundle'; const debugLoggerColorMap = { 'api': 45, // cyan diff --git a/packages/playwright-core/src/utils/env.ts b/packages/playwright-core/src/server/utils/env.ts similarity index 100% rename from packages/playwright-core/src/utils/env.ts rename to packages/playwright-core/src/server/utils/env.ts diff --git a/packages/playwright-core/src/server/utils/happyEyeballs.ts b/packages/playwright-core/src/server/utils/happyEyeballs.ts index a5c6087513..da3481f4c1 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/debug'; +import { assert } from '../../utils/isomorphic/debug'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; import { monotonicTime } from '../../utils/isomorphic/time'; diff --git a/packages/playwright-core/src/utils/hostPlatform.ts b/packages/playwright-core/src/server/utils/hostPlatform.ts similarity index 100% rename from packages/playwright-core/src/utils/hostPlatform.ts rename to packages/playwright-core/src/server/utils/hostPlatform.ts diff --git a/packages/playwright-core/src/server/utils/httpServer.ts b/packages/playwright-core/src/server/utils/httpServer.ts index 21992195c0..0c9f0fe25a 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/debug'; +import { assert } from '../../utils/isomorphic/debug'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; import { createHttpServer } from './network'; diff --git a/packages/playwright-core/src/utils/linuxUtils.ts b/packages/playwright-core/src/server/utils/linuxUtils.ts similarity index 73% rename from packages/playwright-core/src/utils/linuxUtils.ts rename to packages/playwright-core/src/server/utils/linuxUtils.ts index c51d32f9d5..3c85ebe091 100644 --- a/packages/playwright-core/src/utils/linuxUtils.ts +++ b/packages/playwright-core/src/server/utils/linuxUtils.ts @@ -23,26 +23,6 @@ let osRelease: { version: string, } | undefined; -export async function getLinuxDistributionInfo(): Promise<{ id: string, version: string } | undefined> { - if (process.platform !== 'linux') - return undefined; - if (!osRelease && !didFailToReadOSRelease) { - try { - // List of /etc/os-release values for different distributions could be - // found here: https://gist.github.com/aslushnikov/8ceddb8288e4cf9db3039c02e0f4fb75 - const osReleaseText = await fs.promises.readFile('/etc/os-release', 'utf8'); - const fields = parseOSReleaseText(osReleaseText); - osRelease = { - id: fields.get('id') ?? '', - version: fields.get('version_id') ?? '', - }; - } catch (e) { - didFailToReadOSRelease = true; - } - } - return osRelease; -} - export function getLinuxDistributionInfoSync(): { id: string, version: string } | undefined { if (process.platform !== 'linux') return undefined; diff --git a/packages/playwright-core/src/utils/platform.ts b/packages/playwright-core/src/server/utils/nodePlatform.ts similarity index 50% rename from packages/playwright-core/src/utils/platform.ts rename to packages/playwright-core/src/server/utils/nodePlatform.ts index 60f2cdbd3a..e9883da361 100644 --- a/packages/playwright-core/src/utils/platform.ts +++ b/packages/playwright-core/src/server/utils/nodePlatform.ts @@ -19,51 +19,70 @@ import * as fs from 'fs'; import * as path from 'path'; import * as util from 'util'; -export type Platform = { - calculateSha1(text: string): Promise; - createGuid: () => string; - fs: () => typeof fs; - inspectCustom: symbol | undefined; - path: () => typeof path; - ws?: (url: string) => WebSocket; -}; +import { colors } from '../../utilsBundle'; +import { debugLogger } from './debugLogger'; +import { currentZone, emptyZone } from './zones'; + +import type { Platform, Zone } from '../../common/platform'; +import type { Zone as ZoneImpl } from './zones'; + +class NodeZone implements Zone { + private _zone: ZoneImpl; + + constructor(zone: ZoneImpl) { + this._zone = zone; + } + + push(data: T) { + return new NodeZone(this._zone.with('apiZone', data)); + } + + pop() { + return new NodeZone(this._zone.without('apiZone')); + } + + run(func: () => R): R { + return this._zone.run(func); + } + + data(): T | undefined { + return this._zone.data('apiZone'); + } +} export const nodePlatform: Platform = { + name: 'node', + calculateSha1: (text: string) => { const sha1 = crypto.createHash('sha1'); sha1.update(text); return Promise.resolve(sha1.digest('hex')); }, + colors, + createGuid: () => crypto.randomBytes(16).toString('hex'), fs: () => fs, inspectCustom: util.inspect.custom, + isDebuggerAttached: () => !!require('inspector').url(), + + isLogEnabled(name: 'api' | 'channel') { + return debugLogger.isEnabled(name); + }, + + log(name: 'api' | 'channel', message: string | Error | object) { + debugLogger.log(name, message); + }, + path: () => path, -}; - -export const webPlatform: Platform = { - calculateSha1: async (text: string) => { - const bytes = new TextEncoder().encode(text); - const hashBuffer = await crypto.subtle.digest('SHA-1', bytes); - return Array.from(new Uint8Array(hashBuffer), b => b.toString(16).padStart(2, '0')).join(''); - }, - - createGuid: () => { - return Array.from(crypto.getRandomValues(new Uint8Array(16)), b => b.toString(16).padStart(2, '0')).join(''); - }, - - fs: () => { - throw new Error('File system is not available'); - }, - - inspectCustom: undefined, - - path: () => { - throw new Error('Path module is not available'); - }, - - ws: (url: string) => new WebSocket(url), + + pathSeparator: path.sep, + + zones: { + current: () => new NodeZone(currentZone()), + empty: new NodeZone(emptyZone), + } }; diff --git a/packages/playwright-core/src/utils/pipeTransport.ts b/packages/playwright-core/src/server/utils/pipeTransport.ts similarity index 100% rename from packages/playwright-core/src/utils/pipeTransport.ts rename to packages/playwright-core/src/server/utils/pipeTransport.ts diff --git a/packages/playwright-core/src/server/utils/socksProxy.ts b/packages/playwright-core/src/server/utils/socksProxy.ts index 49dab675f6..c0fe0f126a 100644 --- a/packages/playwright-core/src/server/utils/socksProxy.ts +++ b/packages/playwright-core/src/server/utils/socksProxy.ts @@ -17,9 +17,9 @@ import EventEmitter from 'events'; import * as net from 'net'; -import { assert } from '../../utils/debug'; +import { assert } from '../../utils/isomorphic/debug'; import { createGuid } from './crypto'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from './debugLogger'; import { createSocket } from './happyEyeballs'; import type { AddressInfo } from 'net'; diff --git a/packages/playwright-core/src/utils/task.ts b/packages/playwright-core/src/server/utils/task.ts similarity index 100% rename from packages/playwright-core/src/utils/task.ts rename to packages/playwright-core/src/server/utils/task.ts diff --git a/packages/playwright-core/src/utils/userAgent.ts b/packages/playwright-core/src/server/utils/userAgent.ts similarity index 98% rename from packages/playwright-core/src/utils/userAgent.ts rename to packages/playwright-core/src/server/utils/userAgent.ts index 7f3e3d2275..9676a5bae5 100644 --- a/packages/playwright-core/src/utils/userAgent.ts +++ b/packages/playwright-core/src/server/utils/userAgent.ts @@ -77,6 +77,6 @@ export function getEmbedderName(): { embedderName: string, embedderVersion: stri } export function getPlaywrightVersion(majorMinorOnly = false): string { - const version = process.env.PW_VERSION_OVERRIDE || require('./../../package.json').version; + const version = process.env.PW_VERSION_OVERRIDE || require('./../../../package.json').version; return majorMinorOnly ? version.split('.').slice(0, 2).join('.') : version; } diff --git a/packages/playwright-core/src/server/utils/wsServer.ts b/packages/playwright-core/src/server/utils/wsServer.ts index b223802a1b..68362f14de 100644 --- a/packages/playwright-core/src/server/utils/wsServer.ts +++ b/packages/playwright-core/src/server/utils/wsServer.ts @@ -16,7 +16,7 @@ import { createHttpServer } from './network'; import { wsServer } from '../../utilsBundle'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from './debugLogger'; import type { WebSocket, WebSocketServer } from '../../utilsBundle'; import type http from 'http'; diff --git a/packages/playwright-core/src/utils/zipFile.ts b/packages/playwright-core/src/server/utils/zipFile.ts similarity index 95% rename from packages/playwright-core/src/utils/zipFile.ts rename to packages/playwright-core/src/server/utils/zipFile.ts index 1f70c5e1bc..43b5a3430c 100644 --- a/packages/playwright-core/src/utils/zipFile.ts +++ b/packages/playwright-core/src/server/utils/zipFile.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { yauzl } from '../zipBundle'; +import { yauzl } from '../../zipBundle'; -import type { Entry, UnzipFile } from '../zipBundle'; +import type { Entry, UnzipFile } from '../../zipBundle'; export class ZipFile { private _fileName: string; diff --git a/packages/playwright-core/src/utils/zones.ts b/packages/playwright-core/src/server/utils/zones.ts similarity index 63% rename from packages/playwright-core/src/utils/zones.ts rename to packages/playwright-core/src/server/utils/zones.ts index 32664c3898..b5860167b0 100644 --- a/packages/playwright-core/src/utils/zones.ts +++ b/packages/playwright-core/src/server/utils/zones.ts @@ -18,36 +18,13 @@ import { AsyncLocalStorage } from 'async_hooks'; export type ZoneType = 'apiZone' | 'stepZone'; -class ZoneManager { - private readonly _asyncLocalStorage = new AsyncLocalStorage(); - private readonly _emptyZone = Zone.createEmpty(this._asyncLocalStorage); - - run(type: ZoneType, data: T, func: () => R): R { - return this.current().with(type, data).run(func); - } - - zoneData(type: ZoneType): T | undefined { - return this.current().data(type); - } - - current(): Zone { - return this._asyncLocalStorage.getStore() ?? this._emptyZone; - } - - empty(): Zone { - return this._emptyZone; - } -} +const asyncLocalStorage = new AsyncLocalStorage(); export class Zone { private readonly _asyncLocalStorage: AsyncLocalStorage; private readonly _data: ReadonlyMap; - static createEmpty(asyncLocalStorage: AsyncLocalStorage) { - return new Zone(asyncLocalStorage, new Map()); - } - - private constructor(asyncLocalStorage: AsyncLocalStorage, store: Map) { + constructor(asyncLocalStorage: AsyncLocalStorage, store: Map) { this._asyncLocalStorage = asyncLocalStorage; this._data = store; } @@ -71,4 +48,8 @@ export class Zone { } } -export const zones = new ZoneManager(); +export const emptyZone = new Zone(asyncLocalStorage, new Map()); + +export function currentZone(): Zone { + return asyncLocalStorage.getStore() ?? emptyZone; +} diff --git a/packages/playwright-core/src/server/webkit/wkConnection.ts b/packages/playwright-core/src/server/webkit/wkConnection.ts index 0012d8702f..ada50a413b 100644 --- a/packages/playwright-core/src/server/webkit/wkConnection.ts +++ b/packages/playwright-core/src/server/webkit/wkConnection.ts @@ -18,13 +18,13 @@ import { EventEmitter } from 'events'; import { assert } from '../../utils'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { Protocol } from './protocol'; -import type { RecentLogsCollector } from '../../utils/debugLogger'; +import type { RecentLogsCollector } from '../utils/debugLogger'; import type { ProtocolLogger } from '../types'; diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 323e12e553..f9e9517fb2 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -21,8 +21,8 @@ import { assert, debugAssert } from '../../utils'; import { headersArrayToObject } from '../../utils/isomorphic/headers'; import { createGuid } from '../utils/crypto'; import { eventsHelper } from '../utils/eventsHelper'; -import { hostPlatform } from '../../utils/hostPlatform'; -import { splitErrorMessage } from '../../utils/stackTrace'; +import { hostPlatform } from '../utils/hostPlatform'; +import { splitErrorMessage } from '../../utils/isomorphic/stackTrace'; import { PNG, jpegjs } from '../../utilsBundle'; import { BrowserContext } from '../browserContext'; import * as dialog from '../dialog'; @@ -39,7 +39,7 @@ import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './wkInput'; import { WKInterceptableRequest, WKRouteImpl } from './wkInterceptableRequest'; import { WKProvisionalPage } from './wkProvisionalPage'; import { WKWorkers } from './wkWorkers'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { Protocol } from './protocol'; import type { WKBrowserContext } from './wkBrowser'; diff --git a/packages/playwright-core/src/utils.ts b/packages/playwright-core/src/utils.ts index 752d376899..dc5f284955 100644 --- a/packages/playwright-core/src/utils.ts +++ b/packages/playwright-core/src/utils.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export * from './utils/isomorphic/colors'; +export * from './utils/isomorphic/debug'; export * from './utils/isomorphic/locatorGenerators'; export * from './utils/isomorphic/manualPromise'; export * from './utils/isomorphic/mimeType'; @@ -24,29 +26,30 @@ export * from './utils/isomorphic/time'; export * from './utils/isomorphic/timeoutRunner'; export * from './utils/isomorphic/urlMatch'; -export * from './utils/debug'; -export * from './utils/debugLogger'; -export * from './utils/env'; -export * from './utils/hostPlatform'; export * from './utils/isomorphic/headers'; export * from './utils/isomorphic/semaphore'; -export * from './utils/platform'; -export * from './utils/stackTrace'; -export * from './utils/task'; -export * from './utils/userAgent'; -export * from './utils/zipFile'; -export * from './utils/zones'; +export * from './utils/isomorphic/stackTrace'; export * from './server/utils/ascii'; export * from './server/utils/comparators'; export * from './server/utils/crypto'; +export * from './server/utils/debugLogger'; +export * from './server/utils/env'; export * from './server/utils/eventsHelper'; export * from './server/utils/expectUtils'; export * from './server/utils/fileUtils'; +export * from './server/utils/hostPlatform'; export * from './server/utils/httpServer'; export * from './server/utils/network'; +export * from './server/utils/nodePlatform'; export * from './server/utils/processLauncher'; export * from './server/utils/profiler'; export * from './server/utils/socksProxy'; export * from './server/utils/spawnAsync'; +export * from './server/utils/task'; +export * from './server/utils/userAgent'; export * from './server/utils/wsServer'; +export * from './server/utils/zipFile'; +export * from './server/utils/zones'; + +export { colors } from './utilsBundle'; diff --git a/packages/playwright-core/src/utils/isomorphic/colors.ts b/packages/playwright-core/src/utils/isomorphic/colors.ts new file mode 100644 index 0000000000..7be0955ad3 --- /dev/null +++ b/packages/playwright-core/src/utils/isomorphic/colors.ts @@ -0,0 +1,66 @@ +/** + * 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 const webColors = { + enabled: true, + reset: (text: string) => applyStyle(0, 0, text), + + bold: (text: string) => applyStyle(1, 22, text), + dim: (text: string) => applyStyle(2, 22, text), + italic: (text: string) => applyStyle(3, 23, text), + underline: (text: string) => applyStyle(4, 24, text), + inverse: (text: string) => applyStyle(7, 27, text), + hidden: (text: string) => applyStyle(8, 28, text), + strikethrough: (text: string) => applyStyle(9, 29, text), + + black: (text: string) => applyStyle(30, 39, text), + red: (text: string) => applyStyle(31, 39, text), + green: (text: string) => applyStyle(32, 39, text), + yellow: (text: string) => applyStyle(33, 39, text), + blue: (text: string) => applyStyle(34, 39, text), + magenta: (text: string) => applyStyle(35, 39, text), + cyan: (text: string) => applyStyle(36, 39, text), + white: (text: string) => applyStyle(37, 39, text), + gray: (text: string) => applyStyle(90, 39, text), + grey: (text: string) => applyStyle(90, 39, text), +}; + +export type Colors = typeof webColors; + +export const noColors: Colors = { + enabled: false, + reset: t => t, + bold: t => t, + dim: t => t, + italic: t => t, + underline: t => t, + inverse: t => t, + hidden: t => t, + strikethrough: t => t, + black: t => t, + red: t => t, + green: t => t, + yellow: t => t, + blue: t => t, + magenta: t => t, + cyan: t => t, + white: t => t, + gray: t => t, + grey: t => t, +}; + + +const applyStyle = (open: number, close: number, text: string) => `\u001b[${open}m${text}\u001b[${close}m`; diff --git a/packages/playwright-core/src/utils/debug.ts b/packages/playwright-core/src/utils/isomorphic/debug.ts similarity index 83% rename from packages/playwright-core/src/utils/debug.ts rename to packages/playwright-core/src/utils/isomorphic/debug.ts index 2e199b3a31..1eec988aa9 100644 --- a/packages/playwright-core/src/utils/debug.ts +++ b/packages/playwright-core/src/utils/isomorphic/debug.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -import { getFromENV } from './env'; - export function assert(value: any, message?: string): asserts value { if (!value) throw new Error(message || 'Assertion error'); @@ -26,13 +24,18 @@ export function debugAssert(value: any, message?: string): asserts value { throw new Error(message); } -const debugEnv = getFromENV('PWDEBUG') || ''; +let _debugMode: string | undefined; + +export function setDebugMode(mode: string) { + _debugMode = mode; +} + export function debugMode() { - if (debugEnv === 'console') + if (_debugMode === 'console') return 'console'; - if (debugEnv === '0' || debugEnv === 'false') + if (_debugMode === '0' || _debugMode === 'false') return ''; - return debugEnv ? 'inspector' : ''; + return _debugMode ? 'inspector' : ''; } let _isUnderTest = !!process.env.PWTEST_UNDER_TEST; diff --git a/packages/playwright-core/src/utils/isomorphic/mimeType.ts b/packages/playwright-core/src/utils/isomorphic/mimeType.ts index 407d935281..45ac92d645 100644 --- a/packages/playwright-core/src/utils/isomorphic/mimeType.ts +++ b/packages/playwright-core/src/utils/isomorphic/mimeType.ts @@ -1,14 +1,14 @@ /** * 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 * * 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, + * 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. @@ -21,3 +21,426 @@ export function isJsonMimeType(mimeType: string) { export function isTextualMimeType(mimeType: string) { return !!mimeType.match(/^(text\/.*?|application\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\/svg(\+xml)?|application\/.*?(\+json|\+xml))(;\s*charset=.*)?$/); } +export function getMimeTypeForPath(path: string): string | null { + const dotIndex = path.lastIndexOf('.'); + if (dotIndex === -1) + return null; + const extension = path.substring(dotIndex + 1); + return types.get(extension) || null; +} + +const types: Map = new Map([ + ['ez', 'application/andrew-inset'], + ['aw', 'application/applixware'], + ['atom', 'application/atom+xml'], + ['atomcat', 'application/atomcat+xml'], + ['atomdeleted', 'application/atomdeleted+xml'], + ['atomsvc', 'application/atomsvc+xml'], + ['dwd', 'application/atsc-dwd+xml'], + ['held', 'application/atsc-held+xml'], + ['rsat', 'application/atsc-rsat+xml'], + ['bdoc', 'application/bdoc'], + ['xcs', 'application/calendar+xml'], + ['ccxml', 'application/ccxml+xml'], + ['cdfx', 'application/cdfx+xml'], + ['cdmia', 'application/cdmi-capability'], + ['cdmic', 'application/cdmi-container'], + ['cdmid', 'application/cdmi-domain'], + ['cdmio', 'application/cdmi-object'], + ['cdmiq', 'application/cdmi-queue'], + ['cu', 'application/cu-seeme'], + ['mpd', 'application/dash+xml'], + ['davmount', 'application/davmount+xml'], + ['dbk', 'application/docbook+xml'], + ['dssc', 'application/dssc+der'], + ['xdssc', 'application/dssc+xml'], + ['ecma', 'application/ecmascript'], + ['es', 'application/ecmascript'], + ['emma', 'application/emma+xml'], + ['emotionml', 'application/emotionml+xml'], + ['epub', 'application/epub+zip'], + ['exi', 'application/exi'], + ['exp', 'application/express'], + ['fdt', 'application/fdt+xml'], + ['pfr', 'application/font-tdpfr'], + ['geojson', 'application/geo+json'], + ['gml', 'application/gml+xml'], + ['gpx', 'application/gpx+xml'], + ['gxf', 'application/gxf'], + ['gz', 'application/gzip'], + ['hjson', 'application/hjson'], + ['stk', 'application/hyperstudio'], + ['ink', 'application/inkml+xml'], + ['inkml', 'application/inkml+xml'], + ['ipfix', 'application/ipfix'], + ['its', 'application/its+xml'], + ['ear', 'application/java-archive'], + ['jar', 'application/java-archive'], + ['war', 'application/java-archive'], + ['ser', 'application/java-serialized-object'], + ['class', 'application/java-vm'], + ['js', 'application/javascript'], + ['mjs', 'application/javascript'], + ['json', 'application/json'], + ['map', 'application/json'], + ['json5', 'application/json5'], + ['jsonml', 'application/jsonml+json'], + ['jsonld', 'application/ld+json'], + ['lgr', 'application/lgr+xml'], + ['lostxml', 'application/lost+xml'], + ['hqx', 'application/mac-binhex40'], + ['cpt', 'application/mac-compactpro'], + ['mads', 'application/mads+xml'], + ['webmanifest', 'application/manifest+json'], + ['mrc', 'application/marc'], + ['mrcx', 'application/marcxml+xml'], + ['ma', 'application/mathematica'], + ['mb', 'application/mathematica'], + ['nb', 'application/mathematica'], + ['mathml', 'application/mathml+xml'], + ['mbox', 'application/mbox'], + ['mscml', 'application/mediaservercontrol+xml'], + ['metalink', 'application/metalink+xml'], + ['meta4', 'application/metalink4+xml'], + ['mets', 'application/mets+xml'], + ['maei', 'application/mmt-aei+xml'], + ['musd', 'application/mmt-usd+xml'], + ['mods', 'application/mods+xml'], + ['m21', 'application/mp21'], + ['mp21', 'application/mp21'], + ['m4p', 'application/mp4'], + ['mp4s', 'application/mp4'], + ['doc', 'application/msword'], + ['dot', 'application/msword'], + ['mxf', 'application/mxf'], + ['nq', 'application/n-quads'], + ['nt', 'application/n-triples'], + ['cjs', 'application/node'], + ['bin', 'application/octet-stream'], + ['bpk', 'application/octet-stream'], + ['buffer', 'application/octet-stream'], + ['deb', 'application/octet-stream'], + ['deploy', 'application/octet-stream'], + ['dist', 'application/octet-stream'], + ['distz', 'application/octet-stream'], + ['dll', 'application/octet-stream'], + ['dmg', 'application/octet-stream'], + ['dms', 'application/octet-stream'], + ['dump', 'application/octet-stream'], + ['elc', 'application/octet-stream'], + ['exe', 'application/octet-stream'], + ['img', 'application/octet-stream'], + ['iso', 'application/octet-stream'], + ['lrf', 'application/octet-stream'], + ['mar', 'application/octet-stream'], + ['msi', 'application/octet-stream'], + ['msm', 'application/octet-stream'], + ['msp', 'application/octet-stream'], + ['pkg', 'application/octet-stream'], + ['so', 'application/octet-stream'], + ['oda', 'application/oda'], + ['opf', 'application/oebps-package+xml'], + ['ogx', 'application/ogg'], + ['omdoc', 'application/omdoc+xml'], + ['onepkg', 'application/onenote'], + ['onetmp', 'application/onenote'], + ['onetoc', 'application/onenote'], + ['onetoc2', 'application/onenote'], + ['oxps', 'application/oxps'], + ['relo', 'application/p2p-overlay+xml'], + ['xer', 'application/patch-ops-error+xml'], + ['pdf', 'application/pdf'], + ['pgp', 'application/pgp-encrypted'], + ['asc', 'application/pgp-signature'], + ['sig', 'application/pgp-signature'], + ['prf', 'application/pics-rules'], + ['p10', 'application/pkcs10'], + ['p7c', 'application/pkcs7-mime'], + ['p7m', 'application/pkcs7-mime'], + ['p7s', 'application/pkcs7-signature'], + ['p8', 'application/pkcs8'], + ['ac', 'application/pkix-attr-cert'], + ['cer', 'application/pkix-cert'], + ['crl', 'application/pkix-crl'], + ['pkipath', 'application/pkix-pkipath'], + ['pki', 'application/pkixcmp'], + ['pls', 'application/pls+xml'], + ['ai', 'application/postscript'], + ['eps', 'application/postscript'], + ['ps', 'application/postscript'], + ['provx', 'application/provenance+xml'], + ['pskcxml', 'application/pskc+xml'], + ['raml', 'application/raml+yaml'], + ['owl', 'application/rdf+xml'], + ['rdf', 'application/rdf+xml'], + ['rif', 'application/reginfo+xml'], + ['rnc', 'application/relax-ng-compact-syntax'], + ['rl', 'application/resource-lists+xml'], + ['rld', 'application/resource-lists-diff+xml'], + ['rs', 'application/rls-services+xml'], + ['rapd', 'application/route-apd+xml'], + ['sls', 'application/route-s-tsid+xml'], + ['rusd', 'application/route-usd+xml'], + ['gbr', 'application/rpki-ghostbusters'], + ['mft', 'application/rpki-manifest'], + ['roa', 'application/rpki-roa'], + ['rsd', 'application/rsd+xml'], + ['rss', 'application/rss+xml'], + ['rtf', 'application/rtf'], + ['sbml', 'application/sbml+xml'], + ['scq', 'application/scvp-cv-request'], + ['scs', 'application/scvp-cv-response'], + ['spq', 'application/scvp-vp-request'], + ['spp', 'application/scvp-vp-response'], + ['sdp', 'application/sdp'], + ['senmlx', 'application/senml+xml'], + ['sensmlx', 'application/sensml+xml'], + ['setpay', 'application/set-payment-initiation'], + ['setreg', 'application/set-registration-initiation'], + ['shf', 'application/shf+xml'], + ['sieve', 'application/sieve'], + ['siv', 'application/sieve'], + ['smi', 'application/smil+xml'], + ['smil', 'application/smil+xml'], + ['rq', 'application/sparql-query'], + ['srx', 'application/sparql-results+xml'], + ['gram', 'application/srgs'], + ['grxml', 'application/srgs+xml'], + ['sru', 'application/sru+xml'], + ['ssdl', 'application/ssdl+xml'], + ['ssml', 'application/ssml+xml'], + ['swidtag', 'application/swid+xml'], + ['tei', 'application/tei+xml'], + ['teicorpus', 'application/tei+xml'], + ['tfi', 'application/thraud+xml'], + ['tsd', 'application/timestamped-data'], + ['toml', 'application/toml'], + ['trig', 'application/trig'], + ['ttml', 'application/ttml+xml'], + ['ubj', 'application/ubjson'], + ['rsheet', 'application/urc-ressheet+xml'], + ['td', 'application/urc-targetdesc+xml'], + ['vxml', 'application/voicexml+xml'], + ['wasm', 'application/wasm'], + ['wgt', 'application/widget'], + ['hlp', 'application/winhlp'], + ['wsdl', 'application/wsdl+xml'], + ['wspolicy', 'application/wspolicy+xml'], + ['xaml', 'application/xaml+xml'], + ['xav', 'application/xcap-att+xml'], + ['xca', 'application/xcap-caps+xml'], + ['xdf', 'application/xcap-diff+xml'], + ['xel', 'application/xcap-el+xml'], + ['xns', 'application/xcap-ns+xml'], + ['xenc', 'application/xenc+xml'], + ['xht', 'application/xhtml+xml'], + ['xhtml', 'application/xhtml+xml'], + ['xlf', 'application/xliff+xml'], + ['rng', 'application/xml'], + ['xml', 'application/xml'], + ['xsd', 'application/xml'], + ['xsl', 'application/xml'], + ['dtd', 'application/xml-dtd'], + ['xop', 'application/xop+xml'], + ['xpl', 'application/xproc+xml'], + ['*xsl', 'application/xslt+xml'], + ['xslt', 'application/xslt+xml'], + ['xspf', 'application/xspf+xml'], + ['mxml', 'application/xv+xml'], + ['xhvml', 'application/xv+xml'], + ['xvm', 'application/xv+xml'], + ['xvml', 'application/xv+xml'], + ['yang', 'application/yang'], + ['yin', 'application/yin+xml'], + ['zip', 'application/zip'], + ['*3gpp', 'audio/3gpp'], + ['adp', 'audio/adpcm'], + ['amr', 'audio/amr'], + ['au', 'audio/basic'], + ['snd', 'audio/basic'], + ['kar', 'audio/midi'], + ['mid', 'audio/midi'], + ['midi', 'audio/midi'], + ['rmi', 'audio/midi'], + ['mxmf', 'audio/mobile-xmf'], + ['*mp3', 'audio/mp3'], + ['m4a', 'audio/mp4'], + ['mp4a', 'audio/mp4'], + ['m2a', 'audio/mpeg'], + ['m3a', 'audio/mpeg'], + ['mp2', 'audio/mpeg'], + ['mp2a', 'audio/mpeg'], + ['mp3', 'audio/mpeg'], + ['mpga', 'audio/mpeg'], + ['oga', 'audio/ogg'], + ['ogg', 'audio/ogg'], + ['opus', 'audio/ogg'], + ['spx', 'audio/ogg'], + ['s3m', 'audio/s3m'], + ['sil', 'audio/silk'], + ['wav', 'audio/wav'], + ['*wav', 'audio/wave'], + ['weba', 'audio/webm'], + ['xm', 'audio/xm'], + ['ttc', 'font/collection'], + ['otf', 'font/otf'], + ['ttf', 'font/ttf'], + ['woff', 'font/woff'], + ['woff2', 'font/woff2'], + ['exr', 'image/aces'], + ['apng', 'image/apng'], + ['avif', 'image/avif'], + ['bmp', 'image/bmp'], + ['cgm', 'image/cgm'], + ['drle', 'image/dicom-rle'], + ['emf', 'image/emf'], + ['fits', 'image/fits'], + ['g3', 'image/g3fax'], + ['gif', 'image/gif'], + ['heic', 'image/heic'], + ['heics', 'image/heic-sequence'], + ['heif', 'image/heif'], + ['heifs', 'image/heif-sequence'], + ['hej2', 'image/hej2k'], + ['hsj2', 'image/hsj2'], + ['ief', 'image/ief'], + ['jls', 'image/jls'], + ['jp2', 'image/jp2'], + ['jpg2', 'image/jp2'], + ['jpe', 'image/jpeg'], + ['jpeg', 'image/jpeg'], + ['jpg', 'image/jpeg'], + ['jph', 'image/jph'], + ['jhc', 'image/jphc'], + ['jpm', 'image/jpm'], + ['jpf', 'image/jpx'], + ['jpx', 'image/jpx'], + ['jxr', 'image/jxr'], + ['jxra', 'image/jxra'], + ['jxrs', 'image/jxrs'], + ['jxs', 'image/jxs'], + ['jxsc', 'image/jxsc'], + ['jxsi', 'image/jxsi'], + ['jxss', 'image/jxss'], + ['ktx', 'image/ktx'], + ['ktx2', 'image/ktx2'], + ['png', 'image/png'], + ['sgi', 'image/sgi'], + ['svg', 'image/svg+xml'], + ['svgz', 'image/svg+xml'], + ['t38', 'image/t38'], + ['tif', 'image/tiff'], + ['tiff', 'image/tiff'], + ['tfx', 'image/tiff-fx'], + ['webp', 'image/webp'], + ['wmf', 'image/wmf'], + ['disposition-notification', 'message/disposition-notification'], + ['u8msg', 'message/global'], + ['u8dsn', 'message/global-delivery-status'], + ['u8mdn', 'message/global-disposition-notification'], + ['u8hdr', 'message/global-headers'], + ['eml', 'message/rfc822'], + ['mime', 'message/rfc822'], + ['3mf', 'model/3mf'], + ['gltf', 'model/gltf+json'], + ['glb', 'model/gltf-binary'], + ['iges', 'model/iges'], + ['igs', 'model/iges'], + ['mesh', 'model/mesh'], + ['msh', 'model/mesh'], + ['silo', 'model/mesh'], + ['mtl', 'model/mtl'], + ['obj', 'model/obj'], + ['stpx', 'model/step+xml'], + ['stpz', 'model/step+zip'], + ['stpxz', 'model/step-xml+zip'], + ['stl', 'model/stl'], + ['vrml', 'model/vrml'], + ['wrl', 'model/vrml'], + ['*x3db', 'model/x3d+binary'], + ['x3dbz', 'model/x3d+binary'], + ['x3db', 'model/x3d+fastinfoset'], + ['*x3dv', 'model/x3d+vrml'], + ['x3dvz', 'model/x3d+vrml'], + ['x3d', 'model/x3d+xml'], + ['x3dz', 'model/x3d+xml'], + ['x3dv', 'model/x3d-vrml'], + ['appcache', 'text/cache-manifest'], + ['manifest', 'text/cache-manifest'], + ['ics', 'text/calendar'], + ['ifb', 'text/calendar'], + ['coffee', 'text/coffeescript'], + ['litcoffee', 'text/coffeescript'], + ['css', 'text/css'], + ['csv', 'text/csv'], + ['htm', 'text/html'], + ['html', 'text/html'], + ['shtml', 'text/html'], + ['jade', 'text/jade'], + ['jsx', 'text/jsx'], + ['less', 'text/less'], + ['markdown', 'text/markdown'], + ['md', 'text/markdown'], + ['mml', 'text/mathml'], + ['mdx', 'text/mdx'], + ['n3', 'text/n3'], + ['conf', 'text/plain'], + ['def', 'text/plain'], + ['in', 'text/plain'], + ['ini', 'text/plain'], + ['list', 'text/plain'], + ['log', 'text/plain'], + ['text', 'text/plain'], + ['txt', 'text/plain'], + ['rtx', 'text/richtext'], + ['*rtf', 'text/rtf'], + ['sgm', 'text/sgml'], + ['sgml', 'text/sgml'], + ['shex', 'text/shex'], + ['slim', 'text/slim'], + ['slm', 'text/slim'], + ['spdx', 'text/spdx'], + ['styl', 'text/stylus'], + ['stylus', 'text/stylus'], + ['tsv', 'text/tab-separated-values'], + ['man', 'text/troff'], + ['me', 'text/troff'], + ['ms', 'text/troff'], + ['roff', 'text/troff'], + ['t', 'text/troff'], + ['tr', 'text/troff'], + ['ttl', 'text/turtle'], + ['uri', 'text/uri-list'], + ['uris', 'text/uri-list'], + ['urls', 'text/uri-list'], + ['vcard', 'text/vcard'], + ['vtt', 'text/vtt'], + ['*xml', 'text/xml'], + ['yaml', 'text/yaml'], + ['yml', 'text/yaml'], + ['3gp', 'video/3gpp'], + ['3gpp', 'video/3gpp'], + ['3g2', 'video/3gpp2'], + ['h261', 'video/h261'], + ['h263', 'video/h263'], + ['h264', 'video/h264'], + ['m4s', 'video/iso.segment'], + ['jpgv', 'video/jpeg'], + ['jpm', 'video/jpm'], + ['jpgm', 'video/jpm'], + ['mj2', 'video/mj2'], + ['mjp2', 'video/mj2'], + ['ts', 'video/mp2t'], + ['mp4', 'video/mp4'], + ['mp4v', 'video/mp4'], + ['mpg4', 'video/mp4'], + ['m1v', 'video/mpeg'], + ['m2v', 'video/mpeg'], + ['mpe', 'video/mpeg'], + ['mpeg', 'video/mpeg'], + ['mpg', 'video/mpeg'], + ['ogv', 'video/ogg'], + ['mov', 'video/quicktime'], + ['qt', 'video/quicktime'], + ['webm', 'video/webm'] +]); diff --git a/packages/playwright-core/src/utils/stackTrace.ts b/packages/playwright-core/src/utils/isomorphic/stackTrace.ts similarity index 81% rename from packages/playwright-core/src/utils/stackTrace.ts rename to packages/playwright-core/src/utils/isomorphic/stackTrace.ts index d3331698d1..26b79c5c27 100644 --- a/packages/playwright-core/src/utils/stackTrace.ts +++ b/packages/playwright-core/src/utils/isomorphic/stackTrace.ts @@ -14,16 +14,14 @@ * limitations under the License. */ -import * as path from 'path'; - -import { colors } from '../utilsBundle'; -import { findRepeatedSubsequences } from './isomorphic/sequence'; +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): StackFrame | null { - const frame = parseStackFrame(line); +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:'))) @@ -47,12 +45,15 @@ export function rewriteErrorMessage(e: E, newMessage: string): return e; } -const CORE_DIR = path.resolve(__dirname, '..', '..'); +let coreDir: string | undefined; -const internalStackPrefixes = [ - CORE_DIR, -]; -export const addInternalStackPrefix = (prefix: string) => internalStackPrefixes.push(prefix); +const playwrightStackPrefixes: string[] = []; +export const addInternalStackPrefix = (prefix: string) => playwrightStackPrefixes.push(prefix); + +export const setLibraryStackPrefix = (prefix: string) => { + coreDir = prefix; + playwrightStackPrefixes.push(prefix); +}; export type RawStack = string[]; @@ -65,7 +66,7 @@ export function captureRawStack(): RawStack { return stack.split('\n'); } -export function captureLibraryStackTrace(): { frames: StackFrame[], apiName: string } { +export function captureLibraryStackTrace(pathSeparator: string): { frames: StackFrame[], apiName: string } { const stack = captureRawStack(); type ParsedFrame = { @@ -74,10 +75,10 @@ export function captureLibraryStackTrace(): { frames: StackFrame[], apiName: str isPlaywrightLibrary: boolean; }; let parsedFrames = stack.map(line => { - const frame = parseStackTraceLine(line); + const frame = parseStackTraceLine(line, pathSeparator); if (!frame || !frame.file) return null; - const isPlaywrightLibrary = frame.file.startsWith(CORE_DIR); + const isPlaywrightLibrary = !!coreDir && frame.file.startsWith(coreDir); const parsed: ParsedFrame = { frame, frameText: line, @@ -111,7 +112,7 @@ export function captureLibraryStackTrace(): { frames: StackFrame[], apiName: str parsedFrames = parsedFrames.filter(f => { if (process.env.PWDEBUGIMPL) return true; - if (internalStackPrefixes.some(prefix => f.frame.file.startsWith(prefix))) + if (playwrightStackPrefixes.some(prefix => f.frame.file.startsWith(prefix))) return false; return true; }); @@ -133,11 +134,6 @@ export function stringifyStackFrames(frames: StackFrame[]): string[] { return stackLines; } -export function captureLibraryStackText() { - const parsed = captureLibraryStackTrace(); - return stringifyStackFrames(parsed.frames).join('\n'); -} - export function splitErrorMessage(message: string): { name: string, message: string } { const separationIdx = message.indexOf(':'); return { @@ -146,12 +142,12 @@ export function splitErrorMessage(message: string): { name: string, message: str }; } -export function formatCallLog(log: string[] | undefined): string { +export function formatCallLog(platform: Platform, log: string[] | undefined): string { if (!log || !log.some(l => !!l)) return ''; return ` Call log: -${colors.dim(log.join('\n'))} +${platform.colors.dim(log.join('\n'))} `; } diff --git a/packages/playwright-core/src/utils/stackUtils.ts b/packages/playwright-core/src/utils/isomorphic/stackUtils.ts similarity index 88% rename from packages/playwright-core/src/utils/stackUtils.ts rename to packages/playwright-core/src/utils/isomorphic/stackUtils.ts index 024b6fe8d0..20be8081af 100644 --- a/packages/playwright-core/src/utils/stackUtils.ts +++ b/packages/playwright-core/src/utils/isomorphic/stackUtils.ts @@ -19,8 +19,6 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import * as url from 'url'; - type StackData = { line?: number; column?: number; @@ -35,7 +33,7 @@ type StackData = { evalFile?: string | undefined; }; -export function parseStackFrame(line: string): StackData | null { +export function parseStackFrame(line: string, pathSeparator: string): StackData | null { const match = line && line.match(re); if (!match) return null; @@ -92,7 +90,7 @@ export function parseStackFrame(line: string): StackData | null { } } - setFile(res, file); + setFile(res, file, pathSeparator); if (ctor) res.isConstructor = true; @@ -113,10 +111,10 @@ export function parseStackFrame(line: string): StackData | null { return res; } -function setFile(result: StackData, filename: string) { +function setFile(result: StackData, filename: string, pathSeparator: string) { if (filename) { if (filename.startsWith('file://')) - filename = url.fileURLToPath(filename); + filename = fileURLToPath(filename, pathSeparator); result.file = filename; } } @@ -147,3 +145,14 @@ const re = new RegExp('^' + ); 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/timeoutSettings.ts b/packages/playwright-core/src/utils/isomorphic/timeoutSettings.ts similarity index 98% rename from packages/playwright-core/src/common/timeoutSettings.ts rename to packages/playwright-core/src/utils/isomorphic/timeoutSettings.ts index 65c8be1ecf..02c02a33b5 100644 --- a/packages/playwright-core/src/common/timeoutSettings.ts +++ b/packages/playwright-core/src/utils/isomorphic/timeoutSettings.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { debugMode } from '../utils'; +import { debugMode } from './debug'; export const DEFAULT_TIMEOUT = 30000; export const DEFAULT_LAUNCH_TIMEOUT = 3 * 60 * 1000; // 3 minutes diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index 7395e38217..d9c828cc62 100644 --- a/packages/playwright/src/common/testType.ts +++ b/packages/playwright/src/common/testType.ts @@ -15,7 +15,7 @@ */ import { errors } from 'playwright-core'; -import { getPackageManagerExecCommand, monotonicTime, raceAgainstDeadline, zones } from 'playwright-core/lib/utils'; +import { getPackageManagerExecCommand, monotonicTime, raceAgainstDeadline, currentZone } from 'playwright-core/lib/utils'; import { currentTestInfo, currentlyLoadingFileSuite, setCurrentlyLoadingFileSuite } from './globals'; import { Suite, TestCase } from './test'; @@ -266,7 +266,7 @@ export class TestTypeImpl { if (!testInfo) throw new Error(`test.step() can only be called from a test`); const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box }); - return await zones.run('stepZone', step, async () => { + return await currentZone().with('stepZone', step).run(async () => { try { let result: Awaited>> | undefined = undefined; result = await raceAgainstDeadline(async () => { diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index a8d0cc0387..9d6089614c 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, debugMode, isString, jsonStringifyForceASCII, zones } from 'playwright-core/lib/utils'; +import { addInternalStackPrefix, asLocator, createGuid, currentZone, debugMode, isString, jsonStringifyForceASCII } from 'playwright-core/lib/utils'; import { currentTestInfo } from './common/globals'; import { rootTestType } from './common/testType'; @@ -260,7 +260,7 @@ const playwrightFixtures: Fixtures = ({ // Some special calls do not get into steps. if (!testInfo || data.apiName.includes('setTestIdAttribute') || data.apiName === 'tracing.groupEnd') return; - const zone = zones.zoneData('stepZone'); + const zone = currentZone().data('stepZone'); if (zone && zone.category === 'expect') { // Display the internal locator._expect call under the name of the enclosing expect call, // and connect it to the existing expect step. diff --git a/packages/playwright/src/matchers/expect.ts b/packages/playwright/src/matchers/expect.ts index 707e5457e1..58eb9be9fc 100644 --- a/packages/playwright/src/matchers/expect.ts +++ b/packages/playwright/src/matchers/expect.ts @@ -17,9 +17,9 @@ import { captureRawStack, createGuid, + currentZone, isString, pollAgainstDeadline } from 'playwright-core/lib/utils'; -import { zones } from 'playwright-core/lib/utils'; import { ExpectError, isJestError } from './matcherHint'; import { @@ -380,7 +380,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler { try { setMatcherCallContext({ expectInfo: this._info, testInfo, step: step.info }); const callback = () => matcher.call(target, ...args); - const result = zones.run('stepZone', step, callback); + const result = currentZone().with('stepZone', step).run(callback); if (result instanceof Promise) return result.then(finalizer).catch(reportStepError); finalizer(); diff --git a/packages/playwright/src/matchers/matcherHint.ts b/packages/playwright/src/matchers/matcherHint.ts index 1dfa1ceb4a..316ecad685 100644 --- a/packages/playwright/src/matchers/matcherHint.ts +++ b/packages/playwright/src/matchers/matcherHint.ts @@ -15,7 +15,7 @@ */ import { stringifyStackFrames } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import type { ExpectMatcherState } from '../../types/test'; import type { StackFrame } from '@protocol/channels'; diff --git a/packages/playwright/src/matchers/matchers.ts b/packages/playwright/src/matchers/matchers.ts index 2890feaac6..d37e3285f4 100644 --- a/packages/playwright/src/matchers/matchers.ts +++ b/packages/playwright/src/matchers/matchers.ts @@ -15,7 +15,7 @@ */ import { isRegExp, isString, isTextualMimeType, pollAgainstDeadline, serializeExpectedTextValues } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { callLogText, expectTypes } from '../util'; import { toBeTruthy } from './toBeTruthy'; diff --git a/packages/playwright/src/matchers/toHaveURL.ts b/packages/playwright/src/matchers/toHaveURL.ts index c56441fee5..efc0ebd5f2 100644 --- a/packages/playwright/src/matchers/toHaveURL.ts +++ b/packages/playwright/src/matchers/toHaveURL.ts @@ -15,7 +15,7 @@ */ import { constructURLBasedOnBaseURL, urlMatches } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { printReceivedStringContainExpectedResult, printReceivedStringContainExpectedSubstring } from './expect'; import { matcherHint } from './matcherHint'; diff --git a/packages/playwright/src/matchers/toMatchSnapshot.ts b/packages/playwright/src/matchers/toMatchSnapshot.ts index 220c948887..dd54e61aeb 100644 --- a/packages/playwright/src/matchers/toMatchSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchSnapshot.ts @@ -18,7 +18,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { compareBuffersOrStrings, getComparator, isString, sanitizeForFilePath } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { mime } from 'playwright-core/lib/utilsBundle'; import { diff --git a/packages/playwright/src/matchers/toMatchText.ts b/packages/playwright/src/matchers/toMatchText.ts index 961937eb5b..142013f1b5 100644 --- a/packages/playwright/src/matchers/toMatchText.ts +++ b/packages/playwright/src/matchers/toMatchText.ts @@ -15,7 +15,7 @@ */ -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { callLogText, expectTypes } from '../util'; import { diff --git a/packages/playwright/src/plugins/webServerPlugin.ts b/packages/playwright/src/plugins/webServerPlugin.ts index 62583c303e..4bb1bef0db 100644 --- a/packages/playwright/src/plugins/webServerPlugin.ts +++ b/packages/playwright/src/plugins/webServerPlugin.ts @@ -17,7 +17,8 @@ import * as net from 'net'; import * as path from 'path'; import { launchProcess, isURLAvailable, monotonicTime, raceAgainstDeadline } from 'playwright-core/lib/utils'; -import { colors, debug } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; +import { debug } from 'playwright-core/lib/utilsBundle'; import type { TestRunnerPlugin } from '.'; import type { FullConfig } from '../../types/testReporter'; diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index 156f9755db..1748cac9b9 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -18,18 +18,19 @@ import * as path from 'path'; import { getPackageManagerExecCommand } from 'playwright-core/lib/utils'; import { parseStackTraceLine } from 'playwright-core/lib/utils'; -import { colors as realColors, ms as milliseconds } from 'playwright-core/lib/utilsBundle'; +import { ms as milliseconds } from 'playwright-core/lib/utilsBundle'; +import { colors as realColors, noColors } from 'playwright-core/lib/utils'; import { resolveReporterOutputPath } from '../util'; import { getEastAsianWidth } from '../utilsBundle'; import type { ReporterV2 } from './reporterV2'; import type { FullConfig, FullResult, Location, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter'; +import type { Colors } from '@isomorphic/colors'; + export type TestResultOutput = { chunk: string | Buffer, type: 'stdout' | 'stderr' }; export const kOutputSymbol = Symbol('output'); -type Colors = typeof realColors; - type ErrorDetails = { message: string; location?: Location; @@ -53,48 +54,6 @@ export type Screen = { ttyWidth: number; }; -export const noColors: Colors = { - bold: (t: string) => t, - cyan: (t: string) => t, - dim: (t: string) => t, - gray: (t: string) => t, - green: (t: string) => t, - red: (t: string) => t, - yellow: (t: string) => t, - black: (t: string) => t, - blue: (t: string) => t, - magenta: (t: string) => t, - white: (t: string) => t, - grey: (t: string) => t, - bgBlack: (t: string) => t, - bgRed: (t: string) => t, - bgGreen: (t: string) => t, - bgYellow: (t: string) => t, - bgBlue: (t: string) => t, - bgMagenta: (t: string) => t, - bgCyan: (t: string) => t, - bgWhite: (t: string) => t, - strip: (t: string) => t, - stripColors: (t: string) => t, - reset: (t: string) => t, - italic: (t: string) => t, - underline: (t: string) => t, - inverse: (t: string) => t, - hidden: (t: string) => t, - strikethrough: (t: string) => t, - rainbow: (t: string) => t, - zebra: (t: string) => t, - america: (t: string) => t, - trap: (t: string) => t, - random: (t: string) => t, - zalgo: (t: string) => t, - - enabled: false, - enable: () => {}, - disable: () => {}, - setTheme: () => {}, -}; - // Output goes to terminal. export const terminalScreen: Screen = (() => { let isTTY = !!process.stdout.isTTY; @@ -563,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); + const frame = parseStackTraceLine(line, path.sep); if (!frame || !frame.file) continue; if (belongsToNodeModules(frame.file)) diff --git a/packages/playwright/src/reporters/github.ts b/packages/playwright/src/reporters/github.ts index e3677dd3ff..7c65c00b3c 100644 --- a/packages/playwright/src/reporters/github.ts +++ b/packages/playwright/src/reporters/github.ts @@ -16,9 +16,10 @@ import * as path from 'path'; +import { noColors } from 'playwright-core/lib/utils'; import { ms as milliseconds } from 'playwright-core/lib/utilsBundle'; -import { TerminalReporter, formatResultFailure, formatRetry, noColors, stripAnsiEscapes } from './base'; +import { TerminalReporter, formatResultFailure, formatRetry, stripAnsiEscapes } from './base'; import type { FullResult, TestCase, TestError } from '../../types/testReporter'; diff --git a/packages/playwright/src/reporters/html.ts b/packages/playwright/src/reporters/html.ts index 3390a71746..a86a758d48 100644 --- a/packages/playwright/src/reporters/html.ts +++ b/packages/playwright/src/reporters/html.ts @@ -19,7 +19,8 @@ import * as path from 'path'; import { Transform } from 'stream'; import { HttpServer, MultiMap, assert, calculateSha1, getPackageManagerExecCommand, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath, toPosixPath } from 'playwright-core/lib/utils'; -import { colors, open } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; +import { open } from 'playwright-core/lib/utilsBundle'; import { mime } from 'playwright-core/lib/utilsBundle'; import { yazl } from 'playwright-core/lib/zipBundle'; diff --git a/packages/playwright/src/runner/dispatcher.ts b/packages/playwright/src/runner/dispatcher.ts index 084deba11d..cf0c7144c0 100644 --- a/packages/playwright/src/runner/dispatcher.ts +++ b/packages/playwright/src/runner/dispatcher.ts @@ -15,7 +15,7 @@ */ import { ManualPromise, eventsHelper } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { addSuggestedRebaseline } from './rebase'; import { WorkerHost } from './workerHost'; diff --git a/packages/playwright/src/runner/rebase.ts b/packages/playwright/src/runner/rebase.ts index c2d9f96d18..ef7d49f013 100644 --- a/packages/playwright/src/runner/rebase.ts +++ b/packages/playwright/src/runner/rebase.ts @@ -19,7 +19,8 @@ import * as path from 'path'; import { MultiMap } from 'playwright-core/lib/utils'; -import { colors, diff } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; +import { diff } from 'playwright-core/lib/utilsBundle'; import { filterProjects } from './projectUtils'; import { babelParse, traverse, types } from '../transform/babelBundle'; diff --git a/packages/playwright/src/runner/taskRunner.ts b/packages/playwright/src/runner/taskRunner.ts index 12185aa951..ea0561faaa 100644 --- a/packages/playwright/src/runner/taskRunner.ts +++ b/packages/playwright/src/runner/taskRunner.ts @@ -15,7 +15,8 @@ */ import { ManualPromise, monotonicTime } from 'playwright-core/lib/utils'; -import { colors, debug } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; +import { debug } from 'playwright-core/lib/utilsBundle'; import { SigIntWatcher } from './sigIntWatcher'; diff --git a/packages/playwright/src/runner/watchMode.ts b/packages/playwright/src/runner/watchMode.ts index c9768c47ba..ad819853fc 100644 --- a/packages/playwright/src/runner/watchMode.ts +++ b/packages/playwright/src/runner/watchMode.ts @@ -20,7 +20,7 @@ import { EventEmitter } from 'stream'; import { PlaywrightServer } from 'playwright-core/lib/remote/playwrightServer'; import { ManualPromise, createGuid, eventsHelper, getPackageManagerExecCommand } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { separator, terminalScreen } from '../reporters/base'; import { enquirer } from '../utilsBundle'; diff --git a/packages/playwright/src/util.ts b/packages/playwright/src/util.ts index 803aa73f6b..b80a3f387d 100644 --- a/packages/playwright/src/util.ts +++ b/packages/playwright/src/util.ts @@ -21,6 +21,7 @@ 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 type { Location } from './../types/testReporter'; import type { TestInfoErrorImpl } from './common/ipc'; @@ -55,7 +56,7 @@ export function filterStackFile(file: string) { export function filteredStackTrace(rawStack: RawStack): StackFrame[] { const frames: StackFrame[] = []; for (const line of rawStack) { - const frame = parseStackTraceLine(line); + const frame = parseStackTraceLine(line, path.sep); if (!frame || !frame.file) continue; if (!filterStackFile(frame.file)) @@ -224,7 +225,7 @@ export function getContainedPath(parentPath: string, subPath: string = ''): stri export const debugTest = debug('pw:test'); -export const callLogText = formatCallLog; +export const callLogText = (log: string[] | undefined) => formatCallLog(nodePlatform, log); const folderToPackageJsonPath = new Map(); diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index 5e964af5e0..cbb26b6c2f 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -17,7 +17,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { captureRawStack, monotonicTime, sanitizeForFilePath, stringifyStackFrames, zones } from 'playwright-core/lib/utils'; +import { captureRawStack, monotonicTime, sanitizeForFilePath, stringifyStackFrames, currentZone } from 'playwright-core/lib/utils'; import { TimeoutManager, TimeoutManagerError, kMaxDeadline } from './timeoutManager'; import { debugTest, filteredStackTrace, formatLocation, getContainedPath, normalizeAndSaveAttachment, trimLongString, windowsFilesystemFriendlyLength } from '../util'; @@ -245,7 +245,7 @@ export class TestInfoImpl implements TestInfo { } private _parentStep() { - return zones.zoneData('stepZone') + return currentZone().data('stepZone') ?? this._findLastStageStep(this._steps); // If no parent step on stack, assume the current stage as parent. } diff --git a/packages/playwright/src/worker/timeoutManager.ts b/packages/playwright/src/worker/timeoutManager.ts index 7018dafcc9..65e97c9f36 100644 --- a/packages/playwright/src/worker/timeoutManager.ts +++ b/packages/playwright/src/worker/timeoutManager.ts @@ -15,7 +15,7 @@ */ import { ManualPromise, monotonicTime } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import type { Location } from '../../types/testReporter'; diff --git a/packages/playwright/src/worker/workerMain.ts b/packages/playwright/src/worker/workerMain.ts index 30b59c36e7..e09579d0e0 100644 --- a/packages/playwright/src/worker/workerMain.ts +++ b/packages/playwright/src/worker/workerMain.ts @@ -15,7 +15,7 @@ */ import { ManualPromise, gracefullyCloseAll, removeFolders } from 'playwright-core/lib/utils'; -import { colors } from 'playwright-core/lib/utilsBundle'; +import { colors } from 'playwright-core/lib/utils'; import { deserializeConfig } from '../common/configLoader'; import { setCurrentTestInfo, setIsWorkerProcess } from '../common/globals'; diff --git a/tests/assets/modernizr/README.md b/tests/assets/modernizr/README.md index bf3241283d..2a986d23d4 100644 --- a/tests/assets/modernizr/README.md +++ b/tests/assets/modernizr/README.md @@ -7,8 +7,8 @@ ## Updating expectations -1. `npx http-server .` -1. Navigate to `http://127.0.0.1:8080/tests/assets/modernizr/index.html` +1. Serve `tests/assets/modernizr/index.html` from a remote (localhost results will be different) https origin (e.g. https://pages.github.com). +1. Navigate to `https://your-domain.com/tests/assets/modernizr/index.html` Do this with: diff --git a/tests/assets/modernizr/mobile-safari-18.json b/tests/assets/modernizr/mobile-safari-18.json index e86fc3de20..cd2822a65c 100644 --- a/tests/assets/modernizr/mobile-safari-18.json +++ b/tests/assets/modernizr/mobile-safari-18.json @@ -362,6 +362,7 @@ "EXT_polygon_offset_clamp": true, "EXT_shader_texture_lod": true, "EXT_texture_filter_anisotropic": true, + "EXT_texture_mirror_clamp_to_edge": true, "EXT_sRGB": true, "KHR_parallel_shader_compile": true, "OES_element_index_uint": true, @@ -371,6 +372,7 @@ "OES_texture_half_float": true, "OES_texture_half_float_linear": true, "OES_vertex_array_object": true, + "WEBGL_blend_func_extended": true, "WEBGL_color_buffer_float": true, "WEBGL_compressed_texture_astc": true, "WEBGL_compressed_texture_etc": true, diff --git a/tests/config/utils.ts b/tests/config/utils.ts index 3743e97d80..3ab51c9290 100644 --- a/tests/config/utils.ts +++ b/tests/config/utils.ts @@ -15,7 +15,7 @@ */ import type { Frame, Page } from 'playwright-core'; -import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile'; +import { ZipFile } from '../../packages/playwright-core/lib/server/utils/zipFile'; import type { TraceModelBackend } from '../../packages/trace-viewer/src/sw/traceModel'; import type { StackFrame } from '../../packages/protocol/src/channels'; import { parseClientSideCallMetadata } from '../../packages/playwright-core/lib/utils/isomorphic/traceUtils'; diff --git a/tests/library/browsertype-connect.spec.ts b/tests/library/browsertype-connect.spec.ts index e4118e489f..1298e38b5e 100644 --- a/tests/library/browsertype-connect.spec.ts +++ b/tests/library/browsertype-connect.spec.ts @@ -19,7 +19,7 @@ import fs from 'fs'; import type http from 'http'; import type net from 'net'; import * as path from 'path'; -import { getUserAgent, getPlaywrightVersion } from '../../packages/playwright-core/lib/utils/userAgent'; +import { getUserAgent, getPlaywrightVersion } from '../../packages/playwright-core/lib/server/utils/userAgent'; import WebSocket from 'ws'; import { expect, playwrightTest } from '../config/browserTest'; import { parseTrace, suppressCertificateWarning } from '../config/utils'; @@ -258,7 +258,9 @@ for (const kind of ['launchServer', 'run-server'] as const) { }).catch(() => {}) ]); expect(request.headers['user-agent']).toBe(getUserAgent()); - expect(request.headers['x-playwright-browser']).toBe(browserName); + // _bidiFirefox and _bidiChromium are initialized with 'bidi' as browser name. + const bidiAwareBrowserName = browserName.startsWith('_bidi') ? 'bidi' : browserName; + expect(request.headers['x-playwright-browser']).toBe(bidiAwareBrowserName); expect(request.headers['foo']).toBe('bar'); }); diff --git a/tests/library/capabilities.spec.ts b/tests/library/capabilities.spec.ts index 29328f3f80..8b065834ab 100644 --- a/tests/library/capabilities.spec.ts +++ b/tests/library/capabilities.spec.ts @@ -17,7 +17,7 @@ import os from 'os'; import url from 'url'; import { contextTest as it, expect } from '../config/browserTest'; -import { hostPlatform } from '../../packages/playwright-core/src/utils/hostPlatform'; +import { hostPlatform } from '../../packages/playwright-core/src/server/utils/hostPlatform'; it('SharedArrayBuffer should work @smoke', async function({ contextFactory, httpsServer }) { const context = await contextFactory({ ignoreHTTPSErrors: true }); diff --git a/tests/library/chromium/connect-over-cdp.spec.ts b/tests/library/chromium/connect-over-cdp.spec.ts index f31f60baf0..3191403be8 100644 --- a/tests/library/chromium/connect-over-cdp.spec.ts +++ b/tests/library/chromium/connect-over-cdp.spec.ts @@ -18,7 +18,7 @@ import { playwrightTest as test, expect } from '../../config/browserTest'; import http from 'http'; import fs from 'fs'; -import { getUserAgent } from '../../../packages/playwright-core/lib/utils/userAgent'; +import { getUserAgent } from '../../../packages/playwright-core/lib/server/utils/userAgent'; import { suppressCertificateWarning } from '../../config/utils'; test.skip(({ mode }) => mode === 'service2'); diff --git a/tests/library/events/check-listener-leaks.spec.ts b/tests/library/events/check-listener-leaks.spec.ts index d9b51f27ef..5a328d720f 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/debug'; +import { setUnderTest } from '../../../packages/playwright-core/lib/utils/isomorphic/debug'; import { test, expect } from '@playwright/test'; import * as common from './utils'; diff --git a/tests/library/global-fetch.spec.ts b/tests/library/global-fetch.spec.ts index 0ad73bb9b6..9a402ed152 100644 --- a/tests/library/global-fetch.spec.ts +++ b/tests/library/global-fetch.spec.ts @@ -16,7 +16,7 @@ import os from 'os'; import * as util from 'util'; -import { getPlaywrightVersion } from '../../packages/playwright-core/lib/utils/userAgent'; +import { getPlaywrightVersion } from '../../packages/playwright-core/lib/server/utils/userAgent'; import { expect, playwrightTest as base } from '../config/browserTest'; import { kTargetClosedErrorMessage } from 'tests/config/errors'; diff --git a/tests/library/inspector/inspectorTest.ts b/tests/library/inspector/inspectorTest.ts index fa13420fc3..c2ed97d50e 100644 --- a/tests/library/inspector/inspectorTest.ts +++ b/tests/library/inspector/inspectorTest.ts @@ -22,7 +22,7 @@ import type { Source } from '../../../packages/recorder/src/recorderTypes'; import type { CommonFixtures, TestChildProcess } from '../../config/commonFixtures'; import { stripAnsi } from '../../config/utils'; import { expect } from '@playwright/test'; -import { nodePlatform } from '../../../packages/playwright-core/lib/utils/platform'; +import { nodePlatform } from '../../../packages/playwright-core/lib/server/utils/nodePlatform'; export { expect } from '@playwright/test'; type CLITestArgs = { diff --git a/tests/library/modernizr.spec.ts b/tests/library/modernizr.spec.ts index c0a06f3714..0a1b124301 100644 --- a/tests/library/modernizr.spec.ts +++ b/tests/library/modernizr.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { hostPlatform } from '../../packages/playwright-core/src/utils/hostPlatform'; +import { hostPlatform } from '../../packages/playwright-core/src/server/utils/hostPlatform'; import { browserTest as it, expect } from '../config/browserTest'; import fs from 'fs'; import os from 'os'; diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts index bf78efc7c8..a8846e69ae 100644 --- a/tests/playwright-test/reporter-blob.spec.ts +++ b/tests/playwright-test/reporter-blob.spec.ts @@ -23,7 +23,7 @@ import { startHtmlReportServer } from '../../packages/playwright/lib/reporters/h import { expect as baseExpect, test as baseTest, stripAnsi } from './playwright-test-fixtures'; import extractZip from '../../packages/playwright-core/bundles/zip/node_modules/extract-zip'; import * as yazl from '../../packages/playwright-core/bundles/zip/node_modules/yazl'; -import { getUserAgent } from '../../packages/playwright-core/lib/utils/userAgent'; +import { getUserAgent } from '../../packages/playwright-core/lib/server/utils/userAgent'; import { Readable } from 'stream'; const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION; diff --git a/utils/build/build-playwright-driver.sh b/utils/build/build-playwright-driver.sh index c74e3bebf1..6f6082fc8b 100755 --- a/utils/build/build-playwright-driver.sh +++ b/utils/build/build-playwright-driver.sh @@ -4,7 +4,7 @@ set -x trap "cd $(pwd -P)" EXIT SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)" -NODE_VERSION="22.13.1" # autogenerated via ./update-playwright-driver-version.mjs +NODE_VERSION="22.14.0" # autogenerated via ./update-playwright-driver-version.mjs cd "$(dirname "$0")" PACKAGE_VERSION=$(node -p "require('../../package.json').version")