chore: merge the util exports (#20110)

This commit is contained in:
Pavel Feldman 2023-01-13 13:50:38 -08:00 committed by GitHub
parent 736cf5c585
commit d1c161ce99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 466 additions and 345 deletions

View File

@ -22,28 +22,14 @@
"./package.json": "./package.json",
"./lib/grid/gridServer": "./lib/grid/gridServer.js",
"./lib/outofprocess": "./lib/outofprocess.js",
"./lib/utils": "./lib/utils/index.js",
"./lib/image_tools/stats": "./lib/image_tools/stats.js",
"./lib/image_tools/compare": "./lib/image_tools/compare.js",
"./lib/image_tools/imageChannel": "./lib/image_tools/imageChannel.js",
"./lib/image_tools/colorUtils": "./lib/image_tools/colorUtils.js",
"./lib/common/userAgent": "./lib/common/userAgent.js",
"./lib/containers/docker": "./lib/containers/docker.js",
"./lib/utils/comparators": "./lib/utils/comparators.js",
"./lib/utils/eventsHelper": "./lib/utils/eventsHelper.js",
"./lib/utils/fileUtils": "./lib/utils/fileUtils.js",
"./lib/utils/httpServer": "./lib/utils/httpServer.js",
"./lib/utils/hostPlatform": "./lib/utils/hostPlatform.js",
"./lib/utils/manualPromise": "./lib/utils/manualPromise.js",
"./lib/utils/mimeType": "./lib/utils/mimeType.js",
"./lib/utils/multimap": "./lib/utils/multimap.js",
"./lib/utils/processLauncher": "./lib/utils/processLauncher.js",
"./lib/utils/processLauncherCleanupEntrypoint": "./lib/utils/processLauncherCleanupEntrypoint.js",
"./lib/utils/spawnAsync": "./lib/utils/spawnAsync.js",
"./lib/utils/stackTrace": "./lib/utils/stackTrace.js",
"./lib/utils/timeoutRunner": "./lib/utils/timeoutRunner.js",
"./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js",
"./lib/server": "./lib/server/index.js",
"./lib/utils": "./lib/utils/index.js",
"./lib/utilsBundle": "./lib/utilsBundle.js",
"./lib/zipBundle": "./lib/zipBundle.js",
"./types/protocol": "./types/protocol.d.ts",

View File

@ -19,10 +19,10 @@ import path from 'path';
import * as util from 'util';
import type { Serializable } from '../../types/structs';
import type * as api from '../../types/types';
import type { HeadersArray } from '../common/types';
import type { HeadersArray, NameValue } from '../common/types';
import type * as channels from '@protocol/channels';
import { kBrowserOrContextClosedError } from '../common/errors';
import { assert, headersObjectToArray, isFilePayload, isString, objectToArray } from '../utils';
import { assert, headersObjectToArray, isString } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { ChannelOwner } from './channelOwner';
import { RawHeaders } from './network';
@ -336,4 +336,17 @@ function isJsonContentType(headers?: HeadersArray): boolean {
return value === 'application/json';
}
return false;
}
}
function objectToArray(map?: { [key: string]: any }): NameValue[] | undefined {
if (!map)
return undefined;
const result = [];
for (const [name, value] of Object.entries(map))
result.push({ name, value: String(value) });
return result;
}
function isFilePayload(value: any): boolean {
return typeof value === 'object' && value['name'] && value['mimeType'] && value['buffer'];
}

View File

@ -32,7 +32,7 @@ import { Waiter } from './waiter';
import { Events } from './events';
import type { LifecycleEvent, URLMatch, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions, StrictOptions } from './types';
import { kLifecycleEvents } from './types';
import { urlMatches } from '../common/netUtils';
import { urlMatches } from '../utils/network';
import type * as api from '../../types/types';
import type * as structs from '../../types/structs';
import { debugLogger } from '../common/debugLogger';

View File

@ -29,7 +29,7 @@ import type { Page } from './page';
import { Waiter } from './waiter';
import type * as api from '../../types/types';
import type { HeadersArray, URLMatch } from '../common/types';
import { urlMatches } from '../common/netUtils';
import { urlMatches } from '../utils/network';
import { MultiMap } from '../utils/multimap';
import { APIResponse } from './fetch';
import type { Serializable } from '../../types/structs';

View File

@ -20,7 +20,7 @@ import path from 'path';
import type * as structs from '../../types/structs';
import type * as api from '../../types/types';
import { isSafeCloseError } from '../common/errors';
import { urlMatches } from '../common/netUtils';
import { urlMatches } from '../utils/network';
import { TimeoutSettings } from '../common/timeoutSettings';
import type * as channels from '@protocol/channels';
import { parseError, serializeError } from '../protocol/serializers';

