util.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. "use strict";
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  5. var __getOwnPropNames = Object.getOwnPropertyNames;
  6. var __getProtoOf = Object.getPrototypeOf;
  7. var __hasOwnProp = Object.prototype.hasOwnProperty;
  8. var __export = (target, all) => {
  9. for (var name in all)
  10. __defProp(target, name, { get: all[name], enumerable: true });
  11. };
  12. var __copyProps = (to, from, except, desc) => {
  13. if (from && typeof from === "object" || typeof from === "function") {
  14. for (let key of __getOwnPropNames(from))
  15. if (!__hasOwnProp.call(to, key) && key !== except)
  16. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  17. }
  18. return to;
  19. };
  20. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  21. // If the importer is in node compatibility mode or this is not an ESM
  22. // file that has been converted to a CommonJS file using a Babel-
  23. // compatible transform (i.e. "__esModule" has not been set), then set
  24. // "default" to the CommonJS "module.exports" for node compatibility.
  25. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  26. mod
  27. ));
  28. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  29. var util_exports = {};
  30. __export(util_exports, {
  31. addSuffixToFilePath: () => addSuffixToFilePath,
  32. ansiRegex: () => ansiRegex,
  33. createFileFiltersFromArguments: () => createFileFiltersFromArguments,
  34. createFileMatcher: () => createFileMatcher,
  35. createFileMatcherFromArguments: () => createFileMatcherFromArguments,
  36. createTitleMatcher: () => createTitleMatcher,
  37. debugTest: () => debugTest,
  38. errorWithFile: () => errorWithFile,
  39. expectTypes: () => expectTypes,
  40. fileExistsAsync: () => fileExistsAsync,
  41. fileIsModule: () => fileIsModule,
  42. filterStackFile: () => filterStackFile,
  43. filterStackTrace: () => filterStackTrace,
  44. filteredStackTrace: () => filteredStackTrace,
  45. forceRegExp: () => forceRegExp,
  46. formatLocation: () => formatLocation,
  47. getContainedPath: () => getContainedPath,
  48. getPackageJsonPath: () => getPackageJsonPath,
  49. mergeObjects: () => mergeObjects,
  50. normalizeAndSaveAttachment: () => normalizeAndSaveAttachment,
  51. parseLocationArg: () => parseLocationArg,
  52. relativeFilePath: () => relativeFilePath,
  53. removeDirAndLogToConsole: () => removeDirAndLogToConsole,
  54. resolveImportSpecifierAfterMapping: () => resolveImportSpecifierAfterMapping,
  55. resolveReporterOutputPath: () => resolveReporterOutputPath,
  56. sanitizeFilePathBeforeExtension: () => sanitizeFilePathBeforeExtension,
  57. serializeError: () => serializeError,
  58. stripAnsiEscapes: () => stripAnsiEscapes,
  59. trimLongString: () => trimLongString,
  60. windowsFilesystemFriendlyLength: () => windowsFilesystemFriendlyLength
  61. });
  62. module.exports = __toCommonJS(util_exports);
  63. var import_fs = __toESM(require("fs"));
  64. var import_path = __toESM(require("path"));
  65. var import_url = __toESM(require("url"));
  66. var import_util = __toESM(require("util"));
  67. var import_utils = require("playwright-core/lib/utils");
  68. var import_utilsBundle = require("playwright-core/lib/utilsBundle");
  69. const PLAYWRIGHT_TEST_PATH = import_path.default.join(__dirname, "..");
  70. const PLAYWRIGHT_CORE_PATH = import_path.default.dirname(require.resolve("playwright-core/package.json"));
  71. function filterStackTrace(e) {
  72. const name = e.name ? e.name + ": " : "";
  73. const cause = e.cause instanceof Error ? filterStackTrace(e.cause) : void 0;
  74. if (process.env.PWDEBUGIMPL)
  75. return { message: name + e.message, stack: e.stack || "", cause };
  76. const stackLines = (0, import_utils.stringifyStackFrames)(filteredStackTrace(e.stack?.split("\n") || []));
  77. return {
  78. message: name + e.message,
  79. stack: `${name}${e.message}${stackLines.map((line) => "\n" + line).join("")}`,
  80. cause
  81. };
  82. }
  83. function filterStackFile(file) {
  84. if (!process.env.PWDEBUGIMPL && file.startsWith(PLAYWRIGHT_TEST_PATH))
  85. return false;
  86. if (!process.env.PWDEBUGIMPL && file.startsWith(PLAYWRIGHT_CORE_PATH))
  87. return false;
  88. return true;
  89. }
  90. function filteredStackTrace(rawStack) {
  91. const frames = [];
  92. for (const line of rawStack) {
  93. const frame = (0, import_utils.parseStackFrame)(line, import_path.default.sep, !!process.env.PWDEBUGIMPL);
  94. if (!frame || !frame.file)
  95. continue;
  96. if (!filterStackFile(frame.file))
  97. continue;
  98. frames.push(frame);
  99. }
  100. return frames;
  101. }
  102. function serializeError(error) {
  103. if (error instanceof Error)
  104. return filterStackTrace(error);
  105. return {
  106. value: import_util.default.inspect(error)
  107. };
  108. }
  109. function parseLocationArg(arg) {
  110. const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
  111. return {
  112. file: match ? match[1] : arg,
  113. line: match ? parseInt(match[2], 10) : null,
  114. column: match?.[3] ? parseInt(match[3], 10) : null
  115. };
  116. }
  117. function createFileFiltersFromArguments(args) {
  118. return args.map((arg) => {
  119. const parsed = parseLocationArg(arg);
  120. return { re: forceRegExp(parsed.file), line: parsed.line, column: parsed.column };
  121. });
  122. }
  123. function createFileMatcherFromArguments(args) {
  124. const filters = createFileFiltersFromArguments(args);
  125. return createFileMatcher(filters.map((filter) => filter.re || filter.exact || ""));
  126. }
  127. function createFileMatcher(patterns) {
  128. const reList = [];
  129. const filePatterns = [];
  130. for (const pattern of Array.isArray(patterns) ? patterns : [patterns]) {
  131. if ((0, import_utils.isRegExp)(pattern)) {
  132. reList.push(pattern);
  133. } else {
  134. if (!pattern.startsWith("**/"))
  135. filePatterns.push("**/" + pattern);
  136. else
  137. filePatterns.push(pattern);
  138. }
  139. }
  140. return (filePath) => {
  141. for (const re of reList) {
  142. re.lastIndex = 0;
  143. if (re.test(filePath))
  144. return true;
  145. }
  146. if (import_path.default.sep === "\\") {
  147. const fileURL = import_url.default.pathToFileURL(filePath).href;
  148. for (const re of reList) {
  149. re.lastIndex = 0;
  150. if (re.test(fileURL))
  151. return true;
  152. }
  153. }
  154. for (const pattern of filePatterns) {
  155. if ((0, import_utilsBundle.minimatch)(filePath, pattern, { nocase: true, dot: true }))
  156. return true;
  157. }
  158. return false;
  159. };
  160. }
  161. function createTitleMatcher(patterns) {
  162. const reList = Array.isArray(patterns) ? patterns : [patterns];
  163. return (value) => {
  164. for (const re of reList) {
  165. re.lastIndex = 0;
  166. if (re.test(value))
  167. return true;
  168. }
  169. return false;
  170. };
  171. }
  172. function mergeObjects(a, b, c) {
  173. const result = { ...a };
  174. for (const x of [b, c].filter(Boolean)) {
  175. for (const [name, value] of Object.entries(x)) {
  176. if (!Object.is(value, void 0))
  177. result[name] = value;
  178. }
  179. }
  180. return result;
  181. }
  182. function forceRegExp(pattern) {
  183. const match = pattern.match(/^\/(.*)\/([gi]*)$/);
  184. if (match)
  185. return new RegExp(match[1], match[2]);
  186. return new RegExp(pattern, "gi");
  187. }
  188. function relativeFilePath(file) {
  189. if (!import_path.default.isAbsolute(file))
  190. return file;
  191. return import_path.default.relative(process.cwd(), file);
  192. }
  193. function formatLocation(location) {
  194. return relativeFilePath(location.file) + ":" + location.line + ":" + location.column;
  195. }
  196. function errorWithFile(file, message) {
  197. return new Error(`${relativeFilePath(file)}: ${message}`);
  198. }
  199. function expectTypes(receiver, types, matcherName) {
  200. if (typeof receiver !== "object" || !types.includes(receiver.constructor.name)) {
  201. const commaSeparated = types.slice();
  202. const lastType = commaSeparated.pop();
  203. const typesString = commaSeparated.length ? commaSeparated.join(", ") + " or " + lastType : lastType;
  204. throw new Error(`${matcherName} can be only used with ${typesString} object${types.length > 1 ? "s" : ""}`);
  205. }
  206. }
  207. const windowsFilesystemFriendlyLength = 60;
  208. function trimLongString(s, length = 100) {
  209. if (s.length <= length)
  210. return s;
  211. const hash = (0, import_utils.calculateSha1)(s);
  212. const middle = `-${hash.substring(0, 5)}-`;
  213. const start = Math.floor((length - middle.length) / 2);
  214. const end = length - middle.length - start;
  215. return s.substring(0, start) + middle + s.slice(-end);
  216. }
  217. function addSuffixToFilePath(filePath, suffix) {
  218. const ext = import_path.default.extname(filePath);
  219. const base = filePath.substring(0, filePath.length - ext.length);
  220. return base + suffix + ext;
  221. }
  222. function sanitizeFilePathBeforeExtension(filePath, ext) {
  223. ext ??= import_path.default.extname(filePath);
  224. const base = filePath.substring(0, filePath.length - ext.length);
  225. return (0, import_utils.sanitizeForFilePath)(base) + ext;
  226. }
  227. function getContainedPath(parentPath, subPath = "") {
  228. const resolvedPath = import_path.default.resolve(parentPath, subPath);
  229. if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + import_path.default.sep))
  230. return resolvedPath;
  231. return null;
  232. }
  233. const debugTest = (0, import_utilsBundle.debug)("pw:test");
  234. const folderToPackageJsonPath = /* @__PURE__ */ new Map();
  235. function getPackageJsonPath(folderPath) {
  236. const cached = folderToPackageJsonPath.get(folderPath);
  237. if (cached !== void 0)
  238. return cached;
  239. const packageJsonPath = import_path.default.join(folderPath, "package.json");
  240. if (import_fs.default.existsSync(packageJsonPath)) {
  241. folderToPackageJsonPath.set(folderPath, packageJsonPath);
  242. return packageJsonPath;
  243. }
  244. const parentFolder = import_path.default.dirname(folderPath);
  245. if (folderPath === parentFolder) {
  246. folderToPackageJsonPath.set(folderPath, "");
  247. return "";
  248. }
  249. const result = getPackageJsonPath(parentFolder);
  250. folderToPackageJsonPath.set(folderPath, result);
  251. return result;
  252. }
  253. function resolveReporterOutputPath(defaultValue, configDir, configValue) {
  254. if (configValue)
  255. return import_path.default.resolve(configDir, configValue);
  256. let basePath = getPackageJsonPath(configDir);
  257. basePath = basePath ? import_path.default.dirname(basePath) : process.cwd();
  258. return import_path.default.resolve(basePath, defaultValue);
  259. }
  260. async function normalizeAndSaveAttachment(outputPath, name, options = {}) {
  261. if (options.path === void 0 && options.body === void 0)
  262. return { name, contentType: "text/plain" };
  263. if ((options.path !== void 0 ? 1 : 0) + (options.body !== void 0 ? 1 : 0) !== 1)
  264. throw new Error(`Exactly one of "path" and "body" must be specified`);
  265. if (options.path !== void 0) {
  266. const hash = (0, import_utils.calculateSha1)(options.path);
  267. if (!(0, import_utils.isString)(name))
  268. throw new Error('"name" should be string.');
  269. const sanitizedNamePrefix = (0, import_utils.sanitizeForFilePath)(name) + "-";
  270. const dest = import_path.default.join(outputPath, "attachments", sanitizedNamePrefix + hash + import_path.default.extname(options.path));
  271. await import_fs.default.promises.mkdir(import_path.default.dirname(dest), { recursive: true });
  272. await import_fs.default.promises.copyFile(options.path, dest);
  273. const contentType = options.contentType ?? (import_utilsBundle.mime.getType(import_path.default.basename(options.path)) || "application/octet-stream");
  274. return { name, contentType, path: dest };
  275. } else {
  276. const contentType = options.contentType ?? (typeof options.body === "string" ? "text/plain" : "application/octet-stream");
  277. return { name, contentType, body: typeof options.body === "string" ? Buffer.from(options.body) : options.body };
  278. }
  279. }
  280. function fileIsModule(file) {
  281. if (file.endsWith(".mjs") || file.endsWith(".mts"))
  282. return true;
  283. if (file.endsWith(".cjs") || file.endsWith(".cts"))
  284. return false;
  285. const folder = import_path.default.dirname(file);
  286. return folderIsModule(folder);
  287. }
  288. function folderIsModule(folder) {
  289. const packageJsonPath = getPackageJsonPath(folder);
  290. if (!packageJsonPath)
  291. return false;
  292. return require(packageJsonPath).type === "module";
  293. }
  294. const packageJsonMainFieldCache = /* @__PURE__ */ new Map();
  295. function getMainFieldFromPackageJson(packageJsonPath) {
  296. if (!packageJsonMainFieldCache.has(packageJsonPath)) {
  297. let mainField;
  298. try {
  299. mainField = JSON.parse(import_fs.default.readFileSync(packageJsonPath, "utf8")).main;
  300. } catch {
  301. }
  302. packageJsonMainFieldCache.set(packageJsonPath, mainField);
  303. }
  304. return packageJsonMainFieldCache.get(packageJsonPath);
  305. }
  306. const kExtLookups = /* @__PURE__ */ new Map([
  307. [".js", [".jsx", ".ts", ".tsx"]],
  308. [".jsx", [".tsx"]],
  309. [".cjs", [".cts"]],
  310. [".mjs", [".mts"]],
  311. ["", [".js", ".ts", ".jsx", ".tsx", ".cjs", ".mjs", ".cts", ".mts"]]
  312. ]);
  313. function resolveImportSpecifierExtension(resolved) {
  314. if (fileExists(resolved))
  315. return resolved;
  316. for (const [ext, others] of kExtLookups) {
  317. if (!resolved.endsWith(ext))
  318. continue;
  319. for (const other of others) {
  320. const modified = resolved.substring(0, resolved.length - ext.length) + other;
  321. if (fileExists(modified))
  322. return modified;
  323. }
  324. break;
  325. }
  326. }
  327. function resolveImportSpecifierAfterMapping(resolved, afterPathMapping) {
  328. const resolvedFile = resolveImportSpecifierExtension(resolved);
  329. if (resolvedFile)
  330. return resolvedFile;
  331. if (dirExists(resolved)) {
  332. const packageJsonPath = import_path.default.join(resolved, "package.json");
  333. if (afterPathMapping) {
  334. const mainField = getMainFieldFromPackageJson(packageJsonPath);
  335. const mainFieldResolved = mainField ? resolveImportSpecifierExtension(import_path.default.resolve(resolved, mainField)) : void 0;
  336. return mainFieldResolved || resolveImportSpecifierExtension(import_path.default.join(resolved, "index"));
  337. }
  338. if (fileExists(packageJsonPath))
  339. return resolved;
  340. const dirImport = import_path.default.join(resolved, "index");
  341. return resolveImportSpecifierExtension(dirImport);
  342. }
  343. }
  344. function fileExists(resolved) {
  345. return import_fs.default.statSync(resolved, { throwIfNoEntry: false })?.isFile();
  346. }
  347. async function fileExistsAsync(resolved) {
  348. try {
  349. const stat = await import_fs.default.promises.stat(resolved);
  350. return stat.isFile();
  351. } catch {
  352. return false;
  353. }
  354. }
  355. function dirExists(resolved) {
  356. return import_fs.default.statSync(resolved, { throwIfNoEntry: false })?.isDirectory();
  357. }
  358. async function removeDirAndLogToConsole(dir) {
  359. try {
  360. if (!import_fs.default.existsSync(dir))
  361. return;
  362. console.log(`Removing ${await import_fs.default.promises.realpath(dir)}`);
  363. await import_fs.default.promises.rm(dir, { recursive: true, force: true });
  364. } catch {
  365. }
  366. }
  367. const ansiRegex = new RegExp("([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))", "g");
  368. function stripAnsiEscapes(str) {
  369. return str.replace(ansiRegex, "");
  370. }
  371. // Annotate the CommonJS export names for ESM import in node:
  372. 0 && (module.exports = {
  373. addSuffixToFilePath,
  374. ansiRegex,
  375. createFileFiltersFromArguments,
  376. createFileMatcher,
  377. createFileMatcherFromArguments,
  378. createTitleMatcher,
  379. debugTest,
  380. errorWithFile,
  381. expectTypes,
  382. fileExistsAsync,
  383. fileIsModule,
  384. filterStackFile,
  385. filterStackTrace,
  386. filteredStackTrace,
  387. forceRegExp,
  388. formatLocation,
  389. getContainedPath,
  390. getPackageJsonPath,
  391. mergeObjects,
  392. normalizeAndSaveAttachment,
  393. parseLocationArg,
  394. relativeFilePath,
  395. removeDirAndLogToConsole,
  396. resolveImportSpecifierAfterMapping,
  397. resolveReporterOutputPath,
  398. sanitizeFilePathBeforeExtension,
  399. serializeError,
  400. stripAnsiEscapes,
  401. trimLongString,
  402. windowsFilesystemFriendlyLength
  403. });