diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index a144aa7639..27c26b912b 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -24,9 +24,16 @@ "./lib/outofprocess": "./lib/outofprocess.js", "./lib/utils": "./lib/utils/index.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/multimap": "./lib/utils/multimap.js", "./lib/utils/processLauncher": "./lib/utils/processLauncher.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/remote/playwrightClient": "./lib/remote/playwrightClient.js", "./lib/server": "./lib/server/index.js" diff --git a/packages/playwright-core/src/cli/cli.ts b/packages/playwright-core/src/cli/cli.ts index c0eeb2b130..5847d9dc19 100755 --- a/packages/playwright-core/src/cli/cli.ts +++ b/packages/playwright-core/src/cli/cli.ts @@ -33,7 +33,8 @@ import type { BrowserType } from '../client/browserType'; import type { BrowserContextOptions, LaunchOptions } from '../client/types'; import { spawn } from 'child_process'; import { getPlaywrightVersion } from '../common/userAgent'; -import { spawnAsync, wrapInASCIIBox } from '../utils'; +import { wrapInASCIIBox } from '../utils'; +import { spawnAsync } from '../utils/spawnAsync'; import { launchGridAgent } from '../grid/gridAgent'; import type { GridFactory } from '../grid/gridServer'; import { GridServer } from '../grid/gridServer'; diff --git a/packages/playwright-core/src/client/artifact.ts b/packages/playwright-core/src/client/artifact.ts index 7a211fb5b0..70ae9a66c0 100644 --- a/packages/playwright-core/src/client/artifact.ts +++ b/packages/playwright-core/src/client/artifact.ts @@ -17,7 +17,7 @@ import type * as channels from '../protocol/channels'; import * as fs from 'fs'; import { Stream } from './stream'; -import { mkdirIfNeeded } from '../utils'; +import { mkdirIfNeeded } from '../utils/fileUtils'; import { ChannelOwner } from './channelOwner'; import type { Readable } from 'stream'; diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 6d9b67998c..b1a34fc051 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -28,7 +28,8 @@ import { Events } from './events'; import { TimeoutSettings } from '../common/timeoutSettings'; import { Waiter } from './waiter'; import type { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types'; -import { headersObjectToArray, mkdirIfNeeded } from '../utils'; +import { headersObjectToArray } from '../utils'; +import { mkdirIfNeeded } from '../utils/fileUtils'; import { isSafeCloseError } from '../common/errors'; import type * as api from '../../types/types'; import type * as structs from '../../types/structs'; diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index 32674ca434..871f169b99 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -26,7 +26,7 @@ import { envObjectToArray } from './clientHelper'; import { assert, headersObjectToArray, monotonicTime } from '../utils'; import type * as api from '../../types/types'; import { kBrowserClosedError } from '../common/errors'; -import { raceAgainstTimeout } from '../utils'; +import { raceAgainstTimeout } from '../utils/timeoutRunner'; import type { Playwright } from './playwright'; export interface BrowserServerLauncher { diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts index edfa03a951..4b40ffd039 100644 --- a/packages/playwright-core/src/client/channelOwner.ts +++ b/packages/playwright-core/src/client/channelOwner.ts @@ -21,7 +21,8 @@ import { createScheme, ValidationError } from '../protocol/validator'; import { debugLogger } from '../common/debugLogger'; import type { ParsedStackTrace } from '../utils/stackTrace'; import { captureRawStack, captureStackTrace } from '../utils/stackTrace'; -import { isUnderTest, zones } from '../utils'; +import { isUnderTest } from '../utils'; +import { zones } from '../utils/zones'; import type { ClientInstrumentation } from './clientInstrumentation'; import type { Connection } from './connection'; import type { Logger } from './types'; diff --git a/packages/playwright-core/src/client/elementHandle.ts b/packages/playwright-core/src/client/elementHandle.ts index 8377c042cf..fc5fe7c8b8 100644 --- a/packages/playwright-core/src/client/elementHandle.ts +++ b/packages/playwright-core/src/client/elementHandle.ts @@ -23,7 +23,8 @@ import type { SelectOption, FilePayload, Rect, SelectOptionOptions } from './typ import fs from 'fs'; import * as mime from 'mime'; import path from 'path'; -import { assert, isString, mkdirIfNeeded } from '../utils'; +import { assert, isString } from '../utils'; +import { mkdirIfNeeded } from '../utils/fileUtils'; import type * as api from '../../types/types'; import type * as structs from '../../types/structs'; import type { BrowserContext } from './browserContext'; diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index 8c9ab15266..e8cacceb2f 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -22,7 +22,8 @@ import type * as api from '../../types/types'; import type { HeadersArray } from '../common/types'; import type * as channels from '../protocol/channels'; import { kBrowserOrContextClosedError } from '../common/errors'; -import { assert, headersObjectToArray, isFilePayload, isString, mkdirIfNeeded, objectToArray } from '../utils'; +import { assert, headersObjectToArray, isFilePayload, isString, objectToArray } from '../utils'; +import { mkdirIfNeeded } from '../utils/fileUtils'; import { ChannelOwner } from './channelOwner'; import * as network from './network'; import { RawHeaders } from './network'; diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 0d4b2ef594..85dbe5ae13 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -22,14 +22,14 @@ import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from ' import fs from 'fs'; import * as mime from 'mime'; import { isString, headersObjectToArray } from '../utils'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; import { Events } from './events'; 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 './clientHelper'; -import { MultiMap } from '../utils'; +import { MultiMap } from '../utils/multimap'; import { APIResponse } from './fetch'; export type NetworkCookie = { diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 3b1ed97a7e..76d4053573 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -46,7 +46,8 @@ import fs from 'fs'; import path from 'path'; import type { Size, URLMatch, Headers, LifecycleEvent, WaitForEventOptions, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions } from './types'; import { evaluationScript, urlMatches } from './clientHelper'; -import { isString, isRegExp, isObject, mkdirIfNeeded, headersObjectToArray } from '../utils'; +import { isString, isRegExp, isObject, headersObjectToArray } from '../utils'; +import { mkdirIfNeeded } from '../utils/fileUtils'; import { isSafeCloseError } from '../common/errors'; import { Video } from './video'; import { Artifact } from './artifact'; diff --git a/packages/playwright-core/src/outofprocess.ts b/packages/playwright-core/src/outofprocess.ts index a3da11e2b0..e12cd038ac 100644 --- a/packages/playwright-core/src/outofprocess.ts +++ b/packages/playwright-core/src/outofprocess.ts @@ -19,7 +19,7 @@ import { IpcTransport } from './protocol/transport'; import type { Playwright } from './client/playwright'; import * as childProcess from 'child_process'; import * as path from 'path'; -import { ManualPromise } from './utils'; +import { ManualPromise } from './utils/manualPromise'; export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise }> { const client = new PlaywrightClient(env); diff --git a/packages/playwright-core/src/server/android/android.ts b/packages/playwright-core/src/server/android/android.ts index 7ebb960a72..25fc56372b 100644 --- a/packages/playwright-core/src/server/android/android.ts +++ b/packages/playwright-core/src/server/android/android.ts @@ -22,7 +22,8 @@ import os from 'os'; import path from 'path'; import type * as stream from 'stream'; import * as ws from 'ws'; -import { createGuid, makeWaitForNextTask, removeFolders } from '../../utils'; +import { createGuid, makeWaitForNextTask } from '../../utils'; +import { removeFolders } from '../../utils/fileUtils'; import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser'; import type { BrowserContext } from '../browserContext'; import { validateBrowserContextOptions } from '../browserContext'; diff --git a/packages/playwright-core/src/server/artifact.ts b/packages/playwright-core/src/server/artifact.ts index 44a28383fe..9d1b239e0b 100644 --- a/packages/playwright-core/src/server/artifact.ts +++ b/packages/playwright-core/src/server/artifact.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import { assert } from '../utils'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; import { SdkObject } from './instrumentation'; type SaveCallback = (localPath: string, error?: string) => Promise; diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 15ff0282cf..7f81b5d409 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -17,7 +17,8 @@ import * as os from 'os'; import { TimeoutSettings } from '../common/timeoutSettings'; -import { debugMode, mkdirIfNeeded, createGuid } from '../utils'; +import { debugMode, createGuid } from '../utils'; +import { mkdirIfNeeded } from '../utils/fileUtils'; import type { Browser, BrowserOptions } from './browser'; import type { Download } from './download'; import type * as frames from './frames'; diff --git a/packages/playwright-core/src/server/browserType.ts b/packages/playwright-core/src/server/browserType.ts index 484839bb08..7e31a5554f 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -31,7 +31,8 @@ import type { Progress } from './progress'; import { ProgressController } from './progress'; import type * as types from './types'; import { DEFAULT_TIMEOUT, TimeoutSettings } from '../common/timeoutSettings'; -import { debugMode, existsAsync } from '../utils'; +import { debugMode } from '../utils'; +import { existsAsync } from '../utils/fileUtils'; import { helper } from './helper'; import { RecentLogsCollector } from '../common/debugLogger'; import type { CallMetadata } from './instrumentation'; diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index fce45e98f8..019e552c5f 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -33,7 +33,8 @@ import type * as types from '../types'; import type { HTTPRequestParams } from '../../common/netUtils'; import { fetchData } from '../../common/netUtils'; import { getUserAgent } from '../../common/userAgent'; -import { debugMode, headersArrayToObject, removeFolders, streamToString, wrapInASCIIBox } from '../../utils'; +import { debugMode, headersArrayToObject, streamToString, wrapInASCIIBox } from '../../utils'; +import { removeFolders } from '../../utils/fileUtils'; import { RecentLogsCollector } from '../../common/debugLogger'; import type { Progress } from '../progress'; import { ProgressController } from '../progress'; @@ -43,7 +44,7 @@ import type { CallMetadata } from '../instrumentation'; import http from 'http'; import https from 'https'; import { registry } from '../registry'; -import { ManualPromise } from '../../utils'; +import { ManualPromise } from '../../utils/manualPromise'; const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-'); diff --git a/packages/playwright-core/src/server/chromium/crCoverage.ts b/packages/playwright-core/src/server/chromium/crCoverage.ts index e1a2e13219..9f91e11ac0 100644 --- a/packages/playwright-core/src/server/chromium/crCoverage.ts +++ b/packages/playwright-core/src/server/chromium/crCoverage.ts @@ -16,8 +16,8 @@ */ import type { CRSession } from './crConnection'; -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import type { Protocol } from './protocol'; import type * as types from '../types'; import { assert } from '../../utils'; diff --git a/packages/playwright-core/src/server/chromium/crNetworkManager.ts b/packages/playwright-core/src/server/chromium/crNetworkManager.ts index ba98fcbd7a..e517c0b89b 100644 --- a/packages/playwright-core/src/server/chromium/crNetworkManager.ts +++ b/packages/playwright-core/src/server/chromium/crNetworkManager.ts @@ -18,8 +18,8 @@ import type { CRSession } from './crConnection'; import type { Page } from '../page'; import { helper } from '../helper'; -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import type { Protocol } from './protocol'; import * as network from '../network'; import type * as frames from '../frames'; diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index e8b0305439..73fe02568f 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -16,8 +16,8 @@ */ import path from 'path'; -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import { registry } from '../registry'; import { rewriteErrorMessage } from '../../utils/stackTrace'; import { assert, createGuid, headersArrayToObject } from '../../utils'; diff --git a/packages/playwright-core/src/server/chromium/crProtocolHelper.ts b/packages/playwright-core/src/server/chromium/crProtocolHelper.ts index 10d1c9c54b..db7c04a4ad 100644 --- a/packages/playwright-core/src/server/chromium/crProtocolHelper.ts +++ b/packages/playwright-core/src/server/chromium/crProtocolHelper.ts @@ -19,7 +19,7 @@ import type { CRSession } from './crConnection'; import type { Protocol } from './protocol'; import fs from 'fs'; import type * as types from '../types'; -import { mkdirIfNeeded } from '../../utils'; +import { mkdirIfNeeded } from '../../utils/fileUtils'; import { splitErrorMessage } from '../../utils/stackTrace'; export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string { diff --git a/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts b/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts index 95046d4465..ef460e2b38 100644 --- a/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/artifactDispatcher.ts @@ -19,7 +19,7 @@ import type { DispatcherScope } from './dispatcher'; import { Dispatcher } from './dispatcher'; import { StreamDispatcher } from './streamDispatcher'; import fs from 'fs'; -import { mkdirIfNeeded } from '../../utils'; +import { mkdirIfNeeded } from '../../utils/fileUtils'; import type { Artifact } from '../artifact'; export class ArtifactDispatcher extends Dispatcher implements channels.ArtifactChannel { diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 2fd53f9f4b..b371678a5d 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -20,7 +20,7 @@ import path from 'path'; import yauzl from 'yauzl'; import yazl from 'yazl'; import type * as channels from '../../protocol/channels'; -import { ManualPromise } from '../../utils'; +import { ManualPromise } from '../../utils/manualPromise'; import { assert, createGuid } from '../../utils'; import type { DispatcherScope } from './dispatcher'; import { Dispatcher } from './dispatcher'; diff --git a/packages/playwright-core/src/server/electron/electron.ts b/packages/playwright-core/src/server/electron/electron.ts index c055c8031e..461204fed5 100644 --- a/packages/playwright-core/src/server/electron/electron.ts +++ b/packages/playwright-core/src/server/electron/electron.ts @@ -34,7 +34,7 @@ import type { BrowserWindow } from 'electron'; import type { Progress } from '../progress'; import { ProgressController } from '../progress'; import { helper } from '../helper'; -import { eventsHelper } from '../../utils'; +import { eventsHelper } from '../../utils/eventsHelper'; import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser'; import type * as childProcess from 'child_process'; import * as readline from 'readline'; diff --git a/packages/playwright-core/src/server/firefox/ffNetworkManager.ts b/packages/playwright-core/src/server/firefox/ffNetworkManager.ts index 75467d8c6e..c0018a0c42 100644 --- a/packages/playwright-core/src/server/firefox/ffNetworkManager.ts +++ b/packages/playwright-core/src/server/firefox/ffNetworkManager.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import type { FFSession } from './ffConnection'; import type { Page } from '../page'; import * as network from '../network'; diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts index 308bc82eab..ddb21be0e1 100644 --- a/packages/playwright-core/src/server/firefox/ffPage.ts +++ b/packages/playwright-core/src/server/firefox/ffPage.ts @@ -18,8 +18,8 @@ import * as dialog from '../dialog'; import * as dom from '../dom'; import type * as frames from '../frames'; -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import { assert } from '../../utils'; import type { PageBinding, PageDelegate } from '../page'; import { Page, Worker } from '../page'; @@ -34,7 +34,7 @@ import type { Protocol } from './protocol'; import type { Progress } from '../progress'; import { splitErrorMessage } from '../../utils/stackTrace'; import { debugLogger } from '../../common/debugLogger'; -import { ManualPromise } from '../../utils'; +import { ManualPromise } from '../../utils/manualPromise'; export const UTILITY_WORLD_NAME = '__playwright_utility_world__'; diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 442319ba2a..0a3fe73933 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -19,8 +19,8 @@ import type * as channels from '../protocol/channels'; import type { ConsoleMessage } from './console'; import * as dom from './dom'; import { helper } from './helper'; -import type { RegisteredListener } from '../utils'; -import { eventsHelper } from '../utils'; +import type { RegisteredListener } from '../utils/eventsHelper'; +import { eventsHelper } from '../utils/eventsHelper'; import * as js from './javascript'; import * as network from './network'; import type { Dialog } from './dialog'; @@ -30,7 +30,7 @@ import { BrowserContext } from './browserContext'; import type { Progress } from './progress'; import { ProgressController } from './progress'; import { assert, constructURLBasedOnBaseURL, makeWaitForNextTask } from '../utils'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; import { debugLogger } from '../common/debugLogger'; import type { CallMetadata } from './instrumentation'; import { serverSideCallMetadata, SdkObject } from './instrumentation'; diff --git a/packages/playwright-core/src/server/helper.ts b/packages/playwright-core/src/server/helper.ts index 89241e5bfe..51f1bf61d2 100644 --- a/packages/playwright-core/src/server/helper.ts +++ b/packages/playwright-core/src/server/helper.ts @@ -19,8 +19,8 @@ import type { EventEmitter } from 'events'; import type * as types from './types'; import type { Progress } from './progress'; import { debugLogger } from '../common/debugLogger'; -import type { RegisteredListener } from '../utils'; -import { eventsHelper } from '../utils'; +import type { RegisteredListener } from '../utils/eventsHelper'; +import { eventsHelper } from '../utils/eventsHelper'; class Helper { static completeUserURL(urlString: string): string { diff --git a/packages/playwright-core/src/server/javascript.ts b/packages/playwright-core/src/server/javascript.ts index 00168dacae..2f409c2fb0 100644 --- a/packages/playwright-core/src/server/javascript.ts +++ b/packages/playwright-core/src/server/javascript.ts @@ -19,7 +19,7 @@ import * as utilityScriptSource from '../generated/utilityScriptSource'; import { serializeAsCallArgument } from './common/utilityScriptSerializers'; import { type UtilityScript } from './injected/utilityScript'; import { SdkObject } from './instrumentation'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; export type ObjectId = string; export type RemoteObject = { diff --git a/packages/playwright-core/src/server/network.ts b/packages/playwright-core/src/server/network.ts index 9d843f3ef6..d70e450f08 100644 --- a/packages/playwright-core/src/server/network.ts +++ b/packages/playwright-core/src/server/network.ts @@ -18,7 +18,7 @@ import type * as frames from './frames'; import type * as types from './types'; import type * as channels from '../protocol/channels'; import { assert } from '../utils'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; import { SdkObject } from './instrumentation'; import type { NameValue } from '../common/types'; import { APIRequestContext } from './fetch'; diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 8a2fd2745f..2b91818485 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -31,7 +31,7 @@ import { FileChooser } from './fileChooser'; import type { Progress } from './progress'; import { ProgressController } from './progress'; import { assert, isError } from '../utils'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; import { debugLogger } from '../common/debugLogger'; import type { ImageComparatorOptions } from '../utils/comparators'; import { getComparator } from '../utils/comparators'; diff --git a/packages/playwright-core/src/server/progress.ts b/packages/playwright-core/src/server/progress.ts index 196149cd2c..61caf732f0 100644 --- a/packages/playwright-core/src/server/progress.ts +++ b/packages/playwright-core/src/server/progress.ts @@ -19,7 +19,7 @@ import { assert, monotonicTime } from '../utils'; import type { LogName } from '../common/debugLogger'; import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation'; import type { ElementHandle } from './dom'; -import { ManualPromise } from '../utils'; +import { ManualPromise } from '../utils/manualPromise'; import type { LogEntry } from './injected/injectedScript'; export interface Progress { diff --git a/packages/playwright-core/src/server/registry/browserFetcher.ts b/packages/playwright-core/src/server/registry/browserFetcher.ts index 3c378fb548..80231f930b 100644 --- a/packages/playwright-core/src/server/registry/browserFetcher.ts +++ b/packages/playwright-core/src/server/registry/browserFetcher.ts @@ -20,7 +20,7 @@ import fs from 'fs'; import os from 'os'; import path from 'path'; import { getUserAgent } from '../../common/userAgent'; -import { existsAsync } from '../../utils'; +import { existsAsync } from '../../utils/fileUtils'; import { debugLogger } from '../../common/debugLogger'; import { download } from './download'; diff --git a/packages/playwright-core/src/server/registry/dependencies.ts b/packages/playwright-core/src/server/registry/dependencies.ts index ab25951753..17504adebf 100644 --- a/packages/playwright-core/src/server/registry/dependencies.ts +++ b/packages/playwright-core/src/server/registry/dependencies.ts @@ -19,6 +19,8 @@ import path from 'path'; import * as os from 'os'; import childProcess from 'child_process'; import * as utils from '../../utils'; +import { spawnAsync } from '../../utils/spawnAsync'; +import { hostPlatform } from '../../utils/hostPlatform'; import { buildPlaywrightCLICommand } from '.'; import { deps } from './nativeDeps'; import { getUbuntuVersion } from '../../utils/ubuntuVersion'; @@ -66,7 +68,7 @@ export async function installDependenciesWindows(targets: Set, console.log(`${command} ${quoteProcessArgs(args).join(' ')}`); // eslint-disable-line no-console return; } - const { code } = await utils.spawnAsync(command, args, { cwd: BIN_DIRECTORY, stdio: 'inherit' }); + const { code } = await spawnAsync(command, args, { cwd: BIN_DIRECTORY, stdio: 'inherit' }); if (code !== 0) throw new Error('Failed to install windows dependencies!'); } @@ -77,7 +79,7 @@ export async function installDependenciesLinux(targets: Set, dr throw new Error(`Unsupported Linux distribution, only Ubuntu is supported!`); const libraries: string[] = []; for (const target of targets) { - const info = deps[utils.hostPlatform]; + const info = deps[hostPlatform]; if (!info) { console.warn('Cannot install dependencies for this linux distribution!'); // eslint-disable-line no-console return; @@ -92,7 +94,7 @@ export async function installDependenciesLinux(targets: Set, dr commands.push(['apt-get', 'install', '-y', '--no-install-recommends', ...uniqueLibraries, ].join(' ')); - const { command, args, elevatedPermissions } = await utils.transformCommandsForRoot(commands); + const { command, args, elevatedPermissions } = await transformCommandsForRoot(commands); if (dryRun) { console.log(`${command} ${quoteProcessArgs(args).join(' ')}`); // eslint-disable-line no-console return; @@ -189,7 +191,7 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir const missingPackages = new Set(); const libraryToPackageNameMapping = { - ...(deps[utils.hostPlatform]?.lib2package || {}), + ...(deps[hostPlatform]?.lib2package || {}), ...MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU, }; // Translate missing dependencies to package names to install with apt. @@ -287,7 +289,7 @@ async function executablesOrSharedLibraries(directoryPath: string): Promise> { const executable = path.join(__dirname, '..', '..', '..', 'bin', 'PrintDeps.exe'); const dirname = path.dirname(filePath); - const { stdout, code } = await utils.spawnAsync(executable, [filePath], { + const { stdout, code } = await spawnAsync(executable, [filePath], { cwd: dirname, env: { ...process.env, @@ -305,7 +307,7 @@ async function missingFileDependencies(filePath: string, extraLDPaths: string[]) let LD_LIBRARY_PATH = extraLDPaths.join(':'); if (process.env.LD_LIBRARY_PATH) LD_LIBRARY_PATH = `${process.env.LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}`; - const { stdout, code } = await utils.spawnAsync('ldd', [filePath], { + const { stdout, code } = await spawnAsync('ldd', [filePath], { cwd: dirname, env: { ...process.env, @@ -324,7 +326,7 @@ async function missingDLOPENLibraries(libraries: string[]): Promise { // NOTE: Using full-qualified path to `ldconfig` since `/sbin` is not part of the // default PATH in CRON. // @see https://github.com/microsoft/playwright/issues/3397 - const { stdout, code, error } = await utils.spawnAsync('/sbin/ldconfig', ['-p'], {}); + const { stdout, code, error } = await spawnAsync('/sbin/ldconfig', ['-p'], {}); if (code !== 0 || error) return []; const isLibraryAvailable = (library: string) => stdout.toLowerCase().includes(library.toLowerCase()); @@ -346,3 +348,13 @@ function quoteProcessArgs(args: string[]): string[] { return arg; }); } + +export async function transformCommandsForRoot(commands: string[]): Promise<{ command: string, args: string[], elevatedPermissions: boolean}> { + const isRoot = process.getuid() === 0; + if (isRoot) + return { command: 'sh', args: ['-c', `${commands.join('&& ')}`], elevatedPermissions: false }; + const sudoExists = await spawnAsync('which', ['sudo']); + if (sudoExists.code === 0) + return { command: 'sudo', args: ['--', 'sh', '-c', `${commands.join('&& ')}`], elevatedPermissions: true }; + return { command: 'su', args: ['root', '-c', `${commands.join('&& ')}`], elevatedPermissions: true }; +} diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index dffec7579f..adc5f0fd5c 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -23,8 +23,12 @@ import lockfile from 'proper-lockfile'; import { getUbuntuVersion } from '../../utils/ubuntuVersion'; import { fetchData } from '../../common/netUtils'; import { getClientLanguage } from '../../common/userAgent'; -import { getFromENV, getAsBooleanFromENV, calculateSha1, removeFolders, existsAsync, hostPlatform, canAccessFile, spawnAsync, wrapInASCIIBox, transformCommandsForRoot } from '../../utils'; +import { getFromENV, getAsBooleanFromENV, calculateSha1, wrapInASCIIBox } from '../../utils'; +import { removeFolders, existsAsync, canAccessFile } from '../../utils/fileUtils'; +import { hostPlatform } from '../../utils/hostPlatform'; +import { spawnAsync } from '../../utils/spawnAsync'; import type { DependencyGroup } from './dependencies'; +import { transformCommandsForRoot } from './dependencies'; import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies'; import { downloadBrowserWithProgressBar, logPolitely } from './browserFetcher'; export { writeDockerVersion } from './dependencies'; diff --git a/packages/playwright-core/src/server/screenshotter.ts b/packages/playwright-core/src/server/screenshotter.ts index 65f1993efe..644815f8e1 100644 --- a/packages/playwright-core/src/server/screenshotter.ts +++ b/packages/playwright-core/src/server/screenshotter.ts @@ -24,7 +24,7 @@ import type { ParsedSelector } from './common/selectorParser'; import type * as types from './types'; import type { Progress } from './progress'; import { assert } from '../utils'; -import { MultiMap } from '../utils'; +import { MultiMap } from '../utils/multimap'; declare global { interface Window { diff --git a/packages/playwright-core/src/server/supplements/har/harTracer.ts b/packages/playwright-core/src/server/supplements/har/harTracer.ts index d692962450..ce1ba0b3e0 100644 --- a/packages/playwright-core/src/server/supplements/har/harTracer.ts +++ b/packages/playwright-core/src/server/supplements/har/harTracer.ts @@ -22,10 +22,10 @@ import * as network from '../../network'; import { Page } from '../../page'; import type * as har from './har'; import { calculateSha1, monotonicTime } from '../../../utils'; -import type { RegisteredListener } from '../../../utils'; -import { eventsHelper } from '../../../utils'; +import type { RegisteredListener } from '../../../utils/eventsHelper'; +import { eventsHelper } from '../../../utils/eventsHelper'; import * as mime from 'mime'; -import { ManualPromise } from '../../../utils'; +import { ManualPromise } from '../../../utils/manualPromise'; const FALLBACK_HTTP_VERSION = 'HTTP/1.1'; diff --git a/packages/playwright-core/src/server/supplements/recorderSupplement.ts b/packages/playwright-core/src/server/supplements/recorderSupplement.ts index a8988444b6..7c0d455b8c 100644 --- a/packages/playwright-core/src/server/supplements/recorderSupplement.ts +++ b/packages/playwright-core/src/server/supplements/recorderSupplement.ts @@ -38,7 +38,7 @@ import { createGuid, monotonicTime } from '../../utils'; import { metadataToCallLog } from './recorder/recorderUtils'; import { Debugger } from './debugger'; import { EventEmitter } from 'events'; -import { raceAgainstTimeout } from '../../utils'; +import { raceAgainstTimeout } from '../../utils/timeoutRunner'; type BindingSource = { frame: Frame, page: Page }; diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts index 3dbc6f4af3..b3f884c4b0 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts @@ -16,8 +16,8 @@ import { BrowserContext } from '../../browserContext'; import { Page } from '../../page'; -import type { RegisteredListener } from '../../../utils'; -import { eventsHelper } from '../../../utils'; +import type { RegisteredListener } from '../../../utils/eventsHelper'; +import { eventsHelper } from '../../../utils/eventsHelper'; import { debugLogger } from '../../../common/debugLogger'; import type { Frame } from '../../frames'; import type { SnapshotData } from './snapshotterInjected'; diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index bb2ed4599b..3f8c8d3bb5 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -22,10 +22,11 @@ import yazl from 'yazl'; import type { NameValue } from '../../../common/types'; import type { TracingTracingStopChunkParams } from '../../../protocol/channels'; import { commandsWithTracingSnapshots } from '../../../protocol/channels'; -import { ManualPromise } from '../../../utils'; -import type { RegisteredListener } from '../../../utils'; -import { eventsHelper } from '../../../utils'; -import { assert, calculateSha1, createGuid, mkdirIfNeeded, monotonicTime, removeFolders } from '../../../utils'; +import { ManualPromise } from '../../../utils/manualPromise'; +import type { RegisteredListener } from '../../../utils/eventsHelper'; +import { eventsHelper } from '../../../utils/eventsHelper'; +import { assert, calculateSha1, createGuid, monotonicTime } from '../../../utils'; +import { mkdirIfNeeded, removeFolders } from '../../../utils/fileUtils'; import { Artifact } from '../../artifact'; import { BrowserContext } from '../../browserContext'; import { ElementHandle } from '../../dom'; diff --git a/packages/playwright-core/src/server/webkit/wkBrowser.ts b/packages/playwright-core/src/server/webkit/wkBrowser.ts index 6a4638d6df..44a0ae971f 100644 --- a/packages/playwright-core/src/server/webkit/wkBrowser.ts +++ b/packages/playwright-core/src/server/webkit/wkBrowser.ts @@ -18,8 +18,9 @@ import type { BrowserOptions } from '../browser'; import { Browser } from '../browser'; import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext'; -import type { RegisteredListener } from '../../utils'; -import { assert, eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { assert } from '../../utils'; +import { eventsHelper } from '../../utils/eventsHelper'; import * as network from '../network'; import type { Page, PageBinding, PageDelegate } from '../page'; import type { ConnectionTransport } from '../transport'; diff --git a/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts b/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts index 3500f3ae9d..04cc64c648 100644 --- a/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts +++ b/packages/playwright-core/src/server/webkit/wkInterceptableRequest.ts @@ -21,7 +21,7 @@ import type * as types from '../types'; import type { Protocol } from './protocol'; import type { WKSession } from './wkConnection'; import { assert, headersObjectToArray, headersArrayToObject } from '../../utils'; -import { ManualPromise } from '../../utils'; +import { ManualPromise } from '../../utils/manualPromise'; const errorReasons: { [reason: string]: Protocol.Network.ResourceErrorType } = { 'aborted': 'Cancellation', diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 64bb17afe1..624ff70c21 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -19,13 +19,14 @@ import * as jpeg from 'jpeg-js'; import path from 'path'; import * as png from 'pngjs'; import { splitErrorMessage } from '../../utils/stackTrace'; -import { assert, createGuid, debugAssert, headersArrayToObject, headersObjectToArray, hostPlatform } from '../../utils'; +import { assert, createGuid, debugAssert, headersArrayToObject, headersObjectToArray } from '../../utils'; +import { hostPlatform } from '../../utils/hostPlatform'; import type * as accessibility from '../accessibility'; import * as dialog from '../dialog'; import * as dom from '../dom'; import type * as frames from '../frames'; -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import { helper } from '../helper'; import type { JSHandle } from '../javascript'; import * as network from '../network'; @@ -43,7 +44,7 @@ import { WKInterceptableRequest, WKRouteImpl } from './wkInterceptableRequest'; import { WKProvisionalPage } from './wkProvisionalPage'; import { WKWorkers } from './wkWorkers'; import { debugLogger } from '../../common/debugLogger'; -import { ManualPromise } from '../../utils'; +import { ManualPromise } from '../../utils/manualPromise'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; const BINDING_CALL_MESSAGE = '__playwright_binding_call__'; diff --git a/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts b/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts index 7e7b420a42..aa6c06e57d 100644 --- a/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts +++ b/packages/playwright-core/src/server/webkit/wkProvisionalPage.ts @@ -16,8 +16,8 @@ import type { WKSession } from './wkConnection'; import type { WKPage } from './wkPage'; -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import type { Protocol } from './protocol'; import { assert } from '../../utils'; diff --git a/packages/playwright-core/src/server/webkit/wkWorkers.ts b/packages/playwright-core/src/server/webkit/wkWorkers.ts index ddc2755981..f4b28588d1 100644 --- a/packages/playwright-core/src/server/webkit/wkWorkers.ts +++ b/packages/playwright-core/src/server/webkit/wkWorkers.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { RegisteredListener } from '../../utils'; -import { eventsHelper } from '../../utils'; +import type { RegisteredListener } from '../../utils/eventsHelper'; +import { eventsHelper } from '../../utils/eventsHelper'; import type { Page } from '../page'; import { Worker } from '../page'; import type { Protocol } from './protocol'; diff --git a/packages/playwright-core/src/utils/fileUtils.ts b/packages/playwright-core/src/utils/fileUtils.ts new file mode 100644 index 0000000000..8d00f350a5 --- /dev/null +++ b/packages/playwright-core/src/utils/fileUtils.ts @@ -0,0 +1,48 @@ +/** + * 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 fs from 'fs'; +import path from 'path'; +import removeFolder from 'rimraf'; + +export const existsAsync = (path: string): Promise => new Promise(resolve => fs.stat(path, err => resolve(!err))); + +export async function mkdirIfNeeded(filePath: string) { + // This will harmlessly throw on windows if the dirname is the root directory. + await fs.promises.mkdir(path.dirname(filePath), { recursive: true }).catch(() => {}); +} + +export async function removeFolders(dirs: string[]): Promise> { + return await Promise.all(dirs.map((dir: string) => { + return new Promise(fulfill => { + removeFolder(dir, { maxBusyTries: 10 }, error => { + fulfill(error ?? undefined); + }); + }); + })); +} + +export function canAccessFile(file: string) { + if (!file) + return false; + + try { + fs.accessSync(file); + return true; + } catch (e) { + return false; + } +} diff --git a/packages/playwright-core/src/utils/hostPlatform.ts b/packages/playwright-core/src/utils/hostPlatform.ts new file mode 100644 index 0000000000..a1a02874ba --- /dev/null +++ b/packages/playwright-core/src/utils/hostPlatform.ts @@ -0,0 +1,66 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import os from 'os'; +import { getUbuntuVersionSync } from './ubuntuVersion'; + +export type HostPlatform = 'win64' | + 'mac10.13' | + 'mac10.14' | + 'mac10.15' | + 'mac11' | 'mac11-arm64' | + 'mac12' | 'mac12-arm64' | + 'ubuntu18.04' | 'ubuntu18.04-arm64' | + 'ubuntu20.04' | 'ubuntu20.04-arm64' | + 'generic-linux' | 'generic-linux-arm64' | + ''; + +export const hostPlatform = ((): HostPlatform => { + const platform = os.platform(); + if (platform === 'darwin') { + const ver = os.release().split('.').map((a: string) => parseInt(a, 10)); + let macVersion = ''; + if (ver[0] < 18) { + // Everything before 10.14 is considered 10.13. + macVersion = 'mac10.13'; + } else if (ver[0] === 18) { + macVersion = 'mac10.14'; + } else if (ver[0] === 19) { + macVersion = 'mac10.15'; + } else { + // ver[0] >= 20 + const LAST_STABLE_MAC_MAJOR_VERSION = 12; + // Best-effort support for MacOS beta versions. + macVersion = 'mac' + Math.min(ver[0] - 9, LAST_STABLE_MAC_MAJOR_VERSION); + // BigSur is the first version that might run on Apple Silicon. + if (os.cpus().some(cpu => cpu.model.includes('Apple'))) + macVersion += '-arm64'; + } + return macVersion as HostPlatform; + } + if (platform === 'linux') { + const archSuffix = os.arch() === 'arm64' ? '-arm64' : ''; + const ubuntuVersion = getUbuntuVersionSync(); + if (!ubuntuVersion) + return ('generic-linux' + archSuffix) as HostPlatform; + if (parseInt(ubuntuVersion, 10) <= 19) + return ('ubuntu18.04' + archSuffix) as HostPlatform; + return ('ubuntu20.04' + archSuffix) as HostPlatform; + } + if (platform === 'win32') + return 'win64'; + return ''; +})(); diff --git a/packages/playwright-core/src/utils/index.ts b/packages/playwright-core/src/utils/index.ts index ce5282afd0..30adeb4eb1 100644 --- a/packages/playwright-core/src/utils/index.ts +++ b/packages/playwright-core/src/utils/index.ts @@ -14,41 +14,14 @@ * limitations under the License. */ -import type { SpawnOptions } from 'child_process'; -import { spawn } from 'child_process'; import * as crypto from 'crypto'; -import fs from 'fs'; -import os from 'os'; -import path from 'path'; -import removeFolder from 'rimraf'; import type stream from 'stream'; import * as URL from 'url'; -import type { NameValue } from '../protocol/channels'; -import { getUbuntuVersionSync } from './ubuntuVersion'; -export { eventsHelper } from './eventsHelper'; -export type { RegisteredListener } from './eventsHelper'; -export { ManualPromise } from './manualPromise'; -export { MultiMap } from './multimap'; -export { raceAgainstTimeout, TimeoutRunner, TimeoutRunnerError } from './timeoutRunner'; -export { zones } from './zones'; - -export const existsAsync = (path: string): Promise => new Promise(resolve => fs.stat(path, err => resolve(!err))); - -export function spawnAsync(cmd: string, args: string[], options: SpawnOptions = {}): Promise<{stdout: string, stderr: string, code: number | null, error?: Error}> { - const process = spawn(cmd, args, Object.assign({ windowsHide: true }, options)); - - return new Promise(resolve => { - let stdout = ''; - let stderr = ''; - if (process.stdout) - process.stdout.on('data', data => stdout += data); - if (process.stderr) - process.stderr.on('data', data => stderr += data); - process.on('close', code => resolve({ stdout, stderr, code })); - process.on('error', error => resolve({ stdout, stderr, code: 0, error })); - }); -} +type NameValue = { + name: string, + value: string, +}; // See https://joel.tools/microtasks/ export function makeWaitForNextTask() { @@ -149,11 +122,6 @@ export function getAsBooleanFromENV(name: string): boolean { return !!value && value !== 'false' && value !== '0'; } -export async function mkdirIfNeeded(filePath: string) { - // This will harmlessly throw on windows if the dirname is the root directory. - await fs.promises.mkdir(path.dirname(filePath), { recursive: true }).catch(() => {}); -} - type HeadersArray = { name: string, value: string }[]; type HeadersObject = { [key: string]: string }; @@ -214,28 +182,6 @@ export function createGuid(): string { return crypto.randomBytes(16).toString('hex'); } -export async function removeFolders(dirs: string[]): Promise> { - return await Promise.all(dirs.map((dir: string) => { - return new Promise(fulfill => { - removeFolder(dir, { maxBusyTries: 10 }, error => { - fulfill(error ?? undefined); - }); - }); - })); -} - -export function canAccessFile(file: string) { - if (!file) - return false; - - try { - fs.accessSync(file); - return true; - } catch (e) { - return false; - } -} - export function constructURLBasedOnBaseURL(baseURL: string | undefined, givenURL: string): string { try { return (new URL.URL(givenURL, baseURL)).toString(); @@ -244,54 +190,6 @@ export function constructURLBasedOnBaseURL(baseURL: string | undefined, givenURL } } -export type HostPlatform = 'win64' | - 'mac10.13' | - 'mac10.14' | - 'mac10.15' | - 'mac11' | 'mac11-arm64' | - 'mac12' | 'mac12-arm64' | - 'ubuntu18.04' | 'ubuntu18.04-arm64' | - 'ubuntu20.04' | 'ubuntu20.04-arm64' | - 'generic-linux' | 'generic-linux-arm64' | - ''; - -export const hostPlatform = ((): HostPlatform => { - const platform = os.platform(); - if (platform === 'darwin') { - const ver = os.release().split('.').map((a: string) => parseInt(a, 10)); - let macVersion = ''; - if (ver[0] < 18) { - // Everything before 10.14 is considered 10.13. - macVersion = 'mac10.13'; - } else if (ver[0] === 18) { - macVersion = 'mac10.14'; - } else if (ver[0] === 19) { - macVersion = 'mac10.15'; - } else { - // ver[0] >= 20 - const LAST_STABLE_MAC_MAJOR_VERSION = 12; - // Best-effort support for MacOS beta versions. - macVersion = 'mac' + Math.min(ver[0] - 9, LAST_STABLE_MAC_MAJOR_VERSION); - // BigSur is the first version that might run on Apple Silicon. - if (os.cpus().some(cpu => cpu.model.includes('Apple'))) - macVersion += '-arm64'; - } - return macVersion as HostPlatform; - } - if (platform === 'linux') { - const archSuffix = os.arch() === 'arm64' ? '-arm64' : ''; - const ubuntuVersion = getUbuntuVersionSync(); - if (!ubuntuVersion) - return ('generic-linux' + archSuffix) as HostPlatform; - if (parseInt(ubuntuVersion, 10) <= 19) - return ('ubuntu18.04' + archSuffix) as HostPlatform; - return ('ubuntu20.04' + archSuffix) as HostPlatform; - } - if (platform === 'win32') - return 'win64'; - return ''; -})(); - export function wrapInASCIIBox(text: string, padding = 0): string { const lines = text.split('\n'); const maxLength = Math.max(...lines.map(line => line.length)); @@ -314,56 +212,3 @@ export function streamToString(stream: stream.Readable): Promise { stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); }); } - -export async function transformCommandsForRoot(commands: string[]): Promise<{ command: string, args: string[], elevatedPermissions: boolean}> { - const isRoot = process.getuid() === 0; - if (isRoot) - return { command: 'sh', args: ['-c', `${commands.join('&& ')}`], elevatedPermissions: false }; - const sudoExists = await spawnAsync('which', ['sudo']); - if (sudoExists.code === 0) - return { command: 'sudo', args: ['--', 'sh', '-c', `${commands.join('&& ')}`], elevatedPermissions: true }; - return { command: 'su', args: ['root', '-c', `${commands.join('&& ')}`], elevatedPermissions: true }; -} - -export class SigIntWatcher { - private _hadSignal: boolean = false; - private _sigintPromise: Promise; - private _sigintHandler: () => void; - constructor() { - let sigintCallback: () => void; - this._sigintPromise = new Promise(f => sigintCallback = f); - this._sigintHandler = () => { - // We remove the handler so that second Ctrl+C immediately kills the runner - // via the default sigint handler. This is handy in the case where our shutdown - // takes a lot of time or is buggy. - // - // When running through NPM we might get multiple SIGINT signals - // for a single Ctrl+C - this is an NPM bug present since at least NPM v6. - // https://github.com/npm/cli/issues/1591 - // https://github.com/npm/cli/issues/2124 - // - // Therefore, removing the handler too soon will just kill the process - // with default handler without printing the results. - // We work around this by giving NPM 1000ms to send us duplicate signals. - // The side effect is that slow shutdown or bug in our runner will force - // the user to hit Ctrl+C again after at least a second. - setTimeout(() => process.off('SIGINT', this._sigintHandler), 1000); - this._hadSignal = true; - sigintCallback(); - }; - process.on('SIGINT', this._sigintHandler); - } - - promise(): Promise { - return this._sigintPromise; - } - - hadSignal(): boolean { - return this._hadSignal; - } - - disarm() { - process.off('SIGINT', this._sigintHandler); - } -} - diff --git a/packages/playwright-core/src/utils/processLauncher.ts b/packages/playwright-core/src/utils/processLauncher.ts index 057ec81364..e311cab98f 100644 --- a/packages/playwright-core/src/utils/processLauncher.ts +++ b/packages/playwright-core/src/utils/processLauncher.ts @@ -18,7 +18,8 @@ import * as childProcess from 'child_process'; import * as readline from 'readline'; import { eventsHelper } from './eventsHelper'; -import { isUnderTest, removeFolders } from './'; +import { isUnderTest } from './'; +import { removeFolders } from './fileUtils'; import rimraf from 'rimraf'; export type Env = {[key: string]: string | number | boolean | undefined}; diff --git a/packages/playwright-core/src/utils/spawnAsync.ts b/packages/playwright-core/src/utils/spawnAsync.ts new file mode 100644 index 0000000000..9eb33df7b9 --- /dev/null +++ b/packages/playwright-core/src/utils/spawnAsync.ts @@ -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. + */ + +import type { SpawnOptions } from 'child_process'; +import { spawn } from 'child_process'; + +export function spawnAsync(cmd: string, args: string[], options: SpawnOptions = {}): Promise<{stdout: string, stderr: string, code: number | null, error?: Error}> { + const process = spawn(cmd, args, Object.assign({ windowsHide: true }, options)); + + return new Promise(resolve => { + let stdout = ''; + let stderr = ''; + if (process.stdout) + process.stdout.on('data', data => stdout += data); + if (process.stderr) + process.stderr.on('data', data => stderr += data); + process.on('close', code => resolve({ stdout, stderr, code })); + process.on('error', error => resolve({ stdout, stderr, code: 0, error })); + }); +} diff --git a/packages/playwright-test/src/ci.ts b/packages/playwright-test/src/ci.ts index 0dfbcb3b85..a259f914be 100644 --- a/packages/playwright-test/src/ci.ts +++ b/packages/playwright-test/src/ci.ts @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createGuid, spawnAsync } from 'playwright-core/lib/utils'; +import { createGuid } from 'playwright-core/lib/utils'; +import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync'; const GIT_OPERATIONS_TIMEOUT_MS = 1500; const kContentTypePlainText = 'text/plain'; diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 169ab20d62..bd44754533 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -29,7 +29,8 @@ import type { FilePatternFilter } from './util'; import { showHTMLReport } from './reporters/html'; import { GridServer } from 'playwright-core/lib/grid/gridServer'; import dockerFactory from 'playwright-core/lib/grid/dockerGridFactory'; -import { createGuid, hostPlatform } from 'playwright-core/lib/utils'; +import { createGuid } from 'playwright-core/lib/utils'; +import { hostPlatform } from 'playwright-core/lib/utils/hostPlatform'; import { fileIsModule } from './loader'; const defaultTimeout = 30000; diff --git a/packages/playwright-test/src/dispatcher.ts b/packages/playwright-test/src/dispatcher.ts index 1ee2a4cf4c..ad676bf0d5 100644 --- a/packages/playwright-test/src/dispatcher.ts +++ b/packages/playwright-test/src/dispatcher.ts @@ -21,7 +21,7 @@ import type { RunPayload, TestBeginPayload, TestEndPayload, DonePayload, TestOut import type { TestResult, Reporter, TestStep, TestError } from '../types/testReporter'; import type { Suite, TestCase } from './test'; import type { Loader } from './loader'; -import { ManualPromise } from 'playwright-core/lib/utils'; +import { ManualPromise } from 'playwright-core/lib/utils/manualPromise'; export type TestGroup = { workerHash: string; diff --git a/packages/playwright-test/src/expect.ts b/packages/playwright-test/src/expect.ts index e702b27ff0..c86da7b240 100644 --- a/packages/playwright-test/src/expect.ts +++ b/packages/playwright-test/src/expect.ts @@ -15,7 +15,7 @@ */ import expectLibrary from 'expect'; -import { raceAgainstTimeout } from 'playwright-core/lib/utils'; +import { raceAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner'; import path from 'path'; import { INVERTED_COLOR, diff --git a/packages/playwright-test/src/fixtures.ts b/packages/playwright-test/src/fixtures.ts index 60a21cd790..8a643cb9d5 100644 --- a/packages/playwright-test/src/fixtures.ts +++ b/packages/playwright-test/src/fixtures.ts @@ -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'; +import { ManualPromise } from 'playwright-core/lib/utils/manualPromise'; import type { TestInfoImpl } from './testInfo'; import type { FixtureDescription, TimeoutManager } from './timeoutManager'; diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index 4645e40862..641dd7b4cb 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -19,7 +19,8 @@ import * as path from 'path'; import type { LaunchOptions, BrowserContextOptions, Page, BrowserContext, Video, APIRequestContext, Tracing } from 'playwright-core'; import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo } from '../types/test'; import { rootTestType } from './testType'; -import { createGuid, removeFolders, debugMode } from 'playwright-core/lib/utils'; +import { createGuid, debugMode } from 'playwright-core/lib/utils'; +import { removeFolders } from 'playwright-core/lib/utils/fileUtils'; import { GridClient } from 'playwright-core/lib/grid/gridClient'; export { expect } from './expect'; export const _baseTest: TestType<{}, {}> = rootTestType.test; diff --git a/packages/playwright-test/src/reporters/html.ts b/packages/playwright-test/src/reporters/html.ts index f55a841463..7ae48b2c29 100644 --- a/packages/playwright-test/src/reporters/html.ts +++ b/packages/playwright-test/src/reporters/html.ts @@ -22,7 +22,8 @@ import type { TransformCallback } from 'stream'; import { Transform } from 'stream'; import type { FullConfig, Suite, Reporter } from '../../types/testReporter'; import { HttpServer } from 'playwright-core/lib/utils/httpServer'; -import { assert, calculateSha1, removeFolders } from 'playwright-core/lib/utils'; +import { assert, calculateSha1 } from 'playwright-core/lib/utils'; +import { removeFolders } from 'playwright-core/lib/utils/fileUtils'; import type { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonTestResult, JsonTestStep } from './raw'; import RawReporter from './raw'; import yazl from 'yazl'; diff --git a/packages/playwright-test/src/reporters/raw.ts b/packages/playwright-test/src/reporters/raw.ts index 221fdf4e81..9ae8e8c543 100644 --- a/packages/playwright-test/src/reporters/raw.ts +++ b/packages/playwright-test/src/reporters/raw.ts @@ -21,7 +21,7 @@ import { assert, calculateSha1 } 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'; +import { MultiMap } from 'playwright-core/lib/utils/multimap'; import { codeFrameColumns } from '@babel/code-frame'; import type { FullConfigInternal } from '../types'; diff --git a/packages/playwright-test/src/runner.ts b/packages/playwright-test/src/runner.ts index 95dcee3ac0..3aefa5c3ed 100644 --- a/packages/playwright-test/src/runner.ts +++ b/packages/playwright-test/src/runner.ts @@ -42,8 +42,8 @@ import { Minimatch } from 'minimatch'; import type { Config } from './types'; import type { FullConfigInternal } from './types'; import { WebServer } from './webServer'; -import { raceAgainstTimeout } from 'playwright-core/lib/utils'; -import { SigIntWatcher } from 'playwright-core/lib/utils'; +import { raceAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner'; +import { SigIntWatcher } from './sigIntWatcher'; const removeFolderAsync = promisify(rimraf); const readDirAsync = promisify(fs.readdir); diff --git a/packages/playwright-test/src/sigIntWatcher.ts b/packages/playwright-test/src/sigIntWatcher.ts new file mode 100644 index 0000000000..02a8dc851f --- /dev/null +++ b/packages/playwright-test/src/sigIntWatcher.ts @@ -0,0 +1,57 @@ +/** + * 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 class SigIntWatcher { + private _hadSignal: boolean = false; + private _sigintPromise: Promise; + private _sigintHandler: () => void; + constructor() { + let sigintCallback: () => void; + this._sigintPromise = new Promise(f => sigintCallback = f); + this._sigintHandler = () => { + // We remove the handler so that second Ctrl+C immediately kills the runner + // via the default sigint handler. This is handy in the case where our shutdown + // takes a lot of time or is buggy. + // + // When running through NPM we might get multiple SIGINT signals + // for a single Ctrl+C - this is an NPM bug present since at least NPM v6. + // https://github.com/npm/cli/issues/1591 + // https://github.com/npm/cli/issues/2124 + // + // Therefore, removing the handler too soon will just kill the process + // with default handler without printing the results. + // We work around this by giving NPM 1000ms to send us duplicate signals. + // The side effect is that slow shutdown or bug in our runner will force + // the user to hit Ctrl+C again after at least a second. + setTimeout(() => process.off('SIGINT', this._sigintHandler), 1000); + this._hadSignal = true; + sigintCallback(); + }; + process.on('SIGINT', this._sigintHandler); + } + + promise(): Promise { + return this._sigintPromise; + } + + hadSignal(): boolean { + return this._hadSignal; + } + + disarm() { + process.off('SIGINT', this._sigintHandler); + } +} diff --git a/packages/playwright-test/src/timeoutManager.ts b/packages/playwright-test/src/timeoutManager.ts index f2d92488e7..301cac3fca 100644 --- a/packages/playwright-test/src/timeoutManager.ts +++ b/packages/playwright-test/src/timeoutManager.ts @@ -15,7 +15,7 @@ */ import colors from 'colors/safe'; -import { TimeoutRunner, TimeoutRunnerError } from 'playwright-core/lib/utils'; +import { TimeoutRunner, TimeoutRunnerError } from 'playwright-core/lib/utils/timeoutRunner'; import type { TestError } from '../types/test'; import type { Location } from './types'; diff --git a/packages/playwright-test/src/webServer.ts b/packages/playwright-test/src/webServer.ts index 86dd4206ec..8bf492bf32 100644 --- a/packages/playwright-test/src/webServer.ts +++ b/packages/playwright-test/src/webServer.ts @@ -18,7 +18,7 @@ import http from 'http'; import https from 'https'; import net from 'net'; import debug from 'debug'; -import { raceAgainstTimeout } from 'playwright-core/lib/utils'; +import { raceAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner'; import type { WebServerConfig } from './types'; import { launchProcess } from 'playwright-core/lib/utils/processLauncher'; import type { Reporter } from '../types/testReporter'; diff --git a/packages/playwright-test/src/workerRunner.ts b/packages/playwright-test/src/workerRunner.ts index b1db516d44..d5e7629a1e 100644 --- a/packages/playwright-test/src/workerRunner.ts +++ b/packages/playwright-test/src/workerRunner.ts @@ -26,7 +26,7 @@ import type { Suite, TestCase } from './test'; import type { Annotation, TestError, TestStepInternal } from './types'; import type { ProjectImpl } from './project'; import { FixtureRunner } from './fixtures'; -import { ManualPromise } from 'playwright-core/lib/utils'; +import { ManualPromise } from 'playwright-core/lib/utils/manualPromise'; import { TestInfoImpl } from './testInfo'; import type { TimeSlot } from './timeoutManager'; import { TimeoutManager } from './timeoutManager'; diff --git a/tests/config/browserTest.ts b/tests/config/browserTest.ts index cb1a6d78a8..9b5fb9731c 100644 --- a/tests/config/browserTest.ts +++ b/tests/config/browserTest.ts @@ -19,7 +19,7 @@ import * as os from 'os'; import type { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi'; import * as path from 'path'; import type { BrowserContext, BrowserContextOptions, BrowserType, Page } from 'playwright-core'; -import { removeFolders } from '../../packages/playwright-core/lib/utils'; +import { removeFolders } from '../../packages/playwright-core/lib/utils/fileUtils'; import { baseTest } from './baseTest'; import type { RemoteServerOptions } from './remoteServer'; import { RemoteServer } from './remoteServer'; diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index c6c7bdd5b9..0099d7bc55 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -18,7 +18,7 @@ import fs from 'fs'; import { test as baseTest, expect, createImage } from './playwright-test-fixtures'; import type { HttpServer } from '../../packages/playwright-core/lib/utils/httpServer'; import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html'; -import { spawnAsync } from 'playwright-core/lib/utils'; +import { spawnAsync } from 'playwright-core/lib/utils/spawnAsync'; const test = baseTest.extend<{ showReport: () => Promise }>({ showReport: async ({ page }, use, testInfo) => {