View File

@ -20,8 +20,8 @@ import type { AddressInfo } from 'net';
import net from 'net';
import util from 'util';
import { debugLogger } from './debugLogger';
import { createSocket } from './netUtils';
import { assert, createGuid } from '../utils';
import { createSocket } from '../utils/network';
import { assert, createGuid, } from '../utils';
const dnsLookupAsync = util.promisify(dns.lookup);

View File

@ -18,7 +18,7 @@
import path from 'path';
import { spawnAsync } from '../utils/spawnAsync';
import * as utils from '../utils';
import { getPlaywrightVersion, getUserAgent } from '../common/userAgent';
import { getPlaywrightVersion, getUserAgent } from '../utils/userAgent';
import { urlToWSEndpoint } from '../server/dispatchers/localUtilsDispatcher';
import { WebSocketTransport } from '../server/transport';
import { SocksInterceptor } from '../server/socksInterceptor';

View File

@ -17,7 +17,7 @@
import { debug } from '../utilsBundle';
import { ws as WebSocket } from '../utilsBundle';
import { fork } from 'child_process';
import { getPlaywrightVersion } from '../common/userAgent';
import { getPlaywrightVersion } from '../utils/userAgent';
export function launchGridAgent(agentId: string, gridURL: string, runId: string | undefined) {
const log = debug(`pw:grid:agent:${agentId}`);

View File

@ -20,7 +20,7 @@ import { URL } from 'url';
import type { WebSocketServer, WebSocket, WebSocketRawData } from '../utilsBundle';
import { HttpServer } from '../utils/httpServer';
import { assert, createGuid } from '../utils';
import { getPlaywrightVersion } from '../common/userAgent';
import { getPlaywrightVersion } from '../utils/userAgent';
const defaultOS = 'linux';

View File

@ -18,6 +18,7 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
import type stream from 'stream';
import { CRBrowser } from './crBrowser';
import type { Env } from '../../utils/processLauncher';
import { gracefullyCloseSet } from '../../utils/processLauncher';
@ -31,11 +32,12 @@ import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../brows
import { Browser } from '../browser';
import type * as types from '../types';
import type * as channels from '@protocol/channels';
import type { HTTPRequestParams } from '../../common/netUtils';
import { NET_DEFAULT_TIMEOUT } from '../../common/netUtils';
import { fetchData } from '../../common/netUtils';
import { getUserAgent } from '../../common/userAgent';
import { debugMode, headersArrayToObject, streamToString, wrapInASCIIBox } from '../../utils';
import type { HTTPRequestParams } from '../../utils/network';
import { NET_DEFAULT_TIMEOUT } from '../../utils/network';
import { fetchData } from '../../utils/network';
import { getUserAgent } from '../../utils/userAgent';
import { wrapInASCIIBox } from '../../utils/ascii';
import { debugMode, headersArrayToObject, } from '../../utils';
import { removeFolders } from '../../utils/fileUtils';
import { RecentLogsCollector } from '../../common/debugLogger';
import type { Progress } from '../progress';
@ -364,3 +366,12 @@ function addProtocol(url: string) {
return 'http://' + url;
return url;
}
function streamToString(stream: stream.Readable): Promise<string> {
return new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
});
}

View File

@ -30,11 +30,11 @@ import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
import { WebSocketTransport } from '../transport';
import { SocksInterceptor } from '../socksInterceptor';
import type { CallMetadata } from '../instrumentation';
import { getUserAgent } from '../../common/userAgent';
import { getUserAgent } from '../../utils/userAgent';
import type { Progress } from '../progress';
import { ProgressController } from '../progress';
import { fetchData } from '../../common/netUtils';
import type { HTTPRequestParams } from '../../common/netUtils';
import { fetchData } from '../../utils/network';
import type { HTTPRequestParams } from '../../utils/network';
import type http from 'http';
import type { Playwright } from '../playwright';
import { SdkObject } from '../../server/instrumentation';

View File

@ -24,7 +24,7 @@ import url from 'url';
import zlib from 'zlib';
import type { HTTPCredentials } from '../../types/types';
import { TimeoutSettings } from '../common/timeoutSettings';
import { getUserAgent } from '../common/userAgent';
import { getUserAgent } from '../utils/userAgent';
import { assert, createGuid, monotonicTime } from '../utils';
import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle';
import { BrowserContext } from './browserContext';

View File

