json.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 json_exports = {};
  30. __export(json_exports, {
  31. default: () => json_default,
  32. serializePatterns: () => serializePatterns
  33. });
  34. module.exports = __toCommonJS(json_exports);
  35. var import_fs = __toESM(require("fs"));
  36. var import_path = __toESM(require("path"));
  37. var import_utils = require("playwright-core/lib/utils");
  38. var import_base = require("./base");
  39. var import_config = require("../common/config");
  40. class JSONReporter {
  41. constructor(options) {
  42. this._errors = [];
  43. this._resolvedOutputFile = (0, import_base.resolveOutputFile)("JSON", options)?.outputFile;
  44. }
  45. version() {
  46. return "v2";
  47. }
  48. printsToStdio() {
  49. return !this._resolvedOutputFile;
  50. }
  51. onConfigure(config) {
  52. this.config = config;
  53. }
  54. onBegin(suite) {
  55. this.suite = suite;
  56. }
  57. onError(error) {
  58. this._errors.push(error);
  59. }
  60. async onEnd(result) {
  61. await outputReport(this._serializeReport(result), this._resolvedOutputFile);
  62. }
  63. _serializeReport(result) {
  64. const report = {
  65. config: {
  66. ...removePrivateFields(this.config),
  67. rootDir: (0, import_utils.toPosixPath)(this.config.rootDir),
  68. projects: this.config.projects.map((project) => {
  69. return {
  70. outputDir: (0, import_utils.toPosixPath)(project.outputDir),
  71. repeatEach: project.repeatEach,
  72. retries: project.retries,
  73. metadata: project.metadata,
  74. id: (0, import_config.getProjectId)(project),
  75. name: project.name,
  76. testDir: (0, import_utils.toPosixPath)(project.testDir),
  77. testIgnore: serializePatterns(project.testIgnore),
  78. testMatch: serializePatterns(project.testMatch),
  79. timeout: project.timeout
  80. };
  81. })
  82. },
  83. suites: this._mergeSuites(this.suite.suites),
  84. errors: this._errors,
  85. stats: {
  86. startTime: result.startTime.toISOString(),
  87. duration: result.duration,
  88. expected: 0,
  89. skipped: 0,
  90. unexpected: 0,
  91. flaky: 0
  92. }
  93. };
  94. for (const test of this.suite.allTests())
  95. ++report.stats[test.outcome()];
  96. return report;
  97. }
  98. _mergeSuites(suites) {
  99. const fileSuites = new import_utils.MultiMap();
  100. for (const projectSuite of suites) {
  101. const projectId = (0, import_config.getProjectId)(projectSuite.project());
  102. const projectName = projectSuite.project().name;
  103. for (const fileSuite of projectSuite.suites) {
  104. const file = fileSuite.location.file;
  105. const serialized = this._serializeSuite(projectId, projectName, fileSuite);
  106. if (serialized)
  107. fileSuites.set(file, serialized);
  108. }
  109. }
  110. const results = [];
  111. for (const [, suites2] of fileSuites) {
  112. const result = {
  113. title: suites2[0].title,
  114. file: suites2[0].file,
  115. column: 0,
  116. line: 0,
  117. specs: []
  118. };
  119. for (const suite of suites2)
  120. this._mergeTestsFromSuite(result, suite);
  121. results.push(result);
  122. }
  123. return results;
  124. }
  125. _relativeLocation(location) {
  126. if (!location)
  127. return { file: "", line: 0, column: 0 };
  128. return {
  129. file: (0, import_utils.toPosixPath)(import_path.default.relative(this.config.rootDir, location.file)),
  130. line: location.line,
  131. column: location.column
  132. };
  133. }
  134. _locationMatches(s1, s2) {
  135. return s1.file === s2.file && s1.line === s2.line && s1.column === s2.column;
  136. }
  137. _mergeTestsFromSuite(to, from) {
  138. for (const fromSuite of from.suites || []) {
  139. const toSuite = (to.suites || []).find((s) => s.title === fromSuite.title && this._locationMatches(s, fromSuite));
  140. if (toSuite) {
  141. this._mergeTestsFromSuite(toSuite, fromSuite);
  142. } else {
  143. if (!to.suites)
  144. to.suites = [];
  145. to.suites.push(fromSuite);
  146. }
  147. }
  148. for (const spec of from.specs || []) {
  149. const toSpec = to.specs.find((s) => s.title === spec.title && s.file === (0, import_utils.toPosixPath)(import_path.default.relative(this.config.rootDir, spec.file)) && s.line === spec.line && s.column === spec.column);
  150. if (toSpec)
  151. toSpec.tests.push(...spec.tests);
  152. else
  153. to.specs.push(spec);
  154. }
  155. }
  156. _serializeSuite(projectId, projectName, suite) {
  157. if (!suite.allTests().length)
  158. return null;
  159. const suites = suite.suites.map((suite2) => this._serializeSuite(projectId, projectName, suite2)).filter((s) => s);
  160. return {
  161. title: suite.title,
  162. ...this._relativeLocation(suite.location),
  163. specs: suite.tests.map((test) => this._serializeTestSpec(projectId, projectName, test)),
  164. suites: suites.length ? suites : void 0
  165. };
  166. }
  167. _serializeTestSpec(projectId, projectName, test) {
  168. return {
  169. title: test.title,
  170. ok: test.ok(),
  171. tags: test.tags.map((tag) => tag.substring(1)),
  172. // Strip '@'.
  173. tests: [this._serializeTest(projectId, projectName, test)],
  174. id: test.id,
  175. ...this._relativeLocation(test.location)
  176. };
  177. }
  178. _serializeTest(projectId, projectName, test) {
  179. return {
  180. timeout: test.timeout,
  181. annotations: test.annotations,
  182. expectedStatus: test.expectedStatus,
  183. projectId,
  184. projectName,
  185. results: test.results.map((r) => this._serializeTestResult(r, test)),
  186. status: test.outcome()
  187. };
  188. }
  189. _serializeTestResult(result, test) {
  190. const steps = result.steps.filter((s) => s.category === "test.step");
  191. const jsonResult = {
  192. workerIndex: result.workerIndex,
  193. parallelIndex: result.parallelIndex,
  194. status: result.status,
  195. duration: result.duration,
  196. error: result.error,
  197. errors: result.errors.map((e) => this._serializeError(e)),
  198. stdout: result.stdout.map((s) => stdioEntry(s)),
  199. stderr: result.stderr.map((s) => stdioEntry(s)),
  200. retry: result.retry,
  201. steps: steps.length ? steps.map((s) => this._serializeTestStep(s)) : void 0,
  202. startTime: result.startTime.toISOString(),
  203. annotations: result.annotations,
  204. attachments: result.attachments.map((a) => ({
  205. name: a.name,
  206. contentType: a.contentType,
  207. path: a.path,
  208. body: a.body?.toString("base64")
  209. }))
  210. };
  211. if (result.error?.stack)
  212. jsonResult.errorLocation = (0, import_base.prepareErrorStack)(result.error.stack).location;
  213. return jsonResult;
  214. }
  215. _serializeError(error) {
  216. return (0, import_base.formatError)(import_base.nonTerminalScreen, error);
  217. }
  218. _serializeTestStep(step) {
  219. const steps = step.steps.filter((s) => s.category === "test.step");
  220. return {
  221. title: step.title,
  222. duration: step.duration,
  223. error: step.error,
  224. steps: steps.length ? steps.map((s) => this._serializeTestStep(s)) : void 0
  225. };
  226. }
  227. }
  228. async function outputReport(report, resolvedOutputFile) {
  229. const reportString = JSON.stringify(report, void 0, 2);
  230. if (resolvedOutputFile) {
  231. await import_fs.default.promises.mkdir(import_path.default.dirname(resolvedOutputFile), { recursive: true });
  232. await import_fs.default.promises.writeFile(resolvedOutputFile, reportString);
  233. } else {
  234. console.log(reportString);
  235. }
  236. }
  237. function stdioEntry(s) {
  238. if (typeof s === "string")
  239. return { text: s };
  240. return { buffer: s.toString("base64") };
  241. }
  242. function removePrivateFields(config) {
  243. return Object.fromEntries(Object.entries(config).filter(([name, value]) => !name.startsWith("_")));
  244. }
  245. function serializePatterns(patterns) {
  246. if (!Array.isArray(patterns))
  247. patterns = [patterns];
  248. return patterns.map((s) => s.toString());
  249. }
  250. var json_default = JSONReporter;
  251. // Annotate the CommonJS export names for ESM import in node:
  252. 0 && (module.exports = {
  253. serializePatterns
  254. });