diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md
index 5b89e62b88..5515252b39 100644
--- a/docs/src/test-api/class-testconfig.md
+++ b/docs/src/test-api/class-testconfig.md
@@ -334,6 +334,8 @@ Whether to populate `'git.commit.info'` field of the [`property: TestConfig.meta
This information will appear in the HTML and JSON reports and is available in the Reporter API.
+On Github Actions, this feature is enabled by default.
+
**Usage**
```js title="playwright.config.ts"
diff --git a/packages/html-reporter/index.html b/packages/html-reporter/index.html
index 054507220c..54ab833d2b 100644
--- a/packages/html-reporter/index.html
+++ b/packages/html-reporter/index.html
@@ -15,7 +15,7 @@
-->
-
+
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 026473f98a..2ffa077b4e 100644
--- a/packages/playwright-core/src/DEPS.list
+++ b/packages/playwright-core/src/DEPS.list
@@ -8,10 +8,15 @@
**
[inprocess.ts]
+common/
utils/
+server/utils
[outofprocess.ts]
client/
+common/
protocol/
utils/
-common/
\ No newline at end of file
+utils/isomorphic
+server/utils
+common/
diff --git a/packages/playwright-core/src/androidServerImpl.ts b/packages/playwright-core/src/androidServerImpl.ts
index aee8bf7b31..5f3758119a 100644
--- a/packages/playwright-core/src/androidServerImpl.ts
+++ b/packages/playwright-core/src/androidServerImpl.ts
@@ -16,7 +16,7 @@
import { PlaywrightServer } from './remote/playwrightServer';
import { createPlaywright } from './server/playwright';
-import { createGuid } from './utils';
+import { createGuid } from './server/utils/crypto';
import { ws } from './utilsBundle';
import type { BrowserServer } from './client/browserType';
diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts
index 3c5244b7b0..77fb9a9844 100644
--- a/packages/playwright-core/src/browserServerImpl.ts
+++ b/packages/playwright-core/src/browserServerImpl.ts
@@ -20,8 +20,8 @@ import { PlaywrightServer } from './remote/playwrightServer';
import { helper } from './server/helper';
import { serverSideCallMetadata } from './server/instrumentation';
import { createPlaywright } from './server/playwright';
-import { createGuid } from './utils';
-import { rewriteErrorMessage } from './utils/stackTrace';
+import { createGuid } from './server/utils/crypto';
+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 2b49b65ee7..1e16de00bf 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 '../protocol/transport';
+import { PipeTransport } from '../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/android.ts b/packages/playwright-core/src/client/android.ts
index 7f062143fc..f0ca11848f 100644
--- a/packages/playwright-core/src/client/android.ts
+++ b/packages/playwright-core/src/client/android.ts
@@ -21,16 +21,16 @@ import { ChannelOwner } from './channelOwner';
import { TargetClosedError, isTargetClosedError } from './errors';
import { Events } from './events';
import { Waiter } from './waiter';
-import { TimeoutSettings } from '../common/timeoutSettings';
-import { isRegExp, isString } from '../utils/rtti';
-import { monotonicTime } from '../utils/time';
-import { raceAgainstDeadline } from '../utils/timeoutRunner';
+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 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';
diff --git a/packages/playwright-core/src/client/artifact.ts b/packages/playwright-core/src/client/artifact.ts
index fc1e9bca12..815c7a358b 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 '../common/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..88140621fe 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 '../common/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 d625135f73..02aa857366 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 { headersObjectToArray } from '../utils/headers';
+import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
+import { mkdirIfNeeded } from '../common/fileUtils';
+import { headersObjectToArray } from '../utils/isomorphic/headers';
import { urlMatchesEqual } from '../utils/isomorphic/urlMatch';
-import { isRegExp, isString } from '../utils/rtti';
-import { rewriteErrorMessage } from '../utils/stackTrace';
+import { isRegExp, isString } from '../utils/isomorphic/rtti';
+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 {
diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts
index 59ce8c56d7..06bae0419f 100644
--- a/packages/playwright-core/src/client/browserType.ts
+++ b/packages/playwright-core/src/client/browserType.ts
@@ -21,10 +21,10 @@ import { BrowserContext, prepareBrowserContextParams } from './browserContext';
import { ChannelOwner } from './channelOwner';
import { envObjectToArray } from './clientHelper';
import { Events } from './events';
-import { assert } from '../utils/debug';
-import { headersObjectToArray } from '../utils/headers';
-import { monotonicTime } from '../utils/time';
-import { raceAgainstDeadline } from '../utils/timeoutRunner';
+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 type { Playwright } from './playwright';
import type { ConnectOptions, LaunchOptions, LaunchPersistentContextOptions, LaunchServerOptions, Logger } from './types';
@@ -100,7 +100,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 : path.resolve(userDataDir),
+ userDataDir: (path.isAbsolute(userDataDir) || !userDataDir) ? userDataDir : path.resolve(userDataDir),
};
return await this._wrapApiCall(async () => {
const result = await this._channel.launchPersistentContext(persistentParams);
diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts
index 70a5c51777..40bf226c65 100644
--- a/packages/playwright-core/src/client/channelOwner.ts
+++ b/packages/playwright-core/src/client/channelOwner.ts
@@ -16,16 +16,15 @@
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 { isUnderTest } from '../utils/isomorphic/debug';
+import { captureLibraryStackTrace, stringifyStackFrames } from '../utils/isomorphic/stackTrace';
import { zones } from '../utils/zones';
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 +157,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,
@@ -183,13 +182,13 @@ export abstract class ChannelOwner 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 +203,7 @@ export abstract class ChannelOwner {
@@ -139,9 +138,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 };
@@ -159,15 +158,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', ' {
}
async harOpen(params: channels.LocalUtilsHarOpenParams): Promise {
- return await localUtils.harOpen(this._harBackends, params);
+ return await localUtils.harOpen(this._platform, this._harBackends, params);
}
async harLookup(params: channels.LocalUtilsHarLookupParams): Promise {
diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts
index 04cfa5fadf..5d0f0aa0c3 100644
--- a/packages/playwright-core/src/client/locator.ts
+++ b/packages/playwright-core/src/client/locator.ts
@@ -19,8 +19,8 @@ import { parseResult, serializeArgument } from './jsHandle';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import { getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector } from '../utils/isomorphic/locatorUtils';
import { escapeForTextSelector } from '../utils/isomorphic/stringUtils';
-import { isString } from '../utils/rtti';
-import { monotonicTime } from '../utils/time';
+import { isString } from '../utils/isomorphic/rtti';
+import { monotonicTime } from '../utils/isomorphic/time';
import type { Frame } from './frame';
import type { FilePayload, FrameExpectParams, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts
index ae7c7bb251..a2c64c08b1 100644
--- a/packages/playwright-core/src/client/network.ts
+++ b/packages/playwright-core/src/client/network.ts
@@ -23,13 +23,13 @@ import { APIResponse } from './fetch';
import { Frame } from './frame';
import { Waiter } from './waiter';
import { Worker } from './worker';
-import { assert } from '../utils/debug';
-import { headersObjectToArray } from '../utils/headers';
+import { assert } from '../utils/isomorphic/debug';
+import { headersObjectToArray } from '../utils/isomorphic/headers';
import { urlMatches } from '../utils/isomorphic/urlMatch';
-import { LongStandingScope, ManualPromise } from '../utils/manualPromise';
-import { MultiMap } from '../utils/multimap';
-import { isRegExp, isString } from '../utils/rtti';
-import { rewriteErrorMessage } from '../utils/stackTrace';
+import { LongStandingScope, ManualPromise } from '../utils/isomorphic/manualPromise';
+import { MultiMap } from '../utils/isomorphic/multimap';
+import { isRegExp, isString } from '../utils/isomorphic/rtti';
+import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
import { zones } from '../utils/zones';
import { mime } from '../utilsBundle';
diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts
index d4bb32ffbd..b954cca502 100644
--- a/packages/playwright-core/src/client/page.ts
+++ b/packages/playwright-core/src/client/page.ts
@@ -33,14 +33,14 @@ 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 { headersObjectToArray } from '../utils/headers';
+import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
+import { assert } from '../utils/isomorphic/debug';
+import { mkdirIfNeeded } from '../common/fileUtils';
+import { headersObjectToArray } from '../utils/isomorphic/headers';
import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils';
import { urlMatches, urlMatchesEqual } from '../utils/isomorphic/urlMatch';
-import { LongStandingScope } from '../utils/manualPromise';
-import { isObject, isRegExp, isString } from '../utils/rtti';
+import { LongStandingScope } from '../utils/isomorphic/manualPromise';
+import { isObject, isRegExp, isString } from '../utils/isomorphic/rtti';
import type { BrowserContext } from './browserContext';
import type { Clock } from './clock';
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/video.ts b/packages/playwright-core/src/client/video.ts
index 993647dc72..1ff4065be7 100644
--- a/packages/playwright-core/src/client/video.ts
+++ b/packages/playwright-core/src/client/video.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { ManualPromise } from '../utils/manualPromise';
+import { ManualPromise } from '../utils/isomorphic/manualPromise';
import type { Artifact } from './artifact';
import type { Connection } from './connection';
diff --git a/packages/playwright-core/src/client/waiter.ts b/packages/playwright-core/src/client/waiter.ts
index 90e48108a2..e17f93d940 100644
--- a/packages/playwright-core/src/client/waiter.ts
+++ b/packages/playwright-core/src/client/waiter.ts
@@ -15,8 +15,7 @@
*/
import { TimeoutError } from './errors';
-import { createGuid } from '../utils/crypto';
-import { rewriteErrorMessage } from '../utils/stackTrace';
+import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
import { zones } from '../utils/zones';
import type { ChannelOwner } from './channelOwner';
@@ -35,7 +34,7 @@ export class Waiter {
private _savedZone: Zone;
constructor(channelOwner: ChannelOwner, event: string) {
- this._waitId = createGuid();
+ this._waitId = channelOwner._platform.createGuid();
this._channelOwner = channelOwner;
this._savedZone = zones.current().without('apiZone');
diff --git a/packages/playwright-core/src/client/worker.ts b/packages/playwright-core/src/client/worker.ts
index ee9c2dcd6e..5a5a5fe246 100644
--- a/packages/playwright-core/src/client/worker.ts
+++ b/packages/playwright-core/src/client/worker.ts
@@ -18,7 +18,7 @@ import { ChannelOwner } from './channelOwner';
import { TargetClosedError } from './errors';
import { Events } from './events';
import { JSHandle, assertMaxArguments, parseResult, serializeArgument } from './jsHandle';
-import { LongStandingScope } from '../utils/manualPromise';
+import { LongStandingScope } from '../utils/isomorphic/manualPromise';
import type { BrowserContext } from './browserContext';
import type { Page } from './page';
diff --git a/packages/playwright-core/src/common/DEPS.list b/packages/playwright-core/src/common/DEPS.list
index 43bff9dba4..a25dd41c36 100644
--- a/packages/playwright-core/src/common/DEPS.list
+++ b/packages/playwright-core/src/common/DEPS.list
@@ -1,4 +1,5 @@
[*]
../utils/
+../utils/isomorphic/
../utilsBundle.ts
../zipBundle.ts
diff --git a/packages/playwright-core/src/utils/fileUtils.ts b/packages/playwright-core/src/common/fileUtils.ts
similarity index 100%
rename from packages/playwright-core/src/utils/fileUtils.ts
rename to packages/playwright-core/src/common/fileUtils.ts
diff --git a/packages/playwright-core/src/utils/harBackend.ts b/packages/playwright-core/src/common/harBackend.ts
similarity index 92%
rename from packages/playwright-core/src/utils/harBackend.ts
rename to packages/playwright-core/src/common/harBackend.ts
index 5c68e87c7d..f59b7de5f1 100644
--- a/packages/playwright-core/src/utils/harBackend.ts
+++ b/packages/playwright-core/src/common/harBackend.ts
@@ -14,24 +14,24 @@
* limitations under the License.
*/
-import * as fs from 'fs';
-import * as path from 'path';
+import { ZipFile } from '../utils/zipFile';
-import { createGuid } from './crypto';
-import { ZipFile } from './zipFile';
-
-import type { HeadersArray } from '../common/types';
+import type { HeadersArray } from './types';
import type * as har from '@trace/har';
+import type { Platform } from './platform';
const redirectStatus = [301, 302, 303, 307, 308];
export class HarBackend {
- readonly id = createGuid();
+ readonly id: string;
private _harFile: har.HARFile;
private _zipFile: ZipFile | null;
private _baseDir: string | null;
+ private _platform: Platform;
- constructor(harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) {
+ constructor(platform: Platform, harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) {
+ this._platform = platform;
+ this.id = platform.createGuid();
this._harFile = harFile;
this._baseDir = baseDir;
this._zipFile = zipFile;
@@ -79,7 +79,7 @@ export class HarBackend {
if (this._zipFile)
buffer = await this._zipFile.read(file);
else
- buffer = await fs.promises.readFile(path.resolve(this._baseDir!, file));
+ buffer = await this._platform.fs().promises.readFile(this._platform.path().resolve(this._baseDir!, file));
} else {
buffer = Buffer.from(content.text || '', content.encoding === 'base64' ? 'base64' : 'utf-8');
}
diff --git a/packages/playwright-core/src/utils/localUtils.ts b/packages/playwright-core/src/common/localUtils.ts
similarity index 91%
rename from packages/playwright-core/src/utils/localUtils.ts
rename to packages/playwright-core/src/common/localUtils.ts
index d94a7da27e..5020d82b75 100644
--- a/packages/playwright-core/src/utils/localUtils.ts
+++ b/packages/playwright-core/src/common/localUtils.ts
@@ -20,11 +20,11 @@ import * as path from 'path';
import { removeFolders } from './fileUtils';
import { HarBackend } from './harBackend';
-import { ManualPromise } from './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';
-import { assert, calculateSha1 } from '../utils';
+import { serializeClientSideCallMetadata } from '../utils/isomorphic/traceUtils';
+import { assert } from '../utils/isomorphic/debug';
import type { Platform } from './platform';
import type * as channels from '@protocol/channels';
@@ -77,7 +77,7 @@ export async function zip(platform: Platform, stackSessions: Map, params: channels.LocalUtilsHarOpenParams): Promise {
+export async function harOpen(platform: Platform, 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(harBackends: Map, params: chan
return { error: 'Specified archive does not have a .har file' };
const har = await zipFile.read(harEntryName);
const harFile = JSON.parse(har.toString()) as har.HARFile;
- harBackend = new HarBackend(harFile, null, zipFile);
+ harBackend = new HarBackend(platform, harFile, null, zipFile);
} else {
const harFile = JSON.parse(await fs.promises.readFile(params.file, 'utf-8')) as har.HARFile;
- harBackend = new HarBackend(harFile, path.dirname(params.file), null);
+ harBackend = new HarBackend(platform, harFile, path.dirname(params.file), null);
}
harBackends.set(harBackend.id, harBackend);
return { harId: harBackend.id };
diff --git a/packages/playwright-core/src/common/platform.ts b/packages/playwright-core/src/common/platform.ts
new file mode 100644
index 0000000000..64cef77aab
--- /dev/null
+++ b/packages/playwright-core/src/common/platform.ts
@@ -0,0 +1,102 @@
+/**
+ * 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 * as crypto from 'crypto';
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { webColors, noColors } from '../utils/isomorphic/colors';
+
+import type { Colors } from '../utils/isomorphic/colors';
+
+
+export type Platform = {
+ calculateSha1(text: string): Promise;
+ colors: Colors;
+ createGuid: () => string;
+ fs: () => typeof fs;
+ inspectCustom: symbol | undefined;
+ isLogEnabled(name: 'api' | 'channel'): boolean;
+ log(name: 'api' | 'channel', message: string | Error | object): void;
+ path: () => typeof path;
+ pathSeparator: string;
+ ws?: (url: string) => WebSocket;
+};
+
+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('');
+ },
+
+ colors: webColors,
+
+ 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,
+
+
+ isLogEnabled(name: 'api' | 'channel') {
+ return false;
+ },
+
+ log(name: 'api' | 'channel', message: string | Error | object) {},
+
+ path: () => {
+ throw new Error('Path module is not available');
+ },
+
+ pathSeparator: '/',
+
+ ws: (url: string) => new WebSocket(url),
+};
+
+export const emptyPlatform: Platform = {
+ calculateSha1: async () => {
+ throw new Error('Not implemented');
+ },
+
+ colors: noColors,
+
+ createGuid: () => {
+ throw new Error('Not implemented');
+ },
+
+ fs: () => {
+ throw new Error('Not implemented');
+ },
+
+ inspectCustom: undefined,
+
+ isLogEnabled(name: 'api' | 'channel') {
+ return false;
+ },
+
+ log(name: 'api' | 'channel', message: string | Error | object) { },
+
+ path: () => {
+ throw new Error('Function not implemented.');
+ },
+
+ pathSeparator: '/'
+};
diff --git a/packages/playwright-core/src/inProcessFactory.ts b/packages/playwright-core/src/inProcessFactory.ts
index 296c742127..81b40afe19 100644
--- a/packages/playwright-core/src/inProcessFactory.ts
+++ b/packages/playwright-core/src/inProcessFactory.ts
@@ -14,17 +14,29 @@
* limitations under the License.
*/
+import path from 'path';
+
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 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);
+
+ 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 3af4065e1d..c24decb900 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 './protocol/transport';
-import { ManualPromise } from './utils/manualPromise';
-import { nodePlatform } from './utils/platform';
+import { PipeTransport } from './utils/pipeTransport';
+import { ManualPromise } from './utils/isomorphic/manualPromise';
+import { nodePlatform } from './server/utils/nodePlatform';
import type { Playwright } from './client/playwright';
diff --git a/packages/playwright-core/src/remote/DEPS.list b/packages/playwright-core/src/remote/DEPS.list
index 62660da37d..bf3843dca0 100644
--- a/packages/playwright-core/src/remote/DEPS.list
+++ b/packages/playwright-core/src/remote/DEPS.list
@@ -6,4 +6,5 @@
../server/dispatchers/
../server/utils/
../utils/
+../utils/isomorphic
../utilsBundle.ts
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 06193a618a..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 { Semaphore } from '../utils/semaphore';
+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 e47e115112..c1516dea76 100644
--- a/packages/playwright-core/src/server/android/android.ts
+++ b/packages/playwright-core/src/server/android/android.ts
@@ -19,10 +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 '../../protocol/transport';
-import { createGuid, getPackageManagerExecCommand, isUnderTest, makeWaitForNextTask } from '../../utils';
-import { RecentLogsCollector } from '../../utils/debugLogger';
+import { TimeoutSettings } from '../../utils/isomorphic/timeoutSettings';
+import { PipeTransport } from '../../utils/pipeTransport';
+import { createGuid } from '../utils/crypto';
+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 a5f28f1d35..036c74703e 100644
--- a/packages/playwright-core/src/server/android/backendAdb.ts
+++ b/packages/playwright-core/src/server/android/backendAdb.ts
@@ -17,7 +17,8 @@
import { EventEmitter } from 'events';
import * as net from 'net';
-import { assert, createGuid } from '../../utils';
+import { assert } from '../../utils/isomorphic/debug';
+import { createGuid } from '../utils/crypto';
import { debug } from '../../utilsBundle';
import type { Backend, DeviceBackend, SocketBackend } from './android';
diff --git a/packages/playwright-core/src/server/artifact.ts b/packages/playwright-core/src/server/artifact.ts
index 26b97d4d56..703e2d9e89 100644
--- a/packages/playwright-core/src/server/artifact.ts
+++ b/packages/playwright-core/src/server/artifact.ts
@@ -19,7 +19,7 @@ import * as fs from 'fs';
import { assert } from '../utils';
import { TargetClosedError } from './errors';
import { SdkObject } from './instrumentation';
-import { ManualPromise } from '../utils/manualPromise';
+import { ManualPromise } from '../utils/isomorphic/manualPromise';
type SaveCallback = (localPath: string, error?: Error) => Promise;
type CancelCallback = () => Promise;
diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts
index 45c12bc347..5c1cb400d1 100644
--- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts
+++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import { Browser } from '../browser';
import { BrowserContext, assertBrowserContextIsNotOwned } from '../browserContext';
import * as network from '../network';
@@ -23,7 +23,7 @@ import { bidiBytesValueToString } from './bidiNetworkManager';
import { BidiPage } from './bidiPage';
import * as bidi from './third_party/bidiProtocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type { BrowserOptions } from '../browser';
import type { SdkObject } from '../instrumentation';
import type { InitScript, Page } from '../page';
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/bidiInput.ts b/packages/playwright-core/src/server/bidi/bidiInput.ts
index e460a9f3ef..e40b13bb2e 100644
--- a/packages/playwright-core/src/server/bidi/bidiInput.ts
+++ b/packages/playwright-core/src/server/bidi/bidiInput.ts
@@ -79,6 +79,9 @@ export class RawMouseImpl implements input.RawMouse {
}
async move(x: number, y: number, button: types.MouseButton | 'none', buttons: Set, modifiers: Set, forClick: boolean): Promise {
+ // Bidi throws when x/y are not integers.
+ x = Math.floor(x);
+ y = Math.floor(y);
await this._performActions([{ type: 'pointerMove', x, y }]);
}
@@ -91,6 +94,9 @@ export class RawMouseImpl implements input.RawMouse {
}
async wheel(x: number, y: number, buttons: Set, modifiers: Set, deltaX: number, deltaY: number): Promise {
+ // Bidi throws when x/y are not integers.
+ x = Math.floor(x);
+ y = Math.floor(y);
await this._session.send('input.performActions', {
context: this._session.sessionId,
actions: [
diff --git a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts
index 185eee86a0..5da061fcdf 100644
--- a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts
+++ b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import { parseRawCookie } from '../cookieStore';
import * as network from '../network';
import * as bidi from './third_party/bidiProtocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as frames from '../frames';
import type { Page } from '../page';
import type * as types from '../types';
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 dd2d276c18..badd68d1a1 100644
--- a/packages/playwright-core/src/server/bidi/bidiPage.ts
+++ b/packages/playwright-core/src/server/bidi/bidiPage.ts
@@ -15,7 +15,7 @@
*/
import { assert } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import { BrowserContext } from '../browserContext';
import * as dialog from '../dialog';
import * as dom from '../dom';
@@ -26,7 +26,7 @@ import { BidiNetworkManager } from './bidiNetworkManager';
import { BidiPDF } from './bidiPdf';
import * as bidi from './third_party/bidiProtocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as accessibility from '../accessibility';
import type * as frames from '../frames';
import type { InitScript, PageDelegate } from '../page';
@@ -413,17 +413,22 @@ 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 {
- throw new Error('Method not implemented.');
+ // TODO: switch to utility world?
+ const windowHandle = await handle.evaluateHandle(node => {
+ const doc = node.ownerDocument ?? node as Document;
+ return doc.defaultView;
+ });
+ if (!windowHandle)
+ return null;
+ const executionContext = toBidiExecutionContext(handle._context);
+ return executionContext.frameIdForWindowHandle(windowHandle);
}
isElementHandle(remoteObject: bidi.Script.RemoteValue): boolean {
@@ -515,24 +520,25 @@ export class BidiPage implements PageDelegate {
}
async setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise {
- throw new Error('Method not implemented.');
+ throw new Error('Setting FilePayloads is not supported in Bidi.');
}
async setInputFilePaths(handle: dom.ElementHandle, paths: string[]): Promise {
- throw new Error('Method not implemented.');
+ const fromContext = toBidiExecutionContext(handle._context);
+ await this._session.send('input.setFiles', {
+ context: this._session.sessionId,
+ 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 6241b8d855..fd50994343 100644
--- a/packages/playwright-core/src/server/browserContext.ts
+++ b/packages/playwright-core/src/server/browserContext.ts
@@ -18,8 +18,9 @@
import * as fs from 'fs';
import * as path from 'path';
-import { TimeoutSettings } from '../common/timeoutSettings';
-import { createGuid, debugMode } from '../utils';
+import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
+import { createGuid } from './utils/crypto';
+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 ff5c4a826c..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 { ManualPromise } from '../../utils/manualPromise';
+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/chromiumSwitches.ts b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts
index 9d64d58f1c..0f6415745e 100644
--- a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts
+++ b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts
@@ -37,11 +37,10 @@ export const chromiumSwitches = [
// PaintHolding - https://github.com/microsoft/playwright/issues/28023
// ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230
// LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds.
- // PlzDedicatedWorker - https://github.com/microsoft/playwright/issues/31747
// DeferRendererTasksAfterInput - this makes Page.frameScheduledNavigation arrive much later after a click,
// making our navigation auto-wait after click not working. Can be removed once we deperecate noWaitAfter.
// See https://github.com/microsoft/playwright/pull/34372.
- '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker,DeferRendererTasksAfterInput',
+ '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,DeferRendererTasksAfterInput',
'--allow-pre-commit-input',
'--disable-hang-monitor',
'--disable-ipc-flooding-protection',
diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts
index 51ad0ae887..7cc6030486 100644
--- a/packages/playwright-core/src/server/chromium/crBrowser.ts
+++ b/packages/playwright-core/src/server/chromium/crBrowser.ts
@@ -17,7 +17,8 @@
import * as path from 'path';
-import { assert, createGuid } from '../../utils';
+import { assert } from '../../utils/isomorphic/debug';
+import { createGuid } from '../utils/crypto';
import { Artifact } from '../artifact';
import { Browser } from '../browser';
import { BrowserContext, assertBrowserContextIsNotOwned, verifyGeolocation } from '../browserContext';
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/crCoverage.ts b/packages/playwright-core/src/server/chromium/crCoverage.ts
index 81dd2c0f4c..0b4984211a 100644
--- a/packages/playwright-core/src/server/chromium/crCoverage.ts
+++ b/packages/playwright-core/src/server/chromium/crCoverage.ts
@@ -16,11 +16,11 @@
*/
import { assert } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import type { CRSession } from './crConnection';
import type { Protocol } from './protocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as channels from '@protocol/channels';
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/crNetworkManager.ts b/packages/playwright-core/src/server/chromium/crNetworkManager.ts
index b9a5447189..812f62dfac 100644
--- a/packages/playwright-core/src/server/chromium/crNetworkManager.ts
+++ b/packages/playwright-core/src/server/chromium/crNetworkManager.ts
@@ -16,14 +16,14 @@
*/
import { assert, headersArrayToObject, headersObjectToArray } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import { helper } from '../helper';
import * as network from '../network';
import { isProtocolError, isSessionClosedError } from '../protocolError';
import type { CRSession } from './crConnection';
import type { Protocol } from './protocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as contexts from '../browserContext';
import type * as frames from '../frames';
import type { Page } from '../page';
diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts
index 65122407a0..34470470dc 100644
--- a/packages/playwright-core/src/server/chromium/crPage.ts
+++ b/packages/playwright-core/src/server/chromium/crPage.ts
@@ -17,9 +17,10 @@
import * as path from 'path';
-import { assert, createGuid } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
-import { rewriteErrorMessage } from '../../utils/stackTrace';
+import { assert } from '../../utils/isomorphic/debug';
+import { createGuid } from '../utils/crypto';
+import { eventsHelper } from '../utils/eventsHelper';
+import { rewriteErrorMessage } from '../../utils/isomorphic/stackTrace';
import * as dialog from '../dialog';
import * as dom from '../dom';
import * as frames from '../frames';
@@ -45,7 +46,7 @@ import { isSessionClosedError } from '../protocolError';
import type { CRSession } from './crConnection';
import type { Protocol } from './protocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type { InitScript, PageDelegate } from '../page';
import type { Progress } from '../progress';
import type * as types from '../types';
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/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
index 3488964e8e..d3f110f54e 100644
--- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts
@@ -31,7 +31,8 @@ import { Recorder } from '../recorder';
import { TracingDispatcher } from './tracingDispatcher';
import { WebSocketRouteDispatcher } from './webSocketRouteDispatcher';
import { WritableStreamDispatcher } from './writableStreamDispatcher';
-import { createGuid, urlMatches } from '../../utils';
+import { createGuid } from '../utils/crypto';
+import { urlMatches } from '../../utils/isomorphic/urlMatch';
import { RecorderApp } from '../recorder/recorderApp';
import type { Artifact } from '../artifact';
diff --git a/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts b/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts
index a3acb24bd1..1636d9f778 100644
--- a/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/debugControllerDispatcher.ts
@@ -19,7 +19,7 @@ import { DebugController } from '../debugController';
import { Dispatcher } from './dispatcher';
import type { DispatcherConnection, RootDispatcher } from './dispatcher';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as channels from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/dispatchers/dispatcher.ts b/packages/playwright-core/src/server/dispatchers/dispatcher.ts
index 34f2614e08..9912de9849 100644
--- a/packages/playwright-core/src/server/dispatchers/dispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/dispatcher.ts
@@ -16,7 +16,7 @@
import { EventEmitter } from 'events';
-import { eventsHelper } from '../..//utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import { ValidationError, createMetadataValidator, findValidator } from '../../protocol/validator';
import { LongStandingScope, assert, compressCallLog, isUnderTest, monotonicTime, rewriteErrorMessage } from '../../utils';
import { TargetClosedError, isTargetClosedError, serializeError } from '../errors';
@@ -25,7 +25,7 @@ import { isProtocolError } from '../protocolError';
import type { CallMetadata } from '../instrumentation';
import type { PlaywrightDispatcher } from './playwrightDispatcher';
-import type { RegisteredListener } from '../..//utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type { ValidatorContext } from '../../protocol/validator';
import type * as channels from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts b/packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts
index 67f36e94a1..75c1c89fad 100644
--- a/packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/jsonPipeDispatcher.ts
@@ -15,7 +15,7 @@
*/
import { Dispatcher } from './dispatcher';
-import { createGuid } from '../../utils';
+import { createGuid } from '../utils/crypto';
import type { LocalUtilsDispatcher } from './localUtilsDispatcher';
import type * as channels from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts
index 1b8301a1ac..b582aa8788 100644
--- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts
@@ -16,9 +16,9 @@
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 '../../common/localUtils';
+import { nodePlatform } from '../utils/nodePlatform';
+import { getUserAgent } from '../utils/userAgent';
import { deviceDescriptors as descriptors } from '../deviceDescriptors';
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
import { Progress, ProgressController } from '../progress';
@@ -26,7 +26,7 @@ import { SocksInterceptor } from '../socksInterceptor';
import { WebSocketTransport } from '../transport';
import { fetchData } from '../utils/network';
-import type { HarBackend } from '../../utils/harBackend';
+import type { HarBackend } from '../../common/harBackend';
import type { CallMetadata } from '../instrumentation';
import type { Playwright } from '../playwright';
import type { RootDispatcher } from './dispatcher';
@@ -54,7 +54,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
}
async harOpen(params: channels.LocalUtilsHarOpenParams, metadata: CallMetadata): Promise {
- return await localUtils.harOpen(this._harBackends, params);
+ return await localUtils.harOpen(nodePlatform, this._harBackends, params);
}
async harLookup(params: channels.LocalUtilsHarLookupParams, metadata: CallMetadata): Promise {
diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
index 1b8a0677dd..166284366c 100644
--- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts
@@ -25,7 +25,8 @@ import { RequestDispatcher } from './networkDispatchers';
import { ResponseDispatcher } from './networkDispatchers';
import { RouteDispatcher, WebSocketDispatcher } from './networkDispatchers';
import { WebSocketRouteDispatcher } from './webSocketRouteDispatcher';
-import { createGuid, urlMatches } from '../../utils';
+import { createGuid } from '../utils/crypto';
+import { urlMatches } from '../../utils/isomorphic/urlMatch';
import type { Artifact } from '../artifact';
import type { BrowserContext } from '../browserContext';
diff --git a/packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts b/packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts
index 80a1f6a696..6735e9c284 100644
--- a/packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/playwrightDispatcher.ts
@@ -25,12 +25,12 @@ import { ElectronDispatcher } from './electronDispatcher';
import { LocalUtilsDispatcher } from './localUtilsDispatcher';
import { APIRequestContextDispatcher } from './networkDispatchers';
import { SelectorsDispatcher } from './selectorsDispatcher';
-import { createGuid } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
+import { createGuid } from '../utils/crypto';
+import { eventsHelper } from '../utils/eventsHelper';
import type { RootDispatcher } from './dispatcher';
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../utils/socksProxy';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type { AndroidDevice } from '../android/android';
import type { Browser } from '../browser';
import type { Playwright } from '../playwright';
diff --git a/packages/playwright-core/src/server/dispatchers/streamDispatcher.ts b/packages/playwright-core/src/server/dispatchers/streamDispatcher.ts
index 8ceb69e669..f3a0a413b7 100644
--- a/packages/playwright-core/src/server/dispatchers/streamDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/streamDispatcher.ts
@@ -15,7 +15,8 @@
*/
import { Dispatcher } from './dispatcher';
-import { ManualPromise, createGuid } from '../../utils';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
+import { createGuid } from '../utils/crypto';
import type { ArtifactDispatcher } from './artifactDispatcher';
import type * as channels from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts
index fea8d06eec..f31bf7659d 100644
--- a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts
@@ -18,8 +18,9 @@ import { Page } from '../page';
import { Dispatcher, existingDispatcher } from './dispatcher';
import { PageDispatcher } from './pageDispatcher';
import * as webSocketMockSource from '../../generated/webSocketMockSource';
-import { createGuid, urlMatches } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
+import { createGuid } from '../utils/crypto';
+import { urlMatches } from '../../utils/isomorphic/urlMatch';
+import { eventsHelper } from '../utils/eventsHelper';
import type { BrowserContextDispatcher } from './browserContextDispatcher';
import type { BrowserContext } from '../browserContext';
diff --git a/packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts b/packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts
index 7d8ce2e4e1..08757f5d88 100644
--- a/packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts
+++ b/packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts
@@ -17,7 +17,7 @@
import * as fs from 'fs';
import { Dispatcher } from './dispatcher';
-import { createGuid } from '../../utils';
+import { createGuid } from '../utils/crypto';
import type { BrowserContextDispatcher } from './browserContextDispatcher';
import type * as channels from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/electron/DEPS.list b/packages/playwright-core/src/server/electron/DEPS.list
index 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 ce4480b428..4713324ed1 100644
--- a/packages/playwright-core/src/server/electron/electron.ts
+++ b/packages/playwright-core/src/server/electron/electron.ts
@@ -19,11 +19,11 @@ 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 { eventsHelper } from '../../utils/eventsHelper';
+import { RecentLogsCollector } from '../utils/debugLogger';
+import { eventsHelper } from '../utils/eventsHelper';
import { validateBrowserContextOptions } from '../browserContext';
import { CRBrowser } from '../chromium/crBrowser';
import { CRConnection } from '../chromium/crConnection';
diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts
index 13358856d9..6e1d6d02f1 100644
--- a/packages/playwright-core/src/server/fetch.ts
+++ b/packages/playwright-core/src/server/fetch.ts
@@ -21,9 +21,10 @@ import { TLSSocket } from 'tls';
import * as url from 'url';
import * as zlib from 'zlib';
-import { TimeoutSettings } from '../common/timeoutSettings';
-import { assert, constructURLBasedOnBaseURL, createGuid, eventsHelper, monotonicTime } from '../utils';
-import { getUserAgent } from '../utils/userAgent';
+import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
+import { assert, constructURLBasedOnBaseURL, eventsHelper, monotonicTime } from '../utils';
+import { createGuid } from './utils/crypto';
+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..908e7644ac 100644
--- a/packages/playwright-core/src/server/fileUploadUtils.ts
+++ b/packages/playwright-core/src/server/fileUploadUtils.ts
@@ -17,8 +17,8 @@
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 { fileUploadSizeLimit } from '../common/fileUtils';
import { mime } from '../utilsBundle';
import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher';
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/ffNetworkManager.ts b/packages/playwright-core/src/server/firefox/ffNetworkManager.ts
index 95df0643d0..fbd7330739 100644
--- a/packages/playwright-core/src/server/firefox/ffNetworkManager.ts
+++ b/packages/playwright-core/src/server/firefox/ffNetworkManager.ts
@@ -15,12 +15,12 @@
* limitations under the License.
*/
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import * as network from '../network';
import type { FFSession } from './ffConnection';
import type { HeadersArray } from '../../server/types';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as frames from '../frames';
import type { Page } from '../page';
import type * as types from '../types';
diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts
index 0e1755a2da..22229dff3e 100644
--- a/packages/playwright-core/src/server/firefox/ffPage.ts
+++ b/packages/playwright-core/src/server/firefox/ffPage.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import * as dialog from '../dialog';
import * as dom from '../dom';
import { InitScript } from '../page';
@@ -25,15 +25,15 @@ 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';
import type { Progress } from '../progress';
import type { FFBrowserContext } from './ffBrowser';
import type { Protocol } from './protocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as frames from '../frames';
import type { PageDelegate } from '../page';
import type * as types from '../types';
diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts
index 2183d3f409..dee2ad7f08 100644
--- a/packages/playwright-core/src/server/frames.ts
+++ b/packages/playwright-core/src/server/frames.ts
@@ -29,10 +29,10 @@ 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 { eventsHelper } from '../utils/eventsHelper';
+import { debugLogger } from './utils/debugLogger';
+import { eventsHelper } from './utils/eventsHelper';
import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
-import { ManualPromise } from '../utils/manualPromise';
+import { ManualPromise } from '../utils/isomorphic/manualPromise';
import type { ConsoleMessage } from './console';
import type { Dialog } from './dialog';
@@ -40,7 +40,7 @@ import type { ElementStateWithoutStable, FrameExpectParams, InjectedScript } fro
import type { CallMetadata } from './instrumentation';
import type { Progress } from './progress';
import type { ScreenshotOptions } from './screenshotter';
-import type { RegisteredListener } from '../utils/eventsHelper';
+import type { RegisteredListener } from './utils/eventsHelper';
import type { ParsedSelector } from '../utils/isomorphic/selectorParser';
import type * as channels from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/har/harRecorder.ts b/packages/playwright-core/src/server/har/harRecorder.ts
index 7cde24fa3a..12489f1c4c 100644
--- a/packages/playwright-core/src/server/har/harRecorder.ts
+++ b/packages/playwright-core/src/server/har/harRecorder.ts
@@ -19,8 +19,8 @@ import * as path from 'path';
import { Artifact } from '../artifact';
import { HarTracer } from './harTracer';
-import { createGuid } from '../../utils';
-import { ManualPromise } from '../../utils/manualPromise';
+import { createGuid } from '../utils/crypto';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
import { yazl } from '../../zipBundle';
import type { BrowserContext } from '../browserContext';
diff --git a/packages/playwright-core/src/server/har/harTracer.ts b/packages/playwright-core/src/server/har/harTracer.ts
index 0e3a66b938..b1ff48b73d 100644
--- a/packages/playwright-core/src/server/har/harTracer.ts
+++ b/packages/playwright-core/src/server/har/harTracer.ts
@@ -16,8 +16,8 @@
import { assert, calculateSha1, monotonicTime } from '../../utils';
import { getPlaywrightVersion, isTextualMimeType, urlMatches } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
-import { ManualPromise } from '../../utils/manualPromise';
+import { eventsHelper } from '../utils/eventsHelper';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
import { mime } from '../../utilsBundle';
import { BrowserContext } from '../browserContext';
import { APIRequestContext } from '../fetch';
@@ -25,7 +25,7 @@ import { Frame } from '../frames';
import { helper } from '../helper';
import * as network from '../network';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type { APIRequestEvent, APIRequestFinishedEvent } from '../fetch';
import type { Page } from '../page';
import type { Worker } from '../page';
diff --git a/packages/playwright-core/src/server/helper.ts b/packages/playwright-core/src/server/helper.ts
index ccf9af12b0..e9fec62419 100644
--- a/packages/playwright-core/src/server/helper.ts
+++ b/packages/playwright-core/src/server/helper.ts
@@ -15,12 +15,12 @@
* limitations under the License.
*/
-import { debugLogger } from '../utils/debugLogger';
-import { eventsHelper } from '../utils/eventsHelper';
+import { debugLogger } from './utils/debugLogger';
+import { eventsHelper } from './utils/eventsHelper';
import type { Progress } from './progress';
import type * as types from './types';
-import type { RegisteredListener } from '../utils/eventsHelper';
+import type { RegisteredListener } from './utils/eventsHelper';
import type { EventEmitter } from 'events';
diff --git a/packages/playwright-core/src/server/instrumentation.ts b/packages/playwright-core/src/server/instrumentation.ts
index b33ec542ec..076008a31e 100644
--- a/packages/playwright-core/src/server/instrumentation.ts
+++ b/packages/playwright-core/src/server/instrumentation.ts
@@ -16,7 +16,7 @@
import { EventEmitter } from 'events';
-import { createGuid } from '../utils';
+import { createGuid } from './utils/crypto';
import type { Browser } from './browser';
import type { BrowserContext } from './browserContext';
diff --git a/packages/playwright-core/src/server/javascript.ts b/packages/playwright-core/src/server/javascript.ts
index 32ba84179e..a3d65f86a8 100644
--- a/packages/playwright-core/src/server/javascript.ts
+++ b/packages/playwright-core/src/server/javascript.ts
@@ -18,7 +18,7 @@ import { SdkObject } from './instrumentation';
import * as utilityScriptSource from '../generated/utilityScriptSource';
import { isUnderTest } from '../utils';
import { serializeAsCallArgument } from './isomorphic/utilityScriptSerializers';
-import { LongStandingScope } from '../utils/manualPromise';
+import { LongStandingScope } from '../utils/isomorphic/manualPromise';
import type * as dom from './dom';
import type { UtilityScript } from './injected/utilityScript';
diff --git a/packages/playwright-core/src/server/network.ts b/packages/playwright-core/src/server/network.ts
index 81e23abd82..dfee496fc2 100644
--- a/packages/playwright-core/src/server/network.ts
+++ b/packages/playwright-core/src/server/network.ts
@@ -18,7 +18,7 @@ import { assert } from '../utils';
import { BrowserContext } from './browserContext';
import { APIRequestContext } from './fetch';
import { SdkObject } from './instrumentation';
-import { ManualPromise } from '../utils/manualPromise';
+import { ManualPromise } from '../utils/isomorphic/manualPromise';
import type * as contexts from './browserContext';
import type * as frames from './frames';
diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts
index ac7f6dfd81..d2db2e4211 100644
--- a/packages/playwright-core/src/server/page.ts
+++ b/packages/playwright-core/src/server/page.ts
@@ -28,13 +28,14 @@ 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 { LongStandingScope, assert, compressCallLog, createGuid, trimStringWithEllipsis } from '../utils';
+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/manualPromise';
+import { ManualPromise } from '../utils/isomorphic/manualPromise';
import type { Artifact } from './artifact';
import type * as dom from './dom';
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 1b18089233..30db144fbb 100644
--- a/packages/playwright-core/src/server/progress.ts
+++ b/packages/playwright-core/src/server/progress.ts
@@ -16,11 +16,11 @@
import { TimeoutError } from './errors';
import { assert, monotonicTime } from '../utils';
-import { ManualPromise } from '../utils/manualPromise';
+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 {
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/contextRecorder.ts b/packages/playwright-core/src/server/recorder/contextRecorder.ts
index 5cc3e9abae..cede7bb28a 100644
--- a/packages/playwright-core/src/server/recorder/contextRecorder.ts
+++ b/packages/playwright-core/src/server/recorder/contextRecorder.ts
@@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
import { RecorderCollection } from './recorderCollection';
import * as recorderSource from '../../generated/pollingRecorderSource';
import { eventsHelper, monotonicTime, quoteCSSAttributeValue } from '../../utils';
-import { raceAgainstDeadline } from '../../utils/timeoutRunner';
+import { raceAgainstDeadline } from '../../utils/isomorphic/timeoutRunner';
import { BrowserContext } from '../browserContext';
import { languageSet } from '../codegen/languages';
import { Frame } from '../frames';
diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts
index 1780502d7d..8c7b344b8e 100644
--- a/packages/playwright-core/src/server/recorder/recorderCollection.ts
+++ b/packages/playwright-core/src/server/recorder/recorderCollection.ts
@@ -18,8 +18,8 @@ import { EventEmitter } from 'events';
import { performAction } from './recorderRunner';
import { collapseActions } from './recorderUtils';
-import { isUnderTest } from '../../utils/debug';
-import { monotonicTime } from '../../utils/time';
+import { isUnderTest } from '../../utils/isomorphic/debug';
+import { monotonicTime } from '../../utils/isomorphic/time';
import type { Signal } from '../../../../recorder/src/actions';
import type { Frame } from '../frames';
diff --git a/packages/playwright-core/src/server/registry/browserFetcher.ts b/packages/playwright-core/src/server/registry/browserFetcher.ts
index e7ffda483f..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 { ManualPromise } from '../../utils/manualPromise';
-import { getUserAgent } from '../../utils/userAgent';
-import { colors, progress as ProgressBar } from '../../utilsBundle';
+import { debugLogger } from '../utils/debugLogger';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
+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/registry/oopDownloadBrowserMain.ts b/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts
index ef6348aab2..bda904f47a 100644
--- a/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts
+++ b/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts
@@ -17,7 +17,7 @@
import * as fs from 'fs';
import * as path from 'path';
-import { ManualPromise } from '../../utils/manualPromise';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
import { httpRequest } from '../utils/network';
import { extract } from '../../zipBundle';
diff --git a/packages/playwright-core/src/server/screenshotter.ts b/packages/playwright-core/src/server/screenshotter.ts
index 88870b8900..b32f51a6de 100644
--- a/packages/playwright-core/src/server/screenshotter.ts
+++ b/packages/playwright-core/src/server/screenshotter.ts
@@ -17,7 +17,7 @@
import { helper } from './helper';
import { assert } from '../utils';
-import { MultiMap } from '../utils/multimap';
+import { MultiMap } from '../utils/isomorphic/multimap';
import type * as dom from './dom';
import type { Frame } from './frames';
diff --git a/packages/playwright-core/src/server/selectors.ts b/packages/playwright-core/src/server/selectors.ts
index 1d1eece9cc..886108b972 100644
--- a/packages/playwright-core/src/server/selectors.ts
+++ b/packages/playwright-core/src/server/selectors.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { createGuid } from '../utils';
+import { createGuid } from './utils/crypto';
import { InvalidSelectorError, parseSelector, stringifySelector, visitAllSelectorParts } from '../utils/isomorphic/selectorParser';
import type { ParsedSelector } from '../utils/isomorphic/selectorParser';
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/DEPS.list b/packages/playwright-core/src/server/trace/recorder/DEPS.list
index 11a449d26d..bf35323ecc 100644
--- a/packages/playwright-core/src/server/trace/recorder/DEPS.list
+++ b/packages/playwright-core/src/server/trace/recorder/DEPS.list
@@ -5,6 +5,7 @@
../../../protocol/
../../../utils/
../../../utilsBundle.ts
+../../../utils/isomorphic/
../../../zipBundle.ts
../../dispatchers/dispatcher.ts
../../utils
diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts
index 1ad2fdf1ca..f4e288d077 100644
--- a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts
+++ b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts
@@ -15,15 +15,16 @@
*/
import { frameSnapshotStreamer } from './snapshotterInjected';
-import { calculateSha1, createGuid, monotonicTime } from '../../../utils';
-import { debugLogger } from '../../../utils/debugLogger';
-import { eventsHelper } from '../../../utils/eventsHelper';
+import { monotonicTime } from '../../../utils/isomorphic/time';
+import { calculateSha1, createGuid } from '../../utils/crypto';
+import { debugLogger } from '../../utils/debugLogger';
+import { eventsHelper } from '../../utils/eventsHelper';
import { mime } from '../../../utilsBundle';
import { BrowserContext } from '../../browserContext';
import { Page } from '../../page';
import type { SnapshotData } from './snapshotterInjected';
-import type { RegisteredListener } from '../../../utils/eventsHelper';
+import type { RegisteredListener } from '../../utils/eventsHelper';
import type { Frame } from '../../frames';
import type { FrameSnapshot } from '@trace/snapshot';
diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts
index a3dedc8485..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,10 @@ import * as path from 'path';
import { Snapshotter } from './snapshotter';
import { commandsWithTracingSnapshots } from '../../../protocol/debug';
-import { assert, createGuid, eventsHelper, monotonicTime } from '../../../utils';
+import { assert } from '../../../utils/isomorphic/debug';
+import { monotonicTime } from '../../../utils/isomorphic/time';
+import { eventsHelper } from '../../utils/eventsHelper';
+import { createGuid } from '../../utils/crypto';
import { Artifact } from '../../artifact';
import { BrowserContext } from '../../browserContext';
import { Dispatcher } from '../../dispatchers/dispatcher';
diff --git a/packages/playwright-core/src/server/utils/DEPS.list b/packages/playwright-core/src/server/utils/DEPS.list
index 53cad9639a..685e00df46 100644
--- a/packages/playwright-core/src/server/utils/DEPS.list
+++ b/packages/playwright-core/src/server/utils/DEPS.list
@@ -1,9 +1,10 @@
[*]
+../../common
../../utils
+../../utils/isomorphic
../../utilsBundle.ts
../../zipBundle.ts
[comparators.ts]
./image_tools
../../third_party/pixelmatch
-
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/utils/crypto.ts b/packages/playwright-core/src/server/utils/crypto.ts
similarity index 99%
rename from packages/playwright-core/src/utils/crypto.ts
rename to packages/playwright-core/src/server/utils/crypto.ts
index c59b84caf0..7189f38b00 100644
--- a/packages/playwright-core/src/utils/crypto.ts
+++ b/packages/playwright-core/src/server/utils/crypto.ts
@@ -16,7 +16,7 @@
import * as crypto from 'crypto';
-import { assert } from './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/utils/eventsHelper.ts b/packages/playwright-core/src/server/utils/eventsHelper.ts
similarity index 100%
rename from packages/playwright-core/src/utils/eventsHelper.ts
rename to packages/playwright-core/src/server/utils/eventsHelper.ts
diff --git a/packages/playwright-core/src/utils/expectUtils.ts b/packages/playwright-core/src/server/utils/expectUtils.ts
similarity index 94%
rename from packages/playwright-core/src/utils/expectUtils.ts
rename to packages/playwright-core/src/server/utils/expectUtils.ts
index f39af23cee..6d7a2a1e3d 100644
--- a/packages/playwright-core/src/utils/expectUtils.ts
+++ b/packages/playwright-core/src/server/utils/expectUtils.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { isRegExp, isString } from './rtti';
+import { isRegExp, isString } from '../../utils/isomorphic/rtti';
import type { ExpectedTextValue } from '@protocol/channels';
diff --git a/packages/playwright-core/src/server/utils/fileUtils.ts b/packages/playwright-core/src/server/utils/fileUtils.ts
index 5f196963b7..0a0a95d2a1 100644
--- a/packages/playwright-core/src/server/utils/fileUtils.ts
+++ b/packages/playwright-core/src/server/utils/fileUtils.ts
@@ -17,7 +17,7 @@
import * as fs from 'fs';
import * as path from 'path';
-import { ManualPromise } from '../../utils/manualPromise';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
import { yazl } from '../../zipBundle';
import type { EventEmitter } from 'events';
diff --git a/packages/playwright-core/src/server/utils/happyEyeballs.ts b/packages/playwright-core/src/server/utils/happyEyeballs.ts
index ba6188f97c..da3481f4c1 100644
--- a/packages/playwright-core/src/server/utils/happyEyeballs.ts
+++ b/packages/playwright-core/src/server/utils/happyEyeballs.ts
@@ -20,9 +20,9 @@ import * as https from 'https';
import * as net from 'net';
import * as tls from 'tls';
-import { assert } from '../../utils/debug';
-import { ManualPromise } from '../../utils/manualPromise';
-import { monotonicTime } from '../../utils/time';
+import { assert } from '../../utils/isomorphic/debug';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
+import { monotonicTime } from '../../utils/isomorphic/time';
// Implementation(partial) of Happy Eyeballs 2 algorithm described in
// https://www.rfc-editor.org/rfc/rfc8305
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 ee170eca33..0c9f0fe25a 100644
--- a/packages/playwright-core/src/server/utils/httpServer.ts
+++ b/packages/playwright-core/src/server/utils/httpServer.ts
@@ -18,9 +18,9 @@ import * as fs from 'fs';
import * as path from 'path';
import { mime, wsServer } from '../../utilsBundle';
-import { createGuid } from '../../utils/crypto';
-import { assert } from '../../utils/debug';
-import { ManualPromise } from '../../utils/manualPromise';
+import { createGuid } from './crypto';
+import { assert } from '../../utils/isomorphic/debug';
+import { ManualPromise } from '../../utils/isomorphic/manualPromise';
import { createHttpServer } from './network';
import type http from 'http';
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 57%
rename from packages/playwright-core/src/utils/platform.ts
rename to packages/playwright-core/src/server/utils/nodePlatform.ts
index e682ad46b0..d4b8b5d7ad 100644
--- a/packages/playwright-core/src/utils/platform.ts
+++ b/packages/playwright-core/src/server/utils/nodePlatform.ts
@@ -14,36 +14,39 @@
* limitations under the License.
*/
+import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as util from 'util';
-export type Platform = {
- fs: () => typeof fs;
- path: () => typeof path;
- inspectCustom: symbol | undefined;
- ws?: (url: string) => WebSocket;
-};
-
-export const emptyPlatform: Platform = {
- fs: () => {
- throw new Error('File system is not available');
- },
-
- path: () => {
- throw new Error('Path module is not available');
- },
-
- inspectCustom: undefined,
-};
+import { colors } from '../../utilsBundle';
+import { Platform } from '../../common/platform';
+import { debugLogger } from './debugLogger';
export const nodePlatform: Platform = {
- fs: () => fs,
- path: () => path,
- inspectCustom: util.inspect.custom,
-};
+ calculateSha1: (text: string) => {
+ const sha1 = crypto.createHash('sha1');
+ sha1.update(text);
+ return Promise.resolve(sha1.digest('hex'));
+ },
-export const webPlatform: Platform = {
- ...emptyPlatform,
- ws: (url: string) => new WebSocket(url),
+ colors,
+
+ createGuid: () => crypto.randomBytes(16).toString('hex'),
+
+ fs: () => fs,
+
+ inspectCustom: util.inspect.custom,
+
+ isLogEnabled(name: 'api' | 'channel') {
+ return debugLogger.isEnabled(name);
+ },
+
+ log(name: 'api' | 'channel', message: string | Error | object) {
+ debugLogger.log(name, message);
+ },
+
+ path: () => path,
+
+ pathSeparator: path.sep
};
diff --git a/packages/playwright-core/src/server/utils/socksProxy.ts b/packages/playwright-core/src/server/utils/socksProxy.ts
index f5eb9eec1a..c0fe0f126a 100644
--- a/packages/playwright-core/src/server/utils/socksProxy.ts
+++ b/packages/playwright-core/src/server/utils/socksProxy.ts
@@ -17,8 +17,9 @@
import EventEmitter from 'events';
import * as net from 'net';
-import { assert, createGuid, } from '../../utils';
-import { debugLogger } from '../../utils/debugLogger';
+import { assert } from '../../utils/isomorphic/debug';
+import { createGuid } from './crypto';
+import { debugLogger } from './debugLogger';
import { createSocket } from './happyEyeballs';
import type { AddressInfo } from 'net';
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/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 e80031d388..f9e9517fb2 100644
--- a/packages/playwright-core/src/server/webkit/wkPage.ts
+++ b/packages/playwright-core/src/server/webkit/wkPage.ts
@@ -17,10 +17,12 @@
import * as path from 'path';
-import { assert, createGuid, debugAssert, headersArrayToObject } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
-import { hostPlatform } from '../../utils/hostPlatform';
-import { splitErrorMessage } from '../../utils/stackTrace';
+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/isomorphic/stackTrace';
import { PNG, jpegjs } from '../../utilsBundle';
import { BrowserContext } from '../browserContext';
import * as dialog from '../dialog';
@@ -37,11 +39,11 @@ 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';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as accessibility from '../accessibility';
import type * as frames from '../frames';
import type { JSHandle } from '../javascript';
diff --git a/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts b/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts
index 2958f4597c..63d4cc6f0a 100644
--- a/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts
+++ b/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts
@@ -15,12 +15,12 @@
*/
import { assert } from '../../utils';
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import type { Protocol } from './protocol';
import type { WKSession } from './wkConnection';
import type { WKPage } from './wkPage';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type * as network from '../network';
export class WKProvisionalPage {
diff --git a/packages/playwright-core/src/server/webkit/wkWorkers.ts b/packages/playwright-core/src/server/webkit/wkWorkers.ts
index 644c64a27f..719d8df6be 100644
--- a/packages/playwright-core/src/server/webkit/wkWorkers.ts
+++ b/packages/playwright-core/src/server/webkit/wkWorkers.ts
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-import { eventsHelper } from '../../utils/eventsHelper';
+import { eventsHelper } from '../utils/eventsHelper';
import { Worker } from '../page';
import { WKSession } from './wkConnection';
import { WKExecutionContext } from './wkExecutionContext';
import type { Protocol } from './protocol';
-import type { RegisteredListener } from '../../utils/eventsHelper';
+import type { RegisteredListener } from '../utils/eventsHelper';
import type { Page } from '../page';
import type * as types from '../types';
diff --git a/packages/playwright-core/src/utils.ts b/packages/playwright-core/src/utils.ts
index 930acae092..1a0c446c6b 100644
--- a/packages/playwright-core/src/utils.ts
+++ b/packages/playwright-core/src/utils.ts
@@ -14,38 +14,42 @@
* limitations under the License.
*/
-export * from './utils/crypto';
-export * from './utils/debug';
-export * from './utils/debugLogger';
-export * from './utils/env';
-export * from './utils/eventsHelper';
-export * from './utils/expectUtils';
-export * from './utils/headers';
-export * from './utils/hostPlatform';
-export * from './utils/manualPromise';
+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';
+export * from './utils/isomorphic/multimap';
+export * from './utils/isomorphic/rtti';
export * from './utils/isomorphic/stringUtils';
+export * from './utils/isomorphic/time';
+export * from './utils/isomorphic/timeoutRunner';
export * from './utils/isomorphic/urlMatch';
-export * from './utils/multimap';
-export * from './utils/rtti';
-export * from './utils/semaphore';
-export * from './utils/stackTrace';
+
+export * from './utils/isomorphic/headers';
+export * from './utils/isomorphic/semaphore';
+export * from './utils/isomorphic/stackTrace';
export * from './utils/task';
-export * from './utils/time';
-export * from './utils/timeoutRunner';
-export * from './utils/traceUtils';
-export * from './utils/userAgent';
export * from './utils/zipFile';
export * from './utils/zones';
-export * from './server/utils/socksProxy';
-export * from './server/utils/processLauncher';
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/wsServer';
+export * from './server/utils/socksProxy';
export * from './server/utils/spawnAsync';
+export * from './server/utils/userAgent';
+export * from './server/utils/wsServer';
+
+export { colors } from './utilsBundle';
diff --git a/packages/playwright-core/src/utils/DEPS.list b/packages/playwright-core/src/utils/DEPS.list
index 0e2297dfdb..8a14701d4d 100644
--- a/packages/playwright-core/src/utils/DEPS.list
+++ b/packages/playwright-core/src/utils/DEPS.list
@@ -1,4 +1,6 @@
[*]
./
+./isomorphic
../utilsBundle.ts
../zipBundle.ts
+../utils/isomorphic
\ No newline at end of file
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/headers.ts b/packages/playwright-core/src/utils/isomorphic/headers.ts
similarity index 100%
rename from packages/playwright-core/src/utils/headers.ts
rename to packages/playwright-core/src/utils/isomorphic/headers.ts
diff --git a/packages/playwright-core/src/utils/manualPromise.ts b/packages/playwright-core/src/utils/isomorphic/manualPromise.ts
similarity index 92%
rename from packages/playwright-core/src/utils/manualPromise.ts
rename to packages/playwright-core/src/utils/isomorphic/manualPromise.ts
index 467b8de0db..a5034e05ec 100644
--- a/packages/playwright-core/src/utils/manualPromise.ts
+++ b/packages/playwright-core/src/utils/isomorphic/manualPromise.ts
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-import { captureRawStack } from './stackTrace';
-
export class ManualPromise extends Promise {
private _resolve!: (t: T) => void;
private _reject!: (e: Error) => void;
@@ -118,3 +116,12 @@ function cloneError(error: Error, frames: string[]) {
clone.stack = [error.name + ':' + error.message, ...frames].join('\n');
return clone;
}
+
+function captureRawStack(): string[] {
+ const stackTraceLimit = Error.stackTraceLimit;
+ Error.stackTraceLimit = 50;
+ const error = new Error();
+ const stack = error.stack || '';
+ Error.stackTraceLimit = stackTraceLimit;
+ return stack.split('\n');
+}
diff --git a/packages/playwright-core/src/utils/multimap.ts b/packages/playwright-core/src/utils/isomorphic/multimap.ts
similarity index 100%
rename from packages/playwright-core/src/utils/multimap.ts
rename to packages/playwright-core/src/utils/isomorphic/multimap.ts
diff --git a/packages/playwright-core/src/utils/rtti.ts b/packages/playwright-core/src/utils/isomorphic/rtti.ts
similarity index 95%
rename from packages/playwright-core/src/utils/rtti.ts
rename to packages/playwright-core/src/utils/isomorphic/rtti.ts
index a18d3a450a..0bcaef0748 100644
--- a/packages/playwright-core/src/utils/rtti.ts
+++ b/packages/playwright-core/src/utils/isomorphic/rtti.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-export { isString } from './isomorphic/stringUtils';
+export { isString } from './stringUtils';
export function isRegExp(obj: any): obj is RegExp {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
diff --git a/packages/playwright-core/src/utils/semaphore.ts b/packages/playwright-core/src/utils/isomorphic/semaphore.ts
similarity index 100%
rename from packages/playwright-core/src/utils/semaphore.ts
rename to packages/playwright-core/src/utils/isomorphic/semaphore.ts
diff --git a/packages/playwright-core/src/utils/sequence.ts b/packages/playwright-core/src/utils/isomorphic/sequence.ts
similarity index 100%
rename from packages/playwright-core/src/utils/sequence.ts
rename to packages/playwright-core/src/utils/isomorphic/sequence.ts
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 fb97115035..26b79c5c27 100644
--- a/packages/playwright-core/src/utils/stackTrace.ts
+++ b/packages/playwright-core/src/utils/isomorphic/stackTrace.ts
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-import * as path from 'path';
-
-import { colors } from '../utilsBundle';
import { findRepeatedSubsequences } from './sequence';
-import { StackUtils } from './stackUtils';
+import { parseStackFrame } from './stackUtils';
import type { StackFrame } from '@protocol/channels';
+import type { Platform } from '../../common/platform';
-const stackUtils = new StackUtils();
-
-export function parseStackTraceLine(line: string): StackFrame | null {
- const frame = stackUtils.parseLine(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:')))
@@ -49,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[];
@@ -67,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 = {
@@ -76,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,
@@ -113,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;
});
@@ -135,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 {
@@ -148,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 53%
rename from packages/playwright-core/src/utils/stackUtils.ts
rename to packages/playwright-core/src/utils/isomorphic/stackUtils.ts
index 0be4675a86..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,90 +33,88 @@ type StackData = {
evalFile?: string | undefined;
};
-export class StackUtils {
- parseLine(line: string) {
- const match = line && line.match(re);
- if (!match)
- return null;
+export function parseStackFrame(line: string, pathSeparator: string): StackData | null {
+ const match = line && line.match(re);
+ if (!match)
+ return null;
- const ctor = match[1] === 'new';
- let fname = match[2];
- const evalOrigin = match[3];
- const evalFile = match[4];
- const evalLine = Number(match[5]);
- const evalCol = Number(match[6]);
- let file = match[7];
- const lnum = match[8];
- const col = match[9];
- const native = match[10] === 'native';
- const closeParen = match[11] === ')';
- let method;
+ const ctor = match[1] === 'new';
+ let fname = match[2];
+ const evalOrigin = match[3];
+ const evalFile = match[4];
+ const evalLine = Number(match[5]);
+ const evalCol = Number(match[6]);
+ let file = match[7];
+ const lnum = match[8];
+ const col = match[9];
+ const native = match[10] === 'native';
+ const closeParen = match[11] === ')';
+ let method;
- const res: StackData = {};
+ const res: StackData = {};
- if (lnum)
- res.line = Number(lnum);
+ if (lnum)
+ res.line = Number(lnum);
- if (col)
- res.column = Number(col);
+ if (col)
+ res.column = Number(col);
- if (closeParen && file) {
- // make sure parens are balanced
- // if we have a file like "asdf) [as foo] (xyz.js", then odds are
- // that the fname should be += " (asdf) [as foo]" and the file
- // should be just "xyz.js"
- // walk backwards from the end to find the last unbalanced (
- let closes = 0;
- for (let i = file.length - 1; i > 0; i--) {
- if (file.charAt(i) === ')') {
- closes++;
- } else if (file.charAt(i) === '(' && file.charAt(i - 1) === ' ') {
- closes--;
- if (closes === -1 && file.charAt(i - 1) === ' ') {
- const before = file.slice(0, i - 1);
- const after = file.slice(i + 1);
- file = after;
- fname += ` (${before}`;
- break;
- }
+ if (closeParen && file) {
+ // make sure parens are balanced
+ // if we have a file like "asdf) [as foo] (xyz.js", then odds are
+ // that the fname should be += " (asdf) [as foo]" and the file
+ // should be just "xyz.js"
+ // walk backwards from the end to find the last unbalanced (
+ let closes = 0;
+ for (let i = file.length - 1; i > 0; i--) {
+ if (file.charAt(i) === ')') {
+ closes++;
+ } else if (file.charAt(i) === '(' && file.charAt(i - 1) === ' ') {
+ closes--;
+ if (closes === -1 && file.charAt(i - 1) === ' ') {
+ const before = file.slice(0, i - 1);
+ const after = file.slice(i + 1);
+ file = after;
+ fname += ` (${before}`;
+ break;
}
}
}
-
- if (fname) {
- const methodMatch = fname.match(methodRe);
- if (methodMatch) {
- fname = methodMatch[1];
- method = methodMatch[2];
- }
- }
-
- setFile(res, file);
-
- if (ctor)
- res.isConstructor = true;
-
- if (evalOrigin) {
- res.evalOrigin = evalOrigin;
- res.evalLine = evalLine;
- res.evalColumn = evalCol;
- res.evalFile = evalFile && evalFile.replace(/\\/g, '/');
- }
-
- if (native)
- res.native = true;
- if (fname)
- res.function = fname;
- if (method && fname !== method)
- res.method = method;
- return res;
}
+
+ if (fname) {
+ const methodMatch = fname.match(methodRe);
+ if (methodMatch) {
+ fname = methodMatch[1];
+ method = methodMatch[2];
+ }
+ }
+
+ setFile(res, file, pathSeparator);
+
+ if (ctor)
+ res.isConstructor = true;
+
+ if (evalOrigin) {
+ res.evalOrigin = evalOrigin;
+ res.evalLine = evalLine;
+ res.evalColumn = evalCol;
+ res.evalFile = evalFile && evalFile.replace(/\\/g, '/');
+ }
+
+ if (native)
+ res.native = true;
+ if (fname)
+ res.function = fname;
+ if (method && fname !== method)
+ res.method = method;
+ return res;
}
-function setFile(result: StackData, filename: string) {
+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;
}
}
@@ -149,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/utils/isomorphic/time.ts b/packages/playwright-core/src/utils/isomorphic/time.ts
new file mode 100644
index 0000000000..625fd18805
--- /dev/null
+++ b/packages/playwright-core/src/utils/isomorphic/time.ts
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export function monotonicTime(): number {
+ return (performance.now() * 1000 | 0) / 1000;
+}
diff --git a/packages/playwright-core/src/utils/timeoutRunner.ts b/packages/playwright-core/src/utils/isomorphic/timeoutRunner.ts
similarity index 98%
rename from packages/playwright-core/src/utils/timeoutRunner.ts
rename to packages/playwright-core/src/utils/isomorphic/timeoutRunner.ts
index e8f128b9f8..e8016ddb49 100644
--- a/packages/playwright-core/src/utils/timeoutRunner.ts
+++ b/packages/playwright-core/src/utils/isomorphic/timeoutRunner.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { monotonicTime } from '../utils';
+import { monotonicTime } from './time';
export async function raceAgainstDeadline(cb: () => Promise, deadline: number): Promise<{ result: T, timedOut: false } | { timedOut: true }> {
let timer: NodeJS.Timeout | undefined;
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-core/src/utils/isomorphic/traceUtils.ts b/packages/playwright-core/src/utils/isomorphic/traceUtils.ts
index edb1e1c660..f077cc5c4b 100644
--- a/packages/playwright-core/src/utils/isomorphic/traceUtils.ts
+++ b/packages/playwright-core/src/utils/isomorphic/traceUtils.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import type { StackFrame } from '@protocol/channels';
+import type { ClientSideCallMetadata, StackFrame } from '@protocol/channels';
export type SerializedStackFrame = [number, number, number, string];
export type SerializedStack = [number, SerializedStackFrame[]];
@@ -33,3 +33,24 @@ export function parseClientSideCallMetadata(data: SerializedClientSideCallMetada
}
return result;
}
+
+export function serializeClientSideCallMetadata(metadatas: ClientSideCallMetadata[]): SerializedClientSideCallMetadata {
+ const fileNames = new Map();
+ const stacks: SerializedStack[] = [];
+ for (const m of metadatas) {
+ if (!m.stack || !m.stack.length)
+ continue;
+ const stack: SerializedStackFrame[] = [];
+ for (const frame of m.stack) {
+ let ordinal = fileNames.get(frame.file);
+ if (typeof ordinal !== 'number') {
+ ordinal = fileNames.size;
+ fileNames.set(frame.file, ordinal);
+ }
+ const stackFrame: SerializedStackFrame = [ordinal, frame.line || 0, frame.column || 0, frame.function || ''];
+ stack.push(stackFrame);
+ }
+ stacks.push([m.id, stack]);
+ }
+ return { files: [...fileNames.keys()], stacks };
+}
diff --git a/packages/playwright-core/src/protocol/transport.ts b/packages/playwright-core/src/utils/pipeTransport.ts
similarity index 95%
rename from packages/playwright-core/src/protocol/transport.ts
rename to packages/playwright-core/src/utils/pipeTransport.ts
index e647100c63..64b1d80e8f 100644
--- a/packages/playwright-core/src/protocol/transport.ts
+++ b/packages/playwright-core/src/utils/pipeTransport.ts
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-import { makeWaitForNextTask } from '../utils';
+import { makeWaitForNextTask } from './task';
-export interface WritableStream {
+interface WritableStream {
write(data: Buffer): void;
}
-export interface ReadableStream {
+interface ReadableStream {
on(event: 'data', callback: (b: Buffer) => void): void;
on(event: 'close', callback: () => void): void;
}
-export interface ClosableStream {
+interface ClosableStream {
close(): void;
}
diff --git a/packages/playwright-core/src/utils/time.ts b/packages/playwright-core/src/utils/time.ts
deleted file mode 100644
index d00ee62e82..0000000000
--- a/packages/playwright-core/src/utils/time.ts
+++ /dev/null
@@ -1,32 +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.
- */
-
-// The `process.hrtime()` returns a time from some arbitrary
-// date in the past; on certain systems, this is the time from the system boot.
-// The `monotonicTime()` converts this to milliseconds.
-//
-// For a Linux server with uptime of 36 days, the `monotonicTime()` value
-// will be 36 * 86400 * 1000 = 3_110_400_000, which is larger than
-// the maximum value that `setTimeout` accepts as an argument: 2_147_483_647.
-//
-// To make the `monotonicTime()` a reasonable value, we anchor
-// it to the time of the first import of this utility.
-const initialTime = process.hrtime();
-
-export function monotonicTime(): number {
- const [seconds, nanoseconds] = process.hrtime(initialTime);
- return seconds * 1000 + (nanoseconds / 1000 | 0) / 1000;
-}
diff --git a/packages/playwright-core/src/utils/traceUtils.ts b/packages/playwright-core/src/utils/traceUtils.ts
deleted file mode 100644
index c6ecc7988b..0000000000
--- a/packages/playwright-core/src/utils/traceUtils.ts
+++ /dev/null
@@ -1,39 +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.
- */
-
-import type { SerializedClientSideCallMetadata, SerializedStack, SerializedStackFrame } from './isomorphic/traceUtils';
-import type { ClientSideCallMetadata } from '@protocol/channels';
-
-export function serializeClientSideCallMetadata(metadatas: ClientSideCallMetadata[]): SerializedClientSideCallMetadata {
- const fileNames = new Map();
- const stacks: SerializedStack[] = [];
- for (const m of metadatas) {
- if (!m.stack || !m.stack.length)
- continue;
- const stack: SerializedStackFrame[] = [];
- for (const frame of m.stack) {
- let ordinal = fileNames.get(frame.file);
- if (typeof ordinal !== 'number') {
- ordinal = fileNames.size;
- fileNames.set(frame.file, ordinal);
- }
- const stackFrame: SerializedStackFrame = [ordinal, frame.line || 0, frame.column || 0, frame.function || ''];
- stack.push(stackFrame);
- }
- stacks.push([m.id, stack]);
- }
- return { files: [...fileNames.keys()], stacks };
-}
diff --git a/packages/playwright/src/common/config.ts b/packages/playwright/src/common/config.ts
index b0bc394873..59d1973578 100644
--- a/packages/playwright/src/common/config.ts
+++ b/packages/playwright/src/common/config.ts
@@ -78,7 +78,7 @@ export class FullConfigInternal {
const privateConfiguration = (userConfig as any)['@playwright/test'];
this.plugins = (privateConfiguration?.plugins || []).map((p: any) => ({ factory: p }));
this.singleTSConfigPath = pathResolve(configDir, userConfig.tsconfig);
- this.populateGitInfo = takeFirst(userConfig.populateGitInfo, false);
+ this.populateGitInfo = takeFirst(userConfig.populateGitInfo, defaultPopulateGitInfo);
this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
@@ -301,6 +301,7 @@ function resolveScript(id: string | undefined, rootDir: string): string | undefi
export const defaultGrep = /.*/;
export const defaultReporter = process.env.CI ? 'dot' : 'list';
+const defaultPopulateGitInfo = process.env.GITHUB_ACTIONS === 'true';
const configInternalSymbol = Symbol('configInternalSymbol');
diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts
index 9b0fb456ab..a8d0cc0387 100644
--- a/packages/playwright/src/index.ts
+++ b/packages/playwright/src/index.ts
@@ -726,7 +726,7 @@ class ArtifactsRecorder {
return;
(tracing as any)[this._startedCollectingArtifacts] = true;
if (this._testInfo._tracing.traceOptions() && (tracing as any)[kTracingStarted])
- await tracing.stopChunk({ path: this._testInfo._tracing.generateNextTraceRecordingPath() });
+ await tracing.stopChunk({ path: this._testInfo._tracing.maybeGenerateNextTraceRecordingPath() });
}
}
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 aa575710e5..b80a3f387d 100644
--- a/packages/playwright/src/util.ts
+++ b/packages/playwright/src/util.ts
@@ -19,10 +19,9 @@ import * as path from 'path';
import * as url from 'url';
import util from 'util';
-import { sanitizeForFilePath } from 'playwright-core/lib/utils';
-import { calculateSha1, formatCallLog, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
-import { parseStackTraceLine } from 'playwright-core/lib/utils';
+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';
@@ -57,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))
@@ -226,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/testTracing.ts b/packages/playwright/src/worker/testTracing.ts
index a90c006616..ba215c8792 100644
--- a/packages/playwright/src/worker/testTracing.ts
+++ b/packages/playwright/src/worker/testTracing.ts
@@ -46,6 +46,7 @@ export class TestTracing {
private _artifactsDir: string;
private _tracesDir: string;
private _contextCreatedEvent: trace.ContextCreatedTraceEvent;
+ private _didFinishTestFunctionAndAfterEachHooks = false;
constructor(testInfo: TestInfoImpl, artifactsDir: string) {
this._testInfo = testInfo;
@@ -113,6 +114,10 @@ export class TestTracing {
}
}
+ didFinishTestFunctionAndAfterEachHooks() {
+ this._didFinishTestFunctionAndAfterEachHooks = true;
+ }
+
artifactsDir() {
return this._artifactsDir;
}
@@ -133,7 +138,7 @@ export class TestTracing {
return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`;
}
- generateNextTraceRecordingPath() {
+ private _generateNextTraceRecordingPath() {
const file = path.join(this._artifactsDir, createGuid() + '.zip');
this._temporaryTraceFiles.push(file);
return file;
@@ -143,6 +148,22 @@ export class TestTracing {
return this._options;
}
+ maybeGenerateNextTraceRecordingPath() {
+ // Forget about traces that should be saved on failure, when no failure happened
+ // during the test and beforeEach/afterEach hooks.
+ // This avoids downloading traces over the wire when not really needed.
+ if (this._didFinishTestFunctionAndAfterEachHooks && this._shouldAbandonTrace())
+ return;
+ return this._generateNextTraceRecordingPath();
+ }
+
+ private _shouldAbandonTrace() {
+ if (!this._options)
+ return true;
+ const testFailed = this._testInfo.status !== this._testInfo.expectedStatus;
+ return !testFailed && (this._options.mode === 'retain-on-failure' || this._options.mode === 'retain-on-first-failure');
+ }
+
async stopIfNeeded() {
if (!this._options)
return;
@@ -151,10 +172,7 @@ export class TestTracing {
if (error)
throw error;
- const testFailed = this._testInfo.status !== this._testInfo.expectedStatus;
- const shouldAbandonTrace = !testFailed && (this._options.mode === 'retain-on-failure' || this._options.mode === 'retain-on-first-failure');
-
- if (shouldAbandonTrace) {
+ if (this._shouldAbandonTrace()) {
for (const file of this._temporaryTraceFiles)
await fs.promises.unlink(file).catch(() => {});
return;
@@ -213,7 +231,7 @@ export class TestTracing {
await new Promise(f => {
zipFile.end(undefined, () => {
- zipFile.outputStream.pipe(fs.createWriteStream(this.generateNextTraceRecordingPath())).on('close', f);
+ zipFile.outputStream.pipe(fs.createWriteStream(this._generateNextTraceRecordingPath())).on('close', f);
});
});
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 5188614271..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';
@@ -399,6 +399,8 @@ export class WorkerMain extends ProcessRunner {
firstAfterHooksError = firstAfterHooksError ?? error;
}
+ testInfo._tracing.didFinishTestFunctionAndAfterEachHooks();
+
try {
// Teardown test-scoped fixtures. Attribute to 'test' so that users understand
// they should probably increase the test timeout to fix this issue.
diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts
index ab23b480b2..a31bb858d5 100644
--- a/packages/playwright/types/test.d.ts
+++ b/packages/playwright/types/test.d.ts
@@ -1367,6 +1367,8 @@ interface TestConfig {
*
* This information will appear in the HTML and JSON reports and is available in the Reporter API.
*
+ * On Github Actions, this feature is enabled by default.
+ *
* **Usage**
*
* ```js
diff --git a/packages/web/src/components/prompts.ts b/packages/web/src/components/prompts.ts
index 8ecc78e9c6..d446dfa17d 100644
--- a/packages/web/src/components/prompts.ts
+++ b/packages/web/src/components/prompts.ts
@@ -19,9 +19,18 @@ function stripAnsiEscapes(str: string): string {
return str.replace(ansiRegex, '');
}
+function enumerate(items: string[]) {
+ if (items.length === 0)
+ return '';
+ if (items.length === 1)
+ return items[0];
+ return items.slice(0, -1).join(', ') + ' and ' + items[items.length - 1];
+}
+
export function fixTestPrompt(error: string, diff?: string, pageSnapshot?: string) {
+ const includedData = ['the error', diff && 'a code diff', pageSnapshot && 'a snapshot of the page'].filter((v): v is string => Boolean(v));
const promptParts = [
- `My Playwright test failed. What's going wrong?`,
+ `My Playwright test failed, what's going wrong? I've included ${enumerate(includedData)} below.`,
`Please give me a suggestion how to fix it, and then explain what went wrong. Be very concise and apply Playwright best practices.`,
`Don't include many headings in your output. Make sure what you're saying is correct, and take into account whether there might be a bug in the app.`,
'Here is the error:',
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/debug-controller.spec.ts b/tests/library/debug-controller.spec.ts
index b8163296f7..7481e04246 100644
--- a/tests/library/debug-controller.spec.ts
+++ b/tests/library/debug-controller.spec.ts
@@ -16,7 +16,7 @@
import { expect, playwrightTest as baseTest } from '../config/browserTest';
import { PlaywrightServer } from '../../packages/playwright-core/lib/remote/playwrightServer';
-import { createGuid } from '../../packages/playwright-core/lib/utils/crypto';
+import { createGuid } from '../../packages/playwright-core/lib/server/utils/crypto';
import { Backend } from '../config/debugControllerBackend';
import type { Browser, BrowserContext } from '@playwright/test';
import type * as channels from '@protocol/channels';
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/events/remove-all-listeners-wait.spec.ts b/tests/library/events/remove-all-listeners-wait.spec.ts
index 1f8dcc8dd0..e5b4bf2dc5 100644
--- a/tests/library/events/remove-all-listeners-wait.spec.ts
+++ b/tests/library/events/remove-all-listeners-wait.spec.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { ManualPromise } from '../../../packages/playwright-core/lib/utils/manualPromise';
+import { ManualPromise } from '../../../packages/playwright-core/lib/utils/isomorphic/manualPromise';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
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/library/unit/sequence.spec.ts b/tests/library/unit/sequence.spec.ts
index 624722753e..e0decd4d84 100644
--- a/tests/library/unit/sequence.spec.ts
+++ b/tests/library/unit/sequence.spec.ts
@@ -16,7 +16,7 @@
import { test as it, expect } from '@playwright/test';
-import { findRepeatedSubsequences } from '../../../packages/playwright-core/lib/utils/sequence';
+import { findRepeatedSubsequences } from '../../../packages/playwright-core/lib/utils/isomorphic/sequence';
it('should return an empty array when the input is empty', () => {
const input = [];
diff --git a/tests/page/interception.spec.ts b/tests/page/interception.spec.ts
index eef67e8b37..9447a80fcd 100644
--- a/tests/page/interception.spec.ts
+++ b/tests/page/interception.spec.ts
@@ -126,7 +126,6 @@ it('should intercept worker requests when enabled after worker creation', {
}, async ({ page, server, isAndroid, browserName, browserMajorVersion }) => {
it.skip(isAndroid);
it.skip(browserName === 'chromium' && browserMajorVersion < 130, 'fixed in Chromium 130');
- it.fixme(browserName === 'chromium', 'requires PlzDedicatedWorker to be enabled');
await page.goto(server.EMPTY_PAGE);
server.setRoute('/data_for_worker', (req, res) => res.end('failed to intercept'));
diff --git a/tests/page/page-filechooser.spec.ts b/tests/page/page-filechooser.spec.ts
new file mode 100644
index 0000000000..7fc8cf5500
--- /dev/null
+++ b/tests/page/page-filechooser.spec.ts
@@ -0,0 +1,368 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ * Modifications copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { test, expect } from './pageTest';
+import { attachFrame } from '../config/utils';
+
+import fs from 'fs';
+import formidable from 'formidable';
+
+test('should upload multiple large files', async ({ page, server, isAndroid, isWebView2, mode }, testInfo) => {
+ test.skip(isAndroid);
+ test.skip(isWebView2);
+ test.skip(mode.startsWith('service'));
+ test.slow();
+
+ const filesCount = 10;
+ await page.goto(server.PREFIX + '/input/fileupload-multi.html');
+ const uploadFile = testInfo.outputPath('50MB_1.zip');
+ const str = 'A'.repeat(1024);
+ const stream = fs.createWriteStream(uploadFile);
+ // 49 is close to the actual limit
+ for (let i = 0; i < 49 * 1024; i++) {
+ await new Promise((fulfill, reject) => {
+ stream.write(str, err => {
+ if (err)
+ reject(err);
+ else
+ fulfill();
+ });
+ });
+ }
+ await new Promise(f => stream.end(f));
+ const input = page.locator('input[type="file"]');
+ const uploadFiles = [uploadFile];
+ for (let i = 2; i <= filesCount; i++) {
+ const dstFile = testInfo.outputPath(`50MB_${i}.zip`);
+ fs.copyFileSync(uploadFile, dstFile);
+ uploadFiles.push(dstFile);
+ }
+ const fileChooserPromise = page.waitForEvent('filechooser');
+ await input.click();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles(uploadFiles);
+ const filesLen = await page.evaluate('document.getElementsByTagName("input")[0].files.length');
+ expect(fileChooser.isMultiple()).toBe(true);
+ expect(filesLen).toEqual(filesCount);
+ await Promise.all(uploadFiles.map(path => fs.promises.unlink(path)));
+});
+
+test('should emit event once', async ({ page, server }) => {
+ await page.setContent(``);
+ const [chooser] = await Promise.all([
+ new Promise(f => page.once('filechooser', f)),
+ page.click('input'),
+ ]);
+ expect(chooser).toBeTruthy();
+});
+
+test('should emit event via prepend', async ({ page, server }) => {
+ await page.setContent(``);
+ const [chooser] = await Promise.all([
+ new Promise(f => page.prependListener('filechooser', f)),
+ page.click('input'),
+ ]);
+ expect(chooser).toBeTruthy();
+});
+
+test('should emit event for iframe', async ({ page, server }) => {
+ const frame = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await frame.setContent(``);
+ const [chooser] = await Promise.all([
+ new Promise(f => page.once('filechooser', f)),
+ frame.click('input'),
+ ]);
+ expect(chooser).toBeTruthy();
+});
+
+test('should emit event on/off', async ({ page, server }) => {
+ await page.setContent(``);
+ const [chooser] = await Promise.all([
+ new Promise(f => {
+ const listener = chooser => {
+ page.off('filechooser', listener);
+ f(chooser);
+ };
+ page.on('filechooser', listener);
+ }),
+ page.click('input'),
+ ]);
+ expect(chooser).toBeTruthy();
+});
+
+test('should emit event addListener/removeListener', async ({ page, server }) => {
+ await page.setContent(``);
+ const [chooser] = await Promise.all([
+ new Promise(f => {
+ const listener = chooser => {
+ page.removeListener('filechooser', listener);
+ f(chooser);
+ };
+ page.addListener('filechooser', listener);
+ }),
+ page.click('input'),
+ ]);
+ expect(chooser).toBeTruthy();
+});
+
+test('should work when file input is attached to DOM', async ({ page, server }) => {
+ await page.setContent(``);
+ const [chooser] = await Promise.all([
+ page.waitForEvent('filechooser'),
+ page.click('input'),
+ ]);
+ expect(chooser).toBeTruthy();
+});
+
+test('should work when file input is not attached to DOM', async ({ page, asset }) => {
+ const [, content] = await Promise.all([
+ page.waitForEvent('filechooser').then(chooser => chooser.setFiles(asset('file-to-upload.txt'))),
+ page.evaluate(async () => {
+ const el = document.createElement('input');
+ el.type = 'file';
+ el.click();
+ await new Promise(x => el.oninput = x);
+ const reader = new FileReader();
+ const promise = new Promise(fulfill => reader.onload = fulfill);
+ reader.readAsText(el.files[0]);
+ return promise.then(() => reader.result);
+ }),
+ ]);
+ expect(content).toBe('contents of the file');
+});
+
+test('should not throw when filechooser belongs to iframe', async ({ page, server, browserName }) => {
+ await page.goto(server.PREFIX + '/frames/one-frame.html');
+ const frame = page.mainFrame().childFrames()[0];
+ await frame.setContent(`
+