@testomatio/reporter 0.8.0-beta.9 → 0.8.2-beta.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.
@@ -2,7 +2,8 @@
2
2
  const { Formatter, formatterHelpers } = require("@cucumber/cucumber");
3
3
  const chalk = require('chalk');
4
4
  const fs = require('fs');
5
- const { TestomatClient, TRConstants } = require('../../reporter');
5
+ const { STATUS } = require('../../constants');
6
+ const TestomatClient = require('../../client');
6
7
 
7
8
  const { GherkinDocumentParser, PickleParser } = formatterHelpers
8
9
  const {
@@ -19,7 +20,7 @@ class CucumberReporter extends Formatter {
19
20
  this.cases = [];
20
21
 
21
22
  this.client = new TestomatClient();
22
- this.status = TRConstants.PASSED;
23
+ this.status = STATUS.PASSED;
23
24
 
24
25
  }
25
26
 
@@ -33,7 +34,8 @@ class CucumberReporter extends Formatter {
33
34
  const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(testCaseFinished.testCaseStartedId);
34
35
 
35
36
  let example;
36
- let status = TRConstants.PASSED;
37
+
38
+ let status = STATUS.PASSED;
37
39
  let color = 'green';
38
40
  let message;
39
41
 
@@ -41,22 +43,23 @@ class CucumberReporter extends Formatter {
41
43
 
42
44
  if (testCaseAttempt.worstTestStepResult) {
43
45
  if (testCaseAttempt.worstTestStepResult.status === 'SKIPPED') {
44
- status = TRConstants.SKIPPED;
46
+ status = STATUS.SKIPPED;
45
47
  }
46
48
  if (testCaseAttempt.worstTestStepResult.status === 'UNDEFINED') {
47
- status = TRConstants.SKIPPED;
49
+ status = STATUS.SKIPPED;
48
50
  message = 'Undefined steps. Implement missing steps and rerun this scenario';
49
51
  }
50
52
  if (testCaseAttempt.worstTestStepResult.status === 'FAILED') {
51
53
  message = testCaseAttempt?.worstTestStepResult?.message;
52
- status = TRConstants.FAILED;
54
+ status = STATUS.FAILED;
53
55
  }
54
56
  color = getStatusColor(testCaseAttempt.worstTestStepResult.status);
55
- if (status !== TRConstants.PASSED) this.failures.push(testCaseAttempt);
57
+ if (status !== STATUS.PASSED) this.failures.push(testCaseAttempt);
56
58
  }
57
59
 
58
60
  if (testCaseAttempt.pickle.astNodeIds.length > 1) {
59
61
  example = getExample(testCaseAttempt);
62
+ // @ts-ignore
60
63
  testCaseAttempt.example = example;
61
64
  }
62
65
 
@@ -70,8 +73,8 @@ class CucumberReporter extends Formatter {
70
73
  if (message) cliMessage += chalk.gray(message.split('\n')[0]);
71
74
  console.log(cliMessage);
72
75
 
73
- if (status !== TRConstants.PASSED && status !== TRConstants.SKIPPED) {
74
- this.status = TRConstants.FAILED;
76
+ if (status !== STATUS.PASSED && status !== STATUS.SKIPPED) {
77
+ this.status = STATUS.FAILED;
75
78
  }
76
79
 
77
80
  const time = Object.values(testCaseAttempt.stepResults)
@@ -80,12 +83,13 @@ class CucumberReporter extends Formatter {
80
83
 
81
84
  if (!this.client) return;
82
85
 
83
- this.client.addTestRun(testId, status, {
86
+ this.client.addTestRun(status, {
84
87
  // error: testCaseAttempt.worstTestStepResult.message,
85
88
  message,
86
89
  steps: getSteps(testCaseAttempt).map(s => s.toString()).join('\n').trim(),
87
90
  example: { ...example},
88
91
  title: scenario,
92
+ test_id: testId,
89
93
  time,
90
94
  });
91
95
  }
@@ -121,9 +125,9 @@ class CucumberReporter extends Formatter {
121
125
 
122
126
  if (!this.client) return;
123
127
 
124
- this.client.updateRunStatus(testRunFinished.success ? TRConstants.PASSED : TRConstants.FAILED)
128
+ this.client.updateRunStatus(testRunFinished.success ? STATUS.PASSED : STATUS.FAILED)
125
129
  }
126
- };
130
+ }
127
131
 
128
132
  function getSteps(tc) {
129
133
  const stepIds = Object.keys(tc.stepResults);
@@ -1,8 +1,9 @@
1
- // eslint-disable-next-line import/no-unresolved
1
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved
2
2
  const { Formatter } = require('cucumber');
3
3
  const chalk = require('chalk');
4
- const { TestomatClient, TRConstants } = require('../../reporter');
5
4
  const { parseTest } = require('../../util');
5
+ const { STATUS } = require('../../constants');
6
+ const TestomatClient = require('../../client');
6
7
 
7
8
  const createTestomatFormatter = apiKey => {
8
9
  if (!apiKey || apiKey === '') {
@@ -92,18 +93,18 @@ const createTestomatFormatter = apiKey => {
92
93
  if (!apiKey) return;
93
94
 
94
95
  this.client = new TestomatClient({ apiKey });
95
- this.status = TRConstants.PASSED;
96
+ this.status = STATUS.PASSED.toString();
96
97
 
97
98
  options.eventBroadcaster.on('gherkin-document', addDocument);
98
- options.eventBroadcaster.on('test-run-started', () => this.client.createRun());
99
+ options.eventBroadcaster.on('test-run-started', () => this?.client?.createRun());
99
100
  options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
100
- options.eventBroadcaster.on('test-run-finished', () => this.client.updateRunStatus(this.status));
101
+ options.eventBroadcaster.on('test-run-finished', () => this?.client?.updateRunStatus(this.status));
101
102
  }
102
103
 
103
104
  onTestCaseFinished(event) {
104
105
  const scenario = getScenario(event.sourceLocation);
105
106
  const testId = getTestId(scenario);
106
- const status = event.result.status === 'undefined' ? TRConstants.SKIPPED : event.result.status;
107
+ const status = event.result.status === 'undefined' ? STATUS.SKIPPED : event.result.status;
107
108
 
108
109
  let example = getDataTableMap(scenario, event.sourceLocation);
109
110
  if (example) example = JSON.parse(example);
@@ -120,14 +121,17 @@ const createTestomatFormatter = apiKey => {
120
121
  message = 'Undefined steps. Implement missing steps and rerun this scenario';
121
122
  }
122
123
  console.log(cliMessage);
123
- if (status !== TRConstants.PASSED && status !== TRConstants.SKIPPED) {
124
- this.status = TRConstants.FAILED;
124
+ if (status !== STATUS.PASSED && status !== STATUS.SKIPPED) {
125
+ this.status = STATUS.FAILED;
125
126
  }
126
- this.client.addTestRun(testId, status, {
127
- error: event.result.exception,
127
+
128
+ this.client?.addTestRun(status, {
129
+ error: event.result.exception instanceof Error ? event.result.exception : undefined,
128
130
  message,
129
131
  example,
132
+ test_id: testId,
130
133
  title: getTitle(scenario),
134
+ suite_title: getTitle(getFeature(event.sourceLocation.uri)),
131
135
  });
132
136
  }
133
137
  };
@@ -1,4 +1,4 @@
1
- const TRConstants = require('../../constants');
1
+ const { STATUS } = require('../../constants');
2
2
  const { parseTest, parseSuite } = require('../../util');
3
3
  const TestomatClient = require('../../client');
4
4
 
@@ -57,16 +57,16 @@ const testomatioReporter = on => {
57
57
 
58
58
  let state;
59
59
  switch (test.state) {
60
- case 'passed': state = TRConstants.PASSED; break;
61
- case 'failed': state = TRConstants.FAILED; break;
60
+ case 'passed': state = STATUS.PASSED; break;
61
+ case 'failed': state = STATUS.FAILED; break;
62
62
  case 'skipped':
63
63
  case 'pending':
64
64
  default:
65
- state = TRConstants.SKIPPED;
65
+ state = STATUS.SKIPPED;
66
66
  }
67
67
 
68
- addSpecTestsPromises.push(client.addTestRun(testId, state, {
69
- title, time, example, error, files, suite_title: suiteTitle, suite_id: suiteId
68
+ addSpecTestsPromises.push(client.addTestRun(state, {
69
+ title, time, example, error, files, suite_title: suiteTitle, test_id: testId, suite_id: suiteId
70
70
  }));
71
71
  }
72
72
 
@@ -74,7 +74,7 @@ const testomatioReporter = on => {
74
74
  });
75
75
 
76
76
  on('after:run', async results => {
77
- const status = results.totalFailed ? TRConstants.FAILED : TRConstants.PASSED;
77
+ const status = results.totalFailed ? STATUS.FAILED : STATUS.PASSED;
78
78
  await client.updateRunStatus(status);
79
79
  });
80
80
  };
@@ -1,6 +1,6 @@
1
1
  const TestomatClient = require('../client');
2
2
  const { parseTest, ansiRegExp } = require('../util');
3
- const { PASSED, FAILED } = require('../constants');
3
+ const { STATUS } = require('../constants');
4
4
 
5
5
  class JasmineReporter {
6
6
  constructor(options) {
@@ -32,13 +32,14 @@ class JasmineReporter {
32
32
  errorMessage = `${errorMessage}Failure: ${result.failedExpectations[i].message}\n`;
33
33
  errorMessage = `${errorMessage}\n ${result.failedExpectations[i].stack}`;
34
34
  }
35
- console.log(`${title} : ${PASSED}`);
35
+ console.log(`${title} : ${STATUS.PASSED}`);
36
36
  console.log(errorMessage);
37
37
  const testId = parseTest(title);
38
38
  errorMessage = errorMessage.replace(ansiRegExp(), '');
39
- this.client.addTestRun(testId, status, {
39
+ this.client.addTestRun(status, {
40
40
  error: result.failedExpectations[0],
41
41
  message: errorMessage,
42
+ test_id: testId,
42
43
  title,
43
44
  time: this.getDuration(result),
44
45
  });
@@ -48,7 +49,7 @@ class JasmineReporter {
48
49
  if (!this.client) return;
49
50
 
50
51
  const { overallStatus } = suiteInfo;
51
- const status = overallStatus === 'failed' ? FAILED : PASSED;
52
+ const status = overallStatus === 'failed' ? STATUS.FAILED : STATUS.PASSED;
52
53
 
53
54
  this.client.updateRunStatus(status).then(() => done);
54
55
  }
@@ -1,5 +1,5 @@
1
1
  const TestomatClient = require('../client');
2
- const TRConstants = require('../constants');
2
+ const { STATUS } = require('../constants');
3
3
  const { parseTest, ansiRegExp } = require('../util');
4
4
 
5
5
  class JestReporter {
@@ -16,26 +16,22 @@ class JestReporter {
16
16
 
17
17
  const { testResults } = testResult;
18
18
  for (const result of testResults) {
19
- let error = null;
20
- let steps = null;
19
+ let error;
20
+ let steps;
21
21
  const { status, title, duration, failureMessages } = result;
22
22
  if (failureMessages[0]) {
23
23
  let errorMessage = failureMessages[0].replace(ansiRegExp(), '');
24
24
  errorMessage = errorMessage.split('\n')[0];
25
- error = {
26
- message: errorMessage,
27
- };
25
+ error = new Error(errorMessage);
28
26
  steps = failureMessages[0];
29
27
  }
30
28
  const testId = parseTest(title);
31
29
  const deducedStatus = status === 'pending' ? 'skipped' : status;
32
-
33
- const suite_title = result.ancestorTitles && result.ancestorTitles[result.ancestorTitles.length - 1];
34
30
  // In jest if test is not matched with test name pattern it is considered as skipped.
35
31
  // So adding a check if it is skipped for real or because of test pattern
36
32
  if (!this._globalConfig.testNamePattern || deducedStatus !== 'skipped') {
37
- this.client.addTestRun(testId, deducedStatus, {
38
- suite_title,
33
+ this.client.addTestRun(deducedStatus, {
34
+ test_id: testId,
39
35
  error,
40
36
  steps,
41
37
  title,
@@ -49,7 +45,7 @@ class JestReporter {
49
45
  if (!this.client) return;
50
46
 
51
47
  const { numFailedTests } = results;
52
- const status = numFailedTests === 0 ? TRConstants.PASSED : TRConstants.FAILED;
48
+ const status = numFailedTests === 0 ? STATUS.PASSED : STATUS.FAILED;
53
49
  this.client.updateRunStatus(status);
54
50
  }
55
51
  }
@@ -1,47 +1,98 @@
1
1
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
2
2
  const Mocha = require('mocha');
3
+ const debug = require('debug')('@testomatio/reporter:adapter:mocha');
4
+ const chalk = require('chalk');
3
5
  const TestomatClient = require('../client');
4
- const TRConstants = require('../constants');
5
- const { parseTest } = require('../util');
6
+ const { STATUS } = require('../constants');
7
+ const { parseTest, specificTestInfo } = require('../util');
8
+ const ArtifactStorage = require('../ArtifactStorage');
6
9
 
7
- const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS } = Mocha.Runner.constants;
10
+ const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } = Mocha.Runner.constants;
8
11
 
9
12
  function MochaReporter(runner, opts) {
10
13
  Mocha.reporters.Base.call(this, runner);
11
- let passes = 0;
12
- let failures = 0;
14
+ let passes = 0; let failures = 0; let skipped = 0;
15
+ let artifactStore;
13
16
 
14
- const client = new TestomatClient({ apiKey: opts?.reporterOptions?.apiKey });
17
+ const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
18
+
19
+ if (!apiKey) {
20
+ debug('TESTOMATIO key is empty, ignoring reports');
21
+ return;
22
+ }
23
+ const client = new TestomatClient({ apiKey });
15
24
 
16
25
  runner.on(EVENT_RUN_BEGIN, () => {
17
26
  client.createRun();
27
+
28
+ const params = {
29
+ toFile: true
30
+ };
31
+
32
+ artifactStore = runner._workerReporter !== undefined
33
+ ? new ArtifactStorage(params)
34
+ : new ArtifactStorage();
18
35
  });
19
36
 
20
- runner.on(EVENT_TEST_PASS, test => {
37
+ runner.on(EVENT_TEST_PASS, async(test) => {
21
38
  passes += 1;
22
- console.log('pass: %s', test.fullTitle());
39
+ console.log(chalk.bold.green(''), test.fullTitle());
40
+ const testId = parseTest(test.title);
41
+
42
+ const specificTest = specificTestInfo(test);
43
+ const content = await artifactStore.artifactByTestName(specificTest);
44
+
45
+ debug(`test=${specificTest} content = `, content);
46
+
47
+ client.addTestRun(
48
+ STATUS.PASSED,
49
+ {
50
+ test_id: testId,
51
+ title: test.title,
52
+ time: test.duration,
53
+ },
54
+ content
55
+ );
56
+ });
57
+
58
+ runner.on(EVENT_TEST_PENDING, test => {
59
+ skipped += 1;
60
+ console.log('skip: %s', test.fullTitle());
23
61
  const testId = parseTest(test.title);
24
- client.addTestRun(testId, TRConstants.PASSED, {
62
+ client.addTestRun(STATUS.SKIPPED, {
25
63
  title: test.title,
64
+ test_id: testId,
26
65
  time: test.duration,
27
66
  });
28
67
  });
29
68
 
30
- runner.on(EVENT_TEST_FAIL, (test, err) => {
69
+ runner.on(EVENT_TEST_FAIL, async(test, err) => {
31
70
  failures += 1;
32
- console.log('fail: %s -- error: %s', test.fullTitle(), err.message);
71
+ console.log(chalk.bold.red(''), test.fullTitle(), chalk.gray(err.message));
33
72
  const testId = parseTest(test.title);
34
- client.addTestRun(testId, TRConstants.FAILED, {
35
- error: err,
36
- title: test.title,
37
- time: test.duration,
38
- });
73
+
74
+ const specificTest = specificTestInfo(test);
75
+ const content = await artifactStore.artifactByTestName(specificTest);
76
+
77
+ debug(`fail test=${specificTest} content = `, content);
78
+
79
+ client.addTestRun(
80
+ STATUS.FAILED, {
81
+ error: err,
82
+ test_id: testId,
83
+ title: test.title,
84
+ time: test.duration,
85
+ },
86
+ content
87
+ );
39
88
  });
40
89
 
41
90
  runner.on(EVENT_RUN_END, () => {
42
- console.log('end: %d/%d', passes, passes + failures);
43
- const status = failures === 0 ? TRConstants.PASSED : TRConstants.FAILED;
91
+ const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
92
+ console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
44
93
  client.updateRunStatus(status);
94
+
95
+ artifactStore.cleanup();
45
96
  });
46
97
  }
47
98
 
@@ -3,16 +3,17 @@ const crypto = require('crypto');
3
3
  const os = require('os');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const Status = require('../constants');
6
+ const { APP_PREFIX, STATUS: Status } = require('../constants');
7
7
  const TestomatioClient = require('../client');
8
- const upload = require('../fileUploader');
8
+ const { isArtifactsEnabled } = require('../fileUploader');
9
9
  const { parseTest } = require('../util');
10
10
 
11
+
11
12
  class TestomatioReporter {
12
13
  constructor(config = {}) {
13
14
  this.client = new TestomatioClient({ apiKey: config?.apiKey });
14
15
 
15
- this.videos = [];
16
+ this.uploads = [];
16
17
  }
17
18
 
18
19
  onBegin(_config, suite) {
@@ -36,31 +37,15 @@ class TestomatioReporter {
36
37
  appendStep(step, steps);
37
38
  }
38
39
 
39
- const files = [];
40
-
41
- for (const attachment of result.attachments) {
42
- if (!attachment.body && !attachment.path) {
43
- continue;
44
- }
45
- if (attachment.contentType && attachment.contentType.startsWith('video')) {
46
- // video is post-processed
47
- this.videos.push({ testId, attachment, title, suite_title });
48
- continue;
49
- }
50
-
51
- let fileName = attachment.path;
52
- if (attachment.body) {
53
- fileName = tmpFile();
54
- fs.writeFileSync(fileName, attachment.body);
55
- }
56
- files.push({ path: fileName, type: attachment.contentType });
57
- }
40
+ this.uploads.push({
41
+ testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
42
+ })
58
43
 
59
- this.client.addTestRun(testId, checkStatus(result.status), {
44
+ this.client.addTestRun(checkStatus(result.status), {
60
45
  error,
46
+ test_id: testId,
61
47
  suite_title,
62
48
  title,
63
- files,
64
49
  steps: steps.join('\n'),
65
50
  time: duration,
66
51
  });
@@ -69,18 +54,30 @@ class TestomatioReporter {
69
54
  async onEnd(result) {
70
55
  if (!this.client) return;
71
56
 
72
- if (this.videos.length && upload.isArtifactsEnabled) {
73
- console.log(Status.APP_PREFIX, `🎞️ Uploading ${this.videos.length} videos...`);
57
+ if (this.uploads.length && isArtifactsEnabled()) {
58
+ console.log(APP_PREFIX, `🎞️ Uploading ${this.uploads.length} files...`);
74
59
 
75
60
  const promises = [];
76
- for (const video of this.videos) {
77
- const { testId, title, attachment, suite_title } = video;
78
- const file = { path: attachment.path, title, type: attachment.contentType };
61
+
62
+ for (const upload of this.uploads) {
63
+
64
+ const { title, testId, suite_title } = upload;
65
+
66
+ const files = upload.files.map((attachment) => {
67
+ if (attachment.body) {
68
+ const fileName = tmpFile();
69
+ fs.writeFileSync(fileName, attachment.body);
70
+ }
71
+ return { path: attachment.path, title, type: attachment.contentType };
72
+ });
73
+
74
+
79
75
  promises.push(
80
- this.client.addTestRun(testId, undefined, {
76
+ this.client.addTestRun(undefined, {
77
+ test_id: testId,
81
78
  title,
82
79
  suite_title,
83
- files: [file],
80
+ files,
84
81
  }),
85
82
  );
86
83
  }
@@ -43,9 +43,10 @@ class WebdriverReporter extends WDIOReporter {
43
43
  .filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
44
44
  .map(el => Buffer.from(el.result.value, 'base64'));
45
45
 
46
- await this.client.addTestRun(testId, state, {
46
+ await this.client.addTestRun(state, {
47
47
  error,
48
48
  title,
49
+ test_id: testId,
49
50
  time: duration,
50
51
  filesBuffers: screenshotsBuffers,
51
52
  });
@@ -2,7 +2,7 @@
2
2
  const program = require("commander");
3
3
  const chalk = require("chalk");
4
4
  const glob = require('glob');
5
-
5
+ const debug = require('debug')('@testomatio/reporter:xml-cli');
6
6
  const { APP_PREFIX } = require('../constants');
7
7
  const XmlReader = require("../xmlReader");
8
8
 
@@ -22,7 +22,10 @@ program
22
22
  pattern += '.xml';
23
23
  }
24
24
  let { javaTests, lang } = opts;
25
- if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
25
+ if (opts.envFile) {
26
+ debug('Loading env file: %s', opts.envFile)
27
+ require('dotenv').config({ path: opts.envFile }); // eslint-disable-line
28
+ }
26
29
  if (javaTests === true) javaTests = 'src/test/java';
27
30
  lang = lang?.toLowerCase();
28
31
  const runReader = new XmlReader({ javaTests, lang });
@@ -3,7 +3,7 @@ const { spawn } = require('child_process');
3
3
  const program = require('commander');
4
4
  const chalk = require('chalk');
5
5
  const TestomatClient = require('../client');
6
- const { APP_PREFIX, FINISHED } = require('../constants');
6
+ const { APP_PREFIX, STATUS } = require('../constants');
7
7
  const { version } = require('../../package.json');
8
8
 
9
9
  console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
@@ -42,7 +42,7 @@ program
42
42
 
43
43
  const client = new TestomatClient({ apiKey });
44
44
 
45
- client.updateRunStatus(FINISHED, true).then(() => {
45
+ client.updateRunStatus(STATUS.FINISHED, true).then(() => {
46
46
  console.log(chalk.yellow(`Run ${process.env.TESTOMATIO_RUN} was finished`));
47
47
  process.exit(0);
48
48
  });