@ -27,8 +27,8 @@ import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils/eventsHelper';
import { mime } from '../../utilsBundle';
import { ManualPromise } from '../../utils/manualPromise';
import { getPlaywrightVersion } from '../../common/userAgent';
import { urlMatches } from '../../common/netUtils';
import { getPlaywrightVersion } from '../../utils/userAgent';
import { urlMatches } from '../../utils/network';
import { Frame } from '../frames';
import type { HeadersArray, LifecycleEvent } from '../types';
import { isTextualMimeType } from '../../utils/mimeType';

View File

@ -19,7 +19,7 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import childProcess from 'child_process';
import { getUserAgent } from '../../common/userAgent';
import { getUserAgent } from '../../utils/userAgent';
import { existsAsync } from '../../utils/fileUtils';
import { debugLogger } from '../../common/debugLogger';
import { extract } from '../../zipBundle';

View File

@ -23,7 +23,7 @@ import { spawnAsync } from '../../utils/spawnAsync';
import { hostPlatform } from '../../utils/hostPlatform';
import { buildPlaywrightCLICommand } from '.';
import { deps } from './nativeDeps';
import { getPlaywrightVersion } from '../../common/userAgent';
import { getPlaywrightVersion } from '../../utils/userAgent';
const BIN_DIRECTORY = path.join(__dirname, '..', '..', '..', 'bin');
const languageBindingVersion = process.env.PW_CLI_DISPLAY_VERSION || require('../../../package.json').version;

View File

@ -21,8 +21,8 @@ import * as util from 'util';
import * as fs from 'fs';
import { lockfile } from '../../utilsBundle';
import { getLinuxDistributionInfo } from '../../utils/linuxUtils';
import { fetchData } from '../../common/netUtils';
import { getEmbedderName } from '../../common/userAgent';
import { fetchData } from '../../utils/network';
import { getEmbedderName } from '../../utils/userAgent';
import { getFromENV, getAsBooleanFromENV, calculateSha1, wrapInASCIIBox } from '../../utils';
import { removeFolders, existsAsync, canAccessFile } from '../../utils/fileUtils';
import { hostPlatform } from '../../utils/hostPlatform';

View File

@ -16,7 +16,7 @@
import fs from 'fs';
import { progress as ProgressBar } from '../../utilsBundle';
import { httpRequest } from '../../common/netUtils';
import { httpRequest } from '../../utils/network';
import { ManualPromise } from '../../utils/manualPromise';
type OnProgressCallback = (downloadedBytes: number, totalBytes: number) => void;

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function wrapInASCIIBox(text: string, padding = 0): string {
const lines = text.split('\n');
const maxLength = Math.max(...lines.map(line => line.length));
return [
'╔' + '═'.repeat(maxLength + padding * 2) + '╗',
...lines.map(line => '║' + ' '.repeat(padding) + line + ' '.repeat(maxLength - line.length + padding) + '║'),
'╚' + '═'.repeat(maxLength + padding * 2) + '╝',
].join('\n');
}

View File

@ -0,0 +1,27 @@
/**
* 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 crypto from 'crypto';
export function createGuid(): string {
return crypto.randomBytes(16).toString('hex');
}
export function calculateSha1(buffer: Buffer | string): string {
const hash = crypto.createHash('sha1');
hash.update(buffer);
return hash.digest('hex');
}

View File

@ -0,0 +1,45 @@
/**
* 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 { getFromENV } from './env';
export function assert(value: any, message?: string): asserts value {
if (!value)
throw new Error(message || 'Assertion error');
}
export function debugAssert(value: any, message?: string): asserts value {
if (isUnderTest() && !value)
throw new Error(message);
}
const debugEnv = getFromENV('PWDEBUG') || '';
export function debugMode() {
if (debugEnv === 'console')
return 'console';
if (debugEnv === '0' || debugEnv === 'false')
return '';
return debugEnv ? 'inspector' : '';
}
let _isUnderTest = false;
export function setUnderTest() {
_isUnderTest = true;
}
export function isUnderTest(): boolean {
return _isUnderTest;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function getFromENV(name: string): string | undefined {
let value = process.env[name];
value = value === undefined ? process.env[`npm_config_${name.toLowerCase()}`] : value;
value = value === undefined ? process.env[`npm_package_config_${name.toLowerCase()}`] : value;
return value;
}
export function getAsBooleanFromENV(name: string): boolean {
const value = getFromENV(name);
return !!value && value !== 'false' && value !== '0';
}

View File

@ -0,0 +1,73 @@
/**
* 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.
*/
const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);
export function globToRegex(glob: string): RegExp {
const tokens = ['^'];
let inGroup;
for (let i = 0; i < glob.length; ++i) {
const c = glob[i];
if (escapeGlobChars.has(c)) {
tokens.push('\\' + c);
continue;
}
if (c === '*') {
const beforeDeep = glob[i - 1];
let starCount = 1;
while (glob[i + 1] === '*') {
starCount++;
i++;
}
const afterDeep = glob[i + 1];
const isDeep = starCount > 1 &&
(beforeDeep === '/' || beforeDeep === undefined) &&
(afterDeep === '/' || afterDeep === undefined);
if (isDeep) {
tokens.push('((?:[^/]*(?:\/|$))*)');
i++;
} else {
tokens.push('([^/]*)');
}
continue;
}
switch (c) {
case '?':
tokens.push('.');
break;
case '{':
inGroup = true;
tokens.push('(');
break;
case '}':
inGroup = false;
tokens.push(')');
break;
case ',':
if (inGroup) {
tokens.push('|');
break;
}
tokens.push('\\' + c);
break;
default:
tokens.push(c);
}
}
tokens.push('$');
return new RegExp(tokens.join(''));
}

