ortoni-report 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,147 +39,178 @@ function formatDate(date) {
39
39
  const time = date.toLocaleTimeString();
40
40
  return `${day}-${month}-${year} ${time}`;
41
41
  }
42
+ function safeStringify(obj, indent = 2) {
43
+ const cache = /* @__PURE__ */ new Set();
44
+ const json = JSON.stringify(obj, (key, value) => {
45
+ if (typeof value === "object" && value !== null) {
46
+ if (cache.has(value)) {
47
+ return;
48
+ }
49
+ cache.add(value);
50
+ }
51
+ return value;
52
+ }, indent);
53
+ cache.clear();
54
+ return json;
55
+ }
42
56
 
43
57
  // src/ortoni-report.ts
44
58
  var OrtoniReport = class {
45
59
  constructor(config = {}) {
60
+ this.projectRoot = "";
46
61
  this.results = [];
47
62
  this.projectSet = /* @__PURE__ */ new Set();
63
+ this.tagsSet = /* @__PURE__ */ new Set();
48
64
  this.config = config;
49
65
  }
50
66
  onBegin(config, suite) {
51
67
  this.results = [];
68
+ this.projectRoot = config.rootDir;
52
69
  }
53
70
  onTestBegin(test, result) {
54
71
  }
55
72
  onTestEnd(test, result) {
56
- let status = result.status;
57
- if (test.outcome() === "flaky") {
58
- status = "flaky";
59
- }
60
- this.projectSet.add(test.titlePath()[1]);
61
- const testResult = {
62
- retry: result.retry > 0 ? "retry" : "",
63
- isRetry: result.retry,
64
- projectName: test.titlePath()[1],
65
- suite: test.titlePath()[3],
66
- title: test.title,
67
- status,
68
- flaky: test.outcome(),
69
- duration: msToTime(result.duration),
70
- errors: result.errors.map((e) => colors.strip(e.message || e.toString())),
71
- steps: result.steps.map((step) => ({
72
- titlePath: step.titlePath,
73
- category: step.category,
74
- duration: step.duration,
75
- error: step.error,
76
- location: step.location,
77
- parent: step.parent,
78
- startTime: step.startTime,
79
- steps: step.steps,
80
- title: step.title
81
- })),
82
- logs: colors.strip(result.stdout.concat(result.stderr).map((log) => log).join("\n")),
83
- screenshotPath: null,
84
- filePath: normalizeFilePath(test.titlePath()[2]),
85
- projects: this.projectSet
86
- };
87
- if (result.attachments) {
88
- const screenshot = result.attachments.find((attachment) => attachment.name === "screenshot");
89
- if (screenshot && screenshot.path) {
90
- const screenshotContent = fs.readFileSync(screenshot.path, "base64");
91
- testResult.screenshotPath = screenshotContent;
73
+ try {
74
+ let status = result.status;
75
+ if (test.outcome() === "flaky") {
76
+ status = "flaky";
77
+ }
78
+ const projectName = test.titlePath()[1];
79
+ this.projectSet.add(projectName);
80
+ const location = test.location;
81
+ const filePath = normalizeFilePath(test.titlePath()[2]);
82
+ const tagPattern = /@[\w]+/g;
83
+ const testTags = test.title.match(tagPattern) || [];
84
+ const title = test.title.replace(tagPattern, "").trim();
85
+ const suiteTags = test.titlePath()[3].match(tagPattern) || [];
86
+ const suite = test.titlePath()[3].replace(tagPattern, "").trim();
87
+ const testResult = {
88
+ suiteTags,
89
+ testTags,
90
+ location: `${filePath}:${location.line}:${location.column}`,
91
+ retry: result.retry > 0 ? "retry" : "",
92
+ isRetry: result.retry,
93
+ projectName,
94
+ suite,
95
+ title,
96
+ status,
97
+ flaky: test.outcome(),
98
+ duration: msToTime(result.duration),
99
+ errors: result.errors.map((e) => colors.strip(e.message || e.toString())),
100
+ steps: result.steps.map((step) => {
101
+ const location2 = step.location ? `${path2.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
102
+ return {
103
+ snippet: colors.strip(step.error?.snippet || ""),
104
+ title: step.title,
105
+ location: step.error ? location2 : ""
106
+ };
107
+ }),
108
+ logs: colors.strip(result.stdout.concat(result.stderr).map((log) => log).join("\n")),
109
+ filePath,
110
+ projects: this.projectSet,
111
+ base64Image: this.config.base64Image
112
+ };
113
+ if (result.attachments) {
114
+ const screenshot = result.attachments.find((attachment) => attachment.name === "screenshot");
115
+ if (this.config.base64Image) {
116
+ if (screenshot && screenshot.path) {
117
+ try {
118
+ const screenshotContent = fs.readFileSync(screenshot.path, "base64");
119
+ testResult.screenshotPath = `data:image/png;base64,${screenshotContent}`;
120
+ } catch (error) {
121
+ console.error(`OrtoniReport: Failed to read screenshot file: ${screenshot.path}`, error);
122
+ }
123
+ }
124
+ } else {
125
+ if (screenshot && screenshot.path) {
126
+ testResult.screenshotPath = path2.resolve(screenshot.path);
127
+ }
128
+ }
129
+ const tracePath = result.attachments.find((attachment) => attachment.name === "trace");
130
+ if (tracePath?.path) {
131
+ testResult.tracePath = path2.resolve(__dirname, tracePath.path);
132
+ }
133
+ const videoPath = result.attachments.find((attachment) => attachment.name === "video");
134
+ if (videoPath?.path) {
135
+ testResult.videoPath = path2.resolve(__dirname, videoPath.path);
136
+ }
92
137
  }
138
+ this.results.push(testResult);
139
+ } catch (error) {
140
+ console.error("OrtoniReport: Error processing test end:", error);
93
141
  }
94
- this.results.push(testResult);
95
142
  }
96
143
  onEnd(result) {
97
- const filteredResults = this.results.filter((r) => r.status !== "skipped" && !r.isRetry);
98
- const totalDuration = msToTime(result.duration);
99
- this.groupedResults = this.results.reduce((acc, result2, index) => {
100
- const filePath = result2.filePath;
101
- const suiteName = result2.suite;
102
- const projectName = result2.projectName;
103
- if (!acc[filePath]) {
104
- acc[filePath] = {};
105
- }
106
- if (!acc[filePath][suiteName]) {
107
- acc[filePath][suiteName] = {};
108
- }
109
- if (!acc[filePath][suiteName][projectName]) {
110
- acc[filePath][suiteName][projectName] = [];
111
- }
112
- acc[filePath][suiteName][projectName].push({ ...result2, index });
113
- return acc;
114
- }, {});
115
- Handlebars.registerHelper("json", function(context) {
116
- return safeStringify(context);
117
- });
118
- Handlebars.registerHelper("eq", function(actualStatus, expectedStatus) {
119
- return actualStatus === expectedStatus;
120
- });
121
- Handlebars.registerHelper("or", () => {
122
- var args = Array.prototype.slice.call(arguments);
123
- var options = args.pop();
124
- for (var i = 0; i < args.length; i++) {
125
- if (args[i]) {
126
- return options.fn(this);
144
+ try {
145
+ const filteredResults = this.results.filter((r) => r.status !== "skipped" && !r.isRetry);
146
+ const totalDuration = msToTime(result.duration);
147
+ this.groupedResults = this.results.reduce((acc, result2, index) => {
148
+ const filePath = result2.filePath;
149
+ const suiteName = result2.suite;
150
+ const projectName = result2.projectName;
151
+ if (!acc[filePath]) {
152
+ acc[filePath] = {};
127
153
  }
128
- }
129
- return options.inverse(this);
130
- });
131
- Handlebars.registerHelper("gt", function(a, b) {
132
- return a > b;
133
- });
134
- const html = this.generateHTML(filteredResults, totalDuration);
135
- const outputPath = path2.resolve(process.cwd(), "ortoni-report.html");
136
- fs.writeFileSync(outputPath, html);
137
- console.log(`Ortoni HTML report generated at ${outputPath}`);
154
+ if (!acc[filePath][suiteName]) {
155
+ acc[filePath][suiteName] = {};
156
+ }
157
+ if (!acc[filePath][suiteName][projectName]) {
158
+ acc[filePath][suiteName][projectName] = [];
159
+ }
160
+ acc[filePath][suiteName][projectName].push({ ...result2, index });
161
+ return acc;
162
+ }, {});
163
+ Handlebars.registerHelper("json", function(context) {
164
+ return safeStringify(context);
165
+ });
166
+ Handlebars.registerHelper("eq", function(actualStatus, expectedStatus) {
167
+ return actualStatus === expectedStatus;
168
+ });
169
+ const html = this.generateHTML(filteredResults, totalDuration);
170
+ const outputPath = path2.resolve(process.cwd(), "ortoni-report.html");
171
+ fs.writeFileSync(outputPath, html);
172
+ console.log(`Ortoni HTML report generated at ${outputPath}`);
173
+ } catch (error) {
174
+ console.error("OrtoniReport: Error generating report:", error);
175
+ }
138
176
  }
139
177
  generateHTML(filteredResults, totalDuration) {
140
- const totalTests = filteredResults.length;
141
- const passedTests = this.results.filter((r) => r.status === "passed").length;
142
- const flakyTests = this.results.filter((r) => r.flaky === "flaky").length;
143
- const failed = filteredResults.filter((r) => r.status === "failed" || r.status === "timedOut").length;
144
- const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
145
- const templateSource = fs.readFileSync(path2.resolve(__dirname, "report-template.hbs"), "utf-8");
146
- const template = Handlebars.compile(templateSource);
147
- const data = {
148
- totalDuration,
149
- suiteName: this.suiteName,
150
- results: this.results,
151
- retryCount: this.results.filter((r) => r.isRetry).length,
152
- passCount: passedTests,
153
- failCount: failed,
154
- skipCount: this.results.filter((r) => r.status === "skipped").length,
155
- flakyCount: flakyTests,
156
- totalCount: filteredResults.length,
157
- groupedResults: this.groupedResults,
158
- projectName: this.config.projectName,
159
- authorName: this.config.authorName,
160
- testType: this.config.testType,
161
- preferredTheme: this.config.preferredTheme,
162
- successRate,
163
- lastRunDate: formatDate(/* @__PURE__ */ new Date()),
164
- projects: this.projectSet
165
- };
166
- return template(data);
178
+ try {
179
+ const totalTests = filteredResults.length;
180
+ const passedTests = this.results.filter((r) => r.status === "passed").length;
181
+ const flakyTests = this.results.filter((r) => r.flaky === "flaky").length;
182
+ const failed = filteredResults.filter((r) => r.status === "failed" || r.status === "timedOut").length;
183
+ const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
184
+ const templateSource = fs.readFileSync(path2.resolve(__dirname, "report-template.hbs"), "utf-8");
185
+ const template = Handlebars.compile(templateSource);
186
+ const logo = this.config.logo;
187
+ const data = {
188
+ logo: logo ? path2.resolve(logo) : void 0,
189
+ totalDuration,
190
+ suiteName: this.suiteName,
191
+ results: this.results,
192
+ retryCount: this.results.filter((r) => r.isRetry).length,
193
+ passCount: passedTests,
194
+ failCount: failed,
195
+ skipCount: this.results.filter((r) => r.status === "skipped").length,
196
+ flakyCount: flakyTests,
197
+ totalCount: filteredResults.length,
198
+ groupedResults: this.groupedResults,
199
+ projectName: this.config.projectName,
200
+ authorName: this.config.authorName,
201
+ testType: this.config.testType,
202
+ preferredTheme: this.config.preferredTheme,
203
+ successRate,
204
+ lastRunDate: formatDate(/* @__PURE__ */ new Date()),
205
+ projects: this.projectSet
206
+ };
207
+ return template(data);
208
+ } catch (error) {
209
+ console.error("OrtoniReport: Error generating HTML:", error);
210
+ return `<html><body><h1>Report generation failed</h1><pre>${error.stack}</pre></body></html>`;
211
+ }
167
212
  }
168
213
  };
169
- function safeStringify(obj, indent = 2) {
170
- const cache = /* @__PURE__ */ new Set();
171
- const json = JSON.stringify(obj, (key, value) => {
172
- if (typeof value === "object" && value !== null) {
173
- if (cache.has(value)) {
174
- return;
175
- }
176
- cache.add(value);
177
- }
178
- return value;
179
- }, indent);
180
- cache.clear();
181
- return json;
182
- }
183
214
  export {
184
215
  OrtoniReport as default
185
216
  };