@testomatio/reporter 1.2.2 → 1.2.3-beta-batch-upload
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/fileUploader.js +1 -7
- package/lib/pipe/html.js +45 -82
- package/lib/pipe/testomatio.js +49 -18
- package/lib/template/template-draft.hbs +249 -0
- package/lib/template/testomatio.hbs +337 -1185
- package/lib/utils/pipe_utils.js +0 -1
- package/lib/xmlReader.js +4 -0
- package/package.json +1 -1
package/lib/fileUploader.js
CHANGED
|
@@ -20,7 +20,6 @@ const keys = [
|
|
|
20
20
|
'S3_BUCKET',
|
|
21
21
|
'S3_ACCESS_KEY_ID',
|
|
22
22
|
'S3_SECRET_ACCESS_KEY',
|
|
23
|
-
'S3_SESSION_TOKEN',
|
|
24
23
|
'TESTOMATIO_DISABLE_ARTIFACTS',
|
|
25
24
|
'TESTOMATIO_PRIVATE_ARTIFACTS',
|
|
26
25
|
'S3_FORCE_PATH_STYLE',
|
|
@@ -75,8 +74,7 @@ const _getFileExtBase64 = str => {
|
|
|
75
74
|
};
|
|
76
75
|
|
|
77
76
|
const _getS3Config = () => {
|
|
78
|
-
const { S3_REGION,
|
|
79
|
-
getConfig();
|
|
77
|
+
const { S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, S3_ENDPOINT } = getConfig();
|
|
80
78
|
|
|
81
79
|
const cfg = {
|
|
82
80
|
region: S3_REGION,
|
|
@@ -87,10 +85,6 @@ const _getS3Config = () => {
|
|
|
87
85
|
},
|
|
88
86
|
};
|
|
89
87
|
|
|
90
|
-
if (S3_SESSION_TOKEN) {
|
|
91
|
-
cfg.credentials.sessionToken = S3_SESSION_TOKEN;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
88
|
if (S3_ENDPOINT) {
|
|
95
89
|
cfg.endpoint = S3_ENDPOINT;
|
|
96
90
|
}
|
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
|
|
94
|
+
// TODO: this.tests=[] in case of Mocha
|
|
95
95
|
tests: this.tests,
|
|
96
96
|
outputPath: this.htmlOutputPath,
|
|
97
97
|
templatePath: this.templateHtmlPath,
|
|
@@ -123,31 +123,30 @@ class HtmlPipe {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
tests.forEach(test => {
|
|
126
|
-
if (!test.message
|
|
126
|
+
if (!test.message || test.message.trim() === '') {
|
|
127
127
|
test.message = "This test has no 'message' code";
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
if (!test.suite_title
|
|
130
|
+
if (!test.suite_title || test.suite_title.trim() === '') {
|
|
131
131
|
test.suite_title = 'Unknown suite';
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
if (!test.title
|
|
134
|
+
if (!test.title || test.title.trim() === '') {
|
|
135
135
|
test.title = 'Unknown test title';
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
if (!test.files
|
|
138
|
+
if (!test.files || test.files.length === 0) {
|
|
139
139
|
test.files = 'This test has no files';
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
if (
|
|
143
|
-
test.steps
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
if (test.steps) {
|
|
143
|
+
if (!test.steps || test.steps.trim() === '') {
|
|
144
|
+
test.steps = "This test has no 'steps' code";
|
|
145
|
+
} else {
|
|
146
|
+
test.steps = removeAnsiColorCodes(test.steps);
|
|
147
|
+
}
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
// TODO: future-proof: currently there is no need to display Artifacts and Metadata in HTML
|
|
149
|
-
test.artifacts = test.artifacts || [];
|
|
150
|
-
test.meta = test.meta || {};
|
|
151
150
|
// TODO: u can added an additional test values to this checks in the future
|
|
152
151
|
});
|
|
153
152
|
|
|
@@ -200,8 +199,7 @@ class HtmlPipe {
|
|
|
200
199
|
|
|
201
200
|
return template(data);
|
|
202
201
|
} catch (e) {
|
|
203
|
-
console.log(
|
|
204
|
-
console.log(chalk.red(e));
|
|
202
|
+
console.log('Unknown HTML report generation error: ', e);
|
|
205
203
|
}
|
|
206
204
|
}
|
|
207
205
|
|
|
@@ -211,77 +209,42 @@ class HtmlPipe {
|
|
|
211
209
|
(tests, status) => tests.filter(test => test.status.toLowerCase() === status.toLowerCase()).length,
|
|
212
210
|
);
|
|
213
211
|
|
|
214
|
-
handlebars.registerHelper(
|
|
215
|
-
|
|
216
|
-
()
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.replace(/<\/script>/g, '</script>'), // eslint-disable-line
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
const paginationOptions = {
|
|
245
|
-
0: 10,
|
|
246
|
-
1: 25,
|
|
247
|
-
2: 50,
|
|
248
|
-
};
|
|
249
|
-
const statuses = ['all', 'passed', 'failed', 'skipped'];
|
|
250
|
-
const pageItemGroups = {
|
|
251
|
-
all: {},
|
|
252
|
-
passed: {},
|
|
253
|
-
failed: {},
|
|
254
|
-
skipped: {},
|
|
255
|
-
totalTests,
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
function paginateItems(items, pageSize) {
|
|
259
|
-
const paginatedItems = [];
|
|
260
|
-
for (let i = 0; i < items.length; i += pageSize) {
|
|
261
|
-
paginatedItems.push(items.slice(i, i + pageSize));
|
|
262
|
-
}
|
|
263
|
-
return paginatedItems;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
statuses.forEach(status => {
|
|
267
|
-
for (const option in paginationOptions) {
|
|
268
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
269
|
-
if (paginationOptions.hasOwnProperty(option)) {
|
|
270
|
-
const pageSize = paginationOptions[option];
|
|
271
|
-
let filteredItems = totalTests;
|
|
272
|
-
|
|
273
|
-
if (status !== 'all') {
|
|
274
|
-
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
|
+
}
|
|
230
|
+
}
|
|
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];
|
|
238
|
+
}
|
|
275
239
|
}
|
|
276
|
-
|
|
277
|
-
pageItemGroups[status][option] = paginateItems(filteredItems, pageSize);
|
|
278
240
|
}
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
241
|
|
|
282
|
-
|
|
242
|
+
return newObj;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
283
245
|
|
|
284
|
-
|
|
246
|
+
// Remove ANSI escape codes
|
|
247
|
+
return JSON.stringify(replaceScriptTagsInArray(tests));
|
|
285
248
|
});
|
|
286
249
|
}
|
|
287
250
|
|
|
@@ -298,7 +261,7 @@ class HtmlPipe {
|
|
|
298
261
|
*/
|
|
299
262
|
function testExecutionSumTime(tests) {
|
|
300
263
|
const totalMilliseconds = tests.reduce((sum, test) => {
|
|
301
|
-
if (typeof test.run_time === 'number'
|
|
264
|
+
if (typeof test.run_time === 'number') {
|
|
302
265
|
return sum + test.run_time;
|
|
303
266
|
}
|
|
304
267
|
return sum;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -4,7 +4,6 @@ const chalk = require('chalk');
|
|
|
4
4
|
const axiosRetry = require('axios-retry');
|
|
5
5
|
// Default axios instance
|
|
6
6
|
const axios = require('axios');
|
|
7
|
-
const JsonCycle = require('json-cycle');
|
|
8
7
|
|
|
9
8
|
const { APP_PREFIX, STATUS, AXIOS_TIMEOUT, AXIOS_RETRY_TIMEOUT } = require('../constants');
|
|
10
9
|
const { isValidUrl, foundedTestLog } = require('../utils/utils');
|
|
@@ -22,6 +21,12 @@ if (process.env.TESTOMATIO_RUN) {
|
|
|
22
21
|
* @implements {Pipe}
|
|
23
22
|
*/
|
|
24
23
|
class TestomatioPipe {
|
|
24
|
+
batch = {
|
|
25
|
+
tests: [],
|
|
26
|
+
intervalFunction: null,
|
|
27
|
+
intervalTime: 5000, // how often tests are sent
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
constructor(params, store) {
|
|
26
31
|
this.isEnabled = false;
|
|
27
32
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
@@ -122,6 +127,7 @@ class TestomatioPipe {
|
|
|
122
127
|
async createRun() {
|
|
123
128
|
debug('Creating run...');
|
|
124
129
|
if (!this.isEnabled) return;
|
|
130
|
+
this.batch.intervalFunction = setInterval(this.#batchUpload, this.batch.intervalTime);
|
|
125
131
|
|
|
126
132
|
let buildUrl = process.env.BUILD_URL || process.env.CI_JOB_URL || process.env.CIRCLE_BUILD_URL;
|
|
127
133
|
|
|
@@ -194,24 +200,29 @@ class TestomatioPipe {
|
|
|
194
200
|
debug('"createRun" function finished');
|
|
195
201
|
}
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const json = JsonCycle.stringify(data);
|
|
203
|
+
/**
|
|
204
|
+
* Uploads tests as a batch (multiple tests at once). Intended to be used with a setInterval
|
|
205
|
+
*/
|
|
206
|
+
#batchUpload = async () => {
|
|
207
|
+
if (!this.batch.tests.length) return;
|
|
203
208
|
|
|
204
|
-
|
|
209
|
+
// get tests from batch and clear batch
|
|
210
|
+
const testsToSend = this.batch.tests.splice(0);
|
|
211
|
+
debug('📨 Batch upload', testsToSend.length, 'tests');
|
|
205
212
|
|
|
206
213
|
return this.axios
|
|
207
|
-
.post(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
.post(
|
|
215
|
+
`/api/reporter/${this.runId}/testrun`,
|
|
216
|
+
{ api_key: this.apiKey, tests: testsToSend },
|
|
217
|
+
{
|
|
218
|
+
maxContentLength: Infinity,
|
|
219
|
+
maxBodyLength: Infinity,
|
|
220
|
+
headers: {
|
|
221
|
+
// Overwrite Axios's automatically set Content-Type
|
|
222
|
+
'Content-Type': 'application/json',
|
|
223
|
+
},
|
|
213
224
|
},
|
|
214
|
-
|
|
225
|
+
)
|
|
215
226
|
.catch(err => {
|
|
216
227
|
if (err.response) {
|
|
217
228
|
if (err.response.status >= 400) {
|
|
@@ -219,7 +230,7 @@ class TestomatioPipe {
|
|
|
219
230
|
console.log(
|
|
220
231
|
APP_PREFIX,
|
|
221
232
|
chalk.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
222
|
-
chalk.grey(data?.title || ''),
|
|
233
|
+
// chalk.grey(data?.title || ''),
|
|
223
234
|
);
|
|
224
235
|
if (err.response.data.message.includes('could not be matched')) {
|
|
225
236
|
this.hasUnmatchedTests = true;
|
|
@@ -228,13 +239,30 @@ class TestomatioPipe {
|
|
|
228
239
|
}
|
|
229
240
|
console.log(
|
|
230
241
|
APP_PREFIX,
|
|
231
|
-
chalk.yellow(`Warning:
|
|
242
|
+
chalk.yellow(`Warning: (${err.response?.status})`),
|
|
232
243
|
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
233
244
|
);
|
|
234
245
|
} else {
|
|
235
|
-
console.log(APP_PREFIX,
|
|
246
|
+
console.log(APP_PREFIX, "Report couldn't be processed", err);
|
|
236
247
|
}
|
|
237
248
|
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Adds a test to the batch uploader
|
|
253
|
+
*/
|
|
254
|
+
addTest(data) {
|
|
255
|
+
if (!this.isEnabled) return;
|
|
256
|
+
if (!this.runId) return;
|
|
257
|
+
data.api_key = this.apiKey;
|
|
258
|
+
data.create = this.createNewTests;
|
|
259
|
+
// const json = JsonCycle.stringify(data);
|
|
260
|
+
// debug('Adding test', JSON.stringify(data));
|
|
261
|
+
|
|
262
|
+
this.batch.tests.push(data);
|
|
263
|
+
|
|
264
|
+
// if test is added after run already finished
|
|
265
|
+
if (!this.batch.intervalFunction) this.#batchUpload();
|
|
238
266
|
}
|
|
239
267
|
|
|
240
268
|
/**
|
|
@@ -242,6 +270,9 @@ class TestomatioPipe {
|
|
|
242
270
|
* @returns
|
|
243
271
|
*/
|
|
244
272
|
async finishRun(params) {
|
|
273
|
+
this.#batchUpload();
|
|
274
|
+
if (this.batch.intervalFunction) clearInterval(this.batch.intervalFunction);
|
|
275
|
+
|
|
245
276
|
debug('Finishing run...');
|
|
246
277
|
if (!this.isEnabled) return;
|
|
247
278
|
|
|
@@ -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>
|