View File

@ -0,0 +1,44 @@
/**
* 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.
*/
type HeadersArray = { name: string, value: string }[];
type HeadersObject = { [key: string]: string };
export function headersObjectToArray(headers: HeadersObject, separator?: string, setCookieSeparator?: string): HeadersArray {
if (!setCookieSeparator)
setCookieSeparator = separator;
const result: HeadersArray = [];
for (const name in headers) {
const values = headers[name];
if (values === undefined)
continue;
if (separator) {
const sep = name.toLowerCase() === 'set-cookie' ? setCookieSeparator : separator;
for (const value of values.split(sep!))
result.push({ name, value: value.trim() });
} else {
result.push({ name, value: values });
}
}
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;
}

View File

@ -14,206 +14,25 @@
* limitations under the License.
*/
import * as crypto from 'crypto';
import type stream from 'stream';
import * as URL from 'url';
import v8 from 'v8';
type NameValue = {
name: string,
value: string,
};
// See https://joel.tools/microtasks/
export function makeWaitForNextTask() {
// As of Mar 2021, Electron v12 doesn't create new task with `setImmediate` despite
// using Node 14 internally, so we fallback to `setTimeout(0)` instead.
// @see https://github.com/electron/electron/issues/28261
if ((process.versions as any).electron)
return (callback: () => void) => setTimeout(callback, 0);
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 || 'Assertion error');
}
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 && Object.getPrototypeOf(obj)?.name === 'Error');
}
const debugEnv = getFromENV('PWDEBUG') || '';
export function debugMode() {
if (debugEnv === 'console')
return 'console';
if (debugEnv === '0' || debugEnv === 'false')
return '';
return debugEnv ? 'inspector' : '';
}
let _isUnderTest = false;
export function setUnderTest() {
_isUnderTest = true;
}
export function isUnderTest(): boolean {
return _isUnderTest;
}
export function getFromENV(name: string): string | undefined {
let value = process.env[name];
value = value === undefined ? process.env[`npm_config_${name.toLowerCase()}`] : value;
value = value === undefined ? process.env[`npm_package_config_${name.toLowerCase()}`] : value;
return value;
}
export function getAsBooleanFromENV(name: string): boolean {
const value = getFromENV(name);
return !!value && value !== 'false' && value !== '0';
}
type HeadersArray = { name: string, value: string }[];
type HeadersObject = { [key: string]: string };
export function headersObjectToArray(headers: HeadersObject, separator?: string, setCookieSeparator?: string): HeadersArray {
if (!setCookieSeparator)
setCookieSeparator = separator;
const result: HeadersArray = [];
for (const name in headers) {
const values = headers[name];
if (values === undefined)
continue;
if (separator) {
const sep = name.toLowerCase() === 'set-cookie' ? setCookieSeparator : separator;
for (const value of values.split(sep!))
result.push({ name, value: value.trim() });
} else {
result.push({ name, value: values });
}
}
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;
}
export function monotonicTime(): number {
const [seconds, nanoseconds] = process.hrtime();
return seconds * 1000 + (nanoseconds / 1000 | 0) / 1000;
}
export function objectToArray(map?: { [key: string]: any }): NameValue[] | undefined {
if (!map)
return undefined;
const result = [];
for (const [name, value] of Object.entries(map))
result.push({ name, value: String(value) });
return result;
}
export function arrayToObject(array?: NameValue[]): { [key: string]: string } | undefined {
if (!array)
return undefined;
const result: { [key: string]: string } = {};
for (const { name, value } of array)
result[name] = value;
return result;
}
export function calculateSha1(buffer: Buffer | string): string {
const hash = crypto.createHash('sha1');
hash.update(buffer);
return hash.digest('hex');
}
export function createGuid(): string {
return crypto.randomBytes(16).toString('hex');
}
export function constructURLBasedOnBaseURL(baseURL: string | undefined, givenURL: string): string {
try {
return (new URL.URL(givenURL, baseURL)).toString();
} catch (e) {
return givenURL;
}
}
export function wrapInASCIIBox(text: string, padding = 0): string {
const lines = text.split('\n');
const maxLength = Math.max(...lines.map(line => line.length));
return [
'╔' + '═'.repeat(maxLength + padding * 2) + '╗',
...lines.map(line => '║' + ' '.repeat(padding) + line + ' '.repeat(maxLength - line.length + padding) + '║'),
'╚' + '═'.repeat(maxLength + padding * 2) + '╝',
].join('\n');
}
export function isFilePayload(value: any): boolean {
return typeof value === 'object' && value['name'] && value['mimeType'] && value['buffer'];
}
export function streamToString(stream: stream.Readable): Promise<string> {
return new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
});
}
export const isLikelyNpxGlobal = () => process.argv.length >= 2 && process.argv[1].includes('_npx');
export function deepCopy<T>(obj: T): T {
return v8.deserialize(v8.serialize(obj));
}
export * from './ascii';
export * from './comparators';
export * from './crypto';
export * from './debug';
export * from './env';
export * from './fileUtils';
export * from './glob';
export * from './headers';
export * from './httpServer';
export * from './manualPromise';
export * from './mimeType';
export * from './multimap';
export * from './network';
export * from './processLauncher';
export * from './rtti';
export * from './spawnAsync';
export * from './stackTrace';
export * from './task';
export * from './time';
export * from './timeoutRunner';
export * from './userAgent';
export * from './zipFile';

