@testomatio/reporter 1.2.2-beta-html-pagination-feature → 1.2.2-beta.show-error.1

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.
package/lib/pipe/html.js CHANGED
@@ -91,7 +91,7 @@ class HtmlPipe {
91
91
  // GENERATE HTML reports based on the results data
92
92
  this.buildReport({
93
93
  runParams,
94
- // TODO: this.tests=[] in case of Mocha, need retest by Vitalii
94
+ // TODO: this.tests=[] in case of Mocha
95
95
  tests: this.tests,
96
96
  outputPath: this.htmlOutputPath,
97
97
  templatePath: this.templateHtmlPath,
@@ -198,10 +198,8 @@ class HtmlPipe {
198
198
  const template = handlebars.compile(templateSource);
199
199
 
200
200
  return template(data);
201
- }
202
- catch (e) {
203
- console.log(chalk.red("❌ Oops! An unknown error occurred when generating an HTML report"));
204
- console.log(chalk.red(e));
201
+ } catch (e) {
202
+ console.log('Unknown HTML report generation error: ', e);
205
203
  }
206
204
  }
207
205
 
@@ -211,84 +209,43 @@ class HtmlPipe {
211
209
  (tests, status) => tests.filter(test => test.status.toLowerCase() === status.toLowerCase()).length,
212
210
  );
213
211
 
214
- handlebars.registerHelper(
215
- 'selectComponent',
216
- () => new handlebars.SafeString(
217
- `<select style="width: 70px;height: 38px;" class="form-select" aria-label="Tests counter on page">
218
- <option value="0">10</option>
219
- <option value="1">25</option>
220
- <option value="2">50</option>
221
- </select>`
222
- )
223
- );
224
-
225
- handlebars.registerHelper(
226
- 'emptyDataComponent',
227
- () => new handlebars.SafeString(
228
- `<div class="noData m-0">
229
- NO MATCHING TESTS
230
- </div>`
231
- )
232
- );
233
-
234
- handlebars.registerHelper(
235
- 'pageDispleyElements',
236
- tests => {
237
- // We wrapp the lines to the HTML format we need
238
- const totalTests =
239
- JSON.parse(
240
- JSON.stringify(tests)
241
- .replace(/<script>/g, '&lt;script&gt;')
242
- .replace(/<\/script>/g, '&lt;\/script&gt;') // eslint-disable-line
243
- );
244
-
245
- const paginationOptions = {
246
- '0': 10,
247
- '1': 25,
248
- '2': 50
249
- };
250
- const statuses = [
251
- 'all',
252
- 'passed',
253
- 'failed',
254
- 'skipped'
255
- ];
256
- const pageItemGroups = {
257
- 'all': {},
258
- 'passed': {},
259
- 'failed': {},
260
- 'skipped': {},
261
- 'totalTests': totalTests
262
- };
263
-
264
- function paginateItems(items, pageSize) {
265
- const paginatedItems = [];
266
- for (let i = 0; i < items.length; i += pageSize) {
267
- paginatedItems.push(items.slice(i, i + pageSize));
268
- }
269
- return paginatedItems;
270
- }
271
-
272
- statuses.forEach(status => {
273
- for (const option in paginationOptions) {
274
- if (Object.hasOwnProperty.call(paginationOptions, option)) {
275
- const pageSize = paginationOptions[option];
276
- let filteredItems = totalTests;
277
-
278
- if (status !== 'all') {
279
- filteredItems = totalTests.filter(item => item.status === status);
212
+ handlebars.registerHelper('json', tests => {
213
+ // TODO: please refactor the code below, add meaningful variable names, separate into functions
214
+ function replaceScriptTagsInArray(array) {
215
+ return array.map(obj => {
216
+ const keysToCheck = ['steps', 'stack', 'title', 'suite_title', 'message', 'code'];
217
+ const newObj = {};
218
+
219
+ for (const key in obj) {
220
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
221
+ if (key === 'example') {
222
+ newObj[key] = {};
223
+ for (const subKey in obj[key]) {
224
+ if (Object.prototype.hasOwnProperty.call(obj[key], subKey)) {
225
+ newObj[key][subKey] =
226
+ typeof obj[key][subKey] === 'string'
227
+ ? obj[key][subKey].replace(/<script>/g, '<$cript>').replace(/<\/script>/g, '</$cript>')
228
+ : obj[key][subKey];
229
+ }
280
230
  }
281
-
282
- pageItemGroups[status][option] = paginateItems(filteredItems, pageSize);
231
+ } else if (keysToCheck.includes(key)) {
232
+ newObj[key] =
233
+ typeof obj[key] === 'string'
234
+ ? obj[key].replace(/<script>/g, '<$cript>').replace(/<\/script>/g, '</$cript>')
235
+ : obj[key];
236
+ } else {
237
+ newObj[key] = obj[key];
283
238
  }
239
+ }
284
240
  }
285
- });
286
-
287
- pageItemGroups.totalTests = totalTests;
288
241
 
289
- return JSON.stringify(pageItemGroups);
242
+ return newObj;
243
+ });
290
244
  }
291
- );
245
+
246
+ // Remove ANSI escape codes
247
+ return JSON.stringify(replaceScriptTagsInArray(tests));
248
+ });
292
249
  }
293
250
 
294
251
  toString() {
@@ -190,6 +190,7 @@ class TestomatioPipe {
190
190
  'Error creating Testomat.io report, please check if your API key is valid. Skipping report | ',
191
191
  err?.response?.statusText || err?.status || err.message,
192
192
  );
193
+ printCreateIssue(err);
193
194
  }
194
195
  debug('"createRun" function finished');
195
196
  }
@@ -231,8 +232,11 @@ class TestomatioPipe {
231
232
  chalk.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
232
233
  `Report couldn't be processed: ${err?.response?.data?.message}`,
233
234
  );
235
+ printCreateIssue(err);
234
236
  } else {
235
237
  console.log(APP_PREFIX, chalk.blue(data?.title || ''), "Report couldn't be processed", err);
238
+
239
+
236
240
  }
237
241
  });
