@testomatio/reporter 2.0.1-beta.3 → 2.0.1-beta.5-timestamp

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.
Files changed (148) hide show
  1. package/lib/adapter/codecept.js +335 -293
  2. package/lib/adapter/cucumber/current.js +203 -195
  3. package/lib/adapter/cucumber/legacy.js +155 -130
  4. package/lib/adapter/cucumber.js +16 -5
  5. package/lib/adapter/cypress-plugin/index.js +105 -91
  6. package/lib/adapter/jasmine.js +53 -54
  7. package/lib/adapter/jest.js +99 -97
  8. package/lib/adapter/mocha.js +141 -112
  9. package/lib/adapter/playwright.js +231 -199
  10. package/lib/adapter/vitest.js +149 -150
  11. package/lib/adapter/webdriver.js +121 -144
  12. package/lib/bin/cli.js +211 -229
  13. package/lib/bin/reportXml.js +52 -51
  14. package/lib/bin/startTest.js +95 -83
  15. package/lib/bin/uploadArtifacts.js +61 -56
  16. package/lib/client.js +465 -424
  17. package/lib/config.js +23 -18
  18. package/lib/constants.js +44 -50
  19. package/lib/data-storage.js +188 -216
  20. package/lib/junit-adapter/adapter.js +20 -17
  21. package/lib/junit-adapter/csharp.js +14 -28
  22. package/lib/junit-adapter/index.js +25 -27
  23. package/lib/junit-adapter/java.js +53 -41
  24. package/lib/junit-adapter/javascript.js +27 -30
  25. package/lib/junit-adapter/python.js +37 -38
  26. package/lib/junit-adapter/ruby.js +8 -11
  27. package/lib/output.js +52 -44
  28. package/lib/pipe/bitbucket.js +230 -223
  29. package/lib/pipe/csv.js +126 -113
  30. package/lib/pipe/debug.js +99 -118
  31. package/lib/pipe/github.js +213 -218
  32. package/lib/pipe/gitlab.js +206 -183
  33. package/lib/pipe/html.js +321 -258
  34. package/lib/pipe/index.js +66 -94
  35. package/lib/pipe/testomatio.js +474 -429
  36. package/lib/reporter-functions.js +26 -28
  37. package/lib/reporter.js +29 -34
  38. package/lib/services/artifacts.js +51 -55
  39. package/lib/services/index.js +12 -14
  40. package/lib/services/key-values.js +53 -56
  41. package/lib/services/logger.js +245 -226
  42. package/lib/template/testomatio.hbs +1366 -1026
  43. package/lib/uploader.js +364 -295
  44. package/lib/utils/pipe_utils.js +85 -89
  45. package/lib/utils/utils.js +307 -398
  46. package/lib/xmlReader.js +532 -525
  47. package/package.json +21 -64
  48. package/lib/adapter/codecept.d.ts +0 -2
  49. package/lib/adapter/cucumber/current.d.ts +0 -14
  50. package/lib/adapter/cucumber/legacy.d.ts +0 -0
  51. package/lib/adapter/cucumber.d.ts +0 -2
  52. package/lib/adapter/cypress-plugin/index.d.ts +0 -2
  53. package/lib/adapter/jasmine.d.ts +0 -11
  54. package/lib/adapter/jest.d.ts +0 -13
  55. package/lib/adapter/mocha.d.ts +0 -2
  56. package/lib/adapter/nightwatch.d.ts +0 -4
  57. package/lib/adapter/nightwatch.js +0 -80
  58. package/lib/adapter/playwright.d.ts +0 -14
  59. package/lib/adapter/vitest.d.ts +0 -35
  60. package/lib/adapter/webdriver.d.ts +0 -24
  61. package/lib/bin/cli.d.ts +0 -2
  62. package/lib/bin/reportXml.d.ts +0 -2
  63. package/lib/bin/startTest.d.ts +0 -2
  64. package/lib/bin/uploadArtifacts.d.ts +0 -2
  65. package/lib/client.d.ts +0 -76
  66. package/lib/config.d.ts +0 -1
  67. package/lib/constants.d.ts +0 -25
  68. package/lib/data-storage.d.ts +0 -34
  69. package/lib/junit-adapter/adapter.d.ts +0 -9
  70. package/lib/junit-adapter/csharp.d.ts +0 -5
  71. package/lib/junit-adapter/index.d.ts +0 -3
  72. package/lib/junit-adapter/java.d.ts +0 -5
  73. package/lib/junit-adapter/javascript.d.ts +0 -4
  74. package/lib/junit-adapter/python.d.ts +0 -5
  75. package/lib/junit-adapter/ruby.d.ts +0 -4
  76. package/lib/output.d.ts +0 -11
  77. package/lib/package.json +0 -3
  78. package/lib/pipe/bitbucket.d.ts +0 -25
  79. package/lib/pipe/csv.d.ts +0 -47
  80. package/lib/pipe/debug.d.ts +0 -29
  81. package/lib/pipe/github.d.ts +0 -30
  82. package/lib/pipe/gitlab.d.ts +0 -25
  83. package/lib/pipe/html.d.ts +0 -35
  84. package/lib/pipe/index.d.ts +0 -1
  85. package/lib/pipe/testomatio.d.ts +0 -71
  86. package/lib/replay.d.ts +0 -31
  87. package/lib/replay.js +0 -237
  88. package/lib/reporter-functions.d.ts +0 -34
  89. package/lib/reporter.d.ts +0 -232
  90. package/lib/services/artifacts.d.ts +0 -33
  91. package/lib/services/index.d.ts +0 -9
  92. package/lib/services/key-values.d.ts +0 -27
  93. package/lib/services/logger.d.ts +0 -64
  94. package/lib/uploader.d.ts +0 -60
  95. package/lib/utils/pipe_utils.d.ts +0 -41
  96. package/lib/utils/utils.d.ts +0 -54
  97. package/lib/xmlReader.d.ts +0 -92
  98. package/src/adapter/codecept.js +0 -373
  99. package/src/adapter/cucumber/current.js +0 -228
  100. package/src/adapter/cucumber/legacy.js +0 -158
  101. package/src/adapter/cucumber.js +0 -4
  102. package/src/adapter/cypress-plugin/index.js +0 -110
  103. package/src/adapter/jasmine.js +0 -60
  104. package/src/adapter/jest.js +0 -107
  105. package/src/adapter/mocha.cjs +0 -2
  106. package/src/adapter/mocha.js +0 -156
  107. package/src/adapter/nightwatch.js +0 -88
  108. package/src/adapter/playwright.js +0 -254
  109. package/src/adapter/vitest.js +0 -183
  110. package/src/adapter/webdriver.js +0 -142
  111. package/src/bin/cli.js +0 -348
  112. package/src/bin/reportXml.js +0 -77
  113. package/src/bin/startTest.js +0 -124
  114. package/src/bin/uploadArtifacts.js +0 -91
  115. package/src/client.js +0 -508
  116. package/src/config.js +0 -30
  117. package/src/constants.js +0 -53
  118. package/src/data-storage.js +0 -204
  119. package/src/junit-adapter/adapter.js +0 -23
  120. package/src/junit-adapter/csharp.js +0 -28
  121. package/src/junit-adapter/index.js +0 -28
  122. package/src/junit-adapter/java.js +0 -58
  123. package/src/junit-adapter/javascript.js +0 -31
  124. package/src/junit-adapter/python.js +0 -42
  125. package/src/junit-adapter/ruby.js +0 -10
  126. package/src/output.js +0 -57
  127. package/src/pipe/bitbucket.js +0 -252
  128. package/src/pipe/csv.js +0 -140
  129. package/src/pipe/debug.js +0 -119
  130. package/src/pipe/github.js +0 -232
  131. package/src/pipe/gitlab.js +0 -247
  132. package/src/pipe/html.js +0 -373
  133. package/src/pipe/index.js +0 -71
  134. package/src/pipe/testomatio.js +0 -504
  135. package/src/replay.js +0 -245
  136. package/src/reporter-functions.js +0 -55
  137. package/src/reporter.cjs_decprecated +0 -21
  138. package/src/reporter.js +0 -33
  139. package/src/services/artifacts.js +0 -59
  140. package/src/services/index.js +0 -13
  141. package/src/services/key-values.js +0 -59
  142. package/src/services/logger.js +0 -315
  143. package/src/template/emptyData.svg +0 -23
  144. package/src/template/testomatio.hbs +0 -1081
  145. package/src/uploader.js +0 -376
  146. package/src/utils/pipe_utils.js +0 -119
  147. package/src/utils/utils.js +0 -416
  148. package/src/xmlReader.js +0 -614
