util.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. callLogText: () => callLogText,
  34. createFileFiltersFromArguments: () => createFileFiltersFromArguments,
  35. createFileMatcher: () => createFileMatcher,
  36. createFileMatcherFromArguments: () => createFileMatcherFromArguments,
  37. createTitleMatcher: () => createTitleMatcher,
  38. debugTest: () => debugTest,
  39. errorWithFile: () => errorWithFile,
  40. expectTypes: () => expectTypes,
  41. fileExistsAsync: () => fileExistsAsync,
  42. fileIsModule: () => fileIsModule,
  43. filterStackFile: () => filterStackFile,
  44. filterStackTrace: () => filterStackTrace,
  45. filteredStackTrace: () => filteredStackTrace,
  46. forceRegExp: () => forceRegExp,
  47. formatLocation: () => formatLocation,
  48. getContainedPath: () => getContainedPath,
  49. getPackageJsonPath: () => getPackageJsonPath,
  50. mergeObjects: () => mergeObjects,
  51. normalizeAndSaveAttachment: () => normalizeAndSaveAttachment,
  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 createFileFiltersFromArguments(args) {
  110. return args.map((arg) => {
  111. const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
  112. return {
  113. re: forceRegExp(match ? match[1] : arg),
  114. line: match ? parseInt(match[2], 10) : null,
  115. column: match?.[3] ? parseInt(match[3], 10) : null
  116. };
  117. });
  118. }
  119. function createFileMatcherFromArguments(args) {
  120. const filters = createFileFiltersFromArguments(args);
  121. return createFileMatcher(filters.map((filter) => filter.re || filter.exact || ""));
  122. }
  123. function createFileMatcher(patterns) {
  124. const reList = [];
  125. const filePatterns = [];
  126. for (const pattern of Array.isArray(patterns) ? patterns : [patterns]) {
  127. if ((0, import_utils.isRegExp)(pattern)) {
  128. reList.push(pattern);
  129. } else {
  130. if (!pattern.startsWith("**/"))
  131. filePatterns.push("**/" + pattern);
  132. else
  133. filePatterns.push(pattern);
  134. }
  135. }
  136. return (filePath) => {
  137. for (const re of reList) {
  138. re.lastIndex = 0;
  139. if (re.test(filePath))
  140. return true;
  141. }
  142. if (import_path.default.sep === "\\") {
  143. const fileURL = import_url.default.pathToFileURL(filePath).href;
  144. for (const re of reList) {
  145. re.lastIndex = 0;
  146. if (re.test(fileURL))
  147. return true;
  148. }
  149. }
  150. for (const pattern of filePatterns) {
  151. if ((0, import_utilsBundle.minimatch)(filePath, pattern, { nocase: true, dot: true }))
  152. return true;
  153. }
  154. return false;
  155. };
  156. }
  157. function createTitleMatcher(patterns) {
  158. const reList = Array.isArray(patterns) ? patterns : [patterns];
  159. return (value) => {
  160. for (const re of reList) {
  161. re.lastIndex = 0;
  162. if (re.test(value))
  163. return true;
  164. }
  165. return false;
  166. };
  167. }
  168. function mergeObjects(a, b, c) {
  169. const result = { ...a };
  170. for (const x of [b, c].filter(Boolean)) {
  171. for (const [name, value] of Object.entries(x)) {
  172. if (!Object.is(value, void 0))
  173. result[name] = value;
  174. }
  175. }
  176. return result;
  177. }
  178. function forceRegExp(pattern) {
  179. const match = pattern.match(/^\/(.*)\/([gi]*)$/);
  180. if (match)
  181. return new RegExp(match[1], match[2]);
  182. return new RegExp(pattern, "gi");
  183. }
  184. function relativeFilePath(file) {
  185. if (!import_path.default.isAbsolute(file))
  186. return file;
  187. return import_path.default.relative(process.cwd(), file);
  188. }
  189. function formatLocation(location) {
  190. return relativeFilePath(location.file) + ":" + location.line + ":" + location.column;
  191. }
  192. function errorWithFile(file, message) {
  193. return new Error(`${relativeFilePath(file)}: ${message}`);
  194. }
  195. function expectTypes(receiver, types, matcherName) {
  196. if (typeof receiver !== "object" || !types.includes(receiver.constructor.name)) {
  197. const commaSeparated = types.slice();
  198. const lastType = commaSeparated.pop();
  199. const typesString = commaSeparated.length ? commaSeparated.join(", ") + " or " + lastType : lastType;
  200. throw new Error(`${matcherName} can be only used with ${typesString} object${types.length > 1 ? "s" : ""}`);
  201. }
  202. }
  203. const windowsFilesystemFriendlyLength = 60;
  204. function trimLongString(s, length = 100) {
  205. if (s.length <= length)
  206. return s;
  207. const hash = (0, import_utils.calculateSha1)(s);
  208. const middle = `-${hash.substring(0, 5)}-`;
  209. const start = Math.floor((length - middle.length) / 2);
  210. const end = length - middle.length - start;
  211. return s.substring(0, start) + middle + s.slice(-end);
  212. }
  213. function addSuffixToFilePath(filePath, suffix) {
  214. const ext = import_path.default.extname(filePath);
  215. const base = filePath.substring(0, filePath.length - ext.length);
  216. return base + suffix + ext;
  217. }
  218. function sanitizeFilePathBeforeExtension(filePath, ext) {
  219. ext ??= import_path.default.extname(filePath);
  220. const base = filePath.substring(0, filePath.length - ext.length);
  221. return (0, import_utils.sanitizeForFilePath)(base) + ext;
  222. }
  223. function getContainedPath(parentPath, subPath = "") {
  224. const resolvedPath = import_path.default.resolve(parentPath, subPath);
  225. if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + import_path.default.sep))
  226. return resolvedPath;
  227. return null;
  228. }
  229. const debugTest = (0, import_utilsBundle.debug)("pw:test");
  230. const callLogText = (log) => {
  231. if (!log || !log.some((l) => !!l))
  232. return "";
  233. return `
  234. Call log:
  235. ${import_utilsBundle.colors.dim(log.join("\n"))}
  236. `;
  237. };
  238. const folderToPackageJsonPath = /* @__PURE__ */ new Map();
  239. function getPackageJsonPath(folderPath) {
  240. const cached = folderToPackageJsonPath.get(folderPath);
  241. if (cached !== void 0)
  242. return cached;
  243. const packageJsonPath = import_path.default.join(folderPath, "package.json");
  244. if (import_fs.default.existsSync(packageJsonPath)) {
  245. folderToPackageJsonPath.set(folderPath, packageJsonPath);
  246. return packageJsonPath;
  247. }
  248. const parentFolder = import_path.default.dirname(folderPath);
  249. if (folderPath === parentFolder) {
  250. folderToPackageJsonPath.set(folderPath, "");
  251. return "";
  252. }
  253. const result = getPackageJsonPath(parentFolder);
  254. folderToPackageJsonPath.set(folderPath, result);
  255. return result;
  256. }
  257. function resolveReporterOutputPath(defaultValue, configDir, configValue) {
  258. if (configValue)
  259. return import_path.default.resolve(configDir, configValue);
  260. let basePath = getPackageJsonPath(configDir);
  261. basePath = basePath ? import_path.default.dirname(basePath) : process.cwd();
  262. return import_path.default.resolve(basePath, defaultValue);
  263. }
  264. async function normalizeAndSaveAttachment(outputPath, name, options = {}) {
  265. if (options.path === void 0 && options.body === void 0)
  266. return { name, contentType: "text/plain" };
  267. if ((options.path !== void 0 ? 1 : 0) + (options.body !== void 0 ? 1 : 0) !== 1)
  268. throw new Error(`Exactly one of "path" and "body" must be specified`);
  269. if (options.path !== void 0) {
  270. const hash = (0, import_utils.calculateSha1)(options.path);
  271. if (!(0, import_utils.isString)(name))
  272. throw new Error('"name" should be string.');
  273. const sanitizedNamePrefix = (0, import_utils.sanitizeForFilePath)(name) + "-";
  274. const dest = import_path.default.join(outputPath, "attachments", sanitizedNamePrefix + hash + import_path.default.extname(options.path));
  275. await import_fs.default.promises.mkdir(import_path.default.dirname(dest), { recursive: true });
  276. await import_fs.default.promises.copyFile(options.path, dest);
  277. const contentType = options.contentType ?? (import_utilsBundle.mime.getType(import_path.default.basename(options.path)) || "application/octet-stream");
  278. return { name, contentType, path: dest };
  279. } else {
  280. const contentType = options.contentType ?? (typeof options.body === "string" ? "text/plain" : "application/octet-stream");
  281. return { name, contentType, body: typeof options.body === "string" ? Buffer.from(options.body) : options.body };
  282. }
  283. }
  284. function fileIsModule(file) {
  285. if (file.endsWith(".mjs") || file.endsWith(".mts"))
  286. return true;
  287. if (file.endsWith(".cjs") || file.endsWith(".cts"))
  288. return false;
  289. const folder = import_path.default.dirname(file);
  290. return folderIsModule(folder);
  291. }
  292. function folderIsModule(folder) {
  293. const packageJsonPath = getPackageJsonPath(folder);
  294. if (!packageJsonPath)
  295. return false;
  296. return require(packageJsonPath).type === "module";
  297. }
  298. const packageJsonMainFieldCache = /* @__PURE__ */ new Map();
  299. function getMainFieldFromPackageJson(packageJsonPath) {
  300. if (!packageJsonMainFieldCache.has(packageJsonPath)) {
  301. let mainField;
  302. try {
  303. mainField = JSON.parse(import_fs.default.readFileSync(packageJsonPath, "utf8")).main;
  304. } catch {
  305. }
  306. packageJsonMainFieldCache.set(packageJsonPath, mainField);
  307. }
  308. return packageJsonMainFieldCache.get(packageJsonPath);
  309. }
  310. const kExtLookups = /* @__PURE__ */ new Map([
  311. [".js", [".jsx", ".ts", ".tsx"]],
  312. [".jsx", [".tsx"]],
  313. [".cjs", [".cts"]],
  314. [".mjs", [".mts"]],
  315. ["", [".js", ".ts", ".jsx", ".tsx", ".cjs", ".mjs", ".cts", ".mts"]]
  316. ]);
  317. function resolveImportSpecifierExtension(resolved) {
  318. if (fileExists(resolved))
  319. return resolved;
  320. for (const [ext, others] of kExtLookups) {
  321. if (!resolved.endsWith(ext))
  322. continue;
  323. for (const other of others) {
  324. const modified = resolved.substring(0, resolved.length - ext.length) + other;
  325. if (fileExists(modified))
  326. return modified;
  327. }
  328. break;
  329. }
  330. }
  331. function resolveImportSpecifierAfterMapping(resolved, afterPathMapping) {
  332. const resolvedFile = resolveImportSpecifierExtension(resolved);
  333. if (resolvedFile)
  334. return resolvedFile;
  335. if (dirExists(resolved)) {
  336. const packageJsonPath = import_path.default.join(resolved, "package.json");
  337. if (afterPathMapping) {
  338. const mainField = getMainFieldFromPackageJson(packageJsonPath);
  339. const mainFieldResolved = mainField ? resolveImportSpecifierExtension(import_path.default.resolve(resolved, mainField)) : void 0;
  340. return mainFieldResolved || resolveImportSpecifierExtension(import_path.default.join(resolved, "index"));
  341. }
  342. if (fileExists(packageJsonPath))
  343. return resolved;
  344. const dirImport = import_path.default.join(resolved, "index");
  345. return resolveImportSpecifierExtension(dirImport);
  346. }
  347. }
  348. function fileExists(resolved) {
  349. return import_fs.default.statSync(resolved, { throwIfNoEntry: false })?.isFile();
  350. }
  351. async function fileExistsAsync(resolved) {
  352. try {
  353. const stat = await import_fs.default.promises.stat(resolved);
  354. return stat.isFile();
  355. } catch {
  356. return false;
  357. }
  358. }
  359. function dirExists(resolved) {
  360. return import_fs.default.statSync(resolved, { throwIfNoEntry: false })?.isDirectory();
  361. }
  362. async function removeDirAndLogToConsole(dir) {
  363. try {
  364. if (!import_fs.default.existsSync(dir))
  365. return;
  366. console.log(`Removing ${await import_fs.default.promises.realpath(dir)}`);
  367. await import_fs.default.promises.rm(dir, { recursive: true, force: true });
  368. } catch {
  369. }
  370. }
  371. 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");
  372. function stripAnsiEscapes(str) {
  373. return str.replace(ansiRegex, "");
  374. }
  375. // Annotate the CommonJS export names for ESM import in node:
  376. 0 && (module.exports = {
  377. addSuffixToFilePath,
  378. ansiRegex,
  379. callLogText,
  380. createFileFiltersFromArguments,
  381. createFileMatcher,
  382. createFileMatcherFromArguments,
  383. createTitleMatcher,
  384. debugTest,
  385. errorWithFile,
  386. expectTypes,
  387. fileExistsAsync,
  388. fileIsModule,
  389. filterStackFile,
  390. filterStackTrace,
  391. filteredStackTrace,
  392. forceRegExp,
  393. formatLocation,
  394. getContainedPath,
  395. getPackageJsonPath,
  396. mergeObjects,
  397. normalizeAndSaveAttachment,
  398. relativeFilePath,
  399. removeDirAndLogToConsole,
  400. resolveImportSpecifierAfterMapping,
  401. resolveReporterOutputPath,
  402. sanitizeFilePathBeforeExtension,
  403. serializeError,
  404. stripAnsiEscapes,
  405. trimLongString,
  406. windowsFilesystemFriendlyLength
  407. });