View File

@ -14,14 +14,16 @@
* limitations under the License.
*/
import http from 'http';
import https from 'https';
import net from 'net';
import { getProxyForUrl } from '../utilsBundle';
import { HttpsProxyAgent } from '../utilsBundle';
import * as URL from 'url';
import type { URLMatch } from './types';
import { isString, constructURLBasedOnBaseURL, isRegExp } from '../utils';
import type { URLMatch } from '../common/types';
import { isString, isRegExp } from './rtti';
import { globToRegex } from './glob';
export async function createSocket(host: string, port: number): Promise<net.Socket> {
return new Promise((resolve, reject) => {
@ -137,61 +139,10 @@ function parsedURL(url: string): URL | null {
}
}
const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);
// Note: this function is exported so it can be unit-tested.
export function globToRegex(glob: string): RegExp {
const tokens = ['^'];
let inGroup;
for (let i = 0; i < glob.length; ++i) {
const c = glob[i];
if (escapeGlobChars.has(c)) {
tokens.push('\\' + c);
continue;
}
if (c === '*') {
const beforeDeep = glob[i - 1];
let starCount = 1;
while (glob[i + 1] === '*') {
starCount++;
i++;
}
const afterDeep = glob[i + 1];
const isDeep = starCount > 1 &&
(beforeDeep === '/' || beforeDeep === undefined) &&
(afterDeep === '/' || afterDeep === undefined);
if (isDeep) {
tokens.push('((?:[^/]*(?:\/|$))*)');
i++;
} else {
tokens.push('([^/]*)');
}
continue;
}
switch (c) {
case '?':
tokens.push('.');
break;
case '{':
inGroup = true;
tokens.push('(');
break;
case '}':
inGroup = false;
tokens.push(')');
break;
case ',':
if (inGroup) {
tokens.push('|');
break;
}
tokens.push('\\' + c);
break;
default:
tokens.push(c);
}
export function constructURLBasedOnBaseURL(baseURL: string | undefined, givenURL: string): string {
try {
return (new URL.URL(givenURL, baseURL)).toString();
} catch (e) {
return givenURL;
}
tokens.push('$');
return new RegExp(tokens.join(''));
}

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function 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 && Object.getPrototypeOf(obj)?.name === 'Error');
}
export const isLikelyNpxGlobal = () => process.argv.length >= 2 && process.argv[1].includes('_npx');

View File

@ -0,0 +1,55 @@
/**
* 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.
*/
// See https://joel.tools/microtasks/
export function makeWaitForNextTask() {
// As of Mar 2021, Electron v12 doesn't create new task with `setImmediate` despite
// using Node 14 internally, so we fallback to `setTimeout(0)` instead.
// @see https://github.com/electron/electron/issues/28261
if ((process.versions as any).electron)
return (callback: () => void) => setTimeout(callback, 0);
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);
}
};
}

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function monotonicTime(): number {
const [seconds, nanoseconds] = process.hrtime();
return seconds * 1000 + (nanoseconds / 1000 | 0) / 1000;
}

