chore: move shared utilities to src/utils (#3575)
This commit is contained in:
Родитель
b909924a61
Коммит
655013d025
|
@ -20,7 +20,7 @@ import { helper } from './helper';
|
|||
import * as network from './network';
|
||||
import * as path from 'path';
|
||||
import { Page, PageBinding } from './page';
|
||||
import { TimeoutSettings } from './timeoutSettings';
|
||||
import { TimeoutSettings } from './utils/timeoutSettings';
|
||||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import { Download } from './download';
|
||||
|
@ -28,6 +28,7 @@ import { Browser } from './browser';
|
|||
import { EventEmitter } from 'events';
|
||||
import { Progress } from './progress';
|
||||
import { DebugController } from './debug/debugController';
|
||||
import { isDebugMode } from './utils/utils';
|
||||
|
||||
export class Screencast {
|
||||
readonly path: string;
|
||||
|
@ -68,7 +69,7 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
}
|
||||
|
||||
async _initialize() {
|
||||
if (helper.isDebugMode())
|
||||
if (isDebugMode())
|
||||
new DebugController(this);
|
||||
}
|
||||
|
||||
|
@ -250,24 +251,16 @@ export function verifyGeolocation(geolocation?: types.Geolocation) {
|
|||
return;
|
||||
geolocation.accuracy = geolocation.accuracy || 0;
|
||||
const { longitude, latitude, accuracy } = geolocation;
|
||||
if (!helper.isNumber(longitude))
|
||||
throw new Error(`geolocation.longitude: expected number, got ${typeof longitude}`);
|
||||
if (longitude < -180 || longitude > 180)
|
||||
throw new Error(`geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||
if (!helper.isNumber(latitude))
|
||||
throw new Error(`geolocation.latitude: expected number, got ${typeof latitude}`);
|
||||
if (latitude < -90 || latitude > 90)
|
||||
throw new Error(`geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed.`);
|
||||
if (!helper.isNumber(accuracy))
|
||||
throw new Error(`geolocation.accuracy: expected number, got ${typeof accuracy}`);
|
||||
if (accuracy < 0)
|
||||
throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`);
|
||||
}
|
||||
|
||||
export function verifyProxySettings(proxy: types.ProxySettings): types.ProxySettings {
|
||||
let { server, bypass } = proxy;
|
||||
if (!helper.isString(server))
|
||||
throw new Error(`Invalid proxy.server: ` + server);
|
||||
let url = new URL(server);
|
||||
if (!['http:', 'https:', 'socks5:'].includes(url.protocol)) {
|
||||
url = new URL('http://' + server);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { assert } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding, Worker } from '../page';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert, debugLogger } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
||||
import { Protocol } from './protocol';
|
||||
import { EventEmitter } from 'events';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
|
||||
export const ConnectionEvents = {
|
||||
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
*/
|
||||
|
||||
import { CRSession } from './crConnection';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { Protocol } from './protocol';
|
||||
import * as types from '../types';
|
||||
import * as sourceMap from '../utils/sourceMap';
|
||||
import { assert } from '../utils/utils';
|
||||
|
||||
export class CRCoverage {
|
||||
private _jsCoverage: JSCoverage;
|
||||
|
|
|
@ -19,7 +19,7 @@ import * as input from '../input';
|
|||
import * as types from '../types';
|
||||
import { CRSession } from './crConnection';
|
||||
import { macEditingCommands } from '../macEditingCommands';
|
||||
import { helper } from '../helper';
|
||||
import { isString } from '../utils/utils';
|
||||
|
||||
function toModifiersMask(modifiers: Set<types.KeyboardModifier>): number {
|
||||
let mask = 0;
|
||||
|
@ -51,7 +51,7 @@ export class RawKeyboardImpl implements input.RawKeyboard {
|
|||
parts.push(code);
|
||||
const shortcut = parts.join('+');
|
||||
let commands = macEditingCommands[shortcut] || [];
|
||||
if (helper.isString(commands))
|
||||
if (isString(commands))
|
||||
commands = [commands];
|
||||
// remove the trailing : to match the Chromium command names.
|
||||
return commands.map(c => c.substring(0, c.length - 1));
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
|
||||
import { CRSession } from './crConnection';
|
||||
import { Page } from '../page';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { Protocol } from './protocol';
|
||||
import * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
import * as types from '../types';
|
||||
import { CRPage } from './crPage';
|
||||
import { headersObjectToArray } from '../converters';
|
||||
import { assert, headersObjectToArray } from '../utils/utils';
|
||||
|
||||
export class CRNetworkManager {
|
||||
private _client: CRSession;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import * as dom from '../dom';
|
||||
import * as frames from '../frames';
|
||||
import { helper, RegisteredListener, assert } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { CRSession, CRConnection, CRSessionEvents } from './crConnection';
|
||||
import { CRExecutionContext } from './crExecutionContext';
|
||||
|
@ -36,7 +36,7 @@ import * as types from '../types';
|
|||
import { ConsoleMessage } from '../console';
|
||||
import * as sourceMap from '../utils/sourceMap';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { headersArrayToObject } from '../converters';
|
||||
import { assert, headersArrayToObject } from '../utils/utils';
|
||||
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import * as types from '../types';
|
||||
import { CRSession } from './crConnection';
|
||||
import { readProtocolStream } from './crProtocolHelper';
|
||||
|
|
|
@ -20,7 +20,7 @@ import { Protocol } from './protocol';
|
|||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import * as types from '../types';
|
||||
import { mkdirIfNeeded } from '../helper';
|
||||
import { mkdirIfNeeded } from '../utils/utils';
|
||||
|
||||
export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string {
|
||||
if (exceptionDetails.exception)
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as types from './types';
|
||||
|
||||
export function headersObjectToArray(headers: { [key: string]: string }): types.HeadersArray {
|
||||
const result: types.HeadersArray = [];
|
||||
for (const name in headers) {
|
||||
if (!Object.is(headers[name], undefined))
|
||||
result.push({ name, value: headers[name] });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function headersArrayToObject(headers: types.HeadersArray, lowerCase: boolean): { [key: string]: string } {
|
||||
const result: { [key: string]: string } = {};
|
||||
for (const { name, value } of headers)
|
||||
result[lowerCase ? name.toLowerCase() : name] = value;
|
||||
return result;
|
||||
}
|
|
@ -15,7 +15,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert, debugLogger } from './helper';
|
||||
import { assert } from './utils/utils';
|
||||
import { debugLogger } from './utils/debugLogger';
|
||||
|
||||
type OnHandle = (accept: boolean, promptText?: string) => Promise<void>;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import * as frames from './frames';
|
||||
import { assert, helper } from './helper';
|
||||
import { assert } from './utils/utils';
|
||||
import InjectedScript from './injected/injectedScript';
|
||||
import * as injectedScriptSource from './generated/injectedScriptSource';
|
||||
import * as debugScriptSource from './generated/debugScriptSource';
|
||||
|
@ -429,7 +429,6 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
|
||||
async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||
progress.log(`elementHandle.fill("${value}")`);
|
||||
assert(helper.isString(value), `value: expected string, got ${typeof value}`);
|
||||
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
|
||||
progress.log(' waiting for element to be visible, enabled and editable');
|
||||
const poll = await this._evaluateHandleInUtility(([injected, node, value]) => {
|
||||
|
|
|
@ -19,7 +19,7 @@ import * as fs from 'fs';
|
|||
import * as util from 'util';
|
||||
import { Page } from './page';
|
||||
import { Readable } from 'stream';
|
||||
import { assert, mkdirIfNeeded } from './helper';
|
||||
import { assert, mkdirIfNeeded } from './utils/utils';
|
||||
|
||||
export class Download {
|
||||
private _downloadsPath: string;
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, debugLogger } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
||||
import { Protocol } from './protocol';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
|
||||
export const ConnectionEvents = {
|
||||
Disconnected: Symbol('Disconnected'),
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
import * as dialog from '../dialog';
|
||||
import * as dom from '../dom';
|
||||
import * as frames from '../frames';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
||||
import { kScreenshotDuringNavigationError } from '../screenshotter';
|
||||
import * as types from '../types';
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { ConsoleMessage } from './console';
|
||||
import * as dom from './dom';
|
||||
import { assert, helper, RegisteredListener, debugLogger } from './helper';
|
||||
import { helper, RegisteredListener } from './helper';
|
||||
import * as js from './javascript';
|
||||
import * as network from './network';
|
||||
import { Page } from './page';
|
||||
|
@ -26,6 +26,8 @@ import * as types from './types';
|
|||
import { BrowserContext } from './browserContext';
|
||||
import { Progress, ProgressController } from './progress';
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, makeWaitForNextTask } from './utils/utils';
|
||||
import { debugLogger } from './utils/debugLogger';
|
||||
|
||||
type ContextData = {
|
||||
contextPromise: Promise<dom.FrameExecutionContext>;
|
||||
|
@ -134,7 +136,7 @@ export class FrameManager {
|
|||
await barrier.waitFor();
|
||||
this._signalBarriers.delete(barrier);
|
||||
// Resolve in the next task, after all waitForNavigations.
|
||||
await new Promise(helper.makeWaitForNextTask());
|
||||
await new Promise(makeWaitForNextTask());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -879,20 +881,15 @@ export class Frame extends EventEmitter {
|
|||
}
|
||||
|
||||
async _waitForFunctionExpression<R>(expression: string, isFunction: boolean, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
|
||||
const { polling = 'raf' } = options;
|
||||
if (helper.isString(polling))
|
||||
assert(polling === 'raf', 'Unknown polling option: ' + polling);
|
||||
else if (helper.isNumber(polling))
|
||||
assert(polling > 0, 'Cannot poll with non-positive interval: ' + polling);
|
||||
else
|
||||
throw new Error('Unknown polling option: ' + polling);
|
||||
if (typeof options.pollingInterval === 'number')
|
||||
assert(options.pollingInterval > 0, 'Cannot poll with non-positive interval: ' + options.pollingInterval);
|
||||
const predicateBody = isFunction ? 'return (' + expression + ')(arg)' : 'return (' + expression + ')';
|
||||
const task: dom.SchedulableTask<R> = injectedScript => injectedScript.evaluateHandle((injectedScript, { predicateBody, polling, arg }) => {
|
||||
const innerPredicate = new Function('arg', predicateBody) as (arg: any) => R;
|
||||
if (polling === 'raf')
|
||||
if (typeof polling !== 'number')
|
||||
return injectedScript.pollRaf((progress, continuePolling) => innerPredicate(arg) || continuePolling);
|
||||
return injectedScript.pollInterval(polling, (progress, continuePolling) => innerPredicate(arg) || continuePolling);
|
||||
}, { predicateBody, polling, arg });
|
||||
}, { predicateBody, polling: options.pollingInterval, arg });
|
||||
return this._page._runAbortableTask(
|
||||
progress => this._scheduleRerunnableHandleTask(progress, 'main', task),
|
||||
this._page._timeoutSettings.timeout(options));
|
||||
|
|
153
src/helper.ts
153
src/helper.ts
|
@ -21,14 +21,11 @@ import * as fs from 'fs';
|
|||
import * as os from 'os';
|
||||
import * as removeFolder from 'rimraf';
|
||||
import * as util from 'util';
|
||||
import * as path from 'path';
|
||||
import * as types from './types';
|
||||
import { Progress } from './progress';
|
||||
import * as debug from 'debug';
|
||||
|
||||
const removeFolderAsync = util.promisify(removeFolder);
|
||||
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
const mkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||
|
||||
export type RegisteredListener = {
|
||||
emitter: EventEmitter;
|
||||
|
@ -36,10 +33,6 @@ export type RegisteredListener = {
|
|||
handler: (...args: any[]) => void;
|
||||
};
|
||||
|
||||
export type Listener = (...args: any[]) => void;
|
||||
|
||||
const isInDebugMode = !!getFromENV('PWDEBUG');
|
||||
|
||||
class Helper {
|
||||
static addEventListener(
|
||||
emitter: EventEmitter,
|
||||
|
@ -59,30 +52,6 @@ class Helper {
|
|||
listeners.splice(0, listeners.length);
|
||||
}
|
||||
|
||||
static isString(obj: any): obj is string {
|
||||
return typeof obj === 'string' || obj instanceof String;
|
||||
}
|
||||
|
||||
static isNumber(obj: any): obj is number {
|
||||
return typeof obj === 'number' || obj instanceof Number;
|
||||
}
|
||||
|
||||
static isRegExp(obj: any): obj is RegExp {
|
||||
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||
}
|
||||
|
||||
static isError(obj: any): obj is Error {
|
||||
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
||||
}
|
||||
|
||||
static isObject(obj: any): obj is NonNullable<object> {
|
||||
return typeof obj === 'object' && obj !== null;
|
||||
}
|
||||
|
||||
static isBoolean(obj: any): obj is boolean {
|
||||
return typeof obj === 'boolean' || obj instanceof Boolean;
|
||||
}
|
||||
|
||||
static completeUserURL(urlString: string): string {
|
||||
if (urlString.startsWith('localhost') || urlString.startsWith('127.0.0.1'))
|
||||
urlString = 'http://' + urlString;
|
||||
|
@ -101,41 +70,6 @@ class Helper {
|
|||
return { width: Math.floor(size.width + 1e-3), height: Math.floor(size.height + 1e-3) };
|
||||
}
|
||||
|
||||
// See https://joel.tools/microtasks/
|
||||
static makeWaitForNextTask() {
|
||||
if (parseInt(process.versions.node, 10) >= 11)
|
||||
return setImmediate;
|
||||
|
||||
// Unlike Node 11, Node 10 and less have a bug with Task and MicroTask execution order:
|
||||
// - https://github.com/nodejs/node/issues/22257
|
||||
//
|
||||
// So we can't simply run setImmediate to dispatch code in a following task.
|
||||
// However, we can run setImmediate from-inside setImmediate to make sure we're getting
|
||||
// in the following task.
|
||||
|
||||
let spinning = false;
|
||||
const callbacks: (() => void)[] = [];
|
||||
const loop = () => {
|
||||
const callback = callbacks.shift();
|
||||
if (!callback) {
|
||||
spinning = false;
|
||||
return;
|
||||
}
|
||||
setImmediate(loop);
|
||||
// Make sure to call callback() as the last thing since it's
|
||||
// untrusted code that might throw.
|
||||
callback();
|
||||
};
|
||||
|
||||
return (callback: () => void) => {
|
||||
callbacks.push(callback);
|
||||
if (!spinning) {
|
||||
spinning = true;
|
||||
setImmediate(loop);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static guid(): string {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
@ -176,10 +110,6 @@ class Helper {
|
|||
progress.cleanupWhenAborted(dispose);
|
||||
return { promise, dispose };
|
||||
}
|
||||
|
||||
static isDebugMode(): boolean {
|
||||
return isInDebugMode;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUbuntuVersion(): Promise<string> {
|
||||
|
@ -221,87 +151,4 @@ function getUbuntuVersionInternal(osReleaseText: string): string {
|
|||
return fields.get('version_id') || '';
|
||||
}
|
||||
|
||||
export function assert(value: any, message?: string): asserts value {
|
||||
if (!value)
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
let _isUnderTest = false;
|
||||
|
||||
export function setUnderTest() {
|
||||
_isUnderTest = true;
|
||||
}
|
||||
|
||||
export function isUnderTest(): boolean {
|
||||
return _isUnderTest;
|
||||
}
|
||||
|
||||
export function debugAssert(value: any, message?: string): asserts value {
|
||||
if (_isUnderTest && !value)
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
export function getFromENV(name: string) {
|
||||
let value = process.env[name];
|
||||
value = value || process.env[`npm_config_${name.toLowerCase()}`];
|
||||
value = value || process.env[`npm_package_config_${name.toLowerCase()}`];
|
||||
return value;
|
||||
}
|
||||
|
||||
export async function doSlowMo(amount?: number) {
|
||||
if (!amount)
|
||||
return;
|
||||
await new Promise(x => setTimeout(x, amount));
|
||||
}
|
||||
|
||||
export async function mkdirIfNeeded(filePath: string) {
|
||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||
await mkdirAsync(path.dirname(filePath), {recursive: true}).catch(() => {});
|
||||
}
|
||||
|
||||
export const helper = Helper;
|
||||
|
||||
const debugLoggerColorMap = {
|
||||
'api': 45, // cyan
|
||||
'protocol': 34, // green
|
||||
'browser': 0, // reset
|
||||
'error': 160, // red,
|
||||
'channel:command': 33, // blue
|
||||
'channel:response': 202, // orange
|
||||
'channel:event': 207, // magenta
|
||||
};
|
||||
export type LogName = keyof typeof debugLoggerColorMap;
|
||||
|
||||
export class DebugLogger {
|
||||
private _debuggers = new Map<string, debug.IDebugger>();
|
||||
|
||||
constructor() {
|
||||
if (process.env.DEBUG_FILE) {
|
||||
const ansiRegex = new RegExp([
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|'), 'g');
|
||||
const stream = fs.createWriteStream(process.env.DEBUG_FILE);
|
||||
(debug as any).log = (data: string) => {
|
||||
stream.write(data.replace(ansiRegex, ''));
|
||||
stream.write('\n');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
log(name: LogName, message: string | Error | object) {
|
||||
let cachedDebugger = this._debuggers.get(name);
|
||||
if (!cachedDebugger) {
|
||||
cachedDebugger = debug(`pw:${name}`);
|
||||
this._debuggers.set(name, cachedDebugger);
|
||||
(cachedDebugger as any).color = debugLoggerColorMap[name];
|
||||
}
|
||||
cachedDebugger(message);
|
||||
}
|
||||
|
||||
isEnabled(name: LogName) {
|
||||
return debug.enabled(`pw:${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const debugLogger = new DebugLogger();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert } from './helper';
|
||||
import { assert } from './utils/utils';
|
||||
import * as keyboardLayout from './usKeyboardLayout';
|
||||
import * as types from './types';
|
||||
import type { Page } from './page';
|
||||
|
|
|
@ -23,7 +23,7 @@ import * as ProgressBar from 'progress';
|
|||
import { getProxyForUrl } from 'proxy-from-env';
|
||||
import * as URL from 'url';
|
||||
import * as util from 'util';
|
||||
import { assert, getFromENV } from '../helper';
|
||||
import { assert, getFromENV } from '../utils/utils';
|
||||
import * as browserPaths from './browserPaths';
|
||||
import { BrowserName, BrowserPlatform, BrowserDescriptor } from './browserPaths';
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
import { execSync } from 'child_process';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { getFromENV, getUbuntuVersionSync } from '../helper';
|
||||
import { getUbuntuVersionSync } from '../helper';
|
||||
import { getFromENV } from '../utils/utils';
|
||||
|
||||
export type BrowserName = 'chromium'|'webkit'|'firefox';
|
||||
export type BrowserPlatform = 'win32'|'win64'|'mac10.13'|'mac10.14'|'mac10.15'|'ubuntu18.04'|'ubuntu20.04';
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import { getFromENV } from '../helper';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import * as removeFolder from 'rimraf';
|
||||
import * as browserPaths from './browserPaths';
|
||||
import * as browserFetcher from './browserFetcher';
|
||||
import { getFromENV } from '../utils/utils';
|
||||
|
||||
const fsMkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||
const fsReaddirAsync = util.promisify(fs.readdir.bind(fs));
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import { assert } from './helper';
|
||||
import { assert } from './utils/utils';
|
||||
|
||||
export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] {
|
||||
const parsedURLs = urls.map(s => new URL(s));
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
|
||||
import * as dom from './dom';
|
||||
import * as frames from './frames';
|
||||
import { assert, helper, debugLogger } from './helper';
|
||||
import * as input from './input';
|
||||
import * as js from './javascript';
|
||||
import * as network from './network';
|
||||
import { Screenshotter } from './screenshotter';
|
||||
import { TimeoutSettings } from './timeoutSettings';
|
||||
import { TimeoutSettings } from './utils/timeoutSettings';
|
||||
import * as types from './types';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ConsoleMessage } from './console';
|
||||
|
@ -30,6 +29,8 @@ import * as accessibility from './accessibility';
|
|||
import { EventEmitter } from 'events';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { Progress, runAbortableTask } from './progress';
|
||||
import { assert, isError } from './utils/utils';
|
||||
import { debugLogger } from './utils/debugLogger';
|
||||
|
||||
export interface PageDelegate {
|
||||
readonly rawMouse: input.RawMouse;
|
||||
|
@ -462,7 +463,7 @@ export class PageBinding {
|
|||
const result = await binding!.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, ...args);
|
||||
context.evaluateInternal(deliverResult, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
||||
} catch (error) {
|
||||
if (helper.isError(error))
|
||||
if (isError(error))
|
||||
context.evaluateInternal(deliverError, { name, seq, message: error.message, stack: error.stack }).catch(e => debugLogger.log('error', e));
|
||||
else
|
||||
context.evaluateInternal(deliverErrorValue, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
*/
|
||||
|
||||
import { TimeoutError } from './utils/errors';
|
||||
import { assert, debugLogger, LogName } from './helper';
|
||||
import { assert } from './utils/utils';
|
||||
import { rewriteErrorMessage } from './utils/stackTrace';
|
||||
import { debugLogger, LogName } from './utils/debugLogger';
|
||||
|
||||
export interface Progress {
|
||||
readonly aborted: Promise<void>;
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
*/
|
||||
|
||||
import { TimeoutError } from '../utils/errors';
|
||||
import { helper } from '../helper';
|
||||
import { SerializedError, SerializedValue } from './channels';
|
||||
|
||||
export function serializeError(e: any): SerializedError {
|
||||
if (helper.isError(e))
|
||||
if (isError(e))
|
||||
return { error: { message: e.message, stack: e.stack, name: e.name } };
|
||||
return { value: serializeValue(e, value => ({ fallThrough: value }), new Set()) };
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { helper } from '../helper';
|
||||
import { makeWaitForNextTask } from '../utils/utils';
|
||||
|
||||
export class Transport {
|
||||
private _pipeWrite: NodeJS.WritableStream;
|
||||
private _data = Buffer.from([]);
|
||||
private _waitForNextTask = helper.makeWaitForNextTask();
|
||||
private _waitForNextTask = makeWaitForNextTask();
|
||||
private _closed = false;
|
||||
private _bytesLeft = 0;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isUnderTest } from '../helper';
|
||||
import { isUnderTest } from '../utils/utils';
|
||||
|
||||
export class ValidationError extends Error {}
|
||||
export type Validator = (arg: any, path: string) => any;
|
||||
|
|
|
@ -20,9 +20,9 @@ import { Page } from './page';
|
|||
import { ChannelOwner } from './channelOwner';
|
||||
import { Events } from './events';
|
||||
import { BrowserType } from './browserType';
|
||||
import { headersObjectToArray } from '../../converters';
|
||||
import { BrowserContextOptions } from './types';
|
||||
import { validateHeaders } from './network';
|
||||
import { headersObjectToArray } from '../../utils/utils';
|
||||
|
||||
export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
|
||||
readonly _contexts = new Set<BrowserContext>();
|
||||
|
|
|
@ -20,13 +20,13 @@ import { Page, BindingCall } from './page';
|
|||
import * as network from './network';
|
||||
import { BrowserContextChannel, BrowserContextInitializer } from '../../protocol/channels';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { isUnderTest, deprecate, evaluationScript, urlMatches } from './clientHelper';
|
||||
import { deprecate, evaluationScript, urlMatches } from './clientHelper';
|
||||
import { Browser } from './browser';
|
||||
import { Events } from './events';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import { TimeoutSettings } from '../../utils/timeoutSettings';
|
||||
import { Waiter } from './waiter';
|
||||
import { headersObjectToArray } from '../../converters';
|
||||
import { URLMatch, Headers, WaitForEventOptions } from './types';
|
||||
import { isUnderTest, headersObjectToArray } from '../../utils/utils';
|
||||
|
||||
export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserContextInitializer> {
|
||||
_pages = new Set<Page>();
|
||||
|
|
|
@ -18,17 +18,16 @@ import { BrowserTypeChannel, BrowserTypeInitializer, BrowserTypeLaunchParams, Br
|
|||
import { Browser } from './browser';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { headersObjectToArray } from '../../converters';
|
||||
import { assert, helper } from '../../helper';
|
||||
import { LaunchOptions, LaunchServerOptions, ConnectOptions, LaunchPersistentContextOptions } from './types';
|
||||
import * as WebSocket from 'ws';
|
||||
import { Connection } from './connection';
|
||||
import { serializeError } from '../../protocol/serializers';
|
||||
import { Events } from './events';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import { TimeoutSettings } from '../../utils/timeoutSettings';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { envObjectToArray } from './clientHelper';
|
||||
import { validateHeaders } from './network';
|
||||
import { assert, makeWaitForNextTask, headersObjectToArray } from '../../utils/utils';
|
||||
|
||||
export interface BrowserServerLauncher {
|
||||
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
|
||||
|
@ -122,7 +121,7 @@ export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeIni
|
|||
// The 'ws' module in node sometimes sends us multiple messages in a single task.
|
||||
const waitForNextTask = options.slowMo
|
||||
? (cb: () => any) => setTimeout(cb, options.slowMo)
|
||||
: helper.makeWaitForNextTask();
|
||||
: makeWaitForNextTask();
|
||||
connection.onmessage = message => {
|
||||
if (ws.readyState !== WebSocket.OPEN) {
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
|
|||
import type { Channel } from '../../protocol/channels';
|
||||
import type { Connection } from './connection';
|
||||
import type { LoggerSink } from './types';
|
||||
import { debugLogger } from '../../helper';
|
||||
import { debugLogger } from '../../utils/debugLogger';
|
||||
|
||||
export abstract class ChannelOwner<T extends Channel = Channel, Initializer = {}> extends EventEmitter {
|
||||
private _connection: Connection;
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isUnderTest as commonIsUnderTest, helper } from '../../helper';
|
||||
import * as types from './types';
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { isString, isRegExp } from '../../utils/utils';
|
||||
|
||||
const deprecatedHits = new Set();
|
||||
export function deprecate(methodName: string, message: string) {
|
||||
|
@ -28,10 +28,6 @@ export function deprecate(methodName: string, message: string) {
|
|||
console.warn(message);
|
||||
}
|
||||
|
||||
export function isUnderTest() {
|
||||
return commonIsUnderTest();
|
||||
}
|
||||
|
||||
export function envObjectToArray(env: types.Env): { name: string, value: string }[] {
|
||||
const result: { name: string, value: string }[] = [];
|
||||
for (const name in env) {
|
||||
|
@ -49,7 +45,7 @@ export async function evaluationScript(fun: Function | string | { path?: string,
|
|||
}
|
||||
if (arg !== undefined)
|
||||
throw new Error('Cannot evaluate a string with arguments');
|
||||
if (helper.isString(fun))
|
||||
if (isString(fun))
|
||||
return fun;
|
||||
if (fun.content !== undefined)
|
||||
return fun.content;
|
||||
|
@ -65,9 +61,9 @@ export async function evaluationScript(fun: Function | string | { path?: string,
|
|||
export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
|
||||
if (match === undefined || match === '')
|
||||
return true;
|
||||
if (helper.isString(match))
|
||||
if (isString(match))
|
||||
match = globToRegex(match);
|
||||
if (helper.isRegExp(match))
|
||||
if (isRegExp(match))
|
||||
return match.test(urlString);
|
||||
if (typeof match === 'string' && match === urlString)
|
||||
return true;
|
||||
|
|
|
@ -39,7 +39,7 @@ import { Stream } from './stream';
|
|||
import { createScheme, Validator, ValidationError } from '../../protocol/validator';
|
||||
import { WebKitBrowser } from './webkitBrowser';
|
||||
import { FirefoxBrowser } from './firefoxBrowser';
|
||||
import { debugLogger } from '../../helper';
|
||||
import { debugLogger } from '../../utils/debugLogger';
|
||||
|
||||
class Root extends ChannelOwner<Channel, {}> {
|
||||
constructor(connection: Connection) {
|
||||
|
|
|
@ -19,7 +19,7 @@ import { BrowserContext } from './browserContext';
|
|||
import { ChannelOwner } from './channelOwner';
|
||||
import { Page } from './page';
|
||||
import { serializeArgument, FuncOn, parseResult, SmartHandle, JSHandle } from './jsHandle';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import { TimeoutSettings } from '../../utils/timeoutSettings';
|
||||
import { Waiter } from './waiter';
|
||||
import { Events } from './events';
|
||||
import { WaitForEventOptions, Env, LoggerSink } from './types';
|
||||
|
|
|
@ -18,12 +18,12 @@ import { ElementHandleChannel, JSHandleInitializer, ElementHandleScrollIntoViewI
|
|||
import { Frame } from './frame';
|
||||
import { FuncOn, JSHandle, serializeArgument, parseResult } from './jsHandle';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { helper, assert, mkdirIfNeeded } from '../../helper';
|
||||
import { SelectOption, FilePayload, Rect, SelectOptionOptions } from './types';
|
||||
import * as fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import { assert, isString, mkdirIfNeeded } from '../../utils/utils';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
|
||||
|
@ -246,7 +246,7 @@ export function convertSelectOptionValues(values: string | ElementHandle | Selec
|
|||
assert(values[i] !== null, `options[${i}]: expected object, got null`);
|
||||
if (values[0] instanceof ElementHandle)
|
||||
return { elements: (values as ElementHandle[]).map((v: ElementHandle) => v._elementChannel) };
|
||||
if (helper.isString(values[0]))
|
||||
if (isString(values[0]))
|
||||
return { options: (values as string[]).map(value => ({ value })) };
|
||||
return { options: values as SelectOption[] };
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert } from '../../helper';
|
||||
import { assert } from '../../utils/utils';
|
||||
import { FrameChannel, FrameInitializer, FrameNavigatedEvent, FrameGotoOptions, FrameWaitForSelectorOptions, FrameDispatchEventOptions, FrameSetContentOptions, FrameClickOptions, FrameDblclickOptions, FrameFillOptions, FrameFocusOptions, FrameTextContentOptions, FrameInnerTextOptions, FrameInnerHTMLOptions, FrameGetAttributeOptions, FrameHoverOptions, FrameSetInputFilesOptions, FrameTypeOptions, FramePressOptions, FrameCheckOptions, FrameUncheckOptions } from '../../protocol/channels';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
|
|
|
@ -18,12 +18,11 @@ import { URLSearchParams } from 'url';
|
|||
import { RequestChannel, ResponseChannel, RouteChannel, RequestInitializer, ResponseInitializer, RouteInitializer } from '../../protocol/channels';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Frame } from './frame';
|
||||
import { headersArrayToObject, headersObjectToArray } from '../../converters';
|
||||
import { Headers } from './types';
|
||||
import * as fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import * as util from 'util';
|
||||
import { helper } from '../../helper';
|
||||
import { isString, headersObjectToArray, headersArrayToObject } from '../../utils/utils';
|
||||
|
||||
export type NetworkCookie = {
|
||||
name: string,
|
||||
|
@ -175,7 +174,7 @@ export class Route extends ChannelOwner<RouteChannel, RouteInitializer> {
|
|||
body = buffer.toString('base64');
|
||||
isBase64 = true;
|
||||
length = buffer.length;
|
||||
} else if (helper.isString(response.body)) {
|
||||
} else if (isString(response.body)) {
|
||||
body = response.body;
|
||||
isBase64 = false;
|
||||
length = Buffer.byteLength(body);
|
||||
|
@ -204,7 +203,7 @@ export class Route extends ChannelOwner<RouteChannel, RouteInitializer> {
|
|||
}
|
||||
|
||||
async continue(overrides: { method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
|
||||
const postDataBuffer = helper.isString(overrides.postData) ? Buffer.from(overrides.postData, 'utf8') : overrides.postData;
|
||||
const postDataBuffer = isString(overrides.postData) ? Buffer.from(overrides.postData, 'utf8') : overrides.postData;
|
||||
await this._channel.continue({
|
||||
method: overrides.method,
|
||||
headers: overrides.headers ? headersObjectToArray(overrides.headers) : undefined,
|
||||
|
@ -284,7 +283,7 @@ export class Response extends ChannelOwner<ResponseChannel, ResponseInitializer>
|
|||
export function validateHeaders(headers: Headers) {
|
||||
for (const key of Object.keys(headers)) {
|
||||
const value = headers[key];
|
||||
if (!Object.is(value, undefined) && !helper.isString(value))
|
||||
if (!Object.is(value, undefined) && !isString(value))
|
||||
throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
*/
|
||||
|
||||
import { Events } from './events';
|
||||
import { assert, helper, Listener, mkdirIfNeeded } from '../../helper';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import { assert } from '../../utils/utils';
|
||||
import { TimeoutSettings } from '../../utils/timeoutSettings';
|
||||
import { BindingCallChannel, BindingCallInitializer, PageChannel, PageInitializer, PagePdfParams, FrameWaitForSelectorOptions, FrameDispatchEventOptions, FrameSetContentOptions, FrameGotoOptions, PageReloadOptions, PageGoBackOptions, PageGoForwardOptions, PageScreenshotOptions, FrameClickOptions, FrameDblclickOptions, FrameFillOptions, FrameFocusOptions, FrameTextContentOptions, FrameInnerTextOptions, FrameInnerHTMLOptions, FrameGetAttributeOptions, FrameHoverOptions, FrameSetInputFilesOptions, FrameTypeOptions, FramePressOptions, FrameCheckOptions, FrameUncheckOptions } from '../../protocol/channels';
|
||||
import { parseError, serializeError } from '../../protocol/serializers';
|
||||
import { headersObjectToArray } from '../../converters';
|
||||
import { Accessibility } from './accessibility';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
|
@ -42,6 +41,7 @@ import * as fs from 'fs';
|
|||
import * as util from 'util';
|
||||
import { Size, URLMatch, Headers, LifecycleEvent, WaitForEventOptions, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions } from './types';
|
||||
import { evaluationScript, urlMatches } from './clientHelper';
|
||||
import { isString, isRegExp, isObject, mkdirIfNeeded, headersObjectToArray } from '../../utils/utils';
|
||||
|
||||
type PDFOptions = Omit<PagePdfParams, 'width' | 'height' | 'margin'> & {
|
||||
width?: string | number,
|
||||
|
@ -54,6 +54,7 @@ type PDFOptions = Omit<PagePdfParams, 'width' | 'height' | 'margin'> & {
|
|||
},
|
||||
path?: string,
|
||||
};
|
||||
type Listener = (...args: any[]) => void;
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
|
||||
|
@ -197,8 +198,8 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
|||
}
|
||||
|
||||
frame(options: string | { name?: string, url?: URLMatch }): Frame | null {
|
||||
const name = helper.isString(options) ? options : options.name;
|
||||
const url = helper.isObject(options) ? options.url : undefined;
|
||||
const name = isString(options) ? options : options.name;
|
||||
const url = isObject(options) ? options.url : undefined;
|
||||
assert(name || url, 'Either name or url matcher should be specified');
|
||||
return this.frames().find(f => {
|
||||
if (name)
|
||||
|
@ -330,7 +331,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
|||
|
||||
async waitForRequest(urlOrPredicate: string | RegExp | ((r: Request) => boolean), options: { timeout?: number } = {}): Promise<Request> {
|
||||
const predicate = (request: Request) => {
|
||||
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
|
||||
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
|
||||
return urlMatches(request.url(), urlOrPredicate);
|
||||
return urlOrPredicate(request);
|
||||
};
|
||||
|
@ -339,7 +340,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
|||
|
||||
async waitForResponse(urlOrPredicate: string | RegExp | ((r: Response) => boolean), options: { timeout?: number } = {}): Promise<Response> {
|
||||
const predicate = (response: Response) => {
|
||||
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
|
||||
if (isString(urlOrPredicate) || isRegExp(urlOrPredicate))
|
||||
return urlMatches(response.url(), urlOrPredicate);
|
||||
return urlOrPredicate(response);
|
||||
};
|
||||
|
|
|
@ -19,8 +19,8 @@ import type { Playwright as PlaywrightImpl } from '../server/playwright';
|
|||
import type { Playwright as PlaywrightAPI } from './client/playwright';
|
||||
import { PlaywrightDispatcher } from './server/playwrightDispatcher';
|
||||
import { Connection } from './client/connection';
|
||||
import { isUnderTest } from '../helper';
|
||||
import { BrowserServerLauncherImpl } from './browserServerImpl';
|
||||
import { isUnderTest } from '../utils/utils';
|
||||
|
||||
export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI {
|
||||
const clientConnection = new Connection();
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { helper, debugAssert, assert } from '../../helper';
|
||||
import { helper } from '../../helper';
|
||||
import * as channels from '../../protocol/channels';
|
||||
import { serializeError } from '../../protocol/serializers';
|
||||
import { createScheme, Validator, ValidationError } from '../../protocol/validator';
|
||||
import { assert, debugAssert } from '../../utils/utils';
|
||||
|
||||
export const dispatcherSymbol = Symbol('dispatcher');
|
||||
|
||||
|
|
|
@ -173,11 +173,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
|||
}
|
||||
|
||||
async waitForFunction(params: channels.FrameWaitForFunctionParams): Promise<channels.FrameWaitForFunctionResult> {
|
||||
const options = {
|
||||
...params,
|
||||
polling: params.pollingInterval === undefined ? 'raf' as const : params.pollingInterval
|
||||
};
|
||||
return { handle: createHandle(this._scope, await this._frame._waitForFunctionExpression(params.expression, params.isFunction, parseArgument(params.arg), options)) };
|
||||
return { handle: createHandle(this._scope, await this._frame._waitForFunctionExpression(params.expression, params.isFunction, parseArgument(params.arg), params)) };
|
||||
}
|
||||
|
||||
async title(): Promise<channels.FrameTitleResult> {
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
*/
|
||||
|
||||
import * as dom from './dom';
|
||||
import { assert, helper } from './helper';
|
||||
import { helper } from './helper';
|
||||
import { Page } from './page';
|
||||
import * as types from './types';
|
||||
import { rewriteErrorMessage } from './utils/stackTrace';
|
||||
import { Progress } from './progress';
|
||||
import { assert } from './utils/utils';
|
||||
|
||||
export class Screenshotter {
|
||||
private _queue = new TaskQueue();
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import * as dom from './dom';
|
||||
import * as frames from './frames';
|
||||
import { helper, assert } from './helper';
|
||||
import * as js from './javascript';
|
||||
import * as types from './types';
|
||||
import { ParsedSelector, parseSelector } from './common/selectorParser';
|
||||
|
@ -116,7 +115,6 @@ export class Selectors {
|
|||
}
|
||||
|
||||
_parseSelector(selector: string): SelectorInfo {
|
||||
assert(helper.isString(selector), `selector must be a string`);
|
||||
const parsed = parseSelector(selector);
|
||||
for (const {name} of parsed.parts) {
|
||||
if (!this._builtinEngines.has(name) && !this._engines.has(name))
|
||||
|
|
|
@ -22,13 +22,13 @@ import { BrowserContext, verifyProxySettings, validateBrowserContextOptions } fr
|
|||
import * as browserPaths from '../install/browserPaths';
|
||||
import { ConnectionTransport, WebSocketTransport } from '../transport';
|
||||
import { BrowserOptions, Browser, BrowserProcess } from '../browser';
|
||||
import { assert, helper } from '../helper';
|
||||
import { launchProcess, Env, waitForLine, envArrayToObject } from './processLauncher';
|
||||
import { PipeTransport } from './pipeTransport';
|
||||
import { Progress, runAbortableTask } from '../progress';
|
||||
import * as types from '../types';
|
||||
import { TimeoutSettings } from '../timeoutSettings';
|
||||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { validateHostRequirements } from './validateDependencies';
|
||||
import { assert, isDebugMode } from '../utils/utils';
|
||||
|
||||
export interface BrowserType {
|
||||
executablePath(): string;
|
||||
|
@ -223,7 +223,7 @@ function copyTestHooks(from: object, to: object) {
|
|||
}
|
||||
|
||||
function validateLaunchOptions<Options extends types.LaunchOptions>(options: Options): Options {
|
||||
const { devtools = false, headless = !helper.isDebugMode() && !devtools } = options;
|
||||
const { devtools = false, headless = !isDebugMode() && !devtools } = options;
|
||||
return { ...options, devtools, headless };
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { getFromENV, helper } from '../helper';
|
||||
import { CRBrowser } from '../chromium/crBrowser';
|
||||
import { Env } from './processLauncher';
|
||||
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
||||
|
@ -28,6 +27,7 @@ import { BrowserDescriptor } from '../install/browserPaths';
|
|||
import { CRDevTools } from '../chromium/crDevTools';
|
||||
import { BrowserOptions } from '../browser';
|
||||
import * as types from '../types';
|
||||
import { isDebugMode, getFromENV } from '../utils/utils';
|
||||
|
||||
export class Chromium extends BrowserTypeBase {
|
||||
private _devtools: CRDevTools | undefined;
|
||||
|
@ -43,7 +43,7 @@ export class Chromium extends BrowserTypeBase {
|
|||
|
||||
super(packagePath, browser, debugPort ? { webSocketRegex: /^DevTools listening on (ws:\/\/.*)$/, stream: 'stderr' } : null);
|
||||
this._debugPort = debugPort;
|
||||
if (helper.isDebugMode())
|
||||
if (isDebugMode())
|
||||
this._devtools = this._createDevTools();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CRConnection, CRSession } from '../chromium/crConnection';
|
|||
import { CRExecutionContext } from '../chromium/crExecutionContext';
|
||||
import * as js from '../javascript';
|
||||
import { Page } from '../page';
|
||||
import { TimeoutSettings } from '../timeoutSettings';
|
||||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { WebSocketTransport } from '../transport';
|
||||
import * as types from '../types';
|
||||
import { launchProcess, waitForLine, envArrayToObject } from './processLauncher';
|
||||
|
|
|
@ -15,14 +15,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { helper, RegisteredListener, debugLogger } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
||||
import { makeWaitForNextTask } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
|
||||
export class PipeTransport implements ConnectionTransport {
|
||||
private _pipeWrite: NodeJS.WritableStream;
|
||||
private _pendingMessage = '';
|
||||
private _eventListeners: RegisteredListener[];
|
||||
private _waitForNextTask = helper.makeWaitForNextTask();
|
||||
private _waitForNextTask = makeWaitForNextTask();
|
||||
private _closed = false;
|
||||
|
||||
onmessage?: (message: ProtocolResponse) => void;
|
||||
|
|
|
@ -19,9 +19,10 @@ import * as childProcess from 'child_process';
|
|||
import * as readline from 'readline';
|
||||
import * as removeFolder from 'rimraf';
|
||||
import * as stream from 'stream';
|
||||
import { helper, isUnderTest } from '../helper';
|
||||
import { helper } from '../helper';
|
||||
import { Progress } from '../progress';
|
||||
import * as types from '../types';
|
||||
import { isUnderTest } from '../utils/utils';
|
||||
|
||||
export type Env = {[key: string]: string | number | boolean | undefined};
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
*/
|
||||
|
||||
import * as WebSocket from 'ws';
|
||||
import { helper } from './helper';
|
||||
import { Progress } from './progress';
|
||||
import { makeWaitForNextTask } from './utils/utils';
|
||||
|
||||
export type ProtocolRequest = {
|
||||
id: number;
|
||||
|
@ -85,7 +85,7 @@ export class WebSocketTransport implements ConnectionTransport {
|
|||
// In Web, all IO callbacks (e.g. WebSocket callbacks)
|
||||
// are dispatched into separate tasks, so there's no need
|
||||
// to do anything extra.
|
||||
const messageWrap: (cb: () => void) => void = helper.makeWaitForNextTask();
|
||||
const messageWrap: (cb: () => void) => void = makeWaitForNextTask();
|
||||
|
||||
this._ws.addEventListener('message', event => {
|
||||
messageWrap(() => {
|
||||
|
|
|
@ -26,8 +26,7 @@ export type TimeoutOptions = { timeout?: number };
|
|||
|
||||
export type WaitForElementOptions = TimeoutOptions & { state?: 'attached' | 'detached' | 'visible' | 'hidden' };
|
||||
|
||||
export type Polling = 'raf' | number;
|
||||
export type WaitForFunctionOptions = TimeoutOptions & { polling?: Polling };
|
||||
export type WaitForFunctionOptions = TimeoutOptions & { pollingInterval?: number };
|
||||
|
||||
export type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle';
|
||||
export const kLifecycleEvents: Set<LifecycleEvent> = new Set(['load', 'domcontentloaded', 'networkidle']);
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* 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 debug from 'debug';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const debugLoggerColorMap = {
|
||||
'api': 45, // cyan
|
||||
'protocol': 34, // green
|
||||
'browser': 0, // reset
|
||||
'error': 160, // red,
|
||||
'channel:command': 33, // blue
|
||||
'channel:response': 202, // orange
|
||||
'channel:event': 207, // magenta
|
||||
};
|
||||
export type LogName = keyof typeof debugLoggerColorMap;
|
||||
|
||||
class DebugLogger {
|
||||
private _debuggers = new Map<string, debug.IDebugger>();
|
||||
|
||||
constructor() {
|
||||
if (process.env.DEBUG_FILE) {
|
||||
const ansiRegex = new RegExp([
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|'), 'g');
|
||||
const stream = fs.createWriteStream(process.env.DEBUG_FILE);
|
||||
(debug as any).log = (data: string) => {
|
||||
stream.write(data.replace(ansiRegex, ''));
|
||||
stream.write('\n');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
log(name: LogName, message: string | Error | object) {
|
||||
let cachedDebugger = this._debuggers.get(name);
|
||||
if (!cachedDebugger) {
|
||||
cachedDebugger = debug(`pw:${name}`);
|
||||
this._debuggers.set(name, cachedDebugger);
|
||||
(cachedDebugger as any).color = debugLoggerColorMap[name];
|
||||
}
|
||||
cachedDebugger(message);
|
||||
}
|
||||
|
||||
isEnabled(name: LogName) {
|
||||
return debug.enabled(`pw:${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const debugLogger = new DebugLogger();
|
|
@ -17,7 +17,7 @@
|
|||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { getCallerFilePath } from './stackTrace';
|
||||
import { helper } from '../helper';
|
||||
import { isDebugMode } from './utils';
|
||||
|
||||
type Position = {
|
||||
line: number;
|
||||
|
@ -37,7 +37,7 @@ export function ensureSourceUrl(expression: string): string {
|
|||
}
|
||||
|
||||
export async function generateSourceMapUrl(functionText: string, generatedText: string): Promise<string> {
|
||||
if (!helper.isDebugMode())
|
||||
if (!isDebugMode())
|
||||
return generateSourceUrl();
|
||||
const sourceMapUrl = await innerGenerateSourceMapUrl(functionText, generatedText);
|
||||
return sourceMapUrl || generateSourceUrl();
|
||||
|
|
|
@ -15,10 +15,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TimeoutOptions } from './types';
|
||||
import { helper } from './helper';
|
||||
import { isDebugMode } from './utils';
|
||||
|
||||
const DEFAULT_TIMEOUT = helper.isDebugMode() ? 0 : 30000;
|
||||
const DEFAULT_TIMEOUT = isDebugMode() ? 0 : 30000;
|
||||
|
||||
export class TimeoutSettings {
|
||||
private _parent: TimeoutSettings | undefined;
|
||||
|
@ -37,7 +36,7 @@ export class TimeoutSettings {
|
|||
this._defaultNavigationTimeout = timeout;
|
||||
}
|
||||
|
||||
navigationTimeout(options: TimeoutOptions): number {
|
||||
navigationTimeout(options: { timeout?: number }): number {
|
||||
if (typeof options.timeout === 'number')
|
||||
return options.timeout;
|
||||
if (this._defaultNavigationTimeout !== null)
|
||||
|
@ -49,7 +48,7 @@ export class TimeoutSettings {
|
|||
return DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
timeout(options: TimeoutOptions): number {
|
||||
timeout(options: { timeout?: number }): number {
|
||||
if (typeof options.timeout === 'number')
|
||||
return options.timeout;
|
||||
if (this._defaultTimeout !== null)
|
||||
|
@ -59,7 +58,7 @@ export class TimeoutSettings {
|
|||
return DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
static timeout(options: TimeoutOptions): number {
|
||||
static timeout(options: { timeout?: number }): number {
|
||||
if (typeof options.timeout === 'number')
|
||||
return options.timeout;
|
||||
return DEFAULT_TIMEOUT;
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* 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 path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
|
||||
const mkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||
|
||||
// See https://joel.tools/microtasks/
|
||||
export function makeWaitForNextTask() {
|
||||
if (parseInt(process.versions.node, 10) >= 11)
|
||||
return setImmediate;
|
||||
|
||||
// Unlike Node 11, Node 10 and less have a bug with Task and MicroTask execution order:
|
||||
// - https://github.com/nodejs/node/issues/22257
|
||||
//
|
||||
// So we can't simply run setImmediate to dispatch code in a following task.
|
||||
// However, we can run setImmediate from-inside setImmediate to make sure we're getting
|
||||
// in the following task.
|
||||
|
||||
let spinning = false;
|
||||
const callbacks: (() => void)[] = [];
|
||||
const loop = () => {
|
||||
const callback = callbacks.shift();
|
||||
if (!callback) {
|
||||
spinning = false;
|
||||
return;
|
||||
}
|
||||
setImmediate(loop);
|
||||
// Make sure to call callback() as the last thing since it's
|
||||
// untrusted code that might throw.
|
||||
callback();
|
||||
};
|
||||
|
||||
return (callback: () => void) => {
|
||||
callbacks.push(callback);
|
||||
if (!spinning) {
|
||||
spinning = true;
|
||||
setImmediate(loop);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function assert(value: any, message?: string): asserts value {
|
||||
if (!value)
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
export function debugAssert(value: any, message?: string): asserts value {
|
||||
if (isUnderTest() && !value)
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
export function isString(obj: any): obj is string {
|
||||
return typeof obj === 'string' || obj instanceof String;
|
||||
}
|
||||
|
||||
export function isRegExp(obj: any): obj is RegExp {
|
||||
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||
}
|
||||
|
||||
export function isObject(obj: any): obj is NonNullable<object> {
|
||||
return typeof obj === 'object' && obj !== null;
|
||||
}
|
||||
|
||||
export function isError(obj: any): obj is Error {
|
||||
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
||||
}
|
||||
|
||||
const isInDebugMode = !!getFromENV('PWDEBUG');
|
||||
export function isDebugMode(): boolean {
|
||||
return isInDebugMode;
|
||||
}
|
||||
|
||||
let _isUnderTest = false;
|
||||
export function setUnderTest() {
|
||||
_isUnderTest = true;
|
||||
}
|
||||
export function isUnderTest(): boolean {
|
||||
return _isUnderTest;
|
||||
}
|
||||
|
||||
export function getFromENV(name: string) {
|
||||
let value = process.env[name];
|
||||
value = value || process.env[`npm_config_${name.toLowerCase()}`];
|
||||
value = value || process.env[`npm_package_config_${name.toLowerCase()}`];
|
||||
return value;
|
||||
}
|
||||
|
||||
export async function mkdirIfNeeded(filePath: string) {
|
||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||
await mkdirAsync(path.dirname(filePath), {recursive: true}).catch(() => {});
|
||||
}
|
||||
|
||||
type HeadersArray = { name: string, value: string }[];
|
||||
type HeadersObject = { [key: string]: string };
|
||||
|
||||
export function headersObjectToArray(headers: HeadersObject): HeadersArray {
|
||||
const result: HeadersArray = [];
|
||||
for (const name in headers) {
|
||||
if (!Object.is(headers[name], undefined))
|
||||
result.push({ name, value: headers[name] });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function headersArrayToObject(headers: HeadersArray, lowerCase: boolean): HeadersObject {
|
||||
const result: HeadersObject = {};
|
||||
for (const { name, value } of headers)
|
||||
result[lowerCase ? name.toLowerCase() : name] = value;
|
||||
return result;
|
||||
}
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { helper, RegisteredListener, assert } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
import * as path from 'path';
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, debugLogger } from '../helper';
|
||||
import { assert } from '../utils/utils';
|
||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
||||
import { Protocol } from './protocol';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
|
||||
// WKPlaywright uses this special id to issue Browser.close command which we
|
||||
// should ignore.
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
import * as input from '../input';
|
||||
import * as types from '../types';
|
||||
import { helper } from '../helper';
|
||||
import { macEditingCommands } from '../macEditingCommands';
|
||||
import { WKSession } from './wkConnection';
|
||||
import { isString } from '../utils/utils';
|
||||
|
||||
function toModifiersMask(modifiers: Set<types.KeyboardModifier>): number {
|
||||
// From Source/WebKit/Shared/WebEvent.h
|
||||
|
@ -56,7 +56,7 @@ export class RawKeyboardImpl implements input.RawKeyboard {
|
|||
parts.push(code);
|
||||
const shortcut = parts.join('+');
|
||||
let commands = macEditingCommands[shortcut];
|
||||
if (helper.isString(commands))
|
||||
if (isString(commands))
|
||||
commands = [commands];
|
||||
await this._pageProxySession.send('Input.dispatchKeyEvent', {
|
||||
type: 'keyDown',
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
*/
|
||||
|
||||
import * as frames from '../frames';
|
||||
import { assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import { Protocol } from './protocol';
|
||||
import { WKSession } from './wkConnection';
|
||||
import { headersArrayToObject, headersObjectToArray } from '../converters';
|
||||
import { assert, headersObjectToArray, headersArrayToObject } from '../utils/utils';
|
||||
|
||||
const errorReasons: { [reason: string]: Protocol.Network.ResourceErrorType } = {
|
||||
'aborted': 'Cancellation',
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { Screencast, BrowserContext } from '../browserContext';
|
||||
import * as frames from '../frames';
|
||||
import { helper, RegisteredListener, assert, debugAssert } from '../helper';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import * as dom from '../dom';
|
||||
import * as network from '../network';
|
||||
import { WKSession } from './wkConnection';
|
||||
|
@ -37,7 +37,7 @@ import { selectors } from '../selectors';
|
|||
import * as jpeg from 'jpeg-js';
|
||||
import * as png from 'pngjs';
|
||||
import { JSHandle } from '../javascript';
|
||||
import { headersArrayToObject } from '../converters';
|
||||
import { assert, debugAssert, headersArrayToObject } from '../utils/utils';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
const BINDING_CALL_MESSAGE = '__playwright_binding_call__';
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
import { WKSession } from './wkConnection';
|
||||
import { WKPage } from './wkPage';
|
||||
import { RegisteredListener, helper, assert } from '../helper';
|
||||
import { RegisteredListener, helper } from '../helper';
|
||||
import { Protocol } from './protocol';
|
||||
import { assert } from '../utils/utils';
|
||||
|
||||
export class WKProvisionalPage {
|
||||
readonly _session: WKSession;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
}
|
||||
|
||||
const path = require('path');
|
||||
const { setUnderTest } = require(path.join(playwrightPath, 'lib', 'helper'));
|
||||
const { setUnderTest } = require(path.join(playwrightPath, 'lib', 'utils', 'utils'));
|
||||
setUnderTest();
|
||||
const playwright = require(path.join(playwrightPath, 'index'));
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import { LaunchOptions, BrowserType, Browser, BrowserContext, Page, BrowserServe
|
|||
import { TestServer } from '../utils/testserver';
|
||||
import { Connection } from '../lib/rpc/client/connection';
|
||||
import { Transport } from '../lib/protocol/transport';
|
||||
import { setUnderTest } from '../lib/helper';
|
||||
import { setUnderTest } from '../lib/utils/utils';
|
||||
import { installCoverageHooks } from './coverage';
|
||||
import { parameters, registerFixture, registerWorkerFixture } from '../test-runner';
|
||||
|
||||
|
|
|
@ -19,6 +19,13 @@ import { options } from './playwright.fixtures';
|
|||
|
||||
import socks from 'socksv5';
|
||||
|
||||
it('should throw for bad server value', async ({browserType, defaultBrowserOptions}) => {
|
||||
const error = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
proxy: { server: 123 as any }
|
||||
}).catch(e => e);
|
||||
expect(error.message).toContain('proxy.server: expected string, got number');
|
||||
});
|
||||
|
||||
it('should use proxy', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
|
||||
import './playwright.fixtures';
|
||||
|
||||
it('should throw for non-string selector', async({page}) => {
|
||||
const error = await page.$(null).catch(e => e);
|
||||
expect(error.message).toContain('selector: expected string, got object');
|
||||
});
|
||||
|
||||
it('should query existing element with css selector', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const element = await page.$('css=section');
|
||||
|
|
|
@ -50,17 +50,28 @@ async function checkDeps() {
|
|||
}
|
||||
|
||||
function allowImport(from, to) {
|
||||
const rpc = path.join('src', 'rpc');
|
||||
if (!from.includes(rpc) && to.includes(rpc))
|
||||
from = from.substring(from.indexOf('src' + path.sep)).replace(/\\/g, '/');
|
||||
to = to.substring(to.indexOf('src' + path.sep)).replace(/\\/g, '/') + '.ts';
|
||||
while (from.lastIndexOf('/') !== -1) {
|
||||
from = from.substring(0, from.lastIndexOf('/'));
|
||||
const allowed = DEPS.get(from + '/');
|
||||
if (!allowed)
|
||||
continue;
|
||||
for (const prefix of allowed) {
|
||||
if (to.startsWith(prefix))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
const rpcClient = path.join('src', 'rpc', 'client');
|
||||
const rpcServer = path.join('src', 'rpc', 'server');
|
||||
if (from.includes(rpcClient) && to.includes(rpcServer))
|
||||
return false;
|
||||
// if (from.includes(rpcClient) && !to.includes(rpc))
|
||||
// return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const DEPS = new Map([
|
||||
['src/utils/', ['src/utils/']],
|
||||
['src/protocol/', ['src/protocol/', 'src/utils/']],
|
||||
['src/rpc/client/', ['src/rpc/client/', 'src/utils/', 'src/protocol/', 'src/chromium/protocol.ts']],
|
||||
['src/', ['src/']], // Allow everything else for now.
|
||||
]);
|
||||
|
||||
checkDeps();
|
||||
|
|
Загрузка…
Ссылка в новой задаче