@@ -1,158 +0,0 @@
1
- // import { Formatter } from 'cucumber';
2
-
3
- // import pc from 'picocolors';
4
- // import { getTestomatIdFromTestTitle, fileSystem } from '../../utils/utils.js';
5
- // import { STATUS, TESTOMAT_TMP_STORAGE_DIR } from '../../constants.js';
6
- // import TestomatClient from '../../client.js';
7
- // import {config} from '../../config.js';
8
-
9
- // const createTestomatFormatter = apiKey => {
10
- // if (!apiKey || apiKey === '') {
11
- // console.log(pc.red('TESTOMATIO key is empty, ignoring reports'));
12
- // }
13
-
14
- // const documents = {};
15
- // const dataTableMap = {};
16
-
17
- // const addDocument = gherkinDocument => {
18
- // documents[gherkinDocument.uri] = gherkinDocument.document;
19
- // };
20
-
21
- // const getTitle = scenario => {
22
- // let { name } = scenario;
23
- // if (scenario.tags.length) {
24
- // let tags = '';
25
- // for (const tag of scenario.tags) {
26
- // tags = `${tags} ${tag.name}`;
27
- // }
28
- // name = `${name}${tags}`;
29
- // }
30
- // return name;
31
- // };
32
-
33
- // const getFeature = uri => documents[uri].feature;
34
-
35
- // const getScenario = location => {
36
- // const { children } = getFeature(location.uri);
37
- // for (const scenario of children) {
38
- // if (scenario.type === 'Scenario' && scenario.location.line === location.line) {
39
- // return scenario;
40
- // }
41
- // if (scenario.type === 'ScenarioOutline') {
42
- // for (const example of scenario.examples) {
43
- // for (const tableBody of example.tableBody) {
44
- // if (tableBody.location.line === location.line) {
45
- // return scenario;
46
- // }
47
- // }
48
- // }
49
- // }
50
- // }
51
-
52
- // return null;
53
- // };
54
-
55
- // const loadDataTable = (scenario, uri) => {
56
- // if (scenario.type === 'ScenarioOutline') {
57
- // for (const example of scenario.examples) {
58
- // for (const tableBody of example.tableBody) {
59
- // const dataMap = example.tableHeader.cells.reduce((acc, cell, index) => {
60
- // acc[cell.value] = tableBody.cells[index].value;
61
- // return acc;
62
- // }, {});
63
-
64
- // dataTableMap[`${uri}:${tableBody.location.line}`] = JSON.stringify(dataMap);
65
- // }
66
- // }
67
- // }
68
- // };
69
-
70
- // const getDataTableMap = (scenario, sourceLocation) => {
71
- // const key = `${sourceLocation.uri}:${sourceLocation.line}`;
72
- // if (!dataTableMap[key]) {
73
- // loadDataTable(scenario, sourceLocation.uri);
74
- // }
75
-
76
- // return dataTableMap[key] || '';
77
- // };
78
-
79
- // const getTestId = scenario => {
80
- // if (scenario) {
81
- // for (const tag of scenario.tags) {
82
- // const testId = getTestomatIdFromTestTitle(tag.name);
83
- // if (testId) return testId;
84
- // }
85
- // }
86
-
87
- // return null;
88
- // };
89
-
90
- // return class TestomatFormatter extends Formatter {
91
- // constructor(options) {
92
- // super(options);
93
-
94
- // if (!apiKey) return;
95
-
96
- // this.client = new TestomatClient({ apiKey });
97
- // this.status = STATUS.PASSED.toString();
98
-
99
- // options.eventBroadcaster.on('gherkin-document', addDocument);
100
- // options.eventBroadcaster.on('test-run-started', () => {
101
- // fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
102
- // this?.client?.createRun();
103
- // });
104
- // options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
105
- // options.eventBroadcaster.on('test-case-started', this.onTestCaseStarted.bind(this));
106
- // // @ts-ignore
107
- // options.eventBroadcaster.on('test-run-finished', () => this?.client?.updateRunStatus(this.status));
108
-
109
- // global.testomatioRunningEnvironment = 'cucumber:legacy';
110
- // }
111
-
112
- // onTestCaseStarted(event) {
113
- // const scenario = getScenario(event.sourceLocation);
114
- // const testId = getTestId(scenario);
115
-
116
- // if (!global.testomatioDataStore) global.testomatioDataStore = {};
117
- // global.testomatioDataStore.currentlyRunningTestId = testId;
118
- // }
119
-
120
- // onTestCaseFinished(event) {
121
- // const scenario = getScenario(event.sourceLocation);
122
- // const testId = getTestId(scenario);
123
- // const status = event.result.status === 'undefined' ? STATUS.SKIPPED : event.result.status;
124
-
125
- // let example = getDataTableMap(scenario, event.sourceLocation);
126
- // if (example) example = JSON.parse(example);
127
-
128
- // if (!scenario.name) return;
129
-
130
- // const message = '';
131
- // const cliMessage = `- ${scenario.name}: ${pc.bold(status.toUpperCase())}`;
132
-
133
- // // if (event.result.status === 'undefined') {
134
- // // cliMessage += pc.yellow(
135
- // // ' (undefined steps. Run Cucumber without this formatter and implement missing steps)',
136
- // // );
137
- // // message = 'Undefined steps. Implement missing steps and rerun this scenario';
138
- // // }
139
- // console.log(cliMessage);
140
- // if (status !== STATUS.PASSED && status !== STATUS.SKIPPED) {
141
- // this.status = STATUS.FAILED;
142
- // }
143
-
144
- // this.client?.addTestRun(status, {
145
- // error: event.result.exception instanceof Error ? event.result.exception : undefined,
146
- // message,
147
- // example,
148
- // test_id: testId,
149
- // title: getTitle(scenario),
150
- // suite_title: getTitle(getFeature(event.sourceLocation.uri)),
151
- // });
152
- // }
153
- // };
154
- // };
155
-
156
- // const CucumberLegacyReporter = createTestomatFormatter(config.TESTOMATIO);
157
- // export { CucumberLegacyReporter };
158
- // export default CucumberLegacyReporter;
@@ -1,4 +0,0 @@
1
- import { CucumberReporter } from './cucumber/current.js';
2
- // import { CucumberLegacyReporter } from './cucumber/legacy.js';
3
-
4
- export default CucumberReporter;
@@ -1,110 +0,0 @@
1
- import { STATUS } from '../../constants.js';
2
- import { getTestomatIdFromTestTitle, parseSuite } from '../../utils/utils.js';
3
- import TestomatClient from '../../client.js';
4
- import { config } from '../../config.js';
5
-
6
- const testomatioReporter = on => {
7
- if (!config.TESTOMATIO) {
8
- console.log('TESTOMATIO key is empty, ignoring reports');
9
- return;
10
- }
11
- const client = new TestomatClient({ apiKey: config.TESTOMATIO });
12
-
13
- on('before:run', async () => {
14
- // TODO: looks like client.env does not exist
15
- // if (!client.env) {
16
- // client.env = `${run.browser.displayName},${run.system.osName}`;
17
- // }
18
- await client.createRun();
19
- });
20
-
21
- on('after:spec', async (_spec, results) => {
22
- const addSpecTestsPromises = [];
23
-
24
- const videos = [results.video];
25
-
26
- for (const test of results.tests) {
27
- const lastAttemptIndex = test.attempts.length - 1;
28
- const latestAttempt = test.attempts[lastAttemptIndex];
29
-
30
- // latestAttempt.duration && latestAttempt.error were available in adapters version up to 13 JFYI
31
- const time = latestAttempt.duration || latestAttempt.wallClockDuration || test.duration;
32
- let error = latestAttempt.error;
33
-
34
- let title = test.title.pop();
35
- const examples = title.match(/\(example (#\d+)\)/);
36
- let example = null;
37
- if (examples && examples[1]) example = { example: examples[1] };
38
- title = title.replace(/\(example #\d+\)/, '').trim();
39
-
40
- const suiteTitle = test.title.pop();
41
-
42
- const testId = getTestomatIdFromTestTitle(title);
43
- const suiteId = parseSuite(suiteTitle);
44
-
45
- if (!error && test.displayError) {
46
- error = { message: test.displayError };
47
- error.inspect = function () {
48
- return this.message;
49
- };
50
- }
51
-
52
- const formattedError = error
53
- ? {
54
- message: error.message,
55
- name: error.name,
56
- inspect:
57
- error.inspect ||
58
- function () {
59
- return this.message;
60
- },
61
- }
62
- : undefined;
63
-
64
- const screenshots = Array.isArray(results.screenshots)
65
- ? results.screenshots
66
- .filter(screenshot => screenshot?.path && screenshot?.path.includes(title) && screenshot?.takenAt)
67
- .map(screenshot => screenshot.path)
68
- : [];
69
-
70
- const files = [...videos, ...screenshots];
71
-
72
- let state;
73
- switch (test.state) {
74
- case 'passed':
75
- state = STATUS.PASSED;
76
- break;
77
- case 'failed':
78
- state = STATUS.FAILED;
79
- break;
80
- case 'skipped':
81
- case 'pending':
82
- default:
83
- state = STATUS.SKIPPED;
84
- }
85
-
86
- addSpecTestsPromises.push(
87
- client.addTestRun(state, {
88
- title,
89
- time,
90
- example,
91
- error: formattedError,
92
- files,
93
- suite_title: suiteTitle,
94
- test_id: testId,
95
- suite_id: suiteId,
96
- }),
97
- );
98
- }
99
-
100
- await Promise.all(addSpecTestsPromises);
101
- });
102
-
103
- on('after:run', async results => {
104
- const status = results.totalFailed ? STATUS.FAILED : STATUS.PASSED;
105
- // @ts-ignore
106
- await client.updateRunStatus(status);
107
- });
108
- };
109
-
110
- export default testomatioReporter;
@@ -1,60 +0,0 @@
1
- import TestomatClient from '../client.js';
2
- import { getTestomatIdFromTestTitle, ansiRegExp } from '../utils/utils.js';
3
- import { STATUS } from '../constants.js';
4
-
5
- class JasmineReporter {
6
- constructor(options) {
7
- this.testTimeMap = {};
8
- this.client = new TestomatClient({ apiKey: options?.apiKey });
9
- this.client.createRun();
10
- }
11
-
12
- getDuration(test) {
13
- if (this.testTimeMap[test.id]) {
14
- return Date.now() - this.testTimeMap[test.id];
15
- }
16
-
17
- return 0;
18
- }
19
-
20
- specStarted(result) {
21
- this.testTimeMap[result.id] = Date.now();
22
- }
23
-
24
- specDone(result) {
25
- if (!this.client) return;
26
-
27
- const title = result.description;
28
- const { status } = result;
29
- let errorMessage = '';
30
-
31
- for (let i = 0; i < result.failedExpectations.length; i += 1) {
32
- errorMessage = `${errorMessage}Failure: ${result.failedExpectations[i].message}\n`;
33
- errorMessage = `${errorMessage}\n ${result.failedExpectations[i].stack}`;
34
- }
35
- console.log(`${title} : ${STATUS.PASSED}`);
36
- console.log(errorMessage);
37
- const testId = getTestomatIdFromTestTitle(title);
38
- errorMessage = errorMessage.replace(ansiRegExp(), '');
39
- this.client.addTestRun(status, {
40
- error: result.failedExpectations[0],
41
- message: errorMessage,
42
- test_id: testId,
43
- title,
44
- time: this.getDuration(result),
45
- });
46
- }
47
-
48
- jasmineDone(suiteInfo, done) {
49
- if (!this.client) return;
50
-
51
- const { overallStatus } = suiteInfo;
52
- const status = overallStatus === 'failed' ? STATUS.FAILED : STATUS.PASSED;
53
-
54
- // @ts-ignore
55
- this.client.updateRunStatus(status).then(() => done);
56
- }
57
- }
58
-
59
- export { JasmineReporter };
60
- export default JasmineReporter;
@@ -1,107 +0,0 @@
1
- import pc from 'picocolors';
2
- import TestomatClient from '../client.js';
3
- import { TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
4
- import { getTestomatIdFromTestTitle, ansiRegExp, fileSystem } from '../utils/utils.js';
5
- import { services } from '../services/index.js';
6
- import path from 'path';
7
- import createDebugMessages from 'debug';
8
-
9
- const debug = createDebugMessages('@testomatio/reporter:adapter-jest');
10
-
11
- export class JestReporter {
12
- constructor(globalConfig, options) {
13
- this._globalConfig = globalConfig;
14
- this._options = options;
15
-
16
- this.client = new TestomatClient({ apiKey: options?.apiKey });
17
- this.client.createRun();
18
- }
19
-
20
- onRunStart() {
21
- // clear tmp dir
22
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
23
- }
24
-
25
- // start of test file (including beforeAll)
26
- onTestStart(testFile) {
27
- debug('Start running test file:', testFile.path);
28
- services.setContext(testFile.path);
29
- }
30
-
31
- // start of the test (including beforeEach)
32
- onTestCaseStart(test, testCase) {
33
- debug('Start running test:', testCase.fullName);
34
- services.setContext(testCase.fullName);
35
- }
36
-
37
- // end of test file! (there is also onTestCaseResult listener)
38
- onTestResult(test, testResult) {
39
- if (!this.client) return;
40
-
41
- const { testResults } = testResult;
42
- for (const result of testResults) {
43
- let error;
44
- let steps;
45
- const { status, title, duration, failureMessages } = result;
46
- if (failureMessages[0]) {
47
- let errorMessage = failureMessages[0].replace(ansiRegExp(), '');
48
- errorMessage = errorMessage.split('\n')[0];
49
- error = new Error(errorMessage);
50
- steps = failureMessages[0];
51
- }
52
- const testId = getTestomatIdFromTestTitle(title);
53
-
54
- // suite titles from most outer to most inner, separated by space
55
- let fullSuiteTitle = testResult.ancestorTitles?.join(' ');
56
- // if no suite titles, use file name
57
- if (!fullSuiteTitle && testResult.testFilePath) fullSuiteTitle = path.basename(testResult.testFilePath);
58
-
59
- const logs = getTestLogs(result);
60
- const artifacts = services.artifacts.get(result.fullName);
61
- const keyValues = services.keyValues.get(result.fullName);
62
-
63
- const deducedStatus = status === 'pending' ? 'skipped' : status;
64
- // In jest if test is not matched with test name pattern it is considered as skipped.
65
- // So adding a check if it is skipped for real or because of test pattern
66
- if (!this._globalConfig.testNamePattern || deducedStatus !== 'skipped') {
67
- this.client.addTestRun(deducedStatus, {
68
- test_id: testId,
69
- suite_title: fullSuiteTitle,
70
- error,
71
- steps,
72
- title,
73
- time: duration,
74
- logs,
75
- manuallyAttachedArtifacts: artifacts,
76
- meta: keyValues,
77
- });
78
- }
79
- }
80
- }
81
-
82
- onRunComplete(contexts, results) {
83
- if (!this.client) return;
84
-
85
- const { numFailedTests } = results;
86
- const status = numFailedTests === 0 ? 'passed' : 'failed';
87
- this.client.updateRunStatus(status);
88
- }
89
- }
90
-
91
- function getTestLogs(testResult) {
92
- const suiteLogsArr = services.logger.getLogs(testResult.testFilePath);
93
- const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
94
- const testLogsArr = services.logger.getLogs(testResult.fullName);
95
- const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
96
-
97
- let logs = '';
98
- if (suiteLogs) {
99
- logs += `${pc.bold('\t--- Suite ---')}\n${suiteLogs}`;
100
- }
101
- if (testLogs) {
102
- logs += `\n${pc.bold('\t--- Test ---')}\n${testLogs}`;
103
- }
104
- return logs;
105
- }
106
-
107
- export default JestReporter;
@@ -1,2 +0,0 @@
1
- const MochaReporter = require('../../../lib/adapter/mocha.js');
2
- module.exports = MochaReporter;
@@ -1,156 +0,0 @@
1
- import Mocha from 'mocha';
2
- import TestomatClient from '../client.js';
3
- import { STATUS, TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
4
- import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
5
- import { config } from '../config.js';
6
- import { services } from '../services/index.js';
7
- import pc from 'picocolors';
8
-
9
- const {
10
- EVENT_RUN_BEGIN,
11
- EVENT_RUN_END,
12
- EVENT_TEST_FAIL,
13
- EVENT_TEST_PASS,
14
- EVENT_TEST_PENDING,
15
- EVENT_SUITE_BEGIN,
16
- EVENT_SUITE_END,
17
- EVENT_TEST_BEGIN,
18
- EVENT_TEST_END,
19
- } = Mocha.Runner.constants;
20
-
21
- function MochaReporter(runner, opts) {
22
- Mocha.reporters.Base.call(this, runner);
23
- let passes = 0;
24
- let failures = 0;
25
- let skipped = 0;
26
- // let artifactStore;
27
-
28
- const apiKey = opts?.reporterOptions?.apiKey || config.TESTOMATIO;
29
-
30
- const client = new TestomatClient({ apiKey });
31
-
32
- runner.on(EVENT_RUN_BEGIN, () => {
33
- client.createRun();
34
-
35
- // clear dir with artifacts/logs
36
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
37
- });
38
-
39
- runner.on(EVENT_SUITE_BEGIN, async suite => {
40
- services.setContext(suite.fullTitle());
41
- });
42
-
43
- runner.on(EVENT_SUITE_END, async () => {
44
- services.setContext(null);
45
- });
46
-
47
- runner.on(EVENT_TEST_BEGIN, async test => {
48
- services.setContext(test.fullTitle());
49
- });
50
-
51
- runner.on(EVENT_TEST_END, async () => {
52
- services.setContext(null);
53
- });
54
-
55
- runner.on(EVENT_TEST_PASS, async test => {
56
- passes += 1;
57
-
58
- console.log(pc.bold(pc.green('✔')), test.fullTitle());
59
- const testId = getTestomatIdFromTestTitle(test.title);
60
-
61
- const logs = getTestLogs(test);
62
- const artifacts = services.artifacts.get(test.fullTitle());
63
- const keyValues = services.keyValues.get(test.fullTitle());
64
-
65
- client.addTestRun(STATUS.PASSED, {
66
- test_id: testId,
67
- suite_title: getSuiteTitle(test),
68
- title: getTestName(test),
69
- code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
70
- file: getFile(test),
71
- time: test.duration,
72
- logs,
73
- manuallyAttachedArtifacts: artifacts,
74
- meta: keyValues,
75
- });
76
- });
77
-
78
- runner.on(EVENT_TEST_PENDING, test => {
79
- skipped += 1;
80
- console.log('skip: %s', test.fullTitle());
81
- const testId = getTestomatIdFromTestTitle(test.title);
82
- client.addTestRun(STATUS.SKIPPED, {
83
- title: getTestName(test),
84
- suite_title: getSuiteTitle(test),
85
- code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
86
- file: getFile(test),
87
- test_id: testId,
88
- time: test.duration,
89
- });
90
- });
91
-
92
- runner.on(EVENT_TEST_FAIL, async (test, err) => {
93
- failures += 1;
94
- console.log(pc.bold(pc.red('✖')), test.fullTitle(), pc.gray(err.message));
95
- const testId = getTestomatIdFromTestTitle(test.title);
96
-
97
- const logs = getTestLogs(test);
98
-
99
- client.addTestRun(STATUS.FAILED, {
100
- error: err,
101
- suite_title: getSuiteTitle(test),
102
- file: getFile(test),
103
- test_id: testId,
104
- title: getTestName(test),
105
- code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
106
- time: test.duration,
107
- logs,
108
- });
109
- });
110
-
111
- runner.on(EVENT_RUN_END, () => {
112
- const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
113
- console.log(pc.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
114
- // @ts-ignore
115
- client.updateRunStatus(status);
116
- });
117
- }
118
-
119
- function getTestLogs(test) {
120
- const suiteLogsArr = services.logger.getLogs(test.parent.fullTitle());
121
- const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
122
- const testLogsArr = services.logger.getLogs(test.fullTitle());
123
- const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
124
-
125
- let logs = '';
126
- if (suiteLogs) {
127
- logs += `${pc.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
128
- }
129
- if (testLogs) {
130
- logs += `\n${pc.bold('\t--- Test ---')}\n${testLogs}`;
131
- }
132
- return logs;
133
- }
134
-
135
- function getSuiteTitle(test, pathArr = []) {
136
- if (test.parent.parent) getSuiteTitle(test.parent, pathArr);
137
-
138
- pathArr.push(test.parent.title);
139
-
140
- return pathArr.filter(t => !!t)[0];
141
- }
142
-
143
- function getFile(test) {
144
- return test.parent.file?.replace(process.cwd(), '');
145
- }
146
-
147
- function getTestName(test) {
148
- if (process.env.TESTOMATIO_CREATE === 'fulltitle') return test.fullTitle();
149
- return test.title;
150
- }
151
-
152
- // To have this reporter "extend" a built-in reporter uncomment the following line:
153
- // @ts-ignore
154
- Mocha.utils.inherits(MochaReporter, Mocha.reporters.Spec);
155
-
156
- export default MochaReporter;
@@ -1,88 +0,0 @@
1
- import TestomatClient from '../client.js';
2
- import { config } from '../config.js';
3
- import { STATUS } from '../constants.js';
4
- import { getTestomatIdFromTestTitle } from '../utils/utils.js';
5
-
6
- const apiKey = config.TESTOMATIO;
7
- const client = new TestomatClient({ apiKey });
8
-
9
- export default {
10
- write: async (results, options, done) => {
11
- await client.createRun();
12
-
13
- const testFiles = results.modules;
14
-
15
- for (const fileName in testFiles) {
16
- // in nightwatch: object containing tests from a single file
17
- const testModule = testFiles[fileName];
18
-
19
- // passed and failed tests (tests with assertions)
20
- const completedTests = testModule.completed;
21
-
22
- // skipped tests (skipped by user or tests without assertions)
23
- const skippedTests = testModule.skipped;
24
-
25
- const tags = testModule.tags || [];
26
-
27
- // if test file contains multiple suites, the last suite name is used as a name 🤷‍♂️
28
- // no other places which contain suite name (even inside test object)
29
- const suiteTitle = testModule.name;
30
-
31
- for (const testTitle in completedTests) {
32
- const test = completedTests[testTitle];
33
- let status;
34
- switch (test.status) {
35
- case 'pass':
36
- status = STATUS.PASSED;
37
- break;
38
- case 'fail':
39
- status = STATUS.FAILED;
40
- break;
41
- // probably not required (because skipped tests are in separate array), but just in case
42
- case 'skip':
43
- status = STATUS.SKIPPED;
44
- console.info('Skipped test is in completed tests array:', test, 'Not expected behavior.');
45
- break;
46
- default:
47
- console.error('Test status processing error:', test.status);
48
- }
49
-
50
- const testId = getTestomatIdFromTestTitle(testTitle);
51
-
52
- client.addTestRun(status, {
53
- error: { name: test.assertions?.[0]?.name, message: test.assertions?.[0]?.message, stack: test.stackTrace },
54
- file: testModule.modulePath?.replace(process.cwd(), ''),
55
- message: test.assertions?.[0]?.message,
56
- rid: `${testModule.uuid || ''}_${testTitle || ''}`,
57
- stack: test.stackTrace,
58
- suite_title: suiteTitle,
59
- tags,
60
- test_id: testId,
61
- time: test.timeMs,
62
- title: testTitle,
63
- });
64
- }
65
-
66
- // just array with skipped tests titles, no any other info
67
- for (const testTitle of skippedTests) {
68
- client.addTestRun(STATUS.SKIPPED, {
69
- suite_title: suiteTitle,
70
- tags,
71
- rid: `${testModule.uuid || ''}_${testTitle || ''}`,
72
- title: testTitle,
73
- });
74
- }
75
- }
76
-
77
- /**
78
- * @type {'passed' | 'failed' | 'finished'}
79
- */
80
- let runStatus = 'finished';
81
- if (results.failed) runStatus = 'failed';
82
- else if (results.passed) runStatus = 'passed';
83
-
84
- await client.updateRunStatus(runStatus);
85
-
86
- done();
87
- },
88
- };