View File

@ -22,7 +22,7 @@ import type { TestResult, Reporter, TestStep, TestError } from '../types/testRep
import type { Suite } from './test';
import type { Loader } from './loader';
import { TestCase } from './test';
import { ManualPromise } from 'playwright-core/lib/utils/manualPromise';
import { ManualPromise } from 'playwright-core/lib/utils';
import { TestTypeImpl } from './testType';
export type TestGroup = {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { pollAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner';
import { pollAgainstTimeout } from 'playwright-core/lib/utils';
import path from 'path';
import {
toBeChecked,

View File

@ -17,7 +17,7 @@
import { formatLocation, debugTest } from './util';
import * as crypto from 'crypto';
import type { FixturesWithLocation, Location, WorkerInfo } from './types';
import { ManualPromise } from 'playwright-core/lib/utils/manualPromise';
import { ManualPromise } from 'playwright-core/lib/utils';
import type { TestInfoImpl } from './testInfo';
import type { FixtureDescription, TimeoutManager } from './timeoutManager';
import { addFatalError } from './globals';

View File

@ -18,8 +18,7 @@ import * as fs from 'fs';
import * as path from 'path';
import type { APIRequestContext, BrowserContext, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
import * as playwrightLibrary from 'playwright-core';
import { createGuid, debugMode } from 'playwright-core/lib/utils';
import { removeFolders } from 'playwright-core/lib/utils/fileUtils';
import { createGuid, debugMode, removeFolders } from 'playwright-core/lib/utils';
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, TraceMode, VideoMode } from '../types/test';
import { store as _baseStore } from './store';
import type { TestInfoImpl } from './testInfo';

View File

@ -17,15 +17,13 @@
import type { Locator, Page, APIResponse } from 'playwright-core';
import type { FrameExpectOptions } from 'playwright-core/lib/client/types';
import { colors } from 'playwright-core/lib/utilsBundle';
import { constructURLBasedOnBaseURL } from 'playwright-core/lib/utils';
import type { Expect } from '../types';
import { expectTypes, callLogText } from '../util';
import { toBeTruthy } from './toBeTruthy';
import { toEqual } from './toEqual';
import { toExpectedTextValues, toMatchText } from './toMatchText';
import type { ParsedStackTrace } from 'playwright-core/lib/utils/stackTrace';
import { isTextualMimeType } from 'playwright-core/lib/utils/mimeType';
import { pollAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner';
import type { ParsedStackTrace } from 'playwright-core/lib/utils';
import { constructURLBasedOnBaseURL, isTextualMimeType, pollAgainstTimeout } from 'playwright-core/lib/utils';
interface LocatorEx extends Locator {
_expect(customStackTrace: ParsedStackTrace, expression: string, options: Omit<FrameExpectOptions, 'expectedValue'> & { expectedValue?: any }): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }>;

View File

@ -17,8 +17,8 @@
import type { Expect } from '../types';
import { expectTypes } from '../util';
import { callLogText, currentExpectTimeout } from '../util';
import type { ParsedStackTrace } from 'playwright-core/lib/utils/stackTrace';
import { captureStackTrace } from 'playwright-core/lib/utils/stackTrace';
import type { ParsedStackTrace } from 'playwright-core/lib/utils';
import { captureStackTrace } from 'playwright-core/lib/utils';
import { matcherHint } from './matcherHint';
// Omit colon and one or more spaces, so can call getLabelPrinter.

View File

@ -19,8 +19,8 @@ import type { Page as PageEx } from 'playwright-core/lib/client/page';
import type { Locator as LocatorEx } from 'playwright-core/lib/client/locator';
import type { Expect } from '../types';
import { currentTestInfo } from '../globals';
import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils/comparators';
import { getComparator } from 'playwright-core/lib/utils/comparators';
import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils';
import { getComparator } from 'playwright-core/lib/utils';
import type { PageScreenshotOptions } from 'playwright-core/types/types';
import {
addSuffixToFilePath, serializeError, sanitizeForFilePath,

View File

@ -14,8 +14,7 @@
* limitations under the License.
*/
import { createGuid } from 'playwright-core/lib/utils';
import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync';
import { createGuid, spawnAsync } from 'playwright-core/lib/utils';
import type { TestRunnerPlugin } from './';
import type { FullConfig } from '../../types/testReporter';

View File

@ -26,7 +26,7 @@ import { collectComponentUsages, componentInfo } from '../tsxTransform';
import type { FullConfig } from '../types';
import { assert, calculateSha1 } from 'playwright-core/lib/utils';
import type { AddressInfo } from 'net';
import { getPlaywrightVersion } from 'playwright-core/lib/common/userAgent';
import { getPlaywrightVersion } from 'playwright-core/lib/utils';
import type { PlaywrightTestConfig as BasePlaywrightTestConfig } from '@playwright/test';
let stoppableServer: any;

View File

@ -19,8 +19,7 @@ import path from 'path';
import net from 'net';
import { debug } from 'playwright-core/lib/utilsBundle';
import { raceAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner';
import { launchProcess } from 'playwright-core/lib/utils/processLauncher';
import { raceAgainstTimeout, launchProcess } from 'playwright-core/lib/utils';
import type { FullConfig, Reporter, Suite } from '../../types/testReporter';
import type { TestRunnerPlugin } from '.';

View File

@ -21,9 +21,7 @@ import path from 'path';
import type { TransformCallback } from 'stream';
import { Transform } from 'stream';
import type { FullConfig, Suite } from '../../types/testReporter';
import { HttpServer } from 'playwright-core/lib/utils/httpServer';
import { assert, calculateSha1, monotonicTime } from 'playwright-core/lib/utils';
import { copyFileAndMakeWritable, removeFolders } from 'playwright-core/lib/utils/fileUtils';
import { HttpServer, assert, calculateSha1, monotonicTime, copyFileAndMakeWritable, removeFolders } from 'playwright-core/lib/utils';
import type { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonTestResult, JsonTestStep } from './raw';
import RawReporter from './raw';
import { stripAnsiEscapes } from './base';

View File

@ -18,7 +18,7 @@ import fs from 'fs';
import path from 'path';
import type { FullConfig, TestCase, Suite, TestResult, TestError, TestStep, FullResult, Location, Reporter, JSONReport, JSONReportSuite, JSONReportSpec, JSONReportTest, JSONReportTestResult, JSONReportTestStep, JSONReportError } from '../../types/testReporter';
import { formatError, prepareErrorStack } from './base';
import { MultiMap } from 'playwright-core/lib/utils/multimap';
import { MultiMap } from 'playwright-core/lib/utils';
import { assert } from 'playwright-core/lib/utils';
export function toPosixPath(aPath: string): string {

View File

@ -21,7 +21,7 @@ import { assert } from 'playwright-core/lib/utils';
import { sanitizeForFilePath } from '../util';
import { formatResultFailure } from './base';
import { toPosixPath, serializePatterns } from './json';
import { MultiMap } from 'playwright-core/lib/utils/multimap';
import { MultiMap } from 'playwright-core/lib/utils';
import { codeFrameColumns } from '../babelBundle';
import type { Metadata } from '../types';

View File

@ -17,7 +17,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { raceAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner';
import { raceAgainstTimeout } from 'playwright-core/lib/utils';
import { colors, minimatch, rimraf } from 'playwright-core/lib/utilsBundle';
import { promisify } from 'util';
import type { FullResult, Reporter, TestError } from '../types/testReporter';

View File

@ -15,7 +15,7 @@
*/
import { colors } from 'playwright-core/lib/utilsBundle';
import { TimeoutRunner, TimeoutRunnerError } from 'playwright-core/lib/utils/timeoutRunner';
import { TimeoutRunner, TimeoutRunnerError } from 'playwright-core/lib/utils';
import type { Location, TestInfoError } from './types';
export type TimeSlot = {

View File

@ -21,11 +21,10 @@ import path from 'path';
import url from 'url';
import { colors, debug, minimatch } from 'playwright-core/lib/utilsBundle';
import type { TestInfoError, Location } from './types';
import { calculateSha1, isRegExp, isString } from 'playwright-core/lib/utils';
import { isInternalFileName } from 'playwright-core/lib/utils/stackTrace';
import { calculateSha1, isRegExp, isString, captureStackTrace as coreCaptureStackTrace } from 'playwright-core/lib/utils';
import { isInternalFileName } from 'playwright-core/lib/utils';
import { currentTestInfo } from './globals';
import type { ParsedStackTrace } from 'playwright-core/lib/utils/stackTrace';
import { captureStackTrace as coreCaptureStackTrace } from 'playwright-core/lib/utils/stackTrace';
import type { ParsedStackTrace } from 'playwright-core/lib/utils';
export type { ParsedStackTrace };

View File

@ -24,7 +24,7 @@ import { Loader } from './loader';
import type { Suite, TestCase } from './test';
import type { Annotation, FullProjectInternal, TestInfoError } from './types';
import { FixtureRunner } from './fixtures';
import { ManualPromise } from 'playwright-core/lib/utils/manualPromise';
import { ManualPromise } from 'playwright-core/lib/utils';
import { TestInfoImpl } from './testInfo';
import type { TimeSlot } from './timeoutManager';
import { TimeoutManager } from './timeoutManager';

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { getComparator } from 'playwright-core/lib/utils/comparators';
import { getComparator } from '../../packages/playwright-core/lib/utils/comparators';
const pngComparator = getComparator('image/png');
type ComparatorResult = { diff?: Buffer; errorMessage: string; } | null;

View File

@ -22,7 +22,7 @@ import type { TraceViewerFixtures } from '../config/traceViewerFixtures';
import { traceViewerFixtures } from '../config/traceViewerFixtures';
export { expect } from '@playwright/test';
import e2c from 'electron-to-chromium';
import { assert } from 'playwright-core/lib/utils';
import { assert } from '../../packages/playwright-core/lib/utils/debug';
type ElectronTestFixtures = PageTestFixtures & {
electronApp: ElectronApplication;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import path from 'path';
import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync';
import { spawnAsync } from '../../packages/playwright-core/lib/utils/spawnAsync';
import { rimraf } from 'playwright-core/lib/utilsBundle';
import { promisify } from 'util';
import fs from 'fs';

View File

@ -20,7 +20,7 @@ import os from 'os';
import http from 'http';
import type net from 'net';
import * as path from 'path';
import { getUserAgent } from '../../packages/playwright-core/lib/common/userAgent';
import { getUserAgent } from '../../packages/playwright-core/lib/utils/userAgent';
import WebSocket from 'ws';
import { expect, playwrightTest } from '../config/browserTest';
import { parseTrace, suppressCertificateWarning } from '../config/utils';

View File

@ -19,7 +19,7 @@ import { contextTest as test, expect } from '../../config/browserTest';
import { playwrightTest } from '../../config/browserTest';
import http from 'http';
import fs from 'fs';
import { getUserAgent } from '../../../packages/playwright-core/lib/common/userAgent';
import { getUserAgent } from '../../../packages/playwright-core/lib/utils/userAgent';
import { suppressCertificateWarning } from '../../config/utils';
test('should create a worker from a service worker', async ({ page, server }) => {

View File

@ -16,7 +16,7 @@
import { expect, playwrightTest as baseTest } from '../config/browserTest';
import { PlaywrightServer } from '../../packages/playwright-core/lib/remote/playwrightServer';
import { createGuid } from '../../packages/playwright-core/lib/utils';
import { createGuid } from '../../packages/playwright-core/lib/utils/crypto';
import { Backend } from '../config/debugControllerBackend';
import type { Browser, BrowserContext } from '@playwright/test';

View File

@ -16,7 +16,7 @@
import os from 'os';
import * as util from 'util';
import { getPlaywrightVersion } from '../../packages/playwright-core/lib/common/userAgent';
import { getPlaywrightVersion } from '../../packages/playwright-core/lib/utils/userAgent';
import { expect, playwrightTest as it } from '../config/browserTest';
it.skip(({ mode }) => mode !== 'default');

View File

@ -16,7 +16,7 @@
*/
import { test as it, expect } from './pageTest';
import { globToRegex } from '../../packages/playwright-core/lib/common/netUtils';
import { globToRegex } from '../../packages/playwright-core/lib/utils/glob';
import vm from 'vm';
it('should work with navigation @smoke', async ({ page, server }) => {

View File

@ -15,7 +15,7 @@
*/
import { test, expect } from './playwright-test-fixtures';
import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile';
import { ZipFile } from '../../packages/playwright-core/lib/utils';
import fs from 'fs';
test('should stop tracing with trace: on-first-retry, when not retrying', async ({ runInlineTest }, testInfo) => {

View File

@ -18,9 +18,9 @@ import fs from 'fs';
import path from 'path';
import url from 'url';
import { test as baseTest, expect, createImage, stripAnsi } from './playwright-test-fixtures';
import type { HttpServer } from '../../packages/playwright-core/lib/utils/httpServer';
import type { HttpServer } from '../../packages/playwright-core/lib/utils';
import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html';
import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync';
import { spawnAsync } from 'playwright-core/lib/utils';
const test = baseTest.extend<{ showReport: () => Promise<void> }>({
showReport: async ({ page }, use, testInfo) => {

View File

@ -1,6 +1,6 @@
const fs = require('fs');
const path = require('path');
const { ZipFile } = require('../../packages/playwright-core/lib/utils/zipFile');
const { ZipFile } = require('../../packages/playwright-core/lib/utils');
(async () => {
// Extract API.json