238
242
  }
@@ -300,6 +304,7 @@ class TestomatioPipe {
300
304
  }
301
305
  } catch (err) {
302
306
  console.log(APP_PREFIX, 'Error updating status, skipping...', err);
307
+ printCreateIssue(err);
303
308
  }
304
309
  debug('Run finished');
305
310
  }
@@ -309,4 +314,24 @@ class TestomatioPipe {
309
314
  }
310
315
  }
311
316
 
317
+ let registeredErrorHints = false;
318
+ function printCreateIssue(err) {
319
+ if (registeredErrorHints) return;
320
+ registeredErrorHints = true;
321
+ process.on('beforeExit', () => {
322
+ console.log(APP_PREFIX, 'There was error reporting to Testomat.io:');
323
+ console.log(APP_PREFIX, 'If you think this is a bug please create an issue: https://github.com/testomatio/app/issues/new');
324
+ console.log(APP_PREFIX, 'Provide this information:');
325
+ console.log(APP_PREFIX, 'Error:', err.message || err.code);
326
+ if (!err.config) return;
327
+
328
+ const time = new Date().toUTCString();
329
+ const { data, url, baseURL, method } = err?.config || {}
330
+ console.log('```js')
331
+ console.log({ data, url, baseURL, method, time });
332
+ console.log('```')
333
+ console.log();
334
+ });
335
+ }
336
+
312
337
  module.exports = TestomatioPipe;
