2899 lines
111 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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", {});
addToCompilationCache(result2.cache);
}
async function configureESMLoader() {
if (!loaderChannel)
return;
await loaderChannel.send("setSingleTSConfig", { tsconfig: singleTSConfig() });
await loaderChannel.send("addToCompilationCache", { cache: serializeCompilationCache() });
}
async function configureESMLoaderTransformConfig() {
if (!loaderChannel)
return;
await loaderChannel.send("setSingleTSConfig", { tsconfig: singleTSConfig() });
await loaderChannel.send("setTransformConfig", { config: transformConfig() });
}
// packages/playwright/src/common/configLoader.ts
var { isRegExp: isRegExp2 } = require("playwright-core/lib/coreBundle").iso;
var kDefineConfigWasUsed = Symbol("defineConfigWasUsed");
var defineConfig = (...configs) => {
let result2 = configs[0];
for (let i = 1; i < configs.length; ++i) {
const config = configs[i];
const prevProjects = result2.projects;
result2 = {
...result2,
...config,
expect: {
...result2.expect,
...config.expect
},
use: {
...result2.use,
...config.use
},
build: {
...result2.build,
...config.build
},
webServer: [
...Array.isArray(result2.webServer) ? result2.webServer : result2.webServer ? [result2.webServer] : [],
...Array.isArray(config.webServer) ? config.webServer : config.webServer ? [config.webServer] : []
]
};
if (!result2.projects && !config.projects)
continue;
const projectOverrides = /* @__PURE__ */ new Map();
for (const project of config.projects || [])
projectOverrides.set(project.name, project);
const projects = [];
for (const project of prevProjects || []) {
const projectOverride = projectOverrides.get(project.name);
if (projectOverride) {
projects.push({
...project,
...projectOverride,
use: {
...project.use,
...projectOverride.use
}
});
projectOverrides.delete(project.name);
} else {
projects.push(project);
}
}
projects.push(...projectOverrides.values());
result2.projects = projects;
}
result2[kDefineConfigWasUsed] = true;
return result2;
};
async function deserializeConfig(data) {
if (data.compilationCache)
addToCompilationCache(data.compilationCache);
return await loadConfig(data.location, data.configCLIOverrides, void 0, data.metadata ? JSON.parse(data.metadata) : void 0);
}
async function loadUserConfig(location) {
let object = location.resolvedConfigFile ? await requireOrImport(location.resolvedConfigFile) : {};
if (object && typeof object === "object" && "default" in object)
object = object["default"];
return object;
}
async function loadConfig(location, overrides, ignoreProjectDependencies = false, metadata) {
if (!registerESMLoader()) {
if (location.resolvedConfigFile && fileIsModule(location.resolvedConfigFile))
throw errorWithFile(location.resolvedConfigFile, `Playwright requires Node.js 18.19 or higher to load esm modules. Please update your version of Node.js.`);
}
setSingleTSConfig(overrides?.tsconfig);
await configureESMLoader();
const userConfig = await loadUserConfig(location);
validateConfig(location.resolvedConfigFile || "<default config>", userConfig);
const fullConfig = new FullConfigInternal(location, userConfig, overrides || {}, metadata);
fullConfig.defineConfigWasUsed = !!userConfig[kDefineConfigWasUsed];
if (ignoreProjectDependencies) {
for (const project of fullConfig.projects) {
project.deps = [];
project.teardown = void 0;
}
}
const babelPlugins = userConfig["@playwright/test"]?.babelPlugins || [];
const external = userConfig.build?.external || [];
const jsxImportSource = import_path7.default.dirname(require.resolve("playwright"));
setTransformConfig({ babelPlugins, external, jsxImportSource });
if (!overrides?.tsconfig)
setSingleTSConfig(fullConfig?.singleTSConfigPath);
await configureESMLoaderTransformConfig();
return fullConfig;
}
function validateConfig(file2, config) {
if (typeof config !== "object" || !config)
throw errorWithFile(file2, `Configuration file must export a single object`);
validateProject(file2, config, "config");
if ("forbidOnly" in config && config.forbidOnly !== void 0) {
if (typeof config.forbidOnly !== "boolean")
throw errorWithFile(file2, `config.forbidOnly must be a boolean`);
}
if ("globalSetup" in config && config.globalSetup !== void 0) {
if (Array.isArray(config.globalSetup)) {
config.globalSetup.forEach((item, index) => {
if (typeof item !== "string")
throw errorWithFile(file2, `config.globalSetup[${index}] must be a string`);
});
} else if (typeof config.globalSetup !== "string") {
throw errorWithFile(file2, `config.globalSetup must be a string`);
}
}
if ("globalTeardown" in config && config.globalTeardown !== void 0) {
if (Array.isArray(config.globalTeardown)) {
config.globalTeardown.forEach((item, index) => {
if (typeof item !== "string")
throw errorWithFile(file2, `config.globalTeardown[${index}] must be a string`);
});
} else if (typeof config.globalTeardown !== "string") {
throw errorWithFile(file2, `config.globalTeardown must be a string`);
}
}
if ("globalTimeout" in config && config.globalTimeout !== void 0) {
if (typeof config.globalTimeout !== "number" || config.globalTimeout < 0)
throw errorWithFile(file2, `config.globalTimeout must be a non-negative number`);
}
if ("grep" in config && config.grep !== void 0) {
if (Array.isArray(config.grep)) {
config.grep.forEach((item, index) => {
if (!isRegExp2(item))
throw errorWithFile(file2, `config.grep[${index}] must be a RegExp`);
});
} else if (!isRegExp2(config.grep)) {
throw errorWithFile(file2, `config.grep must be a RegExp`);
}
}
if ("grepInvert" in config && config.grepInvert !== void 0) {
if (Array.isArray(config.grepInvert)) {
config.grepInvert.forEach((item, index) => {
if (!isRegExp2(item))
throw errorWithFile(file2, `config.grepInvert[${index}] must be a RegExp`);
});
} else if (!isRegExp2(config.grepInvert)) {
throw errorWithFile(file2, `config.grepInvert must be a RegExp`);
}
}
if ("maxFailures" in config && config.maxFailures !== void 0) {
if (typeof config.maxFailures !== "number" || config.maxFailures < 0)
throw errorWithFile(file2, `config.maxFailures must be a non-negative number`);
}
if ("preserveOutput" in config && config.preserveOutput !== void 0) {
if (typeof config.preserveOutput !== "string" || !["always", "never", "failures-only"].includes(config.preserveOutput))
throw errorWithFile(file2, `config.preserveOutput must be one of "always", "never" or "failures-only"`);
}
if ("projects" in config && config.projects !== void 0) {
if (!Array.isArray(config.projects))
throw errorWithFile(file2, `config.projects must be an array`);
config.projects.forEach((project, index) => {
validateProject(file2, project, `config.projects[${index}]`);
});
}
if ("quiet" in config && config.quiet !== void 0) {
if (typeof config.quiet !== "boolean")
throw errorWithFile(file2, `config.quiet must be a boolean`);
}
if ("reporter" in config && config.reporter !== void 0) {
if (Array.isArray(config.reporter)) {
config.reporter.forEach((item, index) => {
if (!Array.isArray(item) || item.length <= 0 || item.length > 2 || typeof item[0] !== "string")
throw errorWithFile(file2, `config.reporter[${index}] must be a tuple [name, optionalArgument]`);
});
} else if (typeof config.reporter !== "string") {
throw errorWithFile(file2, `config.reporter must be a string`);
}
}
if ("reportSlowTests" in config && config.reportSlowTests !== void 0 && config.reportSlowTests !== null) {
if (!config.reportSlowTests || typeof config.reportSlowTests !== "object")
throw errorWithFile(file2, `config.reportSlowTests must be an object`);
if (!("max" in config.reportSlowTests) || typeof config.reportSlowTests.max !== "number" || config.reportSlowTests.max < 0)
throw errorWithFile(file2, `config.reportSlowTests.max must be a non-negative number`);
if (!("threshold" in config.reportSlowTests) || typeof config.reportSlowTests.threshold !== "number" || config.reportSlowTests.threshold < 0)
throw errorWithFile(file2, `config.reportSlowTests.threshold must be a non-negative number`);
}
if ("shard" in config && config.shard !== void 0 && config.shard !== null) {
if (!config.shard || typeof config.shard !== "object")
throw errorWithFile(file2, `config.shard must be an object`);
if (!("total" in config.shard) || typeof config.shard.total !== "number" || config.shard.total < 1)
throw errorWithFile(file2, `config.shard.total must be a positive number`);
if (!("current" in config.shard) || typeof config.shard.current !== "number" || config.shard.current < 1 || config.shard.current > config.shard.total)
throw errorWithFile(file2, `config.shard.current must be a positive number, not greater than config.shard.total`);
}
if ("updateSnapshots" in config && config.updateSnapshots !== void 0) {
if (typeof config.updateSnapshots !== "string" || !["all", "changed", "missing", "none"].includes(config.updateSnapshots))
throw errorWithFile(file2, `config.updateSnapshots must be one of "all", "changed", "missing" or "none"`);
}
if ("tsconfig" in config && config.tsconfig !== void 0) {
if (typeof config.tsconfig !== "string")
throw errorWithFile(file2, `config.tsconfig must be a string`);
if (!import_fs6.default.existsSync(import_path7.default.resolve(file2, "..", config.tsconfig)))
throw errorWithFile(file2, `config.tsconfig does not exist`);
}
}
function validateProject(file2, project, title) {
if (typeof project !== "object" || !project)
throw errorWithFile(file2, `${title} must be an object`);
if ("name" in project && project.name !== void 0) {
if (typeof project.name !== "string")
throw errorWithFile(file2, `${title}.name must be a string`);
}
if ("outputDir" in project && project.outputDir !== void 0) {
if (typeof project.outputDir !== "string")
throw errorWithFile(file2, `${title}.outputDir must be a string`);
}
if ("repeatEach" in project && project.repeatEach !== void 0) {
if (typeof project.repeatEach !== "number" || project.repeatEach < 0)
throw errorWithFile(file2, `${title}.repeatEach must be a non-negative number`);
}
if ("retries" in project && project.retries !== void 0) {
if (typeof project.retries !== "number" || project.retries < 0)
throw errorWithFile(file2, `${title}.retries must be a non-negative number`);
}
if ("testDir" in project && project.testDir !== void 0) {
if (typeof project.testDir !== "string")
throw errorWithFile(file2, `${title}.testDir must be a string`);
}
for (const prop of ["testIgnore", "testMatch"]) {
if (prop in project && project[prop] !== void 0) {
const value = project[prop];
if (Array.isArray(value)) {
value.forEach((item, index) => {
if (typeof item !== "string" && !isRegExp2(item))
throw errorWithFile(file2, `${title}.${prop}[${index}] must be a string or a RegExp`);
});
} else if (typeof value !== "string" && !isRegExp2(value)) {
throw errorWithFile(file2, `${title}.${prop} must be a string or a RegExp`);
}
}
}
if ("timeout" in project && project.timeout !== void 0) {
if (typeof project.timeout !== "number" || project.timeout < 0)
throw errorWithFile(file2, `${title}.timeout must be a non-negative number`);
}
if ("use" in project && project.use !== void 0) {
if (!project.use || typeof project.use !== "object")
throw errorWithFile(file2, `${title}.use must be an object`);
}
if ("ignoreSnapshots" in project && project.ignoreSnapshots !== void 0) {
if (typeof project.ignoreSnapshots !== "boolean")
throw errorWithFile(file2, `${title}.ignoreSnapshots must be a boolean`);
}
if ("workers" in project && project.workers !== void 0) {
if (typeof project.workers === "number" && project.workers <= 0)
throw errorWithFile(file2, `${title}.workers must be a positive number`);
else if (typeof project.workers === "string" && !project.workers.endsWith("%"))
throw errorWithFile(file2, `${title}.workers must be a number or percentage`);
}
}
function resolveConfigLocation(configFile) {
const configFileOrDirectory = configFile ? import_path7.default.resolve(process.cwd(), configFile) : process.cwd();
const resolvedConfigFile = resolveConfigFile2(configFileOrDirectory);
return {
resolvedConfigFile,
configDir: resolvedConfigFile ? import_path7.default.dirname(resolvedConfigFile) : configFileOrDirectory
};
}
function resolveConfigFile2(configFileOrDirectory) {
const resolveConfig = (configFile) => {
if (import_fs6.default.existsSync(configFile))
return configFile;
};
const resolveConfigFileFromDirectory = (directory) => {
for (const ext of [".ts", ".js", ".mts", ".mjs", ".cts", ".cjs"]) {
const configFile = resolveConfig(import_path7.default.resolve(directory, "playwright.config" + ext));
if (configFile)
return configFile;
}
};
if (!import_fs6.default.existsSync(configFileOrDirectory))
throw new Error(`${configFileOrDirectory} does not exist`);
if (import_fs6.default.statSync(configFileOrDirectory).isDirectory()) {
const configFile = resolveConfigFileFromDirectory(configFileOrDirectory);
if (configFile)
return configFile;
return void 0;
}
return configFileOrDirectory;
}
async function loadConfigFromFile(configFile, overrides, ignoreDeps) {
return await loadConfig(resolveConfigLocation(configFile), overrides, ignoreDeps);
}
async function loadEmptyConfigForMergeReports() {
return await loadConfig({ configDir: process.cwd() });
}
// packages/playwright/src/common/fixtures.ts
var fixtures_exports = {};
__export(fixtures_exports, {
FixturePool: () => FixturePool,
fixtureParameterNames: () => fixtureParameterNames,
formatPotentiallyInternalLocation: () => formatPotentiallyInternalLocation,
inheritFixtureNames: () => inheritFixtureNames
});
var import_crypto2 = __toESM(require("crypto"));
var kScopeOrder = ["test", "worker"];
function isFixtureTuple(value) {
return Array.isArray(value) && typeof value[1] === "object";
}
function isFixtureOption(value) {
return isFixtureTuple(value) && !!value[1].option;
}
var FixturePool = class {
constructor(fixturesList, onLoadError, parentPool, disallowWorkerFixtures, optionOverrides) {
this._registrations = new Map(parentPool ? parentPool._registrations : []);
this._onLoadError = onLoadError;
const allOverrides = optionOverrides?.overrides ?? {};
const overrideKeys = new Set(Object.keys(allOverrides));
for (const list of fixturesList) {
this._appendFixtureList(list, !!disallowWorkerFixtures, false);
const selectedOverrides = {};
for (const [key, value] of Object.entries(list.fixtures)) {
if (isFixtureOption(value) && overrideKeys.has(key))
selectedOverrides[key] = [allOverrides[key], value[1]];
}
if (Object.entries(selectedOverrides).length)
this._appendFixtureList({ fixtures: selectedOverrides, location: optionOverrides.location }, !!disallowWorkerFixtures, true);
}
if (optionOverrides) {
for (const key of overrideKeys) {
const registration = this._registrations.get(key);
if (registration && !registration.option)
this._addLoadError(`Fixture "${key}" cannot be overridden in the configuration "use" section. Only fixtures registered with { option: true } can be set in the config.`, optionOverrides.location);
}
}
this.digest = this.validate();
}
_appendFixtureList(list, disallowWorkerFixtures, isOptionsOverride) {
const { fixtures, location } = list;
for (const entry of Object.entries(fixtures)) {
const name = entry[0];
let value = entry[1];
let options;
if (isFixtureTuple(value)) {
options = {
auto: value[1].auto ?? false,
scope: value[1].scope || "test",
option: !!value[1].option,
timeout: value[1].timeout,
customTitle: value[1].title,
box: value[1].box
};
value = value[0];
}
let fn = value;
const previous = this._registrations.get(name);
if (previous && options) {
if (previous.scope !== options.scope) {
this._addLoadError(`Fixture "${name}" has already been registered as a { scope: '${previous.scope}' } fixture defined in ${formatLocation(previous.location)}.`, location);
continue;
}
if (previous.auto !== options.auto) {
this._addLoadError(`Fixture "${name}" has already been registered as a { auto: '${previous.scope}' } fixture defined in ${formatLocation(previous.location)}.`, location);
continue;
}
} else if (previous) {
options = { auto: previous.auto, scope: previous.scope, option: previous.option, timeout: previous.timeout, customTitle: previous.customTitle };
} else if (!options) {
options = { auto: false, scope: "test", option: false, timeout: void 0 };
}
if (!kScopeOrder.includes(options.scope)) {
this._addLoadError(`Fixture "${name}" has unknown { scope: '${options.scope}' }.`, location);
continue;
}
if (options.scope === "worker" && disallowWorkerFixtures) {
this._addLoadError(`Cannot use({ ${name} }) in a describe group, because it forces a new worker.
Make it top-level in the test file or put in the configuration file.`, location);
continue;
}
if (fn === void 0 && options.option && previous) {
let original = previous;
while (!original.optionOverride && original.super)
original = original.super;
fn = original.fn;
}
const deps = fixtureParameterNames(fn, location, (e) => this._onLoadError(e));
const registration = { id: "", name, location, scope: options.scope, fn, auto: options.auto, option: options.option, timeout: options.timeout, customTitle: options.customTitle, box: options.box, deps, super: previous, optionOverride: isOptionsOverride };
registrationId(registration);
this._registrations.set(name, registration);
}
}
validate() {
const markers = /* @__PURE__ */ new Map();
const stack = [];
let hasDependencyErrors = false;
const addDependencyError = (message, location) => {
hasDependencyErrors = true;
this._addLoadError(message, location);
};
const visit = (registration, boxedOnly) => {
markers.set(registration, "visiting");
stack.push(registration);
for (const name of registration.deps) {
const dep = this.resolve(name, registration);
if (!dep) {
if (name === registration.name)
addDependencyError(`Fixture "${registration.name}" references itself, but does not have a base implementation.`, registration.location);
else
addDependencyError(`Fixture "${registration.name}" has unknown parameter "${name}".`, registration.location);
continue;
}
if (kScopeOrder.indexOf(registration.scope) > kScopeOrder.indexOf(dep.scope)) {
addDependencyError(`${registration.scope} fixture "${registration.name}" cannot depend on a ${dep.scope} fixture "${name}" defined in ${formatPotentiallyInternalLocation(dep.location)}.`, registration.location);
continue;
}
if (!markers.has(dep)) {
visit(dep, boxedOnly);
} else if (markers.get(dep) === "visiting") {
const index = stack.indexOf(dep);
const allRegs = stack.slice(index, stack.length);
const filteredRegs = allRegs.filter((r) => !r.box);
const regs = boxedOnly ? filteredRegs : allRegs;
const names2 = regs.map((r) => `"${r.name}"`);
addDependencyError(`Fixtures ${names2.join(" -> ")} -> "${dep.name}" form a dependency cycle: ${regs.map((r) => formatPotentiallyInternalLocation(r.location)).join(" -> ")} -> ${formatPotentiallyInternalLocation(dep.location)}`, dep.location);
continue;
}
}
markers.set(registration, "visited");
stack.pop();
};
const names = Array.from(this._registrations.keys()).sort();
for (const name of names) {
const registration = this._registrations.get(name);
if (!registration.box)
visit(registration, true);
}
if (!hasDependencyErrors) {
for (const name of names) {
const registration = this._registrations.get(name);
if (registration.box)
visit(registration, false);
}
}
const hash = import_crypto2.default.createHash("sha1");
for (const name of names) {
const registration = this._registrations.get(name);
if (registration.scope === "worker")
hash.update(registration.id + ";");
}
return hash.digest("hex");
}
validateFunction(fn, prefix, location) {
for (const name of fixtureParameterNames(fn, location, (e) => this._onLoadError(e))) {
const registration = this._registrations.get(name);
if (!registration)
this._addLoadError(`${prefix} has unknown parameter "${name}".`, location);
}
}
resolve(name, forFixture) {
if (name === forFixture?.name)
return forFixture.super;
return this._registrations.get(name);
}
autoFixtures() {
return [...this._registrations.values()].filter((r) => r.auto !== false);
}
_addLoadError(message, location) {
this._onLoadError({ message, location });
}
};
var signatureSymbol = Symbol("signature");
function formatPotentiallyInternalLocation(location) {
const isUserFixture = location && filterStackFile(location.file);
return isUserFixture ? formatLocation(location) : "<builtin>";
}
function fixtureParameterNames(fn, location, onError) {
if (typeof fn !== "function")
return [];
if (!fn[signatureSymbol])
fn[signatureSymbol] = innerFixtureParameterNames(fn, location, onError);
return fn[signatureSymbol];
}
function inheritFixtureNames(from, to) {
to[signatureSymbol] = from[signatureSymbol];
}
function innerFixtureParameterNames(fn, location, onError) {
const text = filterOutComments(fn.toString());
const match = text.match(/(?:async)?(?:\s+function)?[^(]*\(([^)]*)/);
if (!match)
return [];
const trimmedParams = match[1].trim();
if (!trimmedParams)
return [];
const [firstParam] = splitByComma(trimmedParams);
if (firstParam[0] !== "{" || firstParam[firstParam.length - 1] !== "}") {
onError({ message: "First argument must use the object destructuring pattern: " + firstParam, location });
return [];
}
const props = splitByComma(firstParam.substring(1, firstParam.length - 1)).map((prop) => {
const colon = prop.indexOf(":");
return colon === -1 ? prop.trim() : prop.substring(0, colon).trim();
});
const restProperty = props.find((prop) => prop.startsWith("..."));
if (restProperty) {
onError({ message: `Rest property "${restProperty}" is not supported. List all used fixtures explicitly, separated by comma.`, location });
return [];
}
return props;
}
function filterOutComments(s) {
const result2 = [];
let commentState = "none";
for (let i = 0; i < s.length; ++i) {
if (commentState === "singleline") {
if (s[i] === "\n")
commentState = "none";
} else if (commentState === "multiline") {
if (s[i - 1] === "*" && s[i] === "/")
commentState = "none";
} else if (commentState === "none") {
if (s[i] === "/" && s[i + 1] === "/") {
commentState = "singleline";
} else if (s[i] === "/" && s[i + 1] === "*") {
commentState = "multiline";
i += 2;
} else {
result2.push(s[i]);
}
}
}
return result2.join("");
}
function splitByComma(s) {
const result2 = [];
const stack = [];
let start = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] === "{" || s[i] === "[") {
stack.push(s[i] === "{" ? "}" : "]");
} else if (s[i] === stack[stack.length - 1]) {
stack.pop();
} else if (!stack.length && s[i] === ",") {
const token = s.substring(start, i).trim();
if (token)
result2.push(token);
start = i + 1;
}
}
const lastToken = s.substring(start).trim();
if (lastToken)
result2.push(lastToken);
return result2;
}
var registrationIdMap = /* @__PURE__ */ new Map();
var lastId = 0;
function registrationId(registration) {
if (registration.id)
return registration.id;
const key = registration.name + "@@@" + (registration.super ? registrationId(registration.super) : "");
let map = registrationIdMap.get(key);
if (!map) {
map = /* @__PURE__ */ new Map();
registrationIdMap.set(key, map);
}
if (!map.has(registration.fn))
map.set(registration.fn, String(lastId++));
registration.id = map.get(registration.fn);
return registration.id;
}
// packages/playwright/src/common/ipc.ts
var ipc_exports = {};
__export(ipc_exports, {
serializeConfig: () => serializeConfig,
stdioChunkToParams: () => stdioChunkToParams,
toTestInfoErrorPayload: () => toTestInfoErrorPayload
});
var import_util6 = __toESM(require("util"));
function serializeConfig(config, passCompilationCache) {
const result2 = {
location: { configDir: config.configDir, resolvedConfigFile: config.config.configFile },
configCLIOverrides: config.configCLIOverrides,
compilationCache: passCompilationCache ? serializeCompilationCache() : void 0
};
try {
result2.metadata = JSON.stringify(config.config.metadata);
} catch (error) {
}
return result2;
}
function stdioChunkToParams(chunk) {
if (chunk instanceof Uint8Array)
return { buffer: Buffer.from(chunk).toString("base64") };
if (typeof chunk !== "string")
return { text: import_util6.default.inspect(chunk) };
return { text: chunk };
}
function toTestInfoErrorPayload(error) {
const result2 = {};
if (error.message !== void 0)
result2.message = error.message;
if (error.stack !== void 0)
result2.stack = error.stack;
if (error.value !== void 0)
result2.value = error.value;
if (error.cause !== void 0)
result2.cause = toTestInfoErrorPayload(error.cause);
return result2;
}
// packages/playwright/src/common/poolBuilder.ts
var poolBuilder_exports = {};
__export(poolBuilder_exports, {
PoolBuilder: () => PoolBuilder
});
var PoolBuilder = class _PoolBuilder {
constructor(type, project) {
this._testTypePools = /* @__PURE__ */ new Map();
this._type = type;
this._project = project;
}
static createForLoader() {
return new _PoolBuilder("loader");
}
static createForWorker(project) {
return new _PoolBuilder("worker", project);
}
buildPools(suite, testErrors) {
suite.forEachTest((test) => {
const pool = this._buildPoolForTest(test, testErrors);
if (this._type === "loader")
test._poolDigest = pool.digest;
if (this._type === "worker")
test._pool = pool;
});
}
_buildPoolForTest(test, testErrors) {
let pool = this._buildTestTypePool(test._testType, testErrors);
const parents = [];
for (let parent = test.parent; parent; parent = parent.parent)
parents.push(parent);
parents.reverse();
for (const parent of parents) {
if (parent._use.length)
pool = new FixturePool(parent._use, (e) => this._handleLoadError(e, testErrors), pool, parent._type === "describe");
for (const hook of parent._hooks)
pool.validateFunction(hook.fn, hook.type + " hook", hook.location);
for (const modifier of parent._modifiers)
pool.validateFunction(modifier.fn, modifier.type + " modifier", modifier.location);
}
pool.validateFunction(test.fn, "Test", test.location);
return pool;
}
_buildTestTypePool(testType, testErrors) {
if (!this._testTypePools.has(testType)) {
const optionOverrides = {
overrides: this._project?.project?.use ?? {},
location: { file: `project#${this._project?.id}`, line: 1, column: 1 }
};
const pool = new FixturePool(testType.fixtures, (e) => this._handleLoadError(e, testErrors), void 0, void 0, optionOverrides);
this._testTypePools.set(testType, pool);
}
return this._testTypePools.get(testType);
}
_handleLoadError(e, testErrors) {
if (testErrors)
testErrors.push(e);
else
throw new Error(`${formatLocation(e.location)}: ${e.message}`);
}
};
// packages/playwright/src/common/process.ts
var process_exports = {};
__export(process_exports, {
ProcessRunner: () => ProcessRunner,
startProcessRunner: () => startProcessRunner
});
var import_bootstrap = require("playwright-core/lib/bootstrap");
var { ManualPromise } = require("playwright-core/lib/coreBundle").iso;
var { setTimeOrigin } = require("playwright-core/lib/coreBundle").iso;
var { startProfiling, stopProfiling } = require("playwright-core/lib/coreBundle").utils;
var ProcessRunner = class {
async gracefullyClose() {
}
dispatchEvent(method, params) {
const response = { method, params };
sendMessageToParent({ method: "__dispatch__", params: response });
}
async sendRequest(method, params) {
return await sendRequestToParent(method, params);
}
async sendMessageNoReply(method, params) {
void sendRequestToParent(method, params).catch(() => {
});
}
};
var gracefullyCloseCalled = false;
var forceExitInitiated = false;
var processRunner;
var processName;
var startingEnv = { ...process.env };
function startProcessRunner(create) {
sendMessageToParent({ method: "ready" });
process.on("disconnect", () => gracefullyCloseAndExit(true));
process.on("SIGINT", () => {
});
process.on("SIGTERM", () => {
});
process.on("message", async (message) => {
if (message.method === "__init__") {
const { processParams, runnerParams } = message.params;
void startProfiling();
setTimeOrigin(processParams.timeOrigin);
processRunner = create(runnerParams);
processName = processParams.processName;
return;
}
if (message.method === "__stop__") {
const keys = /* @__PURE__ */ new Set([...Object.keys(process.env), ...Object.keys(startingEnv)]);
const producedEnv = [...keys].filter((key) => startingEnv[key] !== process.env[key]).map((key) => [key, process.env[key] ?? null]);
sendMessageToParent({ method: "__env_produced__", params: producedEnv });
await gracefullyCloseAndExit(false);
return;
}
if (message.method === "__dispatch__") {
const { id, method, params } = message.params;
try {
const result2 = await processRunner[method](params);
const response = { id, result: result2 };
sendMessageToParent({ method: "__dispatch__", params: response });
} catch (e) {
const response = { id, error: serializeError(e) };
sendMessageToParent({ method: "__dispatch__", params: response });
}
}
if (message.method === "__response__")
handleResponseFromParent(message.params);
});
}
var kForceExitTimeout = +(process.env.PWTEST_FORCE_EXIT_TIMEOUT || 3e4);
async function gracefullyCloseAndExit(forceExit) {
if (forceExit && !forceExitInitiated) {
forceExitInitiated = true;
setTimeout(() => process.exit(0), kForceExitTimeout);
}
if (!gracefullyCloseCalled) {
gracefullyCloseCalled = true;
await processRunner?.gracefullyClose().catch(() => {
});
if (processName)
await stopProfiling(processName).catch(() => {
});
process.exit(0);
}
}
function sendMessageToParent(message) {
try {
process.send(message);
} catch (e) {
try {
JSON.stringify(message);
} catch {
throw e;
}
}
}
var lastId2 = 0;
var requestCallbacks = /* @__PURE__ */ new Map();
async function sendRequestToParent(method, params) {
const id = ++lastId2;
sendMessageToParent({ method: "__request__", params: { id, method, params } });
const promise = new ManualPromise();
requestCallbacks.set(id, promise);
return promise;
}
function handleResponseFromParent(response) {
const promise = requestCallbacks.get(response.id);
if (!promise)
return;
requestCallbacks.delete(response.id);
if (response.error)
promise.reject(new Error(response.error.message));
else
promise.resolve(response.result);
}
// packages/playwright/src/common/suiteUtils.ts
var suiteUtils_exports = {};
__export(suiteUtils_exports, {
applyRepeatEachIndex: () => applyRepeatEachIndex,
bindFileSuiteToProject: () => bindFileSuiteToProject,
createFiltersFromArguments: () => createFiltersFromArguments,
filterOnly: () => filterOnly,
filterTestsRemoveEmptySuites: () => filterTestsRemoveEmptySuites
});
var import_path8 = __toESM(require("path"));
var { calculateSha1: calculateSha13 } = require("playwright-core/lib/coreBundle").utils;
var { toPosixPath } = require("playwright-core/lib/coreBundle").utils;
function filterTestsRemoveEmptySuites(suite, filter) {
const filteredSuites = suite.suites.filter((child) => filterTestsRemoveEmptySuites(child, filter));
const filteredTests = suite.tests.filter(filter);
const entries = /* @__PURE__ */ new Set([...filteredSuites, ...filteredTests]);
suite._entries = suite._entries.filter((e) => entries.has(e));
return !!suite._entries.length;
}
function bindFileSuiteToProject(project, suite) {
const relativeFile = import_path8.default.relative(project.project.testDir, suite.location.file);
const fileId = calculateSha13(toPosixPath(relativeFile)).slice(0, 20);
const result2 = suite._deepClone();
result2._fileId = fileId;
result2.forEachTest((test, suite2) => {
suite2._fileId = fileId;
const [file2, ...titles] = test.titlePath();
const testIdExpression = `[project=${project.id}]${toPosixPath(file2)}${titles.join("")}`;
const testId = fileId + "-" + calculateSha13(testIdExpression).slice(0, 20);
test.id = testId;
test._projectId = project.id;
let inheritedRetries;
let inheritedTimeout;
for (let parentSuite = suite2; parentSuite; parentSuite = parentSuite.parent) {
if (parentSuite._staticAnnotations.length)
test.annotations.unshift(...parentSuite._staticAnnotations);
if (inheritedRetries === void 0 && parentSuite._retries !== void 0)
inheritedRetries = parentSuite._retries;
if (inheritedTimeout === void 0 && parentSuite._timeout !== void 0)
inheritedTimeout = parentSuite._timeout;
}
test.retries = inheritedRetries ?? project.project.retries;
test.timeout = inheritedTimeout ?? project.project.timeout;
if (test.annotations.some((a) => a.type === "skip" || a.type === "fixme"))
test.expectedStatus = "skipped";
if (test._poolDigest)
test._workerHash = `${project.id}-${test._poolDigest}-0`;
});
return result2;
}
function applyRepeatEachIndex(project, fileSuite, repeatEachIndex) {
fileSuite.forEachTest((test, suite) => {
if (repeatEachIndex) {
const [file2, ...titles] = test.titlePath();
const testIdExpression = `[project=${project.id}]${toPosixPath(file2)}${titles.join("")} (repeat:${repeatEachIndex})`;
const testId = suite._fileId + "-" + calculateSha13(testIdExpression).slice(0, 20);
test.id = testId;
test.repeatEachIndex = repeatEachIndex;
if (test._poolDigest)
test._workerHash = `${project.id}-${test._poolDigest}-${repeatEachIndex}`;
}
});
}
function filterOnly(suite) {
if (!suite._getOnlyItems().length)
return;
const suiteFilter = (suite2) => suite2._only;
const testFilter = (test) => test._only;
return filterSuiteWithOnlySemantics(suite, suiteFilter, testFilter);
}
function filterSuiteWithOnlySemantics(suite, suiteFilter, testFilter) {
const onlySuites = suite.suites.filter((child) => filterSuiteWithOnlySemantics(child, suiteFilter, testFilter) || suiteFilter(child));
const onlyTests = suite.tests.filter(testFilter);
const onlyEntries = /* @__PURE__ */ new Set([...onlySuites, ...onlyTests]);
if (onlyEntries.size) {
suite._entries = suite._entries.filter((e) => onlyEntries.has(e));
return true;
}
return false;
}
function createFiltersFromArguments(args) {
const matchers = args.map((arg) => {
const parsed = parseLocationArg(arg);
const fileMatcher = createFileMatcher(forceRegExp(parsed.file));
const locationMatcher2 = (file2, line, column) => fileMatcher(file2) && (parsed.line === line || parsed.line === null) && (parsed.column === column || parsed.column === null);
return { fileMatcher, locationMatcher: locationMatcher2 };
});
const fileFilter = (file2) => matchers.some((m) => m.fileMatcher(file2));
const locationMatcher = (file2, line, column) => matchers.some((m) => m.locationMatcher(file2, line, column));
const testFilter = (test) => {
for (let suite = test.parent; suite; suite = suite.parent) {
if (suite.location && locationMatcher(suite.location.file, suite.location.line, suite.location.column))
return true;
}
return locationMatcher(test.location.file, test.location.line, test.location.column);
};
return { fileFilter, testFilter };
}
// packages/playwright/src/common/test.ts
var test_exports = {};
__export(test_exports, {
Suite: () => Suite,
TestCase: () => TestCase
});
// packages/playwright/src/common/testType.ts
var testType_exports = {};
__export(testType_exports, {
TestTypeImpl: () => TestTypeImpl,
mergeTests: () => mergeTests,
rootTestType: () => rootTestType
});
var import_globals2 = require("../globals");
var import_expect = require("../matchers/expect");
// packages/playwright/src/common/validators.ts
var { validate } = require("playwright-core/lib/coreBundle").iso;
var testAnnotationSchema = {
type: "object",
properties: {
type: { type: "string" },
description: { type: "string" }
},
required: ["type"]
};
var testDetailsSchema = {
type: "object",
properties: {
tag: {
oneOf: [
{ type: "string", pattern: "^@", patternError: "Tag must start with '@'" },
{ type: "array", items: { type: "string", pattern: "^@", patternError: "Tag must start with '@'" } }
]
},
annotation: {
oneOf: [
testAnnotationSchema,
{ type: "array", items: testAnnotationSchema }
]
}
}
};
function validateTestDetails(details, location) {
const errors = validate(details, testDetailsSchema, "details");
if (errors.length)
throw new Error(errors.join("\n"));
const obj = details;
const tag = obj.tag;
const tags = tag === void 0 ? [] : typeof tag === "string" ? [tag] : tag;
const annotation = obj.annotation;
const annotations = annotation === void 0 ? [] : Array.isArray(annotation) ? annotation : [annotation];
return {
annotations: annotations.map((a) => ({ ...a, location })),
tags,
location
};
}
// packages/playwright/src/common/testType.ts
var { monotonicTime } = require("playwright-core/lib/coreBundle").iso;
var { raceAgainstDeadline } = require("playwright-core/lib/coreBundle").iso;
var { getPackageManagerExecCommand } = require("playwright-core/lib/coreBundle").utils;
var { currentZone } = require("playwright-core/lib/coreBundle").utils;
var testTypeSymbol = Symbol("testType");
var TestTypeImpl = class _TestTypeImpl {
constructor(fixtures) {
this.fixtures = fixtures;
const test = wrapFunctionWithLocation(this._createTest.bind(this, "default"));
test[testTypeSymbol] = this;
test.expect = import_expect.expect;
test.only = wrapFunctionWithLocation(this._createTest.bind(this, "only"));
test.describe = wrapFunctionWithLocation(this._describe.bind(this, "default"));
test.describe.only = wrapFunctionWithLocation(this._describe.bind(this, "only"));
test.describe.configure = wrapFunctionWithLocation(this._configure.bind(this));
test.describe.fixme = wrapFunctionWithLocation(this._describe.bind(this, "fixme"));
test.describe.parallel = wrapFunctionWithLocation(this._describe.bind(this, "parallel"));
test.describe.parallel.only = wrapFunctionWithLocation(this._describe.bind(this, "parallel.only"));
test.describe.serial = wrapFunctionWithLocation(this._describe.bind(this, "serial"));
test.describe.serial.only = wrapFunctionWithLocation(this._describe.bind(this, "serial.only"));
test.describe.skip = wrapFunctionWithLocation(this._describe.bind(this, "skip"));
test.beforeEach = wrapFunctionWithLocation(this._hook.bind(this, "beforeEach"));
test.afterEach = wrapFunctionWithLocation(this._hook.bind(this, "afterEach"));
test.beforeAll = wrapFunctionWithLocation(this._hook.bind(this, "beforeAll"));
test.afterAll = wrapFunctionWithLocation(this._hook.bind(this, "afterAll"));
test.skip = wrapFunctionWithLocation(this._modifier.bind(this, "skip"));
test.fixme = wrapFunctionWithLocation(this._modifier.bind(this, "fixme"));
test.fail = wrapFunctionWithLocation(this._modifier.bind(this, "fail"));
test.abort = wrapFunctionWithLocation(this._abort.bind(this));
test.fail.only = wrapFunctionWithLocation(this._createTest.bind(this, "fail.only"));
test.slow = wrapFunctionWithLocation(this._modifier.bind(this, "slow"));
test.setTimeout = wrapFunctionWithLocation(this._setTimeout.bind(this));
test.step = this._step.bind(this, "pass");
test.step.skip = this._step.bind(this, "skip");
test.use = wrapFunctionWithLocation(this._use.bind(this));
test.extend = wrapFunctionWithLocation(this._extend.bind(this));
test.info = () => {
const result2 = (0, import_globals2.currentTestInfo)();
if (!result2)
throw new Error("test.info() can only be called while test is running");
return result2;
};
this.test = test;
}
_currentSuite(location, title) {
const suite = (0, import_globals2.currentlyLoadingFileSuite)();
if (!suite) {
throw new Error([
`Playwright Test did not expect ${title} to be called here.`,
`Most common reasons include:`,
`- You are calling ${title} in a configuration file.`,
`- You are calling ${title} in a file that is imported by the configuration file.`,
`- You have two different versions of @playwright/test. This usually happens`,
` when one of the dependencies in your package.json depends on @playwright/test.`
].join("\n"));
}
return suite;
}
_createTest(type, location, title, fnOrDetails, fn) {
throwIfRunningInsideJest();
const suite = this._currentSuite(location, "test()");
if (!suite)
return;
let details;
let body;
if (typeof fnOrDetails === "function") {
body = fnOrDetails;
details = {};
} else {
body = fn;
details = fnOrDetails;
}
const validatedDetails = validateTestDetails(details, location);
const test = new TestCase(title, body, this, location);
test._requireFile = suite._requireFile;
test.annotations.push(...validatedDetails.annotations);
test._tags.push(...validatedDetails.tags);
suite._addTest(test);
if (type === "only" || type === "fail.only")
test._only = true;
if (type === "skip" || type === "fixme" || type === "fail")
test.annotations.push({ type, location });
else if (type === "fail.only")
test.annotations.push({ type: "fail", location });
}
_describe(type, location, titleOrFn, fnOrDetails, fn) {
throwIfRunningInsideJest();
const suite = this._currentSuite(location, "test.describe()");
if (!suite)
return;
let title;
let body;
let details;
if (typeof titleOrFn === "function") {
title = "";
details = {};
body = titleOrFn;
} else if (typeof fnOrDetails === "function") {
title = titleOrFn;
details = {};
body = fnOrDetails;
} else {
title = titleOrFn;
details = fnOrDetails;
body = fn;
}
const validatedDetails = validateTestDetails(details, location);
const child = new Suite(title, "describe");
child._requireFile = suite._requireFile;
child.location = location;
child._staticAnnotations.push(...validatedDetails.annotations);
child._tags.push(...validatedDetails.tags);
suite._addSuite(child);
if (type === "only" || type === "serial.only" || type === "parallel.only")
child._only = true;
if (type === "serial" || type === "serial.only")
child._parallelMode = "serial";
if (type === "parallel" || type === "parallel.only")
child._parallelMode = "parallel";
if (type === "skip" || type === "fixme")
child._staticAnnotations.push({ type, location });
for (let parent = suite; parent; parent = parent.parent) {
if (parent._parallelMode === "serial" && child._parallelMode === "parallel")
throw new Error("describe.parallel cannot be nested inside describe.serial");
if (parent._parallelMode === "default" && child._parallelMode === "parallel")
throw new Error("describe.parallel cannot be nested inside describe with default mode");
}
(0, import_globals2.setCurrentlyLoadingFileSuite)(child);
body();
(0, import_globals2.setCurrentlyLoadingFileSuite)(suite);
}
_hook(name, location, title, fn) {
const suite = this._currentSuite(location, `test.${name}()`);
if (!suite)
return;
if (typeof title === "function") {
fn = title;
title = `${name} hook`;
}
suite._hooks.push({ type: name, fn, title, location });
}
_configure(location, options) {
throwIfRunningInsideJest();
const suite = this._currentSuite(location, `test.describe.configure()`);
if (!suite)
return;
if (options.timeout !== void 0)
suite._timeout = options.timeout;
if (options.retries !== void 0)
suite._retries = options.retries;
if (options.mode !== void 0) {
if (suite._parallelMode !== "none")
throw new Error(`"${suite._parallelMode}" mode is already assigned for the enclosing scope.`);
suite._parallelMode = options.mode;
for (let parent = suite.parent; parent; parent = parent.parent) {
if (parent._parallelMode === "serial" && suite._parallelMode === "parallel")
throw new Error("describe with parallel mode cannot be nested inside describe with serial mode");
if (parent._parallelMode === "default" && suite._parallelMode === "parallel")
throw new Error("describe with parallel mode cannot be nested inside describe with default mode");
}
}
}
_modifier(type, location, ...modifierArgs) {
const suite = (0, import_globals2.currentlyLoadingFileSuite)();
if (suite) {
if (typeof modifierArgs[0] === "string" && typeof modifierArgs[1] === "function" && (type === "skip" || type === "fixme" || type === "fail")) {
this._createTest(type, location, modifierArgs[0], modifierArgs[1]);
return;
}
if (typeof modifierArgs[0] === "string" && typeof modifierArgs[1] === "object" && typeof modifierArgs[2] === "function" && (type === "skip" || type === "fixme" || type === "fail")) {
this._createTest(type, location, modifierArgs[0], modifierArgs[1], modifierArgs[2]);
return;
}
if (typeof modifierArgs[0] === "function") {
suite._modifiers.push({ type, fn: modifierArgs[0], location, description: modifierArgs[1] });
} else {
if (modifierArgs.length >= 1 && !modifierArgs[0])
return;
const description = modifierArgs[1];
suite._staticAnnotations.push({ type, description, location });
}
return;
}
const testInfo = (0, import_globals2.currentTestInfo)();
if (!testInfo)
throw new Error(`test.${type}() can only be called inside test, describe block or fixture`);
if (typeof modifierArgs[0] === "function")
throw new Error(`test.${type}() with a function can only be called inside describe block`);
testInfo._modifier(type, location, modifierArgs);
}
_abort(location, message) {
const testInfo = (0, import_globals2.currentTestInfo)();
if (!testInfo)
throw new Error(`test.abort() can only be called inside a test or fixture`);
testInfo._abort(location, message);
}
_setTimeout(location, timeout) {
const suite = (0, import_globals2.currentlyLoadingFileSuite)();
if (suite) {
suite._timeout = timeout;
return;
}
const testInfo = (0, import_globals2.currentTestInfo)();
if (!testInfo)
throw new Error(`test.setTimeout() can only be called from a test`);
testInfo.setTimeout(timeout);
}
_use(location, fixtures) {
const suite = this._currentSuite(location, `test.use()`);
if (!suite)
return;
suite._use.push({ fixtures, location });
}
async _step(expectation, title, body, options = {}) {
const testInfo = (0, import_globals2.currentTestInfo)();
if (!testInfo)
throw new Error(`test.step() can only be called from a test`);
await testInfo._onUserStepBegin?.(title);
const step = testInfo._addStep({ category: "test.step", title, location: options.location, box: options.box });
return await currentZone().with("stepZone", step).run(async () => {
try {
let result2 = void 0;
result2 = await raceAgainstDeadline(async () => {
try {
return await step.info._runStepBody(expectation === "skip", body, step.location);
} catch (e) {
if (result2?.timedOut)
testInfo._failWithError(e);
throw e;
}
}, options.timeout ? monotonicTime() + options.timeout : 0);
if (result2.timedOut)
throw new TimeoutError(`Step timeout of ${options.timeout}ms exceeded.`);
step.complete({});
return result2.result;
} catch (error) {
step.complete({ error });
throw error;
} finally {
await testInfo._onUserStepEnd?.();
}
});
}
_extend(location, fixtures) {
if (fixtures[testTypeSymbol])
throw new Error(`test.extend() accepts fixtures object, not a test object.
Did you mean to call mergeTests()?`);
const fixturesWithLocation = { fixtures, location };
return new _TestTypeImpl([...this.fixtures, fixturesWithLocation]).test;
}
};
function throwIfRunningInsideJest() {
if (process.env.JEST_WORKER_ID) {
const packageManagerCommand = getPackageManagerExecCommand();
throw new Error(
`Playwright Test needs to be invoked via '${packageManagerCommand} playwright test' and excluded from Jest test runs.
Creating one directory for Playwright tests and one for Jest is the recommended way of doing it.
See https://playwright.dev/docs/intro for more information about Playwright Test.`
);
}
}
var rootTestType = new TestTypeImpl([]);
function mergeTests(...tests) {
let result2 = rootTestType;
for (const t of tests) {
const testTypeImpl = t[testTypeSymbol];
if (!testTypeImpl)
throw new Error(`mergeTests() accepts "test" functions as parameters.
Did you mean to call test.extend() with fixtures instead?`);
const newFixtures = testTypeImpl.fixtures.filter((theirs) => !result2.fixtures.find((ours) => ours.fixtures === theirs.fixtures));
result2 = new TestTypeImpl([...result2.fixtures, ...newFixtures]);
}
return result2.test;
}
var TimeoutError = class extends Error {
constructor(message) {
super(message);
this.name = "TimeoutError";
}
};
// packages/playwright/src/isomorphic/teleReceiver.ts
var baseFullConfig = {
forbidOnly: false,
fullyParallel: false,
globalSetup: null,
globalTeardown: null,
globalTimeout: 0,
grep: /.*/,
grepInvert: null,
maxFailures: 0,
metadata: {},
preserveOutput: "always",
projects: [],
reporter: [[process.env.CI ? "dot" : "list"]],
reportSlowTests: {
max: 5,
threshold: 3e5
/* 5 minutes */
},
configFile: "",
rootDir: "",
quiet: false,
shard: null,
tags: [],
updateSnapshots: "missing",
updateSourceMethod: "patch",
version: "",
workers: 0,
webServer: null
};
function computeTestCaseOutcome(test) {
let skipped = 0;
let didNotRun = 0;
let expected = 0;
let interrupted = 0;
let unexpected = 0;
for (const result2 of test.results) {
if (result2.status === "interrupted") {
++interrupted;
} else if (result2.status === "skipped" && test.expectedStatus === "skipped") {
++skipped;
} else if (result2.status === "skipped") {
++didNotRun;
} else if (result2.status === test.expectedStatus) {
++expected;
} else {
++unexpected;
}
}
if (expected === 0 && unexpected === 0)
return "skipped";
if (unexpected === 0)
return "expected";
if (expected === 0 && skipped === 0)
return "unexpected";
return "flaky";
}
// packages/playwright/src/common/test.ts
var Base = class {
constructor(title) {
this._only = false;
this._requireFile = "";
this.title = title;
}
};
var Suite = class _Suite extends Base {
constructor(title, type) {
super(title);
this._use = [];
this._entries = [];
this._hooks = [];
// Annotations known statically before running the test, e.g. `test.describe.skip()` or `test.describe({ annotation }, body)`.
this._staticAnnotations = [];
// Explicitly declared tags that are not a part of the title.
this._tags = [];
this._modifiers = [];
this._parallelMode = "none";
this._type = type;
}
get type() {
return this._type;
}
entries() {
return this._entries;
}
get suites() {
return this._entries.filter((entry) => entry instanceof _Suite);
}
get tests() {
return this._entries.filter((entry) => entry instanceof TestCase);
}
_addTest(test) {
test.parent = this;
this._entries.push(test);
}
_addSuite(suite) {
suite.parent = this;
this._entries.push(suite);
}
_prependSuite(suite) {
suite.parent = this;
this._entries.unshift(suite);
}
allTests() {
const result2 = [];
const visit = (suite) => {
for (const entry of suite._entries) {
if (entry instanceof _Suite)
visit(entry);
else
result2.push(entry);
}
};
visit(this);
return result2;
}
_hasTests() {
let result2 = false;
const visit = (suite) => {
for (const entry of suite._entries) {
if (result2)
return;
if (entry instanceof _Suite)
visit(entry);
else
result2 = true;
}
};
visit(this);
return result2;
}
titlePath() {
const titlePath = this.parent ? this.parent.titlePath() : [];
if (this.title || this._type !== "describe")
titlePath.push(this.title);
return titlePath;
}
_collectGrepTitlePath(path10) {
if (this.parent)
this.parent._collectGrepTitlePath(path10);
if (this.title || this._type !== "describe")
path10.push(this.title);
path10.push(...this._tags);
}
_collectTagTitlePath(path10) {
this.parent?._collectTagTitlePath(path10);
if (this._type === "describe")
path10.push(this.title);
path10.push(...this._tags);
}
_getOnlyItems() {
const items = [];
if (this._only)
items.push(this);
for (const suite of this.suites)
items.push(...suite._getOnlyItems());
items.push(...this.tests.filter((test) => test._only));
return items;
}
_deepClone() {
const suite = this._clone();
for (const entry of this._entries) {
if (entry instanceof _Suite)
suite._addSuite(entry._deepClone());
else
suite._addTest(entry._clone());
}
return suite;
}
_deepSerialize() {
const suite = this._serialize();
suite.entries = [];
for (const entry of this._entries) {
if (entry instanceof _Suite)
suite.entries.push(entry._deepSerialize());
else
suite.entries.push(entry._serialize());
}
return suite;
}
static _deepParse(data) {
const suite = _Suite._parse(data);
for (const entry of data.entries) {
if (entry.kind === "suite")
suite._addSuite(_Suite._deepParse(entry));
else
suite._addTest(TestCase._parse(entry));
}
return suite;
}
forEachTest(visitor) {
for (const entry of this._entries) {
if (entry instanceof _Suite)
entry.forEachTest(visitor);
else
visitor(entry, this);
}
}
_serialize() {
return {
kind: "suite",
title: this.title,
type: this._type,
location: this.location,
only: this._only,
requireFile: this._requireFile,
timeout: this._timeout,
retries: this._retries,
staticAnnotations: this._staticAnnotations.slice(),
tags: this._tags.slice(),
modifiers: this._modifiers.slice(),
parallelMode: this._parallelMode,
hooks: this._hooks.map((h) => ({ type: h.type, location: h.location, title: h.title })),
fileId: this._fileId
};
}
static _parse(data) {
const suite = new _Suite(data.title, data.type);
suite.location = data.location;
suite._only = data.only;
suite._requireFile = data.requireFile;
suite._timeout = data.timeout;
suite._retries = data.retries;
suite._staticAnnotations = data.staticAnnotations;
suite._tags = data.tags;
suite._modifiers = data.modifiers;
suite._parallelMode = data.parallelMode;
suite._hooks = data.hooks.map((h) => ({ type: h.type, location: h.location, title: h.title, fn: () => {
} }));
suite._fileId = data.fileId;
return suite;
}
_clone() {
const data = this._serialize();
const suite = _Suite._parse(data);
suite._use = this._use.slice();
suite._hooks = this._hooks.slice();
suite._fullProject = this._fullProject;
return suite;
}
project() {
return this._fullProject?.project || this.parent?.project();
}
};
var TestCase = class _TestCase extends Base {
constructor(title, fn, testType, location) {
super(title);
this.results = [];
this.type = "test";
this.expectedStatus = "passed";
this.timeout = 0;
this.annotations = [];
this.retries = 0;
this.repeatEachIndex = 0;
this.id = "";
this._poolDigest = "";
this._workerHash = "";
this._projectId = "";
// Explicitly declared tags that are not a part of the title.
this._tags = [];
this.fn = fn;
this._testType = testType;
this.location = location;
}
titlePath() {
const titlePath = this.parent ? this.parent.titlePath() : [];
titlePath.push(this.title);
return titlePath;
}
outcome() {
return computeTestCaseOutcome(this);
}
ok() {
const status = this.outcome();
return status === "expected" || status === "flaky" || status === "skipped";
}
get tags() {
const path10 = [];
this.parent._collectTagTitlePath(path10);
path10.push(this.title);
const titleTags = path10.join(" ").match(/@[\S]+/g) || [];
return [
...titleTags,
...this._tags
];
}
_serialize() {
return {
kind: "test",
id: this.id,
title: this.title,
retries: this.retries,
timeout: this.timeout,
expectedStatus: this.expectedStatus,
location: this.location,
only: this._only,
requireFile: this._requireFile,
poolDigest: this._poolDigest,
workerHash: this._workerHash,
annotations: this.annotations.slice(),
tags: this._tags.slice(),
projectId: this._projectId
};
}
static _parse(data) {
const test = new _TestCase(data.title, () => {
}, rootTestType, data.location);
test.id = data.id;
test.retries = data.retries;
test.timeout = data.timeout;
test.expectedStatus = data.expectedStatus;
test._only = data.only;
test._requireFile = data.requireFile;
test._poolDigest = data.poolDigest;
test._workerHash = data.workerHash;
test.annotations = data.annotations;
test._tags = data.tags;
test._projectId = data.projectId;
return test;
}
_clone() {
const data = this._serialize();
const test = _TestCase._parse(data);
test._testType = this._testType;
test.fn = this.fn;
return test;
}
_appendTestResult() {
const result2 = {
retry: this.results.length,
parallelIndex: -1,
workerIndex: -1,
duration: 0,
startTime: /* @__PURE__ */ new Date(),
stdout: [],
stderr: [],
attachments: [],
status: "skipped",
steps: [],
errors: [],
annotations: []
};
this.results.push(result2);
return result2;
}
_grepBaseTitlePath() {
const path10 = [];
this.parent._collectGrepTitlePath(path10);
path10.push(this.title);
return path10;
}
_grepTitleWithTags() {
const path10 = this._grepBaseTitlePath();
path10.push(...this._tags);
return path10.join(" ");
}
};
// packages/playwright/src/common/testLoader.ts
var testLoader_exports = {};
__export(testLoader_exports, {
defaultTimeout: () => defaultTimeout2,
loadTestFile: () => loadTestFile
});
var import_path9 = __toESM(require("path"));
var import_util10 = __toESM(require("util"));
var import_globals3 = require("../globals");
var defaultTimeout2 = 3e4;
var cachedFileSuites = /* @__PURE__ */ new Map();
async function loadTestFile(file2, config, testErrors) {
if (cachedFileSuites.has(file2))
return cachedFileSuites.get(file2);
const suite = new Suite(import_path9.default.relative(config.config.rootDir, file2) || import_path9.default.basename(file2), "file");
suite._requireFile = file2;
suite.location = { file: file2, line: 0, column: 0 };
suite._tags = [...config.config.tags];
(0, import_globals3.setCurrentlyLoadingFileSuite)(suite);
if (!(0, import_globals3.isWorkerProcess)()) {
startCollectingFileDeps();
await startCollectingFileDeps2();
}
try {
await requireOrImport(file2);
cachedFileSuites.set(file2, suite);
} catch (e) {
if (!testErrors)
throw e;
testErrors.push(serializeLoadError(file2, e));
} finally {
(0, import_globals3.setCurrentlyLoadingFileSuite)(void 0);
if (!(0, import_globals3.isWorkerProcess)()) {
stopCollectingFileDeps(file2);
await stopCollectingFileDeps2(file2);
}
}
{
const files = /* @__PURE__ */ new Set();
suite.allTests().map((t) => files.add(t.location.file));
if (files.size === 1) {
const mappedFile = files.values().next().value;
if (suite.location.file !== mappedFile) {
if (import_path9.default.extname(mappedFile) !== import_path9.default.extname(suite.location.file))
suite.location.file = mappedFile;
}
}
}
return suite;
}
function serializeLoadError(file2, error) {
if (error instanceof Error) {
const result2 = filterStackTrace(error);
const loc = error.loc;
result2.location = loc ? {
file: file2,
line: loc.line || 0,
column: loc.column || 0
} : void 0;
return result2;
}
return { value: import_util10.default.inspect(error) };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FullConfigInternal,
ProcessRunner,
builtInReporters,
cc,
config,
configLoader,
defineConfig,
esm,
fixtures,
ipc,
mergeTests,
poolBuilder,
processRunner,
startProcessRunner,
suiteUtils,
test,
testLoader,
testType,
transform
});