chore: introduce platform for client (1) (#34683)
This commit is contained in:
parent
0672f1ce67
commit
5d500dde22
|
@ -7,7 +7,11 @@
|
|||
[inProcessFactory.ts]
|
||||
**
|
||||
|
||||
[inprocess.ts]
|
||||
common/
|
||||
|
||||
[outofprocess.ts]
|
||||
client/
|
||||
protocol/
|
||||
utils/
|
||||
common/
|
|
@ -4,6 +4,7 @@
|
|||
../common
|
||||
../debug/injected
|
||||
../generated/
|
||||
../server/
|
||||
../server/injected/
|
||||
../server/trace
|
||||
../utils
|
||||
|
|
|
@ -22,7 +22,7 @@ import * as playwright from '../..';
|
|||
import { PipeTransport } from '../protocol/transport';
|
||||
import { PlaywrightServer } from '../remote/playwrightServer';
|
||||
import { DispatcherConnection, PlaywrightDispatcher, RootDispatcher, createPlaywright } from '../server';
|
||||
import { gracefullyProcessExitDoNotHang } from '../utils/processLauncher';
|
||||
import { gracefullyProcessExitDoNotHang } from '../server/processLauncher';
|
||||
|
||||
import type { BrowserType } from '../client/browserType';
|
||||
import type { LaunchServerOptions } from '../client/types';
|
||||
|
|
|
@ -21,11 +21,11 @@ import * as os from 'os';
|
|||
import * as path from 'path';
|
||||
|
||||
import * as playwright from '../..';
|
||||
import { registry, writeDockerVersion } from '../server';
|
||||
import { launchBrowserServer, printApiJson, runDriver, runServer } from './driver';
|
||||
import { isTargetClosedError } from '../client/errors';
|
||||
import { gracefullyProcessExitDoNotHang, registry, writeDockerVersion } from '../server';
|
||||
import { runTraceInBrowser, runTraceViewerApp } from '../server/trace/viewer/traceViewer';
|
||||
import { assert, getPackageManagerExecCommand, gracefullyProcessExitDoNotHang, isLikelyNpxGlobal, wrapInASCIIBox } from '../utils';
|
||||
import { assert, getPackageManagerExecCommand, isLikelyNpxGlobal, wrapInASCIIBox } from '../utils';
|
||||
import { dotenv, program } from '../utilsBundle';
|
||||
|
||||
import type { Browser } from '../client/browser';
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { getPackageManager, gracefullyProcessExitDoNotHang } from '../utils';
|
||||
import { gracefullyProcessExitDoNotHang } from '../server';
|
||||
import { getPackageManager } from '../utils';
|
||||
import { program } from './program';
|
||||
export { program } from './program';
|
||||
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { isRegExp, isString, monotonicTime } from '../utils';
|
||||
import { BrowserContext, prepareBrowserContextParams } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Connection } from './connection';
|
||||
|
@ -25,12 +23,15 @@ 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 type { Page } from './page';
|
||||
import type * as types from './types';
|
||||
import type * as api from '../../types/types';
|
||||
import type { AndroidServerLauncherImpl } from '../androidServerImpl';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
type Direction = 'down' | 'up' | 'left' | 'right';
|
||||
|
@ -73,7 +74,7 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
|
|||
const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout };
|
||||
const { pipe } = await localUtils._channel.connect(connectParams);
|
||||
const closePipe = () => pipe.close().catch(() => {});
|
||||
const connection = new Connection(localUtils, this._instrumentation);
|
||||
const connection = new Connection(localUtils, this._platform, this._instrumentation);
|
||||
connection.markAsRemote();
|
||||
connection.on('close', closePipe);
|
||||
|
||||
|
@ -232,7 +233,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
|
|||
async screenshot(options: { path?: string } = {}): Promise<Buffer> {
|
||||
const { binary } = await this._channel.screenshot();
|
||||
if (options.path)
|
||||
await fs.promises.writeFile(options.path, binary);
|
||||
await this._platform.fs().promises.writeFile(options.path, binary);
|
||||
return binary;
|
||||
}
|
||||
|
||||
|
@ -267,15 +268,15 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
|
|||
}
|
||||
|
||||
async installApk(file: string | Buffer, options?: { args: string[] }): Promise<void> {
|
||||
await this._channel.installApk({ file: await loadFile(file), args: options && options.args });
|
||||
await this._channel.installApk({ file: await loadFile(this._platform, file), args: options && options.args });
|
||||
}
|
||||
|
||||
async push(file: string | Buffer, path: string, options?: { mode: number }): Promise<void> {
|
||||
await this._channel.push({ file: await loadFile(file), path, mode: options ? options.mode : undefined });
|
||||
await this._channel.push({ file: await loadFile(this._platform, file), path, mode: options ? options.mode : undefined });
|
||||
}
|
||||
|
||||
async launchBrowser(options: types.BrowserContextOptions & { pkg?: string } = {}): Promise<BrowserContext> {
|
||||
const contextOptions = await prepareBrowserContextParams(options);
|
||||
const contextOptions = await prepareBrowserContextParams(this._platform, options);
|
||||
const result = await this._channel.launchBrowser(contextOptions);
|
||||
const context = BrowserContext.from(result.context) as BrowserContext;
|
||||
context._setOptions(contextOptions, {});
|
||||
|
@ -321,9 +322,9 @@ export class AndroidSocket extends ChannelOwner<channels.AndroidSocketChannel> i
|
|||
}
|
||||
}
|
||||
|
||||
async function loadFile(file: string | Buffer): Promise<Buffer> {
|
||||
async function loadFile(platform: Platform, file: string | Buffer): Promise<Buffer> {
|
||||
if (isString(file))
|
||||
return await fs.promises.readFile(file);
|
||||
return await platform.fs().promises.readFile(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Stream } from './stream';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
|
@ -42,9 +40,9 @@ export class Artifact extends ChannelOwner<channels.ArtifactChannel> {
|
|||
|
||||
const result = await this._channel.saveAsStream();
|
||||
const stream = Stream.from(result.stream);
|
||||
await mkdirIfNeeded(path);
|
||||
await mkdirIfNeeded(this._platform, path);
|
||||
await new Promise((resolve, reject) => {
|
||||
stream.stream().pipe(fs.createWriteStream(path))
|
||||
stream.stream().pipe(this._platform.fs().createWriteStream(path))
|
||||
.on('finish' as any, resolve)
|
||||
.on('error' as any, reject);
|
||||
});
|
||||
|
|
|
@ -14,15 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { Artifact } from './artifact';
|
||||
import { BrowserContext, prepareBrowserContextParams } from './browserContext';
|
||||
import { CDPSession } from './cdpSession';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { isTargetClosedError } from './errors';
|
||||
import { Events } from './events';
|
||||
import { mkdirIfNeeded } from '../utils';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
|
||||
import type { BrowserType } from './browserType';
|
||||
import type { Page } from './page';
|
||||
|
@ -83,7 +81,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
|||
|
||||
async _innerNewContext(options: BrowserContextOptions = {}, forReuse: boolean): Promise<BrowserContext> {
|
||||
options = { ...this._browserType._playwright._defaultContextOptions, ...options };
|
||||
const contextOptions = await prepareBrowserContextParams(options);
|
||||
const contextOptions = await prepareBrowserContextParams(this._platform, options);
|
||||
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
|
||||
const context = BrowserContext.from(response.context);
|
||||
await this._browserType._didCreateContext(context, contextOptions, this._options, options.logger || this._logger);
|
||||
|
@ -126,8 +124,8 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
|||
const buffer = await artifact.readIntoBuffer();
|
||||
await artifact.delete();
|
||||
if (this._path) {
|
||||
await mkdirIfNeeded(this._path);
|
||||
await fs.promises.writeFile(this._path, buffer);
|
||||
await mkdirIfNeeded(this._platform, this._path);
|
||||
await this._platform.fs().promises.writeFile(this._path, buffer);
|
||||
this._path = undefined;
|
||||
}
|
||||
return buffer;
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { Artifact } from './artifact';
|
||||
import { Browser } from './browser';
|
||||
import { CDPSession } from './cdpSession';
|
||||
|
@ -38,14 +35,18 @@ import { Waiter } from './waiter';
|
|||
import { WebError } from './webError';
|
||||
import { Worker } from './worker';
|
||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||
import { headersObjectToArray, isRegExp, isString, mkdirIfNeeded, urlMatchesEqual } from '../utils';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import { headersObjectToArray } from '../utils/headers';
|
||||
import { urlMatchesEqual } from '../utils/isomorphic/urlMatch';
|
||||
import { isRegExp, isString } from '../utils/rtti';
|
||||
import { rewriteErrorMessage } from '../utils/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';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
||||
|
@ -107,7 +108,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
this.emit(Events.BrowserContext.ServiceWorker, serviceWorker);
|
||||
});
|
||||
this._channel.on('console', event => {
|
||||
const consoleMessage = new ConsoleMessage(event);
|
||||
const consoleMessage = new ConsoleMessage(this._platform, event);
|
||||
this.emit(Events.BrowserContext.Console, consoleMessage);
|
||||
const page = consoleMessage.page();
|
||||
if (page)
|
||||
|
@ -321,7 +322,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
}
|
||||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void> {
|
||||
const source = await evaluationScript(script, arg);
|
||||
const source = await evaluationScript(this._platform, script, arg);
|
||||
await this._channel.addInitScript({ source });
|
||||
}
|
||||
|
||||
|
@ -431,8 +432,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
async storageState(options: { path?: string, indexedDB?: boolean } = {}): Promise<StorageState> {
|
||||
const state = await this._channel.storageState({ indexedDB: options.indexedDB });
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||
await mkdirIfNeeded(this._platform, options.path);
|
||||
await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
@ -500,11 +501,11 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
}
|
||||
}
|
||||
|
||||
async function prepareStorageState(options: BrowserContextOptions): Promise<channels.BrowserNewContextParams['storageState']> {
|
||||
async function prepareStorageState(platform: Platform, options: BrowserContextOptions): Promise<channels.BrowserNewContextParams['storageState']> {
|
||||
if (typeof options.storageState !== 'string')
|
||||
return options.storageState;
|
||||
try {
|
||||
return JSON.parse(await fs.promises.readFile(options.storageState, 'utf8'));
|
||||
return JSON.parse(await platform.fs().promises.readFile(options.storageState, 'utf8'));
|
||||
} catch (e) {
|
||||
rewriteErrorMessage(e, `Error reading storage state from ${options.storageState}:\n` + e.message);
|
||||
throw e;
|
||||
|
@ -524,7 +525,7 @@ function prepareRecordHarOptions(options: BrowserContextOptions['recordHar']): c
|
|||
};
|
||||
}
|
||||
|
||||
export async function prepareBrowserContextParams(options: BrowserContextOptions): Promise<channels.BrowserNewContextParams> {
|
||||
export async function prepareBrowserContextParams(platform: Platform, options: BrowserContextOptions): Promise<channels.BrowserNewContextParams> {
|
||||
if (options.videoSize && !options.videosPath)
|
||||
throw new Error(`"videoSize" option requires "videosPath" to be specified`);
|
||||
if (options.extraHTTPHeaders)
|
||||
|
@ -534,7 +535,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
|
|||
viewport: options.viewport === null ? undefined : options.viewport,
|
||||
noDefaultViewport: options.viewport === null,
|
||||
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
|
||||
storageState: await prepareStorageState(options),
|
||||
storageState: await prepareStorageState(platform, options),
|
||||
serviceWorkers: options.serviceWorkers,
|
||||
recordHar: prepareRecordHarOptions(options.recordHar),
|
||||
colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme,
|
||||
|
@ -542,7 +543,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
|
|||
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
|
||||
contrast: options.contrast === null ? 'no-override' : options.contrast,
|
||||
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
||||
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
|
||||
clientCertificates: await toClientCertificatesProtocol(platform, options.clientCertificates),
|
||||
};
|
||||
if (!contextParams.recordVideo && options.videosPath) {
|
||||
contextParams.recordVideo = {
|
||||
|
@ -551,7 +552,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
|
|||
};
|
||||
}
|
||||
if (contextParams.recordVideo && contextParams.recordVideo.dir)
|
||||
contextParams.recordVideo.dir = path.resolve(process.cwd(), contextParams.recordVideo.dir);
|
||||
contextParams.recordVideo.dir = platform.path().resolve(process.cwd(), contextParams.recordVideo.dir);
|
||||
return contextParams;
|
||||
}
|
||||
|
||||
|
@ -563,7 +564,7 @@ function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
|
|||
return 'deny';
|
||||
}
|
||||
|
||||
export async function toClientCertificatesProtocol(certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
|
||||
export async function toClientCertificatesProtocol(platform: Platform, certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
|
||||
if (!certs)
|
||||
return undefined;
|
||||
|
||||
|
@ -571,7 +572,7 @@ export async function toClientCertificatesProtocol(certs?: BrowserContextOptions
|
|||
if (value)
|
||||
return value;
|
||||
if (path)
|
||||
return await fs.promises.readFile(path);
|
||||
return await platform.fs().promises.readFile(path);
|
||||
};
|
||||
|
||||
return await Promise.all(certs.map(async cert => ({
|
||||
|
|
|
@ -20,7 +20,9 @@ import { ChannelOwner } from './channelOwner';
|
|||
import { envObjectToArray } from './clientHelper';
|
||||
import { Connection } from './connection';
|
||||
import { Events } from './events';
|
||||
import { assert, headersObjectToArray, monotonicTime } from '../utils';
|
||||
import { assert } from '../utils/debug';
|
||||
import { headersObjectToArray } from '../utils/headers';
|
||||
import { monotonicTime } from '../utils/time';
|
||||
import { raceAgainstDeadline } from '../utils/timeoutRunner';
|
||||
|
||||
import type { Playwright } from './playwright';
|
||||
|
@ -90,7 +92,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
|
||||
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
||||
options = { ...this._playwright._defaultLaunchOptions, ...this._playwright._defaultContextOptions, ...options };
|
||||
const contextParams = await prepareBrowserContextParams(options);
|
||||
const contextParams = await prepareBrowserContextParams(this._platform, options);
|
||||
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
||||
...contextParams,
|
||||
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
||||
|
@ -133,7 +135,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding;
|
||||
const { pipe, headers: connectHeaders } = await localUtils._channel.connect(connectParams);
|
||||
const closePipe = () => pipe.close().catch(() => {});
|
||||
const connection = new Connection(localUtils, this._instrumentation);
|
||||
const connection = new Connection(localUtils, this._platform, this._instrumentation);
|
||||
connection.markAsRemote();
|
||||
connection.on('close', closePipe);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { EventEmitter } from './eventEmitter';
|
||||
import { ValidationError, maybeFindValidator } from '../protocol/validator';
|
||||
import { isUnderTest } from '../utils';
|
||||
import { isUnderTest } from '../utils/debug';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { captureLibraryStackTrace, stringifyStackFrames } from '../utils/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
|
@ -24,6 +24,7 @@ import { zones } from '../utils/zones';
|
|||
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||
import type { Connection } from './connection';
|
||||
import type { Logger } from './types';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type { ValidatorContext } from '../protocol/validator';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
|
@ -39,6 +40,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
|||
readonly _channel: T;
|
||||
readonly _initializer: channels.InitializerTraits<T>;
|
||||
_logger: Logger | undefined;
|
||||
readonly _platform: Platform;
|
||||
readonly _instrumentation: ClientInstrumentation;
|
||||
private _eventToSubscriptionMapping: Map<string, string> = new Map();
|
||||
private _isInternalType = false;
|
||||
|
@ -52,6 +54,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
|||
this._guid = guid;
|
||||
this._parent = parent instanceof ChannelOwner ? parent : undefined;
|
||||
this._instrumentation = this._connection._instrumentation;
|
||||
this._platform = this._connection.platform;
|
||||
|
||||
this._connection._objects.set(guid, this);
|
||||
if (this._parent) {
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { isString } from '../utils';
|
||||
import { isString } from '../utils/rtti';
|
||||
|
||||
import type * as types from './types';
|
||||
import type { Platform } from '../common/platform';
|
||||
|
||||
export function envObjectToArray(env: types.Env): { name: string, value: string }[] {
|
||||
const result: { name: string, value: string }[] = [];
|
||||
|
@ -30,7 +29,7 @@ export function envObjectToArray(env: types.Env): { name: string, value: string
|
|||
return result;
|
||||
}
|
||||
|
||||
export async function evaluationScript(fun: Function | string | { path?: string, content?: string }, arg?: any, addSourceUrl: boolean = true): Promise<string> {
|
||||
export async function evaluationScript(platform: Platform, fun: Function | string | { path?: string, content?: string }, arg?: any, addSourceUrl: boolean = true): Promise<string> {
|
||||
if (typeof fun === 'function') {
|
||||
const source = fun.toString();
|
||||
const argString = Object.is(arg, undefined) ? 'undefined' : JSON.stringify(arg);
|
||||
|
@ -43,7 +42,7 @@ export async function evaluationScript(fun: Function | string | { path?: string,
|
|||
if (fun.content !== undefined)
|
||||
return fun.content;
|
||||
if (fun.path !== undefined) {
|
||||
let source = await fs.promises.readFile(fun.path, 'utf8');
|
||||
let source = await platform.fs().promises.readFile(fun.path, 'utf8');
|
||||
if (addSourceUrl)
|
||||
source = addSourceUrlToScript(source, fun.path);
|
||||
return source;
|
||||
|
|
|
@ -42,10 +42,12 @@ import { Tracing } from './tracing';
|
|||
import { Worker } from './worker';
|
||||
import { WritableStream } from './writableStream';
|
||||
import { ValidationError, findValidator } from '../protocol/validator';
|
||||
import { formatCallLog, rewriteErrorMessage, zones } from '../utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { formatCallLog, rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
|
||||
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type { ValidatorContext } from '../protocol/validator';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
|
@ -78,11 +80,13 @@ export class Connection extends EventEmitter {
|
|||
toImpl: ((client: ChannelOwner) => any) | undefined;
|
||||
private _tracingCount = 0;
|
||||
readonly _instrumentation: ClientInstrumentation;
|
||||
readonly platform: Platform;
|
||||
|
||||
constructor(localUtils: LocalUtils | undefined, instrumentation: ClientInstrumentation | undefined) {
|
||||
constructor(localUtils: LocalUtils | undefined, platform: Platform, instrumentation: ClientInstrumentation | undefined) {
|
||||
super();
|
||||
this._instrumentation = instrumentation || createInstrumentation();
|
||||
this._localUtils = localUtils;
|
||||
this.platform = platform;
|
||||
this._rootObject = new Root(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,12 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as util from 'util';
|
||||
|
||||
import { JSHandle } from './jsHandle';
|
||||
import { Page } from './page';
|
||||
|
||||
import type * as api from '../../types/types';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
type ConsoleMessageLocation = channels.BrowserContextConsoleEvent['location'];
|
||||
|
@ -29,9 +28,11 @@ export class ConsoleMessage implements api.ConsoleMessage {
|
|||
private _page: Page | null;
|
||||
private _event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent;
|
||||
|
||||
constructor(event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent) {
|
||||
constructor(platform: Platform, event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent) {
|
||||
this._page = ('page' in event && event.page) ? Page.from(event.page) : null;
|
||||
this._event = event;
|
||||
if (platform.inspectCustom)
|
||||
(this as any)[platform.inspectCustom] = () => this._inspect();
|
||||
}
|
||||
|
||||
page() {
|
||||
|
@ -54,7 +55,7 @@ export class ConsoleMessage implements api.ConsoleMessage {
|
|||
return this._event.location;
|
||||
}
|
||||
|
||||
[util.inspect.custom]() {
|
||||
private _inspect() {
|
||||
return this.text();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
|
|||
|
||||
async launch(options: ElectronOptions = {}): Promise<ElectronApplication> {
|
||||
const params: channels.ElectronLaunchParams = {
|
||||
...await prepareBrowserContextParams(options),
|
||||
...await prepareBrowserContextParams(this._platform, options),
|
||||
env: envObjectToArray(options.env ? options.env : process.env),
|
||||
tracesDir: options.tracesDir,
|
||||
};
|
||||
|
@ -81,7 +81,7 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
|
|||
this._channel.on('close', () => {
|
||||
this.emit(Events.ElectronApplication.Close);
|
||||
});
|
||||
this._channel.on('console', event => this.emit(Events.ElectronApplication.Console, new ConsoleMessage(event)));
|
||||
this._channel.on('console', event => this.emit(Events.ElectronApplication.Console, new ConsoleMessage(this._platform, event)));
|
||||
this._setEventToSubscriptionMapping(new Map<string, channels.ElectronApplicationUpdateSubscriptionParams['event']>([
|
||||
[Events.ElectronApplication.Console, 'console'],
|
||||
]));
|
||||
|
|
|
@ -14,15 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { pipeline } from 'stream';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { Frame } from './frame';
|
||||
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||
import { assert, isString } from '../utils';
|
||||
import { assert } from '../utils/debug';
|
||||
import { fileUploadSizeLimit, mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import { isString } from '../utils/rtti';
|
||||
import { mime } from '../utilsBundle';
|
||||
import { WritableStream } from './writableStream';
|
||||
|
||||
|
@ -32,6 +31,7 @@ import type { Locator } from './locator';
|
|||
import type { FilePayload, Rect, SelectOption, SelectOptionOptions } from './types';
|
||||
import type * as structs from '../../types/structs';
|
||||
import type * as api from '../../types/types';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
const pipelineAsync = promisify(pipeline);
|
||||
|
@ -156,7 +156,7 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
|
|||
const frame = await this.ownerFrame();
|
||||
if (!frame)
|
||||
throw new Error('Cannot set input files to detached element');
|
||||
const converted = await convertInputFiles(files, frame.page().context());
|
||||
const converted = await convertInputFiles(this._platform, files, frame.page().context());
|
||||
await this._elementChannel.setInputFiles({ ...converted, ...options });
|
||||
}
|
||||
|
||||
|
@ -204,8 +204,8 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
|
|||
}
|
||||
const result = await this._elementChannel.screenshot(copy);
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fs.promises.writeFile(options.path, result.binary);
|
||||
await mkdirIfNeeded(this._platform, options.path);
|
||||
await this._platform.fs().promises.writeFile(options.path, result.binary);
|
||||
}
|
||||
return result.binary;
|
||||
}
|
||||
|
@ -263,18 +263,18 @@ function filePayloadExceedsSizeLimit(payloads: FilePayload[]) {
|
|||
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= fileUploadSizeLimit;
|
||||
}
|
||||
|
||||
async function resolvePathsAndDirectoryForInputFiles(items: string[]): Promise<[string[] | undefined, string | undefined]> {
|
||||
async function resolvePathsAndDirectoryForInputFiles(platform: Platform, items: string[]): Promise<[string[] | undefined, string | undefined]> {
|
||||
let localPaths: string[] | undefined;
|
||||
let localDirectory: string | undefined;
|
||||
for (const item of items) {
|
||||
const stat = await fs.promises.stat(item as string);
|
||||
const stat = await platform.fs().promises.stat(item as string);
|
||||
if (stat.isDirectory()) {
|
||||
if (localDirectory)
|
||||
throw new Error('Multiple directories are not supported');
|
||||
localDirectory = path.resolve(item as string);
|
||||
localDirectory = platform.path().resolve(item as string);
|
||||
} else {
|
||||
localPaths ??= [];
|
||||
localPaths.push(path.resolve(item as string));
|
||||
localPaths.push(platform.path().resolve(item as string));
|
||||
}
|
||||
}
|
||||
if (localPaths?.length && localDirectory)
|
||||
|
@ -282,30 +282,30 @@ async function resolvePathsAndDirectoryForInputFiles(items: string[]): Promise<[
|
|||
return [localPaths, localDirectory];
|
||||
}
|
||||
|
||||
export async function convertInputFiles(files: string | FilePayload | string[] | FilePayload[], context: BrowserContext): Promise<SetInputFilesFiles> {
|
||||
export async function convertInputFiles(platform: Platform, files: string | FilePayload | string[] | FilePayload[], context: BrowserContext): Promise<SetInputFilesFiles> {
|
||||
const items: (string | FilePayload)[] = Array.isArray(files) ? files.slice() : [files];
|
||||
|
||||
if (items.some(item => typeof item === 'string')) {
|
||||
if (!items.every(item => typeof item === 'string'))
|
||||
throw new Error('File paths cannot be mixed with buffers');
|
||||
|
||||
const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(items);
|
||||
const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(platform, items);
|
||||
|
||||
if (context._connection.isRemote()) {
|
||||
const files = localDirectory ? (await fs.promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter(f => f.isFile()).map(f => path.join(f.path, f.name)) : localPaths!;
|
||||
const files = localDirectory ? (await platform.fs().promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter(f => f.isFile()).map(f => platform.path().join(f.path, f.name)) : localPaths!;
|
||||
const { writableStreams, rootDir } = await context._wrapApiCall(async () => context._channel.createTempFiles({
|
||||
rootDirName: localDirectory ? path.basename(localDirectory) : undefined,
|
||||
rootDirName: localDirectory ? platform.path().basename(localDirectory) : undefined,
|
||||
items: await Promise.all(files.map(async file => {
|
||||
const lastModifiedMs = (await fs.promises.stat(file)).mtimeMs;
|
||||
const lastModifiedMs = (await platform.fs().promises.stat(file)).mtimeMs;
|
||||
return {
|
||||
name: localDirectory ? path.relative(localDirectory, file) : path.basename(file),
|
||||
name: localDirectory ? platform.path().relative(localDirectory, file) : platform.path().basename(file),
|
||||
lastModifiedMs
|
||||
};
|
||||
})),
|
||||
}), true);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const writable = WritableStream.from(writableStreams[i]);
|
||||
await pipelineAsync(fs.createReadStream(files[i]), writable.stream());
|
||||
await pipelineAsync(platform.fs().createReadStream(files[i]), writable.stream());
|
||||
}
|
||||
return {
|
||||
directoryStream: rootDir,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import { parseSerializedValue, serializeValue } from '../protocol/serializers';
|
||||
import { isError } from '../utils';
|
||||
import { isError } from '../utils/rtti';
|
||||
|
||||
import type { SerializedError } from '@protocol/channels';
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
import { EventEmitter as OriginalEventEmitter } from 'events';
|
||||
|
||||
import { isUnderTest } from '../utils';
|
||||
import { isUnderTest } from '../utils/debug';
|
||||
|
||||
import type { EventEmitter as EventEmitterType } from 'events';
|
||||
|
||||
|
|
|
@ -14,24 +14,24 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
|
||||
import { assert, headersObjectToArray, isString } from '../utils';
|
||||
import { toClientCertificatesProtocol } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { TargetClosedError, isTargetClosedError } from './errors';
|
||||
import { RawHeaders } from './network';
|
||||
import { Tracing } from './tracing';
|
||||
import { assert } from '../utils/debug';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import { headersObjectToArray } from '../utils/headers';
|
||||
import { isString } from '../utils/rtti';
|
||||
|
||||
import type { Playwright } from './playwright';
|
||||
import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types';
|
||||
import type { Serializable } from '../../types/structs';
|
||||
import type * as api from '../../types/types';
|
||||
import type { Platform } from '../common/platform';
|
||||
import type { HeadersArray, NameValue } from '../common/types';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import type * as fs from 'fs';
|
||||
|
||||
export type FetchOptions = {
|
||||
params?: { [key: string]: string | number | boolean; } | URLSearchParams | string,
|
||||
|
@ -70,14 +70,14 @@ export class APIRequest implements api.APIRequest {
|
|||
...options,
|
||||
};
|
||||
const storageState = typeof options.storageState === 'string' ?
|
||||
JSON.parse(await fs.promises.readFile(options.storageState, 'utf8')) :
|
||||
JSON.parse(await this._playwright._platform.fs().promises.readFile(options.storageState, 'utf8')) :
|
||||
options.storageState;
|
||||
const context = APIRequestContext.from((await this._playwright._channel.newRequest({
|
||||
...options,
|
||||
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
|
||||
storageState,
|
||||
tracesDir: this._playwright._defaultLaunchOptions?.tracesDir, // We do not expose tracesDir in the API, so do not allow options to accidentally override it.
|
||||
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
|
||||
clientCertificates: await toClientCertificatesProtocol(this._playwright._platform, options.clientCertificates),
|
||||
})).request);
|
||||
this._contexts.add(context);
|
||||
context._request = this;
|
||||
|
@ -232,7 +232,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
} else {
|
||||
// Convert file-like values to ServerFilePayload structs.
|
||||
for (const [name, value] of Object.entries(options.multipart))
|
||||
multipartData.push(await toFormField(name, value));
|
||||
multipartData.push(await toFormField(this._platform, name, value));
|
||||
}
|
||||
}
|
||||
if (postDataBuffer === undefined && jsonData === undefined && formData === undefined && multipartData === undefined)
|
||||
|
@ -264,23 +264,24 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
async storageState(options: { path?: string, indexedDB?: boolean } = {}): Promise<StorageState> {
|
||||
const state = await this._channel.storageState({ indexedDB: options.indexedDB });
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||
await mkdirIfNeeded(this._platform, options.path);
|
||||
await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
async function toFormField(name: string, value: string|number|boolean|fs.ReadStream|FilePayload): Promise<channels.FormField> {
|
||||
async function toFormField(platform: Platform, name: string, value: string | number | boolean | fs.ReadStream | FilePayload): Promise<channels.FormField> {
|
||||
const typeOfValue = typeof value;
|
||||
if (isFilePayload(value)) {
|
||||
const payload = value as FilePayload;
|
||||
if (!Buffer.isBuffer(payload.buffer))
|
||||
throw new Error(`Unexpected buffer type of 'data.${name}'`);
|
||||
return { name, file: filePayloadToJson(payload) };
|
||||
} else if (value instanceof fs.ReadStream) {
|
||||
return { name, file: await readStreamToJson(value as fs.ReadStream) };
|
||||
} else {
|
||||
} else if (typeOfValue === 'string' || typeOfValue === 'number' || typeOfValue === 'boolean') {
|
||||
return { name, value: String(value) };
|
||||
} else {
|
||||
return { name, file: await readStreamToJson(platform, value as fs.ReadStream) };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,6 +308,9 @@ export class APIResponse implements api.APIResponse {
|
|||
this._request = context;
|
||||
this._initializer = initializer;
|
||||
this._headers = new RawHeaders(this._initializer.headers);
|
||||
|
||||
if (context._platform.inspectCustom)
|
||||
(this as any)[context._platform.inspectCustom] = () => this._inspect();
|
||||
}
|
||||
|
||||
ok(): boolean {
|
||||
|
@ -364,7 +368,7 @@ export class APIResponse implements api.APIResponse {
|
|||
await this._request._channel.disposeAPIResponse({ fetchUid: this._fetchUid() });
|
||||
}
|
||||
|
||||
[util.inspect.custom]() {
|
||||
private _inspect() {
|
||||
const headers = this.headersArray().map(({ name, value }) => ` ${name}: ${value}`);
|
||||
return `APIResponse: ${this.status()} ${this.statusText()}\n${headers.join('\n')}`;
|
||||
}
|
||||
|
@ -389,7 +393,7 @@ function filePayloadToJson(payload: FilePayload): ServerFilePayload {
|
|||
};
|
||||
}
|
||||
|
||||
async function readStreamToJson(stream: fs.ReadStream): Promise<ServerFilePayload> {
|
||||
async function readStreamToJson(platform: Platform, stream: fs.ReadStream): Promise<ServerFilePayload> {
|
||||
const buffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
const chunks: Buffer[] = [];
|
||||
stream.on('data', chunk => chunks.push(chunk as Buffer));
|
||||
|
@ -398,7 +402,7 @@ async function readStreamToJson(stream: fs.ReadStream): Promise<ServerFilePayloa
|
|||
});
|
||||
const streamPath: string = Buffer.isBuffer(stream.path) ? stream.path.toString('utf8') : stream.path;
|
||||
return {
|
||||
name: path.basename(streamPath),
|
||||
name: platform.path().basename(streamPath),
|
||||
buffer,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,28 +16,27 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { FrameLocator, Locator, testIdAttributeName } from './locator';
|
||||
import { assert } from '../utils';
|
||||
import { urlMatches } from '../utils';
|
||||
import { addSourceUrlToScript } from './clientHelper';
|
||||
import { ElementHandle, convertInputFiles, convertSelectOptionValues } from './elementHandle';
|
||||
import { Events } from './events';
|
||||
import { JSHandle, assertMaxArguments, parseResult, serializeArgument } from './jsHandle';
|
||||
import { FrameLocator, Locator, testIdAttributeName } from './locator';
|
||||
import * as network from './network';
|
||||
import { kLifecycleEvents } from './types';
|
||||
import { Waiter } from './waiter';
|
||||
import { assert } from '../utils/debug';
|
||||
import { getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector } from '../utils/isomorphic/locatorUtils';
|
||||
import { urlMatches } from '../utils/isomorphic/urlMatch';
|
||||
|
||||
import type { LocatorOptions } from './locator';
|
||||
import type { Page } from './page';
|
||||
import type { FilePayload, LifecycleEvent, SelectOption, SelectOptionOptions, StrictOptions, WaitForFunctionOptions } from './types';
|
||||
import type * as structs from '../../types/structs';
|
||||
import type * as api from '../../types/types';
|
||||
import type { URLMatch } from '../utils';
|
||||
import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils';
|
||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
export type WaitForNavigationOptions = {
|
||||
|
@ -269,7 +268,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
|
|||
async addScriptTag(options: { url?: string, path?: string, content?: string, type?: string } = {}): Promise<ElementHandle> {
|
||||
const copy = { ...options };
|
||||
if (copy.path) {
|
||||
copy.content = (await fs.promises.readFile(copy.path)).toString();
|
||||
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
|
||||
copy.content = addSourceUrlToScript(copy.content, copy.path);
|
||||
}
|
||||
return ElementHandle.from((await this._channel.addScriptTag({ ...copy })).element);
|
||||
|
@ -278,7 +277,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
|
|||
async addStyleTag(options: { url?: string; path?: string; content?: string; } = {}): Promise<ElementHandle> {
|
||||
const copy = { ...options };
|
||||
if (copy.path) {
|
||||
copy.content = (await fs.promises.readFile(copy.path)).toString();
|
||||
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
|
||||
copy.content += '/*# sourceURL=' + copy.path.replace(/\n/g, '') + '*/';
|
||||
}
|
||||
return ElementHandle.from((await this._channel.addStyleTag({ ...copy })).element);
|
||||
|
@ -403,7 +402,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
|
|||
}
|
||||
|
||||
async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options: channels.FrameSetInputFilesOptions = {}): Promise<void> {
|
||||
const converted = await convertInputFiles(files, this.page().context());
|
||||
const converted = await convertInputFiles(this._platform, files, this.page().context());
|
||||
await this._channel.setInputFiles({ selector, ...converted, ...options });
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ import { debugLogger } from '../utils/debugLogger';
|
|||
import type { BrowserContext } from './browserContext';
|
||||
import type { LocalUtils } from './localUtils';
|
||||
import type { Route } from './network';
|
||||
import type { URLMatch } from '../utils';
|
||||
import type { Page } from './page';
|
||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||
|
||||
type HarNotFoundAction = 'abort' | 'fallback';
|
||||
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as util from 'util';
|
||||
|
||||
import { asLocator, isString, monotonicTime } from '../utils';
|
||||
import { ElementHandle } from './elementHandle';
|
||||
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 type { Frame } from './frame';
|
||||
import type { FilePayload, FrameExpectParams, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
|
||||
|
@ -64,6 +64,9 @@ export class Locator implements api.Locator {
|
|||
throw new Error(`Inner "hasNot" locator must belong to the same frame.`);
|
||||
this._selector += ` >> internal:has-not=` + JSON.stringify(locator._selector);
|
||||
}
|
||||
|
||||
if (this._frame._platform.inspectCustom)
|
||||
(this as any)[this._frame._platform.inspectCustom] = () => this._inspect();
|
||||
}
|
||||
|
||||
private async _withElement<R>(task: (handle: ElementHandle<SVGElement | HTMLElement>, timeout?: number) => Promise<R>, timeout?: number): Promise<R> {
|
||||
|
@ -291,8 +294,9 @@ export class Locator implements api.Locator {
|
|||
return await this._frame.press(this._selector, key, { strict: true, ...options });
|
||||
}
|
||||
|
||||
async screenshot(options: Omit<channels.ElementHandleScreenshotOptions, 'mask'> & { path?: string, mask?: Locator[] } = {}): Promise<Buffer> {
|
||||
return await this._withElement((h, timeout) => h.screenshot({ ...options, timeout }), options.timeout);
|
||||
async screenshot(options: Omit<channels.ElementHandleScreenshotOptions, 'mask'> & { path?: string, mask?: api.Locator[] } = {}): Promise<Buffer> {
|
||||
const mask = options.mask as Locator[] | undefined;
|
||||
return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), options.timeout);
|
||||
}
|
||||
|
||||
async ariaSnapshot(options?: { _id?: boolean, _mode?: 'raw' | 'regex' } & TimeoutOptions): Promise<string> {
|
||||
|
@ -370,7 +374,7 @@ export class Locator implements api.Locator {
|
|||
return result;
|
||||
}
|
||||
|
||||
[util.inspect.custom]() {
|
||||
private _inspect() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
|
@ -22,19 +21,26 @@ import { isTargetClosedError } from './errors';
|
|||
import { Events } from './events';
|
||||
import { APIResponse } from './fetch';
|
||||
import { Frame } from './frame';
|
||||
import { Worker } from './worker';
|
||||
import { MultiMap, assert, headersObjectToArray, isRegExp, isString, rewriteErrorMessage, urlMatches, zones } from '../utils';
|
||||
import { Waiter } from './waiter';
|
||||
import { Worker } from './worker';
|
||||
import { assert } from '../utils/debug';
|
||||
import { headersObjectToArray } from '../utils/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 { zones } from '../utils/zones';
|
||||
import { mime } from '../utilsBundle';
|
||||
|
||||
import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
|
||||
import type { URLMatch, Zone } from '../utils';
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import type { Page } from './page';
|
||||
import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
|
||||
import type { Serializable } from '../../types/structs';
|
||||
import type * as api from '../../types/types';
|
||||
import type { HeadersArray } from '../common/types';
|
||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||
import type { Zone } from '../utils/zones';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
export type NetworkCookie = {
|
||||
|
@ -387,7 +393,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
|
|||
let isBase64 = false;
|
||||
let length = 0;
|
||||
if (options.path) {
|
||||
const buffer = await fs.promises.readFile(options.path);
|
||||
const buffer = await this._platform.fs().promises.readFile(options.path);
|
||||
body = buffer.toString('base64');
|
||||
isBase64 = true;
|
||||
length = buffer.length;
|
||||
|
|
|
@ -15,12 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { TargetClosedError, isTargetClosedError, serializeError } from './errors';
|
||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||
import { LongStandingScope, assert, headersObjectToArray, isObject, isRegExp, isString, mkdirIfNeeded, trimStringWithEllipsis, urlMatches, urlMatchesEqual } from '../utils';
|
||||
import { Accessibility } from './accessibility';
|
||||
import { Artifact } from './artifact';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
|
@ -28,16 +22,25 @@ import { evaluationScript } from './clientHelper';
|
|||
import { Coverage } from './coverage';
|
||||
import { Download } from './download';
|
||||
import { ElementHandle, determineScreenshotType } from './elementHandle';
|
||||
import { TargetClosedError, isTargetClosedError, serializeError } from './errors';
|
||||
import { Events } from './events';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { Frame, verifyLoadState } from './frame';
|
||||
import { HarRouter } from './harRouter';
|
||||
import { Keyboard, Mouse, Touchscreen } from './input';
|
||||
import { JSHandle, assertMaxArguments, parseResult, serializeArgument } from './jsHandle';
|
||||
import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRouteHandler, validateHeaders } from './network';
|
||||
import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRouteHandler, validateHeaders } from './network';
|
||||
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 { 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 type { BrowserContext } from './browserContext';
|
||||
import type { Clock } from './clock';
|
||||
|
@ -48,8 +51,8 @@ import type { Request, RouteHandlerCallback, WebSocketRouteHandlerCallback } fro
|
|||
import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, WaitForEventOptions, WaitForFunctionOptions } from './types';
|
||||
import type * as structs from '../../types/structs';
|
||||
import type * as api from '../../types/types';
|
||||
import type { URLMatch } from '../utils';
|
||||
import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils';
|
||||
import type { URLMatch } from '../utils/isomorphic/urlMatch';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
type PDFOptions = Omit<channels.PagePdfParams, 'width' | 'height' | 'margin'> & {
|
||||
|
@ -512,7 +515,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
}
|
||||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||
const source = await evaluationScript(script, arg);
|
||||
const source = await evaluationScript(this._platform, script, arg);
|
||||
await this._channel.addInitScript({ source });
|
||||
}
|
||||
|
||||
|
@ -590,8 +593,8 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
}
|
||||
const result = await this._channel.screenshot(copy);
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fs.promises.writeFile(options.path, result.binary);
|
||||
await mkdirIfNeeded(this._platform, options.path);
|
||||
await this._platform.fs().promises.writeFile(options.path, result.binary);
|
||||
}
|
||||
return result.binary;
|
||||
}
|
||||
|
@ -820,8 +823,9 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
}
|
||||
const result = await this._channel.pdf(transportOptions);
|
||||
if (options.path) {
|
||||
await fs.promises.mkdir(path.dirname(options.path), { recursive: true });
|
||||
await fs.promises.writeFile(options.path, result.pdf);
|
||||
const platform = this._platform;
|
||||
await platform.fs().promises.mkdir(platform.path().dirname(options.path), { recursive: true });
|
||||
await platform.fs().promises.writeFile(options.path, result.pdf);
|
||||
}
|
||||
return result.pdf;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
import { ChannelOwner } from './channelOwner';
|
||||
import { evaluationScript } from './clientHelper';
|
||||
import { setTestIdAttribute, testIdAttributeName } from './locator';
|
||||
import { nodePlatform } from '../common/platform';
|
||||
|
||||
import type { SelectorEngine } from './types';
|
||||
import type * as api from '../../types/types';
|
||||
|
@ -28,7 +29,7 @@ export class Selectors implements api.Selectors {
|
|||
private _registrations: channels.SelectorsRegisterParams[] = [];
|
||||
|
||||
async register(name: string, script: string | (() => SelectorEngine) | { path?: string, content?: string }, options: { contentScript?: boolean } = {}): Promise<void> {
|
||||
const source = await evaluationScript(script, undefined, false);
|
||||
const source = await evaluationScript(nodePlatform, script, undefined, false);
|
||||
const params = { ...options, name, source };
|
||||
for (const channel of this._channels)
|
||||
await channel._channel.register(params);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ManualPromise } from '../utils';
|
||||
import { ManualPromise } from '../utils/manualPromise';
|
||||
|
||||
import type { Artifact } from './artifact';
|
||||
import type { Connection } from './connection';
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
*/
|
||||
|
||||
import { TimeoutError } from './errors';
|
||||
import { createGuid, zones } from '../utils';
|
||||
import { createGuid } from '../utils/crypto';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { zones } from '../utils/zones';
|
||||
|
||||
import type { Zone } from '../utils';
|
||||
import type { ChannelOwner } from './channelOwner';
|
||||
import type { Zone } from '../utils/zones';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import type { EventEmitter } from 'events';
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
*/
|
||||
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { TargetClosedError } from './errors';
|
||||
import { Events } from './events';
|
||||
import { JSHandle, assertMaxArguments, parseResult, serializeArgument } from './jsHandle';
|
||||
import { LongStandingScope } from '../utils';
|
||||
import { TargetClosedError } from './errors';
|
||||
import { LongStandingScope } from '../utils/manualPromise';
|
||||
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import type { Page } from './page';
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 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 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;
|
||||
};
|
||||
|
||||
export const emptyPlatform: Platform = {
|
||||
fs: () => {
|
||||
throw new Error('File system is not available');
|
||||
},
|
||||
|
||||
path: () => {
|
||||
throw new Error('Path module is not available');
|
||||
},
|
||||
|
||||
inspectCustom: undefined,
|
||||
};
|
||||
|
||||
export const nodePlatform: Platform = {
|
||||
fs: () => fs,
|
||||
path: () => path,
|
||||
inspectCustom: util.inspect.custom,
|
||||
};
|
|
@ -20,12 +20,13 @@ import { Connection } from './client/connection';
|
|||
import { DispatcherConnection, PlaywrightDispatcher, RootDispatcher, createPlaywright } from './server';
|
||||
|
||||
import type { Playwright as PlaywrightAPI } from './client/playwright';
|
||||
import type { Platform } from './common/platform';
|
||||
import type { Language } from './utils';
|
||||
|
||||
export function createInProcessPlaywright(): PlaywrightAPI {
|
||||
export function createInProcessPlaywright(platform: Platform): PlaywrightAPI {
|
||||
const playwright = createPlaywright({ sdkLanguage: (process.env.PW_LANG_NAME as Language | undefined) || 'javascript' });
|
||||
|
||||
const clientConnection = new Connection(undefined, undefined);
|
||||
const clientConnection = new Connection(undefined, platform, undefined);
|
||||
clientConnection.useRawBuffers();
|
||||
const dispatcherConnection = new DispatcherConnection(true /* local */);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { nodePlatform } from './common/platform';
|
||||
import { createInProcessPlaywright } from './inProcessFactory';
|
||||
|
||||
module.exports = createInProcessPlaywright();
|
||||
module.exports = createInProcessPlaywright(nodePlatform);
|
||||
|
|
|
@ -18,12 +18,12 @@ import * as childProcess from 'child_process';
|
|||
import * as path from 'path';
|
||||
|
||||
import { Connection } from './client/connection';
|
||||
import { nodePlatform } from './common/platform';
|
||||
import { PipeTransport } from './protocol/transport';
|
||||
import { ManualPromise } from './utils/manualPromise';
|
||||
|
||||
import type { Playwright } from './client/playwright';
|
||||
|
||||
|
||||
export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise<void> }> {
|
||||
const client = new PlaywrightClient(env);
|
||||
const playwright = await client._playwright;
|
||||
|
@ -48,7 +48,7 @@ class PlaywrightClient {
|
|||
this._driverProcess.unref();
|
||||
this._driverProcess.stderr!.on('data', data => process.stderr.write(data));
|
||||
|
||||
const connection = new Connection(undefined, undefined);
|
||||
const connection = new Connection(undefined, nodePlatform, undefined);
|
||||
const transport = new PipeTransport(this._driverProcess.stdin!, this._driverProcess.stdout!);
|
||||
connection.onmessage = message => transport.send(JSON.stringify(message));
|
||||
transport.onmessage = message => connection.dispatch(JSON.parse(message));
|
||||
|
|
|
@ -23,15 +23,15 @@ import { TimeoutSettings } from '../../common/timeoutSettings';
|
|||
import { PipeTransport } from '../../protocol/transport';
|
||||
import { createGuid, getPackageManagerExecCommand, isUnderTest, makeWaitForNextTask } from '../../utils';
|
||||
import { RecentLogsCollector } from '../../utils/debugLogger';
|
||||
import { removeFolders } from '../../utils/fileUtils';
|
||||
import { gracefullyCloseSet } from '../../utils/processLauncher';
|
||||
import { debug } from '../../utilsBundle';
|
||||
import { wsReceiver, wsSender } from '../../utilsBundle';
|
||||
import { validateBrowserContextOptions } from '../browserContext';
|
||||
import { chromiumSwitches } from '../chromium/chromiumSwitches';
|
||||
import { CRBrowser } from '../chromium/crBrowser';
|
||||
import { removeFolders } from '../fileUtils';
|
||||
import { helper } from '../helper';
|
||||
import { SdkObject, serverSideCallMetadata } from '../instrumentation';
|
||||
import { gracefullyCloseSet } from '../processLauncher';
|
||||
import { ProgressController } from '../progress';
|
||||
import { registry } from '../registry';
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ import { BidiBrowser } from './bidiBrowser';
|
|||
import { kBrowserCloseMessageId } from './bidiConnection';
|
||||
import { chromiumSwitches } from '../chromium/chromiumSwitches';
|
||||
|
||||
import type { Env } from '../../utils/processLauncher';
|
||||
import type { BrowserOptions } from '../browser';
|
||||
import type { SdkObject } from '../instrumentation';
|
||||
import type { Env } from '../processLauncher';
|
||||
import type { ProtocolError } from '../protocolError';
|
||||
import type { ConnectionTransport } from '../transport';
|
||||
import type * as types from '../types';
|
||||
|
|
|
@ -23,9 +23,9 @@ import { BidiBrowser } from './bidiBrowser';
|
|||
import { kBrowserCloseMessageId } from './bidiConnection';
|
||||
import { createProfile } from './third_party/firefoxPrefs';
|
||||
|
||||
import type { Env } from '../../utils/processLauncher';
|
||||
import type { BrowserOptions } from '../browser';
|
||||
import type { SdkObject } from '../instrumentation';
|
||||
import type { Env } from '../processLauncher';
|
||||
import type { ProtocolError } from '../protocolError';
|
||||
import type { ConnectionTransport } from '../transport';
|
||||
import type * as types from '../types';
|
||||
|
|
|
@ -23,6 +23,7 @@ import { createGuid, debugMode } from '../utils';
|
|||
import { Clock } from './clock';
|
||||
import { Debugger } from './debugger';
|
||||
import { BrowserContextAPIRequestContext } from './fetch';
|
||||
import { mkdirIfNeeded } from './fileUtils';
|
||||
import { HarRecorder } from './har/harRecorder';
|
||||
import { helper } from './helper';
|
||||
import { SdkObject, serverSideCallMetadata } from './instrumentation';
|
||||
|
@ -31,9 +32,8 @@ import * as network from './network';
|
|||
import { InitScript } from './page';
|
||||
import { Page, PageBinding } from './page';
|
||||
import { Recorder } from './recorder';
|
||||
import * as storageScript from './storageScript';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import { RecorderApp } from './recorder/recorderApp';
|
||||
import * as storageScript from './storageScript';
|
||||
import * as consoleApiSource from '../generated/consoleApiSource';
|
||||
import { Tracing } from './trace/recorder/tracing';
|
||||
|
||||
|
|
|
@ -21,27 +21,27 @@ import * as path from 'path';
|
|||
import { normalizeProxySettings, validateBrowserContextOptions } from './browserContext';
|
||||
import { DEFAULT_TIMEOUT, TimeoutSettings } from '../common/timeoutSettings';
|
||||
import { ManualPromise, debugMode } from '../utils';
|
||||
import { existsAsync } from './fileUtils';
|
||||
import { helper } from './helper';
|
||||
import { SdkObject } from './instrumentation';
|
||||
import { PipeTransport } from './pipeTransport';
|
||||
import { envArrayToObject, launchProcess } from './processLauncher';
|
||||
import { ProgressController } from './progress';
|
||||
import { isProtocolError } from './protocolError';
|
||||
import { registry } from './registry';
|
||||
import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
|
||||
import { WebSocketTransport } from './transport';
|
||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||
import { existsAsync } from '../utils/fileUtils';
|
||||
import { envArrayToObject, launchProcess } from '../utils/processLauncher';
|
||||
|
||||
import type { Browser, BrowserOptions, BrowserProcess } from './browser';
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import type { CallMetadata } from './instrumentation';
|
||||
import type { Env } from './processLauncher';
|
||||
import type { Progress } from './progress';
|
||||
import type { ProtocolError } from './protocolError';
|
||||
import type { BrowserName } from './registry';
|
||||
import type { ConnectionTransport } from './transport';
|
||||
import type * as types from './types';
|
||||
import type { Env } from '../utils/processLauncher';
|
||||
import type * as channels from '@protocol/channels';
|
||||
|
||||
export const kNoXServerRunningError = 'Looks like you launched a headed browser without having a XServer running.\n' +
|
||||
|
|
|
@ -26,10 +26,8 @@ import { TimeoutSettings } from '../../common/timeoutSettings';
|
|||
import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils';
|
||||
import { wrapInASCIIBox } from '../../utils/ascii';
|
||||
import { RecentLogsCollector } from '../../utils/debugLogger';
|
||||
import { removeFolders } from '../../utils/fileUtils';
|
||||
import { ManualPromise } from '../../utils/manualPromise';
|
||||
import { fetchData } from '../../utils/network';
|
||||
import { gracefullyCloseSet } from '../../utils/processLauncher';
|
||||
import { getUserAgent } from '../../utils/userAgent';
|
||||
import { validateBrowserContextOptions } from '../browserContext';
|
||||
import { BrowserType, kNoXServerRunningError } from '../browserType';
|
||||
|
@ -39,12 +37,14 @@ import { registry } from '../registry';
|
|||
import { WebSocketTransport } from '../transport';
|
||||
import { CRDevTools } from './crDevTools';
|
||||
import { Browser } from '../browser';
|
||||
import { removeFolders } from '../fileUtils';
|
||||
import { gracefullyCloseSet } from '../processLauncher';
|
||||
import { ProgressController } from '../progress';
|
||||
|
||||
import type { HTTPRequestParams } from '../../utils/network';
|
||||
import type { Env } from '../../utils/processLauncher';
|
||||
import type { BrowserOptions, BrowserProcess } from '../browser';
|
||||
import type { CallMetadata, SdkObject } from '../instrumentation';
|
||||
import type { Env } from '../processLauncher';
|
||||
import type { Progress } from '../progress';
|
||||
import type { ProtocolError } from '../protocolError';
|
||||
import type { ConnectionTransport, ProtocolRequest } from '../transport';
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { mkdirIfNeeded } from '../../utils/fileUtils';
|
||||
import { splitErrorMessage } from '../../utils/stackTrace';
|
||||
import { mkdirIfNeeded } from '../fileUtils';
|
||||
|
||||
import type { CRSession } from './crConnection';
|
||||
import type { Protocol } from './protocol';
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
|
||||
import { assert, monotonicTime } from '../../utils';
|
||||
import { launchProcess } from '../../utils/processLauncher';
|
||||
import { serverSideCallMetadata } from '../instrumentation';
|
||||
import { Page } from '../page';
|
||||
import { launchProcess } from '../processLauncher';
|
||||
import { ProgressController } from '../progress';
|
||||
|
||||
import type { Progress } from '../progress';
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
|
||||
import { SdkObject, createInstrumentation, serverSideCallMetadata } from './instrumentation';
|
||||
import { gracefullyProcessExitDoNotHang } from './processLauncher';
|
||||
import { Recorder } from './recorder';
|
||||
import { asLocator } from '../utils';
|
||||
import { parseAriaSnapshotUnsafe } from '../utils/isomorphic/ariaSnapshot';
|
||||
import { yaml } from '../utilsBundle';
|
||||
import { EmptyRecorderApp } from './recorder/recorderApp';
|
||||
import { unsafeLocatorOrSelectorAsSelector } from '../utils/isomorphic/locatorParser';
|
||||
import { gracefullyProcessExitDoNotHang } from '../utils/processLauncher';
|
||||
|
||||
import type { Language } from '../utils';
|
||||
import type { Browser } from './browser';
|
||||
|
|
|
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
|||
|
||||
import { Dispatcher, existingDispatcher } from './dispatcher';
|
||||
import { StreamDispatcher } from './streamDispatcher';
|
||||
import { mkdirIfNeeded } from '../../utils/fileUtils';
|
||||
import { mkdirIfNeeded } from '../fileUtils';
|
||||
|
||||
import type { DispatcherScope } from './dispatcher';
|
||||
import type { Artifact } from '../artifact';
|
||||
|
|
|
@ -20,7 +20,7 @@ import * as path from 'path';
|
|||
|
||||
import { Dispatcher } from './dispatcher';
|
||||
import { SdkObject } from '../../server/instrumentation';
|
||||
import { assert, calculateSha1, createGuid, removeFolders } from '../../utils';
|
||||
import { assert, calculateSha1, createGuid } from '../../utils';
|
||||
import { serializeClientSideCallMetadata } from '../../utils';
|
||||
import { ManualPromise } from '../../utils/manualPromise';
|
||||
import { fetchData } from '../../utils/network';
|
||||
|
@ -29,6 +29,7 @@ import { ZipFile } from '../../utils/zipFile';
|
|||
import { yauzl, yazl } from '../../zipBundle';
|
||||
import { deviceDescriptors as descriptors } from '../deviceDescriptors';
|
||||
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
|
||||
import { removeFolders } from '../fileUtils';
|
||||
import { ProgressController } from '../progress';
|
||||
import { SocksInterceptor } from '../socksInterceptor';
|
||||
import { WebSocketTransport } from '../transport';
|
||||
|
|
|
@ -23,7 +23,6 @@ import { TimeoutSettings } from '../../common/timeoutSettings';
|
|||
import { ManualPromise, wrapInASCIIBox } from '../../utils';
|
||||
import { RecentLogsCollector } from '../../utils/debugLogger';
|
||||
import { eventsHelper } from '../../utils/eventsHelper';
|
||||
import { envArrayToObject, launchProcess } from '../../utils/processLauncher';
|
||||
import { validateBrowserContextOptions } from '../browserContext';
|
||||
import { CRBrowser } from '../chromium/crBrowser';
|
||||
import { CRConnection } from '../chromium/crConnection';
|
||||
|
@ -33,6 +32,7 @@ import { ConsoleMessage } from '../console';
|
|||
import { helper } from '../helper';
|
||||
import { SdkObject, serverSideCallMetadata } from '../instrumentation';
|
||||
import * as js from '../javascript';
|
||||
import { envArrayToObject, launchProcess } from '../processLauncher';
|
||||
import { ProgressController } from '../progress';
|
||||
import { WebSocketTransport } from '../transport';
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/**
|
||||
* 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 fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { ManualPromise } from '../utils/manualPromise';
|
||||
import { yazl } from '../zipBundle';
|
||||
|
||||
import type { EventEmitter } from 'events';
|
||||
|
||||
export const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
|
||||
|
||||
export async function mkdirIfNeeded(filePath: string) {
|
||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||
await fs.promises.mkdir(path.dirname(filePath), { recursive: true }).catch(() => {});
|
||||
}
|
||||
|
||||
export async function removeFolders(dirs: string[]): Promise<Error[]> {
|
||||
return await Promise.all(dirs.map((dir: string) =>
|
||||
fs.promises.rm(dir, { recursive: true, force: true, maxRetries: 10 }).catch(e => e)
|
||||
));
|
||||
}
|
||||
|
||||
export function canAccessFile(file: string) {
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
try {
|
||||
fs.accessSync(file);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function copyFileAndMakeWritable(from: string, to: string) {
|
||||
await fs.promises.copyFile(from, to);
|
||||
await fs.promises.chmod(to, 0o664);
|
||||
}
|
||||
|
||||
export function sanitizeForFilePath(s: string) {
|
||||
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
|
||||
}
|
||||
|
||||
export function toPosixPath(aPath: string): string {
|
||||
return aPath.split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
|
||||
type NameValue = { name: string, value: string };
|
||||
type SerializedFSOperation = {
|
||||
op: 'mkdir', dir: string,
|
||||
} | {
|
||||
op: 'writeFile', file: string, content: string | Buffer, skipIfExists?: boolean,
|
||||
} | {
|
||||
op: 'appendFile', file: string, content: string,
|
||||
} | {
|
||||
op: 'copyFile', from: string, to: string,
|
||||
} | {
|
||||
op: 'zip', entries: NameValue[], zipFileName: string,
|
||||
};
|
||||
|
||||
export class SerializedFS {
|
||||
private _buffers = new Map<string, string[]>(); // Should never be accessed from within appendOperation.
|
||||
private _error: Error | undefined;
|
||||
private _operations: SerializedFSOperation[] = [];
|
||||
private _operationsDone: ManualPromise<void>;
|
||||
|
||||
constructor() {
|
||||
this._operationsDone = new ManualPromise();
|
||||
this._operationsDone.resolve(); // No operations scheduled yet.
|
||||
}
|
||||
|
||||
mkdir(dir: string) {
|
||||
this._appendOperation({ op: 'mkdir', dir });
|
||||
}
|
||||
|
||||
writeFile(file: string, content: string | Buffer, skipIfExists?: boolean) {
|
||||
this._buffers.delete(file); // No need to flush the buffer since we'll overwrite anyway.
|
||||
this._appendOperation({ op: 'writeFile', file, content, skipIfExists });
|
||||
}
|
||||
|
||||
appendFile(file: string, text: string, flush?: boolean) {
|
||||
if (!this._buffers.has(file))
|
||||
this._buffers.set(file, []);
|
||||
this._buffers.get(file)!.push(text);
|
||||
if (flush)
|
||||
this._flushFile(file);
|
||||
}
|
||||
|
||||
private _flushFile(file: string) {
|
||||
const buffer = this._buffers.get(file);
|
||||
if (buffer === undefined)
|
||||
return;
|
||||
const content = buffer.join('');
|
||||
this._buffers.delete(file);
|
||||
this._appendOperation({ op: 'appendFile', file, content });
|
||||
}
|
||||
|
||||
copyFile(from: string, to: string) {
|
||||
this._flushFile(from);
|
||||
this._buffers.delete(to); // No need to flush the buffer since we'll overwrite anyway.
|
||||
this._appendOperation({ op: 'copyFile', from, to });
|
||||
}
|
||||
|
||||
async syncAndGetError() {
|
||||
for (const file of this._buffers.keys())
|
||||
this._flushFile(file);
|
||||
await this._operationsDone;
|
||||
return this._error;
|
||||
}
|
||||
|
||||
zip(entries: NameValue[], zipFileName: string) {
|
||||
for (const file of this._buffers.keys())
|
||||
this._flushFile(file);
|
||||
|
||||
// Chain the export operation against write operations,
|
||||
// so that files do not change during the export.
|
||||
this._appendOperation({ op: 'zip', entries, zipFileName });
|
||||
}
|
||||
|
||||
// This method serializes all writes to the trace.
|
||||
private _appendOperation(op: SerializedFSOperation): void {
|
||||
const last = this._operations[this._operations.length - 1];
|
||||
if (last?.op === 'appendFile' && op.op === 'appendFile' && last.file === op.file) {
|
||||
// Merge pending appendFile operations for performance.
|
||||
last.content += op.content;
|
||||
return;
|
||||
}
|
||||
|
||||
this._operations.push(op);
|
||||
if (this._operationsDone.isDone())
|
||||
this._performOperations();
|
||||
}
|
||||
|
||||
private async _performOperations() {
|
||||
this._operationsDone = new ManualPromise();
|
||||
while (this._operations.length) {
|
||||
const op = this._operations.shift()!;
|
||||
// Ignore all operations after the first error.
|
||||
if (this._error)
|
||||
continue;
|
||||
try {
|
||||
await this._performOperation(op);
|
||||
} catch (e) {
|
||||
this._error = e;
|
||||
}
|
||||
}
|
||||
this._operationsDone.resolve();
|
||||
}
|
||||
|
||||
private async _performOperation(op: SerializedFSOperation) {
|
||||
switch (op.op) {
|
||||
case 'mkdir': {
|
||||
await fs.promises.mkdir(op.dir, { recursive: true });
|
||||
return;
|
||||
}
|
||||
case 'writeFile': {
|
||||
// Note: 'wx' flag only writes when the file does not exist.
|
||||
// See https://nodejs.org/api/fs.html#file-system-flags.
|
||||
// This way tracing never have to write the same resource twice.
|
||||
if (op.skipIfExists)
|
||||
await fs.promises.writeFile(op.file, op.content, { flag: 'wx' }).catch(() => {});
|
||||
else
|
||||
await fs.promises.writeFile(op.file, op.content);
|
||||
return;
|
||||
}
|
||||
case 'copyFile': {
|
||||
await fs.promises.copyFile(op.from, op.to);
|
||||
return;
|
||||
}
|
||||
case 'appendFile': {
|
||||
await fs.promises.appendFile(op.file, op.content);
|
||||
return;
|
||||
}
|
||||
case 'zip': {
|
||||
const zipFile = new yazl.ZipFile();
|
||||
const result = new ManualPromise<void>();
|
||||
(zipFile as any as EventEmitter).on('error', error => result.reject(error));
|
||||
for (const entry of op.entries)
|
||||
zipFile.addFile(entry.value, entry.name);
|
||||
zipFile.end();
|
||||
zipFile.outputStream
|
||||
.pipe(fs.createWriteStream(op.zipFileName))
|
||||
.on('close', () => result.resolve())
|
||||
.on('error', error => result.reject(error));
|
||||
await result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,9 +24,9 @@ import { wrapInASCIIBox } from '../../utils';
|
|||
import { BrowserType, kNoXServerRunningError } from '../browserType';
|
||||
import { BrowserReadyState } from '../browserType';
|
||||
|
||||
import type { Env } from '../../utils/processLauncher';
|
||||
import type { BrowserOptions } from '../browser';
|
||||
import type { SdkObject } from '../instrumentation';
|
||||
import type { Env } from '../processLauncher';
|
||||
import type { ProtocolError } from '../protocolError';
|
||||
import type { ConnectionTransport } from '../transport';
|
||||
import type * as types from '../types';
|
||||
|
|
|
@ -31,3 +31,5 @@ export type { Playwright } from './playwright';
|
|||
export { installRootRedirect, openTraceInBrowser, openTraceViewerApp, runTraceViewerApp, startTraceViewerServer } from './trace/viewer/traceViewer';
|
||||
export { serverSideCallMetadata } from './instrumentation';
|
||||
export { SocksProxy } from '../common/socksProxy';
|
||||
export * from './fileUtils';
|
||||
export * from './processLauncher';
|
||||
|
|
|
@ -20,8 +20,7 @@ import * as fs from 'fs';
|
|||
import * as readline from 'readline';
|
||||
|
||||
import { removeFolders } from './fileUtils';
|
||||
|
||||
import { isUnderTest } from './';
|
||||
import { isUnderTest } from '../utils';
|
||||
|
||||
export type Env = {[key: string]: string | number | boolean | undefined};
|
||||
|
|
@ -21,10 +21,10 @@ import * as os from 'os';
|
|||
import * as path from 'path';
|
||||
|
||||
import { debugLogger } from '../../utils/debugLogger';
|
||||
import { existsAsync } from '../../utils/fileUtils';
|
||||
import { ManualPromise } from '../../utils/manualPromise';
|
||||
import { getUserAgent } from '../../utils/userAgent';
|
||||
import { colors, progress as ProgressBar } from '../../utilsBundle';
|
||||
import { existsAsync } from '../fileUtils';
|
||||
|
||||
import { browserDirectoryToMarkerFilePath } from '.';
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ import { dockerVersion, readDockerVersionSync, transformCommandsForRoot } from '
|
|||
import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies';
|
||||
import { calculateSha1, getAsBooleanFromENV, getFromENV, getPackageManagerExecCommand, wrapInASCIIBox } from '../../utils';
|
||||
import { debugLogger } from '../../utils/debugLogger';
|
||||
import { canAccessFile, existsAsync, removeFolders } from '../../utils/fileUtils';
|
||||
import { hostPlatform, isOfficiallySupportedPlatform } from '../../utils/hostPlatform';
|
||||
import { fetchData } from '../../utils/network';
|
||||
import { spawnAsync } from '../../utils/spawnAsync';
|
||||
import { getEmbedderName } from '../../utils/userAgent';
|
||||
import { lockfile } from '../../utilsBundle';
|
||||
import { canAccessFile, existsAsync, removeFolders } from '../fileUtils';
|
||||
|
||||
import type { DependencyGroup } from './dependencies';
|
||||
import type { HostPlatform } from '../../utils/hostPlatform';
|
||||
|
|
|
@ -20,11 +20,12 @@ import * as path from 'path';
|
|||
|
||||
import { Snapshotter } from './snapshotter';
|
||||
import { commandsWithTracingSnapshots } from '../../../protocol/debug';
|
||||
import { SerializedFS, assert, createGuid, eventsHelper, monotonicTime, removeFolders } from '../../../utils';
|
||||
import { assert, createGuid, eventsHelper, monotonicTime } from '../../../utils';
|
||||
import { Artifact } from '../../artifact';
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { Dispatcher } from '../../dispatchers/dispatcher';
|
||||
import { serializeError } from '../../errors';
|
||||
import { SerializedFS, removeFolders } from '../../fileUtils';
|
||||
import { HarTracer } from '../../har/harTracer';
|
||||
import { SdkObject } from '../../instrumentation';
|
||||
import { Page } from '../../page';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { gracefullyProcessExitDoNotHang, isUnderTest } from '../../../utils';
|
||||
import { gracefullyProcessExitDoNotHang } from '../../../server';
|
||||
import { isUnderTest } from '../../../utils';
|
||||
import { HttpServer } from '../../../utils/httpServer';
|
||||
import { open } from '../../../utilsBundle';
|
||||
import { serverSideCallMetadata } from '../../instrumentation';
|
||||
|
|
|
@ -22,9 +22,9 @@ import { wrapInASCIIBox } from '../../utils';
|
|||
import { BrowserType, kNoXServerRunningError } from '../browserType';
|
||||
import { WKBrowser } from '../webkit/wkBrowser';
|
||||
|
||||
import type { Env } from '../../utils/processLauncher';
|
||||
import type { BrowserOptions } from '../browser';
|
||||
import type { SdkObject } from '../instrumentation';
|
||||
import type { Env } from '../processLauncher';
|
||||
import type { ProtocolError } from '../protocolError';
|
||||
import type { ConnectionTransport } from '../transport';
|
||||
import type * as types from '../types';
|
||||
|
|
|
@ -14,194 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { ManualPromise } from './manualPromise';
|
||||
import { yazl } from '../zipBundle';
|
||||
|
||||
import type { EventEmitter } from 'events';
|
||||
import type { Platform } from '../common/platform';
|
||||
|
||||
export const fileUploadSizeLimit = 50 * 1024 * 1024;
|
||||
|
||||
export const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
|
||||
|
||||
export async function mkdirIfNeeded(filePath: string) {
|
||||
export async function mkdirIfNeeded(platform: Platform, filePath: string) {
|
||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||
await fs.promises.mkdir(path.dirname(filePath), { recursive: true }).catch(() => {});
|
||||
}
|
||||
|
||||
export async function removeFolders(dirs: string[]): Promise<Error[]> {
|
||||
return await Promise.all(dirs.map((dir: string) =>
|
||||
fs.promises.rm(dir, { recursive: true, force: true, maxRetries: 10 }).catch(e => e)
|
||||
));
|
||||
}
|
||||
|
||||
export function canAccessFile(file: string) {
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
try {
|
||||
fs.accessSync(file);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function copyFileAndMakeWritable(from: string, to: string) {
|
||||
await fs.promises.copyFile(from, to);
|
||||
await fs.promises.chmod(to, 0o664);
|
||||
}
|
||||
|
||||
export function sanitizeForFilePath(s: string) {
|
||||
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
|
||||
}
|
||||
|
||||
export function toPosixPath(aPath: string): string {
|
||||
return aPath.split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
|
||||
type NameValue = { name: string, value: string };
|
||||
type SerializedFSOperation = {
|
||||
op: 'mkdir', dir: string,
|
||||
} | {
|
||||
op: 'writeFile', file: string, content: string | Buffer, skipIfExists?: boolean,
|
||||
} | {
|
||||
op: 'appendFile', file: string, content: string,
|
||||
} | {
|
||||
op: 'copyFile', from: string, to: string,
|
||||
} | {
|
||||
op: 'zip', entries: NameValue[], zipFileName: string,
|
||||
};
|
||||
|
||||
export class SerializedFS {
|
||||
private _buffers = new Map<string, string[]>(); // Should never be accessed from within appendOperation.
|
||||
private _error: Error | undefined;
|
||||
private _operations: SerializedFSOperation[] = [];
|
||||
private _operationsDone: ManualPromise<void>;
|
||||
|
||||
constructor() {
|
||||
this._operationsDone = new ManualPromise();
|
||||
this._operationsDone.resolve(); // No operations scheduled yet.
|
||||
}
|
||||
|
||||
mkdir(dir: string) {
|
||||
this._appendOperation({ op: 'mkdir', dir });
|
||||
}
|
||||
|
||||
writeFile(file: string, content: string | Buffer, skipIfExists?: boolean) {
|
||||
this._buffers.delete(file); // No need to flush the buffer since we'll overwrite anyway.
|
||||
this._appendOperation({ op: 'writeFile', file, content, skipIfExists });
|
||||
}
|
||||
|
||||
appendFile(file: string, text: string, flush?: boolean) {
|
||||
if (!this._buffers.has(file))
|
||||
this._buffers.set(file, []);
|
||||
this._buffers.get(file)!.push(text);
|
||||
if (flush)
|
||||
this._flushFile(file);
|
||||
}
|
||||
|
||||
private _flushFile(file: string) {
|
||||
const buffer = this._buffers.get(file);
|
||||
if (buffer === undefined)
|
||||
return;
|
||||
const content = buffer.join('');
|
||||
this._buffers.delete(file);
|
||||
this._appendOperation({ op: 'appendFile', file, content });
|
||||
}
|
||||
|
||||
copyFile(from: string, to: string) {
|
||||
this._flushFile(from);
|
||||
this._buffers.delete(to); // No need to flush the buffer since we'll overwrite anyway.
|
||||
this._appendOperation({ op: 'copyFile', from, to });
|
||||
}
|
||||
|
||||
async syncAndGetError() {
|
||||
for (const file of this._buffers.keys())
|
||||
this._flushFile(file);
|
||||
await this._operationsDone;
|
||||
return this._error;
|
||||
}
|
||||
|
||||
zip(entries: NameValue[], zipFileName: string) {
|
||||
for (const file of this._buffers.keys())
|
||||
this._flushFile(file);
|
||||
|
||||
// Chain the export operation against write operations,
|
||||
// so that files do not change during the export.
|
||||
this._appendOperation({ op: 'zip', entries, zipFileName });
|
||||
}
|
||||
|
||||
// This method serializes all writes to the trace.
|
||||
private _appendOperation(op: SerializedFSOperation): void {
|
||||
const last = this._operations[this._operations.length - 1];
|
||||
if (last?.op === 'appendFile' && op.op === 'appendFile' && last.file === op.file) {
|
||||
// Merge pending appendFile operations for performance.
|
||||
last.content += op.content;
|
||||
return;
|
||||
}
|
||||
|
||||
this._operations.push(op);
|
||||
if (this._operationsDone.isDone())
|
||||
this._performOperations();
|
||||
}
|
||||
|
||||
private async _performOperations() {
|
||||
this._operationsDone = new ManualPromise();
|
||||
while (this._operations.length) {
|
||||
const op = this._operations.shift()!;
|
||||
// Ignore all operations after the first error.
|
||||
if (this._error)
|
||||
continue;
|
||||
try {
|
||||
await this._performOperation(op);
|
||||
} catch (e) {
|
||||
this._error = e;
|
||||
}
|
||||
}
|
||||
this._operationsDone.resolve();
|
||||
}
|
||||
|
||||
private async _performOperation(op: SerializedFSOperation) {
|
||||
switch (op.op) {
|
||||
case 'mkdir': {
|
||||
await fs.promises.mkdir(op.dir, { recursive: true });
|
||||
return;
|
||||
}
|
||||
case 'writeFile': {
|
||||
// Note: 'wx' flag only writes when the file does not exist.
|
||||
// See https://nodejs.org/api/fs.html#file-system-flags.
|
||||
// This way tracing never have to write the same resource twice.
|
||||
if (op.skipIfExists)
|
||||
await fs.promises.writeFile(op.file, op.content, { flag: 'wx' }).catch(() => {});
|
||||
else
|
||||
await fs.promises.writeFile(op.file, op.content);
|
||||
return;
|
||||
}
|
||||
case 'copyFile': {
|
||||
await fs.promises.copyFile(op.from, op.to);
|
||||
return;
|
||||
}
|
||||
case 'appendFile': {
|
||||
await fs.promises.appendFile(op.file, op.content);
|
||||
return;
|
||||
}
|
||||
case 'zip': {
|
||||
const zipFile = new yazl.ZipFile();
|
||||
const result = new ManualPromise<void>();
|
||||
(zipFile as any as EventEmitter).on('error', error => result.reject(error));
|
||||
for (const entry of op.entries)
|
||||
zipFile.addFile(entry.value, entry.name);
|
||||
zipFile.end();
|
||||
zipFile.outputStream
|
||||
.pipe(fs.createWriteStream(op.zipFileName))
|
||||
.on('close', () => result.resolve())
|
||||
.on('error', error => result.reject(error));
|
||||
await result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => {});
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ export * from './isomorphic/stringUtils';
|
|||
export * from './isomorphic/urlMatch';
|
||||
export * from './multimap';
|
||||
export * from './network';
|
||||
export * from './processLauncher';
|
||||
export * from './profiler';
|
||||
export * from './rtti';
|
||||
export * from './semaphore';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { gracefullyProcessExitDoNotHang, isRegExp } from 'playwright-core/lib/utils';
|
||||
import { gracefullyProcessExitDoNotHang } from 'playwright-core/lib/server';
|
||||
import { isRegExp } from 'playwright-core/lib/utils';
|
||||
|
||||
import { requireOrImport, setSingleTSConfig, setTransformConfig } from '../transform/transform';
|
||||
import { errorWithFile, fileIsModule } from '../util';
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
import * as path from 'path';
|
||||
|
||||
import { calculateSha1, toPosixPath } from 'playwright-core/lib/utils';
|
||||
import { toPosixPath } from 'playwright-core/lib/server';
|
||||
import { calculateSha1 } from 'playwright-core/lib/utils';
|
||||
|
||||
import { createFileMatcher } from '../util';
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { escapeTemplateString, isString, sanitizeForFilePath } from 'playwright-core/lib/utils';
|
||||
import { sanitizeForFilePath } from 'playwright-core/lib/server';
|
||||
import { escapeTemplateString, isString } from 'playwright-core/lib/utils';
|
||||
|
||||
import { kNoElementsFoundError, matcherHint } from './matcherHint';
|
||||
import { EXPECTED_COLOR } from '../common/expectBundle';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { compareBuffersOrStrings, getComparator, isString, sanitizeForFilePath } from 'playwright-core/lib/utils';
|
||||
import { sanitizeForFilePath } from 'playwright-core/lib/server';
|
||||
import { compareBuffersOrStrings, getComparator, isString } from 'playwright-core/lib/utils';
|
||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||
import { mime } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
import * as net from 'net';
|
||||
import * as path from 'path';
|
||||
|
||||
import { isURLAvailable, launchProcess, monotonicTime, raceAgainstDeadline } from 'playwright-core/lib/utils';
|
||||
import { launchProcess } from 'playwright-core/lib/server';
|
||||
import { isURLAvailable, monotonicTime, raceAgainstDeadline } from 'playwright-core/lib/utils';
|
||||
import { colors, debug } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
import type { TestRunnerPlugin } from '.';
|
||||
|
|
|
@ -20,7 +20,8 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
|
||||
import { program } from 'playwright-core/lib/cli/program';
|
||||
import { gracefullyProcessExitDoNotHang, startProfiling, stopProfiling } from 'playwright-core/lib/utils';
|
||||
import { gracefullyProcessExitDoNotHang } from 'playwright-core/lib/server';
|
||||
import { startProfiling, stopProfiling } from 'playwright-core/lib/utils';
|
||||
|
||||
import { builtInReporters, defaultReporter, defaultTimeout } from './common/config';
|
||||
import { loadConfigFromFileRestartIfNeeded, loadEmptyConfigForMergeReports, resolveConfigLocation } from './common/configLoader';
|
||||
|
|
|
@ -18,7 +18,8 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
import { ManualPromise, calculateSha1, createGuid, getUserAgent, removeFolders, sanitizeForFilePath } from 'playwright-core/lib/utils';
|
||||
import { removeFolders, sanitizeForFilePath } from 'playwright-core/lib/server';
|
||||
import { ManualPromise, calculateSha1, createGuid, getUserAgent } from 'playwright-core/lib/utils';
|
||||
import { mime } from 'playwright-core/lib/utilsBundle';
|
||||
import { yazl } from 'playwright-core/lib/zipBundle';
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import { Transform } from 'stream';
|
||||
|
||||
import { MultiMap, getPackageManagerExecCommand } from 'playwright-core/lib/utils';
|
||||
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath, toPosixPath } from 'playwright-core/lib/utils';
|
||||
import { copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath, toPosixPath } from 'playwright-core/lib/server';
|
||||
import { HttpServer, MultiMap, assert, calculateSha1, getPackageManagerExecCommand } from 'playwright-core/lib/utils';
|
||||
import { colors, open } from 'playwright-core/lib/utilsBundle';
|
||||
import { mime } from 'playwright-core/lib/utilsBundle';
|
||||
import { yazl } from 'playwright-core/lib/zipBundle';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { MultiMap, toPosixPath } from 'playwright-core/lib/utils';
|
||||
import { toPosixPath } from 'playwright-core/lib/server';
|
||||
import { MultiMap } from 'playwright-core/lib/utils';
|
||||
|
||||
import { formatError, nonTerminalScreen, prepareErrorStack, resolveOutputFile } from './base';
|
||||
import { getProjectId } from '../common/config';
|
||||
|
|
|
@ -18,7 +18,8 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { monotonicTime, removeFolders } from 'playwright-core/lib/utils';
|
||||
import { removeFolders } from 'playwright-core/lib/server';
|
||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||
import { debug } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
import { Dispatcher } from './dispatcher';
|
||||
|
@ -26,12 +27,12 @@ import { FailureTracker } from './failureTracker';
|
|||
import { collectProjectsAndTestFiles, createRootSuite, loadFileSuites, loadGlobalHook } from './loadUtils';
|
||||
import { buildDependentProjects, buildTeardownToSetupsMap, filterProjects } from './projectUtils';
|
||||
import { applySuggestedRebaselines, clearSuggestedRebaselines } from './rebase';
|
||||
import { Suite } from '../common/test';
|
||||
import { createTestGroups } from '../runner/testGroups';
|
||||
import { removeDirAndLogToConsole } from '../util';
|
||||
import { TaskRunner } from './taskRunner';
|
||||
import { detectChangedTestFiles } from './vcs';
|
||||
import { Suite } from '../common/test';
|
||||
import { createTestGroups } from '../runner/testGroups';
|
||||
import { cacheDir } from '../transform/compilationCache';
|
||||
import { removeDirAndLogToConsole } from '../util';
|
||||
|
||||
import type { TestGroup } from '../runner/testGroups';
|
||||
import type { Matcher } from '../util';
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { installRootRedirect, openTraceInBrowser, openTraceViewerApp, registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
||||
import { ManualPromise, gracefullyProcessExitDoNotHang, isUnderTest } from 'playwright-core/lib/utils';
|
||||
import { gracefullyProcessExitDoNotHang, installRootRedirect, openTraceInBrowser, openTraceViewerApp, registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
||||
import { ManualPromise, isUnderTest } from 'playwright-core/lib/utils';
|
||||
import { open } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
import { createErrorCollectingReporter, createReporterForTestServer, createReporters } from './reporters';
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { removeFolders } from 'playwright-core/lib/utils';
|
||||
import { removeFolders } from 'playwright-core/lib/server';
|
||||
|
||||
import { ProcessHost } from './processHost';
|
||||
import { stdioChunkToParams } from '../common/ipc';
|
||||
|
|
|
@ -19,8 +19,8 @@ import * as path from 'path';
|
|||
import * as url from 'url';
|
||||
import util from 'util';
|
||||
|
||||
import { formatCallLog } from 'playwright-core/lib/utils';
|
||||
import { calculateSha1, isRegExp, isString, sanitizeForFilePath, stringifyStackFrames } from 'playwright-core/lib/utils';
|
||||
import { sanitizeForFilePath } from 'playwright-core/lib/server';
|
||||
import { calculateSha1, formatCallLog, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
|
||||
import { debug, mime, minimatch, parseStackTraceLine } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
import type { Location } from './../types/testReporter';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { captureRawStack, monotonicTime, sanitizeForFilePath, stringifyStackFrames, zones } from 'playwright-core/lib/utils';
|
||||
import { sanitizeForFilePath } from 'playwright-core/lib/server';
|
||||
import { captureRawStack, monotonicTime, stringifyStackFrames, zones } from 'playwright-core/lib/utils';
|
||||
|
||||
import { TimeoutManager, TimeoutManagerError, kMaxDeadline } from './timeoutManager';
|
||||
import { debugTest, filteredStackTrace, formatLocation, getContainedPath, normalizeAndSaveAttachment, trimLongString, windowsFilesystemFriendlyLength } from '../util';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { ManualPromise, SerializedFS, calculateSha1, createGuid, monotonicTime } from 'playwright-core/lib/utils';
|
||||
import { SerializedFS } from 'playwright-core/lib/server';
|
||||
import { ManualPromise, calculateSha1, createGuid, monotonicTime } from 'playwright-core/lib/utils';
|
||||
import { yauzl, yazl } from 'playwright-core/lib/zipBundle';
|
||||
|
||||
import { filteredStackTrace } from '../util';
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ManualPromise, gracefullyCloseAll, removeFolders } from 'playwright-core/lib/utils';
|
||||
import { removeFolders } from 'playwright-core/lib/server';
|
||||
import { gracefullyCloseAll } from 'playwright-core/lib/server';
|
||||
import { ManualPromise } from 'playwright-core/lib/utils';
|
||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||
|
||||
import { deserializeConfig } from '../common/configLoader';
|
||||
|
|
|
@ -16,15 +16,17 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import type { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi';
|
||||
import * as path from 'path';
|
||||
import type { BrowserContext, BrowserContextOptions, BrowserType, Page } from 'playwright-core';
|
||||
import { removeFolders } from '../../packages/playwright-core/lib/utils/fileUtils';
|
||||
import { baseTest } from './baseTest';
|
||||
import { type RemoteServerOptions, type PlaywrightServer, RunServer, RemoteServer } from './remoteServer';
|
||||
import type { Log } from '../../packages/trace/src/har';
|
||||
import { RunServer, RemoteServer } from './remoteServer';
|
||||
import { removeFolders } from '../../packages/playwright-core/lib/server/fileUtils';
|
||||
import { parseHar } from '../config/utils';
|
||||
import { createSkipTestPredicate } from '../bidi/expectationUtil';
|
||||
|
||||
import type { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi';
|
||||
import type { RemoteServerOptions, PlaywrightServer } from './remoteServer';
|
||||
import type { BrowserContext, BrowserContextOptions, BrowserType, Page } from 'playwright-core';
|
||||
import type { Log } from '../../packages/trace/src/har';
|
||||
import type { TestInfo } from '@playwright/test';
|
||||
|
||||
export type BrowserTestWorkerFixtures = PageWorkerFixtures & {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { spawnAsync } from '../../packages/playwright-core/lib/utils/spawnAsync';
|
||||
import { removeFolders } from '../../packages/playwright-core/lib/utils/fileUtils';
|
||||
import { removeFolders } from '../../packages/playwright-core/lib/server/fileUtils';
|
||||
import { TMP_WORKSPACES } from './npmTest';
|
||||
|
||||
const PACKAGE_BUILDER_SCRIPT = path.join(__dirname, '..', '..', 'utils', 'pack_package.js');
|
||||
|
|
|
@ -22,7 +22,7 @@ import debugLogger from 'debug';
|
|||
import { Registry } from './registry';
|
||||
import type { CommonFixtures, CommonWorkerFixtures } from '../config/commonFixtures';
|
||||
import { commonFixtures } from '../config/commonFixtures';
|
||||
import { removeFolders } from '../../packages/playwright-core/lib/utils/fileUtils';
|
||||
import { removeFolders } from '../../packages/playwright-core/lib/server/fileUtils';
|
||||
import { spawnAsync } from '../../packages/playwright-core/lib/utils/spawnAsync';
|
||||
import type { SpawnOptions } from 'child_process';
|
||||
|
||||
|
|
|
@ -22,6 +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/common/platform';
|
||||
export { expect } from '@playwright/test';
|
||||
|
||||
type CLITestArgs = {
|
||||
|
@ -46,7 +47,7 @@ const codegenLang2Id: Map<string, string> = new Map([
|
|||
]);
|
||||
const codegenLangId2lang = new Map([...codegenLang2Id.entries()].map(([lang, langId]) => [langId, lang]));
|
||||
|
||||
const playwrightToAutomateInspector = require('../../../packages/playwright-core/lib/inProcessFactory').createInProcessPlaywright();
|
||||
const playwrightToAutomateInspector = require('../../../packages/playwright-core/lib/inProcessFactory').createInProcessPlaywright(nodePlatform);
|
||||
|
||||
export const test = contextTest.extend<CLITestArgs>({
|
||||
recorderPageGetter: async ({ context, toImpl, mode }, run, testInfo) => {
|
||||
|
|
Loading…
Reference in New Issue