playwright
Version:
A high-level API to automate web browsers
1,214 lines (1,200 loc) • 114 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/playwright/src/common/index.ts
var index_exports = {};
__export(index_exports, {
FullConfigInternal: () => FullConfigInternal,
ProcessRunner: () => ProcessRunner,
builtInReporters: () => builtInReporters,
cc: () => compilationCache_exports,
config: () => config_exports,
configLoader: () => configLoader_exports,
defineConfig: () => defineConfig,
esm: () => esmLoaderHost_exports,
fixtures: () => fixtures_exports,
ipc: () => ipc_exports,
mergeTests: () => mergeTests,
poolBuilder: () => poolBuilder_exports,
processRunner: () => process_exports,
startProcessRunner: () => startProcessRunner,
suiteUtils: () => suiteUtils_exports,
test: () => test_exports,
testLoader: () => testLoader_exports,
testType: () => testType_exports,
transform: () => transform_exports
});
module.exports = __toCommonJS(index_exports);
// packages/playwright/src/transform/compilationCache.ts
var compilationCache_exports = {};
__export(compilationCache_exports, {
addToCompilationCache: () => addToCompilationCache,
affectedTestFiles: () => affectedTestFiles,
belongsToNodeModules: () => belongsToNodeModules,
cacheDir: () => cacheDir,
collectAffectedTestFiles: () => collectAffectedTestFiles,
currentFileDepsCollector: () => currentFileDepsCollector,
dependenciesForTestFile: () => dependenciesForTestFile,
fileDependenciesForTest: () => fileDependenciesForTest,
getFromCompilationCache: () => getFromCompilationCache,
getUserData: () => getUserData,
installSourceMapSupport: () => installSourceMapSupport,
internalDependenciesForTestFile: () => internalDependenciesForTestFile,
serializeCompilationCache: () => serializeCompilationCache,
setExternalDependencies: () => setExternalDependencies,
startCollectingFileDeps: () => startCollectingFileDeps,
stopCollectingFileDeps: () => stopCollectingFileDeps
});
var import_fs = __toESM(require("fs"));
var import_os = __toESM(require("os"));
var import_path = __toESM(require("path"));
var import_globals = require("../globals");
var import_package = require("../package");
var sourceMapSupport = require("playwright-core/lib/utilsBundle").sourceMapSupport;
var { calculateSha1 } = require("playwright-core/lib/coreBundle").utils;
var { isUnderTest } = require("playwright-core/lib/coreBundle").utils;
var cacheDir = process.env.PWTEST_CACHE_DIR || (() => {
if (process.platform === "win32")
return import_path.default.join(import_os.default.tmpdir(), `playwright-transform-cache`);
return import_path.default.join(import_os.default.tmpdir(), `playwright-transform-cache-` + process.geteuid?.());
})();
var sourceMaps = /* @__PURE__ */ new Map();
var memoryCache = /* @__PURE__ */ new Map();
var fileDependencies = /* @__PURE__ */ new Map();
var externalDependencies = /* @__PURE__ */ new Map();
var devSourceInfix = import_path.default.sep + "playwright" + import_path.default.sep + "packages" + import_path.default.sep;
function installSourceMapSupport() {
Error.stackTraceLimit = 200;
sourceMapSupport.install({
environment: "node",
handleUncaughtExceptions: false,
retrieveSourceMap(source) {
if (!process.env.PWDEBUGIMPL && isUnderTest() && source.includes(devSourceInfix))
return { map: identitySourceMap(source), url: source };
if (!sourceMaps.has(source))
return null;
const sourceMapPath = sourceMaps.get(source);
try {
return {
map: JSON.parse(import_fs.default.readFileSync(sourceMapPath, "utf-8")),
url: source
};
} catch {
return null;
}
}
});
}
function identitySourceMap(source) {
const lineCount = import_fs.default.readFileSync(source, "utf8").split("\n").length;
return {
version: 3,
sources: [source],
mappings: lineCount ? "AAAA" + ";AACA".repeat(lineCount - 1) : ""
};
}
function _innerAddToCompilationCacheAndSerialize(filename, entry) {
sourceMaps.set(entry.moduleUrl || filename, entry.sourceMapPath);
memoryCache.set(filename, entry);
return {
sourceMaps: [[entry.moduleUrl || filename, entry.sourceMapPath]],
memoryCache: [[filename, entry]],
fileDependencies: [],
externalDependencies: []
};
}
function getFromCompilationCache(filename, contentHash, moduleUrl) {
const cache = memoryCache.get(filename);
if (cache?.codePath) {
try {
return { cachedCode: import_fs.default.readFileSync(cache.codePath, "utf-8") };
} catch {
}
}
const filePathHash = calculateFilePathHash(filename);
const hashPrefix = filePathHash + "_" + contentHash.substring(0, 7);
const cacheFolderName = filePathHash.substring(0, 2);
const cachePath = calculateCachePath(filename, cacheFolderName, hashPrefix);
const codePath = cachePath + ".js";
const sourceMapPath = cachePath + ".map";
const dataPath = cachePath + ".data";
try {
const cachedCode = import_fs.default.readFileSync(codePath, "utf8");
const serializedCache = _innerAddToCompilationCacheAndSerialize(filename, { codePath, sourceMapPath, dataPath, moduleUrl });
return { cachedCode, serializedCache };
} catch {
}
return {
addToCache: (code, map, data) => {
if ((0, import_globals.isWorkerProcess)())
return {};
clearOldCacheEntries(cacheFolderName, filePathHash);
import_fs.default.mkdirSync(import_path.default.dirname(cachePath), { recursive: true });
if (map)
import_fs.default.writeFileSync(sourceMapPath, JSON.stringify(map), "utf8");
if (data.size)
import_fs.default.writeFileSync(dataPath, JSON.stringify(Object.fromEntries(data.entries()), void 0, 2), "utf8");
import_fs.default.writeFileSync(codePath, code, "utf8");
const serializedCache = _innerAddToCompilationCacheAndSerialize(filename, { codePath, sourceMapPath, dataPath, moduleUrl });
return { serializedCache };
}
};
}
function serializeCompilationCache() {
return {
sourceMaps: [...sourceMaps.entries()],
memoryCache: [...memoryCache.entries()],
fileDependencies: [...fileDependencies.entries()].map(([filename, deps]) => [filename, [...deps]]),
externalDependencies: [...externalDependencies.entries()].map(([filename, deps]) => [filename, [...deps]])
};
}
function addToCompilationCache(payload) {
for (const entry of payload.sourceMaps)
sourceMaps.set(entry[0], entry[1]);
for (const entry of payload.memoryCache)
memoryCache.set(entry[0], entry[1]);
for (const entry of payload.fileDependencies) {
const existing = fileDependencies.get(entry[0]) || [];
fileDependencies.set(entry[0], /* @__PURE__ */ new Set([...entry[1], ...existing]));
}
for (const entry of payload.externalDependencies) {
const existing = externalDependencies.get(entry[0]) || [];
externalDependencies.set(entry[0], /* @__PURE__ */ new Set([...entry[1], ...existing]));
}
}
function calculateFilePathHash(filePath) {
return calculateSha1(filePath).substring(0, 10);
}
function calculateCachePath(filePath, cacheFolderName, hashPrefix) {
const fileName2 = hashPrefix + "_" + import_path.default.basename(filePath, import_path.default.extname(filePath)).replace(/\W/g, "");
return import_path.default.join(cacheDir, cacheFolderName, fileName2);
}
function clearOldCacheEntries(cacheFolderName, filePathHash) {
const cachePath = import_path.default.join(cacheDir, cacheFolderName);
try {
const cachedRelevantFiles = import_fs.default.readdirSync(cachePath).filter((file2) => file2.startsWith(filePathHash));
for (const file2 of cachedRelevantFiles)
import_fs.default.rmSync(import_path.default.join(cachePath, file2), { force: true });
} catch {
}
}
var depsCollector2;
function startCollectingFileDeps() {
depsCollector2 = /* @__PURE__ */ new Set();
}
function stopCollectingFileDeps(filename) {
if (!depsCollector2)
return;
depsCollector2.delete(filename);
for (const dep of depsCollector2) {
if (belongsToNodeModules(dep))
depsCollector2.delete(dep);
}
fileDependencies.set(filename, depsCollector2);
depsCollector2 = void 0;
}
function currentFileDepsCollector() {
return depsCollector2;
}
function setExternalDependencies(filename, deps) {
const depsSet = new Set(deps.filter((dep) => !belongsToNodeModules(dep) && dep !== filename));
externalDependencies.set(filename, depsSet);
}
function fileDependenciesForTest() {
return Object.fromEntries([...fileDependencies.entries()].map((entry) => [import_path.default.basename(entry[0]), [...entry[1]].map((f) => import_path.default.basename(f)).sort()]));
}
function collectAffectedTestFiles(changedFile, testFileCollector) {
const isTestFile = (file2) => fileDependencies.has(file2);
if (isTestFile(changedFile))
testFileCollector.add(changedFile);
for (const [testFile, deps] of fileDependencies) {
if (deps.has(changedFile))
testFileCollector.add(testFile);
}
for (const [importingFile, depsOfImportingFile] of externalDependencies) {
if (depsOfImportingFile.has(changedFile)) {
if (isTestFile(importingFile))
testFileCollector.add(importingFile);
for (const [testFile, depsOfTestFile] of fileDependencies) {
if (depsOfTestFile.has(importingFile))
testFileCollector.add(testFile);
}
}
}
}
function affectedTestFiles(changes) {
const result2 = /* @__PURE__ */ new Set();
for (const change of changes)
collectAffectedTestFiles(change, result2);
return [...result2];
}
function internalDependenciesForTestFile(filename) {
return fileDependencies.get(filename);
}
function dependenciesForTestFile(filename) {
const result2 = /* @__PURE__ */ new Set();
for (const testDependency of fileDependencies.get(filename) || []) {
result2.add(testDependency);
for (const externalDependency of externalDependencies.get(testDependency) || [])
result2.add(externalDependency);
}
for (const dep of externalDependencies.get(filename) || [])
result2.add(dep);
return result2;
}
var kPlaywrightInternalPrefix = import_package.packageRoot;
function belongsToNodeModules(file2) {
if (file2.includes(`${import_path.default.sep}node_modules${import_path.default.sep}`))
return true;
if (file2.startsWith(kPlaywrightInternalPrefix) && (file2.endsWith(".js") || file2.endsWith(".mjs")))
return true;
return false;
}
async function getUserData(pluginName) {
const result2 = /* @__PURE__ */ new Map();
for (const [fileName2, cache] of memoryCache) {
if (!cache.dataPath)
continue;
if (!import_fs.default.existsSync(cache.dataPath))
continue;
const data = JSON.parse(await import_fs.default.promises.readFile(cache.dataPath, "utf8"));
if (data[pluginName])
result2.set(fileName2, data[pluginName]);
}
return result2;
}
// packages/playwright/src/common/config.ts
var config_exports = {};
__export(config_exports, {
FullConfigInternal: () => FullConfigInternal,
FullProjectInternal: () => FullProjectInternal,
builtInReporters: () => builtInReporters,
defaultGrep: () => defaultGrep,
defaultReporter: () => defaultReporter,
defaultTimeout: () => defaultTimeout,
getProjectId: () => getProjectId,
toReporters: () => toReporters
});
var import_fs3 = __toESM(require("fs"));
var import_os2 = __toESM(require("os"));
var import_path3 = __toESM(require("path"));
var import_package2 = require("../package");
// packages/playwright/src/util.ts
var import_fs2 = __toESM(require("fs"));
var import_path2 = __toESM(require("path"));
var import_url = __toESM(require("url"));
var import_util = __toESM(require("util"));
var debug = require("playwright-core/lib/utilsBundle").debug;
var mime = require("playwright-core/lib/utilsBundle").mime;
var minimatch = require("playwright-core/lib/utilsBundle").minimatch;
var { calculateSha1: calculateSha12 } = require("playwright-core/lib/coreBundle").utils;
var { sanitizeForFilePath } = require("playwright-core/lib/coreBundle").utils;
var { isRegExp } = require("playwright-core/lib/coreBundle").iso;
var { parseStackFrame, stringifyStackFrames } = require("playwright-core/lib/coreBundle").iso;
var { ansiRegex, isString, stripAnsiEscapes } = require("playwright-core/lib/coreBundle").iso;
var PLAYWRIGHT_TEST_PATH = import_path2.default.join(__dirname, "..");
var PLAYWRIGHT_CORE_PATH = import_path2.default.dirname(require.resolve("playwright-core/package.json"));
function filterStackTrace(e) {
const name = e.name ? e.name + ": " : "";
const cause = e.cause instanceof Error ? filterStackTrace(e.cause) : void 0;
if (process.env.PWDEBUGIMPL)
return { message: name + e.message, stack: e.stack || "", cause };
const stackLines = stringifyStackFrames(filteredStackTrace(e.stack?.split("\n") || []));
return {
message: name + e.message,
stack: `${name}${e.message}${stackLines.map((line) => "\n" + line).join("")}`,
cause
};
}
function filterStackFile(file2) {
if (process.env.PWDEBUGIMPL)
return true;
if (file2.startsWith(PLAYWRIGHT_TEST_PATH))
return false;
if (file2.startsWith(PLAYWRIGHT_CORE_PATH))
return false;
return true;
}
function filteredStackTrace(rawStack) {
const frames = [];
for (const line of rawStack) {
const frame = parseStackFrame(line, import_path2.default.sep, !!process.env.PWDEBUGIMPL);
if (!frame || !frame.file)
continue;
if (!filterStackFile(frame.file))
continue;
frames.push(frame);
}
return frames;
}
function serializeError(error) {
if (error instanceof Error)
return filterStackTrace(error);
return {
value: import_util.default.inspect(error)
};
}
function parseLocationArg(arg) {
const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
return {
file: match ? match[1] : arg,
line: match ? parseInt(match[2], 10) : null,
column: match?.[3] ? parseInt(match[3], 10) : null
};
}
function createFileMatcher(patterns) {
const reList = [];
const filePatterns = [];
for (const pattern of Array.isArray(patterns) ? patterns : [patterns]) {
if (isRegExp(pattern)) {
reList.push(pattern);
} else {
if (!pattern.startsWith("**/"))
filePatterns.push("**/" + pattern);
else
filePatterns.push(pattern);
}
}
return (filePath) => {
for (const re of reList) {
re.lastIndex = 0;
if (re.test(filePath))
return true;
}
if (import_path2.default.sep === "\\") {
const fileURL = import_url.default.pathToFileURL(filePath).href;
for (const re of reList) {
re.lastIndex = 0;
if (re.test(fileURL))
return true;
}
}
for (const pattern of filePatterns) {
if (minimatch(filePath, pattern, { nocase: true, dot: true }))
return true;
}
return false;
};
}
function mergeObjects(a, b, c) {
const result2 = { ...a };
for (const x of [b, c].filter(Boolean)) {
for (const [name, value] of Object.entries(x)) {
if (!Object.is(value, void 0))
result2[name] = value;
}
}
return result2;
}
function forceRegExp(pattern) {
const match = pattern.match(/^\/(.*)\/([gi]*)$/);
if (match)
return new RegExp(match[1], match[2]);
return new RegExp(pattern, "gi");
}
function relativeFilePath(file2) {
if (!import_path2.default.isAbsolute(file2))
return file2;
return import_path2.default.relative(process.cwd(), file2);
}
function formatLocation(location) {
return relativeFilePath(location.file) + ":" + location.line + ":" + location.column;
}
function errorWithFile(file2, message) {
return new Error(`${relativeFilePath(file2)}: ${message}`);
}
var debugTest = debug("pw:test");
var folderToPackageJsonPath = /* @__PURE__ */ new Map();
function getPackageJsonPath(folderPath) {
const cached = folderToPackageJsonPath.get(folderPath);
if (cached !== void 0)
return cached;
const packageJsonPath = import_path2.default.join(folderPath, "package.json");
if (import_fs2.default.existsSync(packageJsonPath)) {
folderToPackageJsonPath.set(folderPath, packageJsonPath);
return packageJsonPath;
}
const parentFolder = import_path2.default.dirname(folderPath);
if (folderPath === parentFolder) {
folderToPackageJsonPath.set(folderPath, "");
return "";
}
const result2 = getPackageJsonPath(parentFolder);
folderToPackageJsonPath.set(folderPath, result2);
return result2;
}
function fileIsModule(file2) {
if (file2.endsWith(".mjs") || file2.endsWith(".mts"))
return true;
if (file2.endsWith(".cjs") || file2.endsWith(".cts"))
return false;
const folder = import_path2.default.dirname(file2);
return folderIsModule(folder);
}
function folderIsModule(folder) {
const packageJsonPath = getPackageJsonPath(folder);
if (!packageJsonPath)
return false;
return require(packageJsonPath).type === "module";
}
var packageJsonMainFieldCache = /* @__PURE__ */ new Map();
function getMainFieldFromPackageJson(packageJsonPath) {
if (!packageJsonMainFieldCache.has(packageJsonPath)) {
let mainField;
try {
mainField = JSON.parse(import_fs2.default.readFileSync(packageJsonPath, "utf8")).main;
} catch {
}
packageJsonMainFieldCache.set(packageJsonPath, mainField);
}
return packageJsonMainFieldCache.get(packageJsonPath);
}
var kExtLookups = /* @__PURE__ */ new Map([
[".js", [".jsx", ".ts", ".tsx"]],
[".jsx", [".tsx"]],
[".cjs", [".cts"]],
[".mjs", [".mts"]],
["", [".js", ".ts", ".jsx", ".tsx", ".cjs", ".mjs", ".cts", ".mts"]]
]);
function resolveImportSpecifierExtension(resolved) {
if (fileExists(resolved))
return resolved;
for (const [ext, others] of kExtLookups) {
if (!resolved.endsWith(ext))
continue;
for (const other of others) {
const modified = resolved.substring(0, resolved.length - ext.length) + other;
if (fileExists(modified))
return modified;
}
break;
}
}
function resolveImportSpecifierAfterMapping(resolved, afterPathMapping) {
const resolvedFile = resolveImportSpecifierExtension(resolved);
if (resolvedFile)
return resolvedFile;
if (dirExists(resolved)) {
const packageJsonPath = import_path2.default.join(resolved, "package.json");
if (afterPathMapping) {
const mainField = getMainFieldFromPackageJson(packageJsonPath);
const mainFieldResolved = mainField ? resolveImportSpecifierExtension(import_path2.default.resolve(resolved, mainField)) : void 0;
return mainFieldResolved || resolveImportSpecifierExtension(import_path2.default.join(resolved, "index"));
}
if (fileExists(packageJsonPath))
return resolved;
const dirImport = import_path2.default.join(resolved, "index");
return resolveImportSpecifierExtension(dirImport);
}
}
function fileExists(resolved) {
return import_fs2.default.statSync(resolved, { throwIfNoEntry: false })?.isFile();
}
function dirExists(resolved) {
return import_fs2.default.statSync(resolved, { throwIfNoEntry: false })?.isDirectory();
}
function takeFirst(...args) {
for (const arg of args) {
if (arg !== void 0)
return arg;
}
return void 0;
}
// packages/playwright/src/common/config.ts
var defaultTimeout = 3e4;
var FullConfigInternal = class {
constructor(location, userConfig, configCLIOverrides, metadata) {
this.projects = [];
this.defineConfigWasUsed = false;
this.globalSetups = [];
this.globalTeardowns = [];
if (configCLIOverrides.projects && userConfig.projects)
throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
const { resolvedConfigFile, configDir } = location;
const packageJsonPath = getPackageJsonPath(configDir);
const packageJsonDir = packageJsonPath ? import_path3.default.dirname(packageJsonPath) : process.cwd();
this.configDir = configDir;
this.configCLIOverrides = configCLIOverrides;
const privateConfiguration = userConfig["@playwright/test"];
this.plugins = (privateConfiguration?.plugins || []).map((p) => ({ factory: p }));
this.singleTSConfigPath = pathResolve(configDir, userConfig.tsconfig);
this.captureGitInfo = userConfig.captureGitInfo;
this.failOnFlakyTests = takeFirst(configCLIOverrides.failOnFlakyTests, userConfig.failOnFlakyTests, false);
this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map((s) => resolveScript(s, configDir)).filter((script) => script !== void 0);
this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map((s) => resolveScript(s, configDir)).filter((script) => script !== void 0);
userConfig.metadata = userConfig.metadata || {};
const globalTags = Array.isArray(userConfig.tag) ? userConfig.tag : userConfig.tag ? [userConfig.tag] : [];
for (const tag of globalTags) {
if (tag[0] !== "@")
throw new Error(`Tag must start with "@" symbol, got "${tag}" instead.`);
}
this.config = {
configFile: resolvedConfigFile,
rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
forbidOnly: takeFirst(configCLIOverrides.forbidOnly, userConfig.forbidOnly, false),
fullyParallel: takeFirst(configCLIOverrides.fullyParallel, userConfig.fullyParallel, false),
globalSetup: this.globalSetups[0] ?? null,
globalTeardown: this.globalTeardowns[0] ?? null,
globalTimeout: takeFirst(configCLIOverrides.debug ? 0 : void 0, configCLIOverrides.globalTimeout, userConfig.globalTimeout, 0),
grep: takeFirst(userConfig.grep, defaultGrep),
grepInvert: takeFirst(userConfig.grepInvert, null),
maxFailures: takeFirst(configCLIOverrides.debug ? 1 : void 0, configCLIOverrides.maxFailures, userConfig.maxFailures, 0),
metadata: metadata ?? userConfig.metadata,
preserveOutput: takeFirst(userConfig.preserveOutput, "always"),
projects: [],
quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
reportSlowTests: takeFirst(userConfig.reportSlowTests, {
max: 5,
threshold: 3e5
/* 5 minutes */
}),
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
tags: globalTags,
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, "missing"),
updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, "patch"),
version: import_package2.packageJSON.version,
workers: resolveWorkers(takeFirst(configCLIOverrides.debug || configCLIOverrides.pause ? 1 : void 0, configCLIOverrides.workers, userConfig.workers, "50%")),
webServer: null
};
for (const key in userConfig) {
if (key.startsWith("@"))
this.config[key] = userConfig[key];
}
this.config[configInternalSymbol] = this;
const webServers = takeFirst(userConfig.webServer, null);
if (Array.isArray(webServers)) {
this.config.webServer = null;
this.webServers = webServers;
} else if (webServers) {
this.config.webServer = webServers;
this.webServers = [webServers];
} else {
this.webServers = [];
}
const projectConfigs = configCLIOverrides.projects || userConfig.projects || [{ ...userConfig, workers: void 0 }];
this.projects = projectConfigs.map((p) => new FullProjectInternal(configDir, userConfig, this, p, this.configCLIOverrides, packageJsonDir));
resolveProjectDependencies(this.projects);
this._assignUniqueProjectIds(this.projects);
this.config.projects = this.projects.map((p) => p.project);
}
_assignUniqueProjectIds(projects) {
const usedNames = /* @__PURE__ */ new Set();
for (const p of projects) {
const name = p.project.name || "";
for (let i = 0; i < projects.length; ++i) {
const candidate = name + (i ? i : "");
if (usedNames.has(candidate))
continue;
p.id = candidate;
p.project.__projectId = p.id;
usedNames.add(candidate);
break;
}
}
}
};
var FullProjectInternal = class {
constructor(configDir, config, fullConfig, projectConfig, configCLIOverrides, packageJsonDir) {
this.id = "";
this.deps = [];
this.fullConfig = fullConfig;
const testDir = takeFirst(pathResolve(configDir, projectConfig.testDir), pathResolve(configDir, config.testDir), fullConfig.configDir);
this.snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate);
this.project = {
grep: takeFirst(projectConfig.grep, config.grep, defaultGrep),
grepInvert: takeFirst(projectConfig.grepInvert, config.grepInvert, null),
outputDir: takeFirst(configCLIOverrides.outputDir, pathResolve(configDir, projectConfig.outputDir), pathResolve(configDir, config.outputDir), import_path3.default.join(packageJsonDir, "test-results")),
// Note: we either apply the cli override for repeatEach or not, depending on whether the
// project is top-level vs dependency. See collectProjectsAndTestFiles in loadUtils.
repeatEach: takeFirst(projectConfig.repeatEach, config.repeatEach, 1),
retries: takeFirst(configCLIOverrides.retries, projectConfig.retries, config.retries, 0),
metadata: takeFirst(projectConfig.metadata, config.metadata, {}),
name: takeFirst(projectConfig.name, config.name, ""),
testDir,
snapshotDir: takeFirst(pathResolve(configDir, projectConfig.snapshotDir), pathResolve(configDir, config.snapshotDir), testDir),
testIgnore: takeFirst(projectConfig.testIgnore, config.testIgnore, []),
testMatch: takeFirst(projectConfig.testMatch, config.testMatch, "**/*.@(spec|test).?(c|m)[jt]s?(x)"),
timeout: takeFirst(configCLIOverrides.debug === "inspector" ? 0 : void 0, configCLIOverrides.timeout, projectConfig.timeout, config.timeout, defaultTimeout),
use: mergeObjects(config.use, projectConfig.use, configCLIOverrides.use),
dependencies: projectConfig.dependencies || [],
teardown: projectConfig.teardown,
ignoreSnapshots: takeFirst(configCLIOverrides.ignoreSnapshots, projectConfig.ignoreSnapshots, config.ignoreSnapshots, false)
};
this.fullyParallel = takeFirst(configCLIOverrides.fullyParallel, projectConfig.fullyParallel, config.fullyParallel, void 0);
this.expect = takeFirst(projectConfig.expect, config.expect, {});
if (this.expect.toHaveScreenshot?.stylePath) {
const stylePaths = Array.isArray(this.expect.toHaveScreenshot.stylePath) ? this.expect.toHaveScreenshot.stylePath : [this.expect.toHaveScreenshot.stylePath];
this.expect.toHaveScreenshot.stylePath = stylePaths.map((stylePath) => import_path3.default.resolve(configDir, stylePath));
}
this.respectGitIgnore = takeFirst(projectConfig.respectGitIgnore, config.respectGitIgnore, !projectConfig.testDir && !config.testDir);
this.workers = projectConfig.workers ? resolveWorkers(projectConfig.workers) : void 0;
if (configCLIOverrides.debug && this.workers)
this.workers = 1;
}
};
function pathResolve(baseDir, relative) {
if (!relative)
return void 0;
return import_path3.default.resolve(baseDir, relative);
}
function resolveReporters(reporters, rootDir) {
return toReporters(reporters)?.map(([id, arg]) => {
if (builtInReporters.includes(id))
return [id, arg];
return [require.resolve(id, { paths: [rootDir] }), arg];
});
}
function resolveWorkers(workers) {
if (typeof workers === "string") {
if (workers.endsWith("%")) {
const cpus = import_os2.default.cpus().length;
return Math.max(1, Math.floor(cpus * (parseInt(workers, 10) / 100)));
}
const parsedWorkers = parseInt(workers, 10);
if (isNaN(parsedWorkers))
throw new Error(`Workers ${workers} must be a number or percentage.`);
if (parsedWorkers < 1)
throw new Error(`Workers must be a positive number, received ${parsedWorkers}.`);
return parsedWorkers;
}
if (workers < 1)
throw new Error(`Workers must be a positive number, received ${workers}.`);
return workers;
}
function resolveProjectDependencies(projects) {
const teardownSet = /* @__PURE__ */ new Set();
for (const project of projects) {
for (const dependencyName of project.project.dependencies) {
const dependencies = projects.filter((p) => p.project.name === dependencyName);
if (!dependencies.length)
throw new Error(`Project '${project.project.name}' depends on unknown project '${dependencyName}'`);
if (dependencies.length > 1)
throw new Error(`Project dependencies should have unique names, reading ${dependencyName}`);
project.deps.push(...dependencies);
}
if (project.project.teardown) {
const teardowns = projects.filter((p) => p.project.name === project.project.teardown);
if (!teardowns.length)
throw new Error(`Project '${project.project.name}' has unknown teardown project '${project.project.teardown}'`);
if (teardowns.length > 1)
throw new Error(`Project teardowns should have unique names, reading ${project.project.teardown}`);
const teardown = teardowns[0];
project.teardown = teardown;
teardownSet.add(teardown);
}
}
for (const teardown of teardownSet) {
if (teardown.deps.length)
throw new Error(`Teardown project ${teardown.project.name} must not have dependencies`);
}
for (const project of projects) {
for (const dep of project.deps) {
if (teardownSet.has(dep))
throw new Error(`Project ${project.project.name} must not depend on a teardown project ${dep.project.name}`);
}
}
}
function toReporters(reporters) {
if (!reporters)
return;
if (typeof reporters === "string")
return [[reporters]];
return reporters;
}
var builtInReporters = ["list", "line", "dot", "json", "junit", "null", "github", "html", "blob"];
function resolveScript(id, rootDir) {
if (!id)
return void 0;
const localPath = import_path3.default.resolve(rootDir, id);
if (import_fs3.default.existsSync(localPath))
return localPath;
return require.resolve(id, { paths: [rootDir] });
}
var defaultGrep = /.*/;
var defaultReporter = process.env.CI ? "dot" : "list";
var configInternalSymbol = Symbol("configInternalSymbol");
function getProjectId(project) {
return project.__projectId;
}
// packages/playwright/src/common/configLoader.ts
var configLoader_exports = {};
__export(configLoader_exports, {
defineConfig: () => defineConfig,
deserializeConfig: () => deserializeConfig,
loadConfig: () => loadConfig,
loadConfigFromFile: () => loadConfigFromFile,
loadEmptyConfigForMergeReports: () => loadEmptyConfigForMergeReports,
resolveConfigLocation: () => resolveConfigLocation
});
var import_fs6 = __toESM(require("fs"));
var import_path7 = __toESM(require("path"));
// packages/playwright/src/transform/transform.ts
var transform_exports = {};
__export(transform_exports, {
requireOrImport: () => requireOrImport,
resolveHook: () => resolveHook,
setSingleTSConfig: () => setSingleTSConfig,
setTransformConfig: () => setTransformConfig,
setTransformData: () => setTransformData,
shouldTransform: () => shouldTransform,
singleTSConfig: () => singleTSConfig,
transformConfig: () => transformConfig,
transformHook: () => transformHook,
wrapFunctionWithLocation: () => wrapFunctionWithLocation
});
var import_fs5 = __toESM(require("fs"));
var import_module2 = __toESM(require("module"));
var import_path6 = __toESM(require("path"));
var import_url2 = __toESM(require("url"));
var import_crypto = __toESM(require("crypto"));
// packages/playwright/src/transform/tsconfig-loader.ts
var import_path4 = __toESM(require("path"));
var import_fs4 = __toESM(require("fs"));
var json5 = require("playwright-core/lib/utilsBundle").json5;
function loadTsConfig(configPath) {
try {
const references = [];
const config = innerLoadTsConfig(configPath, references);
return [config, ...references];
} catch (e) {
throw new Error(`Failed to load tsconfig file at ${configPath}:
${e.message}`);
}
}
function resolveConfigFile(baseConfigFile, referencedConfigFile) {
if (!referencedConfigFile.endsWith(".json"))
referencedConfigFile += ".json";
const currentDir = import_path4.default.dirname(baseConfigFile);
let resolvedConfigFile = import_path4.default.resolve(currentDir, referencedConfigFile);
if (referencedConfigFile.includes("/") && referencedConfigFile.includes(".") && !import_fs4.default.existsSync(resolvedConfigFile))
resolvedConfigFile = import_path4.default.join(currentDir, "node_modules", referencedConfigFile);
return resolvedConfigFile;
}
function innerLoadTsConfig(configFilePath, references, visited = /* @__PURE__ */ new Map()) {
if (visited.has(configFilePath))
return visited.get(configFilePath);
let result2 = {
tsConfigPath: configFilePath
};
visited.set(configFilePath, result2);
if (!import_fs4.default.existsSync(configFilePath))
return result2;
const configString = import_fs4.default.readFileSync(configFilePath, "utf-8");
const cleanedJson = StripBom(configString);
const parsedConfig = json5.parse(cleanedJson);
const extendsArray = Array.isArray(parsedConfig.extends) ? parsedConfig.extends : parsedConfig.extends ? [parsedConfig.extends] : [];
for (const extendedConfig of extendsArray) {
const extendedConfigPath = resolveConfigFile(configFilePath, extendedConfig);
const base = innerLoadTsConfig(extendedConfigPath, references, visited);
Object.assign(result2, base, { tsConfigPath: configFilePath });
}
if (parsedConfig.compilerOptions?.allowJs !== void 0)
result2.allowJs = parsedConfig.compilerOptions.allowJs;
if (parsedConfig.compilerOptions?.paths !== void 0) {
result2.paths = {
mapping: parsedConfig.compilerOptions.paths,
pathsBasePath: import_path4.default.dirname(configFilePath)
};
}
if (parsedConfig.compilerOptions?.baseUrl !== void 0) {
result2.absoluteBaseUrl = import_path4.default.resolve(import_path4.default.dirname(configFilePath), parsedConfig.compilerOptions.baseUrl);
}
for (const ref of parsedConfig.references || [])
references.push(innerLoadTsConfig(resolveConfigFile(configFilePath, ref.path), references, visited));
if (import_path4.default.basename(configFilePath) === "jsconfig.json" && result2.allowJs === void 0)
result2.allowJs = true;
return result2;
}
function StripBom(string) {
if (typeof string !== "string") {
throw new TypeError(`Expected a string, got ${typeof string}`);
}
if (string.charCodeAt(0) === 65279) {
return string.slice(1);
}
return string;
}
// packages/playwright/src/transform/transform.ts
var import_package3 = require("../package");
// packages/playwright/src/transform/pirates.ts
var import_module = __toESM(require("module"));
var import_path5 = __toESM(require("path"));
function addHook(transformHook2, shouldTransform2, extensions) {
const extensionsToOverwrite = extensions.filter((e) => e !== ".cjs");
const allSupportedExtensions = new Set(extensions);
const loaders = import_module.default._extensions;
const jsLoader = loaders[".js"];
for (const extension of extensionsToOverwrite) {
let newLoader2 = function(mod, filename, ...loaderArgs) {
if (allSupportedExtensions.has(import_path5.default.extname(filename)) && shouldTransform2(filename)) {
let newCompile2 = function(code, file2, ...ignoredArgs) {
mod._compile = oldCompile;
return oldCompile.call(this, transformHook2(code, filename), file2);
};
var newCompile = newCompile2;
const oldCompile = mod._compile;
mod._compile = newCompile2;
}
originalLoader.call(this, mod, filename, ...loaderArgs);
};
var newLoader = newLoader2;
const originalLoader = loaders[extension] || jsLoader;
loaders[extension] = newLoader2;
}
}
// packages/playwright/src/transform/transform.ts
var sourceMapSupport2 = require("playwright-core/lib/utilsBundle").sourceMapSupport;
var version = import_package3.packageJSON.version;
var cachedTSConfigs = /* @__PURE__ */ new Map();
var _transformConfig = {
babelPlugins: [],
external: []
};
var _externalMatcher = () => false;
function setTransformConfig(config) {
_transformConfig = config;
_externalMatcher = createFileMatcher(_transformConfig.external);
}
function transformConfig() {
return _transformConfig;
}
var _singleTSConfigPath;
var _singleTSConfig;
function setSingleTSConfig(value) {
_singleTSConfigPath = value;
}
function singleTSConfig() {
return _singleTSConfigPath;
}
function validateTsConfig(tsconfig) {
const pathsBase = tsconfig.absoluteBaseUrl ?? tsconfig.paths?.pathsBasePath;
const pathsFallback = tsconfig.absoluteBaseUrl ? [{ key: "*", values: ["*"] }] : [];
return {
allowJs: !!tsconfig.allowJs,
pathsBase,
paths: Object.entries(tsconfig.paths?.mapping || {}).map(([key, values]) => ({ key, values })).concat(pathsFallback)
};
}
function loadAndValidateTsconfigsForFile(file2) {
if (_singleTSConfigPath && !_singleTSConfig)
_singleTSConfig = loadTsConfig(_singleTSConfigPath).map(validateTsConfig);
if (_singleTSConfig)
return _singleTSConfig;
return loadAndValidateTsconfigsForFolder(import_path6.default.dirname(file2));
}
function loadAndValidateTsconfigsForFolder(folder) {
const foldersWithConfig = [];
let currentFolder = import_path6.default.resolve(folder);
let result2;
while (true) {
const cached = cachedTSConfigs.get(currentFolder);
if (cached) {
result2 = cached;
break;
}
foldersWithConfig.push(currentFolder);
for (const name of ["tsconfig.json", "jsconfig.json"]) {
const configPath = import_path6.default.join(currentFolder, name);
if (import_fs5.default.existsSync(configPath)) {
const loaded = loadTsConfig(configPath);
result2 = loaded.map(validateTsConfig);
break;
}
}
if (result2)
break;
const parentFolder = import_path6.default.resolve(currentFolder, "../");
if (currentFolder === parentFolder)
break;
currentFolder = parentFolder;
}
result2 = result2 || [];
for (const folder2 of foldersWithConfig)
cachedTSConfigs.set(folder2, result2);
return result2;
}
var pathSeparator = process.platform === "win32" ? ";" : ":";
var builtins = new Set(import_module2.default.builtinModules);
function resolveHook(filename, specifier) {
if (specifier.startsWith("node:") || builtins.has(specifier))
return;
if (!shouldTransform(filename))
return;
if (isRelativeSpecifier(specifier))
return resolveImportSpecifierAfterMapping(import_path6.default.resolve(import_path6.default.dirname(filename), specifier), false);
const isTypeScript = filename.endsWith(".ts") || filename.endsWith(".tsx");
const tsconfigs = loadAndValidateTsconfigsForFile(filename);
for (const tsconfig of tsconfigs) {
if (!isTypeScript && !tsconfig.allowJs)
continue;
let longestPrefixLength = -1;
let pathMatchedByLongestPrefix;
for (const { key, values } of tsconfig.paths) {
let matchedPartOfSpecifier = specifier;
const [keyPrefix, keySuffix] = key.split("*");
if (key.includes("*")) {
if (keyPrefix) {
if (!specifier.startsWith(keyPrefix))
continue;
matchedPartOfSpecifier = matchedPartOfSpecifier.substring(keyPrefix.length, matchedPartOfSpecifier.length);
}
if (keySuffix) {
if (!specifier.endsWith(keySuffix))
continue;
matchedPartOfSpecifier = matchedPartOfSpecifier.substring(0, matchedPartOfSpecifier.length - keySuffix.length);
}
} else {
if (specifier !== key)
continue;
matchedPartOfSpecifier = specifier;
}
if (keyPrefix.length <= longestPrefixLength)
continue;
for (const value of values) {
let candidate = value;
if (value.includes("*"))
candidate = candidate.replace("*", matchedPartOfSpecifier);
candidate = import_path6.default.resolve(tsconfig.pathsBase, candidate);
const existing = resolveImportSpecifierAfterMapping(candidate, true);
if (existing) {
longestPrefixLength = keyPrefix.length;
pathMatchedByLongestPrefix = existing;
}
}
}
if (pathMatchedByLongestPrefix)
return pathMatchedByLongestPrefix;
}
if (import_path6.default.isAbsolute(specifier)) {
return resolveImportSpecifierAfterMapping(specifier, false);
}
}
function shouldTransform(filename) {
if (_externalMatcher(filename))
return false;
return !belongsToNodeModules(filename);
}
var transformData;
function setTransformData(pluginName, value) {
transformData.set(pluginName, value);
}
function transformHook(originalCode, filename, moduleUrl) {
const hasPreprocessor = process.env.PW_TEST_SOURCE_TRANSFORM && process.env.PW_TEST_SOURCE_TRANSFORM_SCOPE && process.env.PW_TEST_SOURCE_TRANSFORM_SCOPE.split(pathSeparator).some((f) => filename.startsWith(f));
const pluginsPrologue = _transformConfig.babelPlugins;
const pluginsEpilogue = hasPreprocessor ? [[process.env.PW_TEST_SOURCE_TRANSFORM]] : [];
const hash = calculateHash(originalCode, filename, !!moduleUrl, pluginsPrologue, pluginsEpilogue);
const { cachedCode, addToCache, serializedCache } = getFromCompilationCache(filename, hash, moduleUrl);
if (cachedCode !== void 0)
return { code: cachedCode, serializedCache };
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
const { babelTransform } = require((0, import_package3.libPath)("transform", "babelBundle"));
transformData = /* @__PURE__ */ new Map();
const setTransformDataForPlugin = (key, value) => transformData.set(key, value);
const wrappedPrologue = pluginsPrologue.map(([name, opts]) => [
name,
{ ...opts || {}, setTransformData: setTransformDataForPlugin }
]);
const babelResult = babelTransform(originalCode, filename, !!moduleUrl, wrappedPrologue, pluginsEpilogue, _transformConfig.jsxImportSource);
if (!babelResult?.code)
return { code: originalCode, serializedCache };
const { code, map } = babelResult;
const added = addToCache(code, map, transformData);
return { code, serializedCache: added.serializedCache };
}
function calculateHash(content, filePath, isModule2, pluginsPrologue, pluginsEpilogue) {
const hash = import_crypto.default.createHash("sha1").update(isModule2 ? "esm" : "no_esm").update(content).update(filePath).update(version).update(pluginsPrologue.map((p) => p[0]).join(",")).update(pluginsEpilogue.map((p) => p[0]).join(",")).digest("hex");
return hash;
}
async function requireOrImport(file) {
installTransformIfNeeded();
const isModule = fileIsModule(file);
if (isModule) {
const fileName = import_url2.default.pathToFileURL(file);
const esmImport = () => eval(`import(${JSON.stringify(fileName)})`);
await eval(`import(${JSON.stringify(fileName + ".esm.preflight")})`).catch((error) => debugTest("Failed to load preflight for " + file + ", source maps may be missing for errors thrown during loading.", error)).finally(nextTask);
return await esmImport().finally(nextTask);
}
const result = require(file);
const depsCollector = currentFileDepsCollector();
if (depsCollector) {
const module2 = require.cache[file];
if (module2)
collectCJSDependencies(module2, depsCollector);
}
return result;
}
var transformInstalled = false;
function installTransformIfNeeded() {
if (transformInstalled)
return;
transformInstalled = true;
installSourceMapSupport();
const originalResolveFilename = import_module2.default._resolveFilename;
function resolveFilename(specifier, parent, ...rest) {
if (parent) {
const resolved = resolveHook(parent.filename, specifier);
if (resolved !== void 0)
specifier = resolved;
}
return originalResolveFilename.call(this, specifier, parent, ...rest);
}
import_module2.default._resolveFilename = resolveFilename;
addHook((code, filename) => {
return transformHook(code, filename).code;
}, shouldTransform, [".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts", ".cjs", ".cts"]);
}
var collectCJSDependencies = (module2, dependencies) => {
module2.children.forEach((child) => {
if (!belongsToNodeModules(child.filename) && !dependencies.has(child.filename)) {
dependencies.add(child.filename);
collectCJSDependencies(child, dependencies);
}
});
};
function wrapFunctionWithLocation(func) {
return (...args) => {
const oldPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (error, stackFrames) => {
const frame = sourceMapSupport2.wrapCallSite(stackFrames[1]);
const fileName2 = frame.getFileName();
const file2 = fileName2 && fileName2.startsWith("file://") ? import_url2.default.fileURLToPath(fileName2) : fileName2;
return {
file: file2,
line: frame.getLineNumber(),
column: frame.getColumnNumber()
};
};
const oldStackTraceLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 2;
const obj = {};
Error.captureStackTrace(obj);
const location = obj.stack;
Error.stackTraceLimit = oldStackTraceLimit;
Error.prepareStackTrace = oldPrepareStackTrace;
return func(location, ...args);
};
}
function isRelativeSpecifier(specifier) {
return specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../");
}
async function nextTask() {
return new Promise((resolve) => setTimeout(resolve, 0));
}
// packages/playwright/src/common/esmLoaderHost.ts
var esmLoaderHost_exports = {};
__export(esmLoaderHost_exports, {
configureESMLoader: () => configureESMLoader,
configureESMLoaderTransformConfig: () => configureESMLoaderTransformConfig,
incorporateCompilationCache: () => incorporateCompilationCache,
registerESMLoader: () => registerESMLoader,
startCollectingFileDeps: () => startCollectingFileDeps2,
stopCollectingFileDeps: () => stopCollectingFileDeps2
});
var import_url3 = __toESM(require("url"));
// packages/playwright/src/transform/portTransport.ts
var PortTransport = class {
constructor(port, handler) {
this._lastId = 0;
this._callbacks = /* @__PURE__ */ new Map();
this._port = port;
port.addEventListener("message", async (event) => {
const message = event.data;
const { id, ackId, method, params, result: result2 } = message;
if (ackId) {
const callback = this._callbacks.get(ackId);
this._callbacks.delete(ackId);
this._resetRef();
callback?.(result2);
return;
}
const handlerResult = await handler(method, params);
if (id)
this._port.postMessage({ ackId: id, result: handlerResult });
});
this._resetRef();
}
post(method, params) {
this._port.postMessage({ method, params });
}
async send(method, params) {
return await new Promise((f) => {
const id = ++this._lastId;
this._callbacks.set(id, f);
this._resetRef();
this._port.postMessage({ id, method, params });
});
}
_resetRef() {
if (this._callbacks.size) {
this._port.ref();
} else {
this._port.unref();
}
}
};
// packages/playwright/src/common/esmLoaderHost.ts
var loaderChannel;
function registerESMLoader() {
if (process.env.PW_DISABLE_TS_ESM)
return true;
if ("Bun" in globalThis)
return true;
if (loaderChannel)
return true;
const register = require("node:module").register;
if (!register)
return false;
const { port1, port2 } = new MessageChannel();
register(import_url3.default.pathToFileURL(require.resolve("../transform/esmLoader.js")), {
data: { port: port2 },
transferList: [port2]
});
loaderChannel = createPortTransport(port1);
return true;
}
function createPortTransport(port) {
return new PortTransport(port, async (method, params) => {
if (method === "pushToCompilationCache")
addToCompilationCache(params.cache);
});
}
async function startCollectingFileDeps2() {
if (!loaderChannel)
return;
await loaderChannel.send("startCollectingFileDeps", {});
}
async function stopCollectingFileDeps2(file2) {
if (!loaderChannel)
return;
await loaderChannel.send("stopCollectingFileDeps", { file: file2 });
}
async function incorporateCompilationCache() {
if (!loaderChannel)
return;
const result2 = await loaderChannel.send("getCompilationCache