@@ -0,0 +1,249 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&display=swap" rel="stylesheet">
7
+ <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/accordionjs@2.1.2/accordion.min.css">
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
10
+ <title>Report {{runId}} - Testomat.io</title>
11
+ <link rel="stylesheet" href="./style.css">
12
+ {{!-- TODO: styling create as a new additional file instead of template => copy default one --}}
13
+ <style>
14
+ body {
15
+ font-family: "Fira Sans", sans-serif;
16
+ }
17
+ </style>
18
+ </head>
19
+
20
+ <body class="bg-gray-50 pt-6 px-4 lg:pt-4 lg:px-40 h-full;">
21
+ <div id="testomatio_report">
22
+ <div class="flex items-center flex-col space-y-4 md:flex-row md:justify-between md:h-14 mb-6">
23
+ <div class="flex flex-col md:flex-row space-x-3 items-center">
24
+ <h2 class="text-gray-900 leading-none text-3xl font-semibold mb-0">{{runId}}
25
+ </h2>
26
+ {{!-- <span class="text-sm text-gray-500">
27
+ {{executionDate}}
28
+ </span> --}}
29
+ </div>
30
+ <div class="flex space-x-3 items-center">
31
+ <a class="inline-flex items-center border border-transparent leading-none text-white bg-indigo-500 hover:bg-indigo-700 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-pointer px-4 py-2 text-base leading-none"
32
+ style="min-height: 38px"
33
+ href="{{runUrl}}"
34
+ target="_blank">
35
+ Full Report
36
+ </a>
37
+ </div>
38
+ </div>
39
+ <div class="bg-white border overflow-hidden rounded-lg divide-y divide-gray-200 mb-12">
40
+ <div class="flex flex-col lg:divide-x divide-gray-200 sm:flex-col md:flex-col lg:flex-row">
41
+ <div class="w-full lg:w-1/2">
42
+ <!-- Left chart section -->
43
+ <div class="p-4">
44
+ <h2 class="text-xl font-semibold mb-4">Testomatio Test Results</h2>
45
+ <span class="text-sm text-gray-500 items-center">{{executionDate}}</span>
46
+ <div class="hart-container" style="height: 200px;">
47
+ <canvas id="testomatio_chart"></canvas>
48
+ <div for="testomatio_chart"></div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <div class="w-full lg:w-1/2">
53
+ <!-- Right chart section -->
54
+ <div class="p-4">
55
+ <h2 class="text-xl font-semibold mb-4">Testomatio Run Info</h2>
56
+ <dl>
57
+ <div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
58
+ <dt class="text-sm font-medium text-gray-500">
59
+ RUN Status
60
+ </dt>
61
+ <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
62
+ <span class="inline-flex items-center justify-center px-2 py-0.5 text-xs font-bold leading-none bg-green-200 text-green-600 rounded-full">
63
+ {{status}}
64
+ </span>
65
+ </dd>
66
+ </div>
67
+ <div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
68
+ <dt class="text-sm font-medium text-gray-500">
69
+ Duration
70
+ </dt>
71
+ <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
72
+ <span>
73
+ <i class="fa fa-clock-o"></i>
74
+ </span>
75
+ {{executionTime}}
76
+ </dd>
77
+ </div>
78
+ </div>
79
+ </dl>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <div class="px-4 py-5 lg:px-20 lg:py-5">
86
+ <!-- TODO: maybe add a search section by test NAME!! -->
87
+ <div class="px-4 py-5 lg:px-20 lg:py-5 border rounded-md mb-4">
88
+ <div class="mb-2">
89
+ <h3 class="text-2xl leading-10 font-medium text-gray-900">
90
+ Search and Summary
91
+ </h3>
92
+ </div>
93
+ <div class="flex items-center space-x-4">
94
+ <div class="relative">
95
+ <input type="text" id="search-input" placeholder="Search by test title..." class="border rounded-md py-1 px-2 w-48 focus:outline-none focus:ring focus:border-blue-300">
96
+
97
+ </div>
98
+ <div class="test-status">
99
+ <div class="flex items-center">
100
+ <span class="font-medium mr-2">All:</span>
101
+ <span class="text-blue-600 font-semibold">{{tests.length}}</span>
102
+ <span class="font-medium ml-4 mr-2">Passed:</span>
103
+ <span class="text-green-600 font-semibold">{{getTestsByStatus tests "PASSED"}}</span>
104
+ <span class="font-medium ml-4 mr-2">Failed:</span>
105
+ <span class="text-red-600 font-semibold">{{getTestsByStatus tests "FAILED"}}</span>
106
+ <span class="font-medium ml-4 mr-2">Skipped:</span>
107
+ <span class="text-yellow-600 font-semibold">{{getTestsByStatus tests "SKIPPED"}}</span>
108
+ </div>
109
+ </div>
110
+
111
+ </div>
112
+ </div>
113
+
114
+ <ul class="accordionjs bg-gray-50 rounded-md divide-y divide-gray-200 text-gray-900">
115
+ <li class="pl-3 pr-4 py-3 text-sm cursor-pointer">
116
+ <h4 class="text-lg flex items-center space-x-1">
117
+ <span class="font-medium">Passed</span>
118
+ <span class="inline-flex items-center justify-center px-2 py-0.5 text-xs font-bold leading-none bg-green-200 text-green-600 rounded-full">
119
+ {{getTestsByStatus tests "PASSED"}}
120
+ </span>
121
+ </h4>
122
+ <ul role="list" class="pl-2 divide-y divide-gray-200">
123
+ {{#each tests}}
124
+ <ul role="list" class="pl-2 divide-y divide-gray-200">
125
+ {{#eq status "passed"}}
126
+ <li class="py-3 flex justify-between items-center">
127
+ <div>
128
+ <div class="success"></div>
129
+ <a class="testrun hover:underline" data-toggle="test-{{test_id}}" data-target="test-code-{{test_id}}">
130
+ {{title}} title
131
+ </a>
132
+ </div>
133
+ <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
134
+ <span>
135
+ <i class="fa fa-clock-o"></i>
136
+ </span>
137
+ {{#if run_time}}
138
+ {{run_time}}ms
139
+ {{/if}}
140
+ </dd>
141
+ </li>
142
+ <li class="py-3 test-code-container test-code" id="test-code-{{test_id}}">
143
+ <pre>
144
+ <code>
145
+ {{steps}}
146
+ </code>
147
+ </pre>
148
+ </li>
149
+ {{/eq}}
150
+ </ul>
151
+ {{/each}}
152
+ </ul>
153
+ </li>
154
+
155
+ <li class="pl-3 pr-4 py-3 text-sm cursor-pointer">
156
+ <h4 class="text-lg flex items-center space-x-1">
157
+ <span class="font-medium">Failed</span>
158
+ <span class="inline-flex items-center justify-center px-2 py-0.5 text-xs font-bold leading-none bg-red-200 text-red-500 rounded-full">
159
+ {{getTestsByStatus tests "FAILED"}}
160
+ </span>
161
+ </h4>
162
+
163
+ <ul role="list" class="pl-2 divide-y divide-gray-200">
164
+ {{#each tests}}
165
+ {{#eq status "failed"}}
166
+ <li class="py-3 flex justify-between items-center">
167
+ <div>
168
+ <div class="fail"></div>
169
+ <a class="testrun hover:underline" data-toggle="test-{{test_id}}" data-target="test-code-{{test_id}}">
170
+ {{title}} title
171
+ </a>
172
+ </div>
173
+ <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
174
+ <span>
175
+ <i class="fa fa-clock-o"></i>
176
+ </span>
177
+ {{#if run_time}}
178
+ {{run_time}}ms
179
+ {{/if}}
180
+ </dd>
181
+ </li>
182
+ <li class="py-3 test-code-container test-code" id="test-code-{{test_id}}">
183
+ <pre>
184
+ <code>
185
+ {{steps}}
186
+ </code>
187
+ </pre>
188
+ </li>
189
+ {{/eq}}
190
+ {{/each}}
191
+ </ul>
192
+ </li>
193
+ </ul>
194
+ </div>
195
+ </div>
196
+ <div
197
+ class="fixed bg-white border-t inset-x-0 bottom-0 mx-auto py-1 px-3 sm:px-6 lg:px-8 flex items-center justify-between flex-wrap">
198
+ <p class="ml-3 text-gray-500 text-xs lg:truncate">
199
+ This is a public report generated by <a class="hover:underline text-gray-700 font-semibold"
200
+ href="https://testomat.io" target="_blank">Testomat.io</a>. Everyone who has accesse to this link can
201
+ see this report.<br>
202
+ </p>
203
+ </div>
204
+
205
+ </body>
206
+ <!-- Jquery from a CDN -->
207
+ <script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.3.2/chart.min.js"></script>
208
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
209
+ <script src="https://cdn.jsdelivr.net/npm/accordionjs@2.1.2/accordion.min.js"></script>
210
+ <!-- Chart.js from a CDN -->
211
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
212
+
213
+ <!-- Include Handlebars from a CDN -->
214
+ <script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
215
+ <!-- Handlebars tamplate actions -->
216
+ <script>
217
+ $(document).ready(function ($) {
218
+ $(".accordionjs").accordionjs();
219
+
220
+ $('#copy-url').on('click', () => {
221
+ navigator.clipboard.writeText(window.location.href);
222
+ })
223
+ });
224
+ //Open code section handler
225
+ document.querySelectorAll('.test-code').forEach(element => {
226
+ element.style.display = 'none';
227
+ });
228
+ const testrunLinks = document.querySelectorAll('.testrun');
229
+
230
+ testrunLinks.forEach(link => {
231
+ link.addEventListener('click', function () {
232
+ // get "data-toggle" & "data-target" from link env
233
+ const toggleId = this.getAttribute('data-toggle');
234
+ const targetId = this.getAttribute('data-target');
235
+
236
+ // get "tests.code" elements and change view
237
+ const targetElement = document.querySelector(`#${targetId}`);
238
+
239
+ // SET "tests.code" viewing after each click
240
+ if (targetElement.style.display === 'none') {
241
+ targetElement.style.display = 'block';
242
+ } else {
243
+ targetElement.style.display = 'none';
244
+ }
245
+ });
246
+ });
247
+ </script>
248
+ <script type="text/javascript" src="./chart-my.js"></script>
249
+ </html>