@testomatio/reporter 1.3.5-beta → 1.4.1-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.
package/README.md CHANGED
@@ -11,12 +11,12 @@ Testomat.io Reporter (this npm package) supports:
11
11
 
12
12
  * 🏄 Integarion with all popular [JavaScript/TypeScript frameworks](./docs/frameworks.md)
13
13
  * 🗄️ Screenshots, videos, traces [uploaded into S3 bucket](./docs/artifacts.md)
14
- * 🔎 Stack traces and error messages
15
- * 🐙 [GitHub](./docs/pipes/github.d) & [GitLab](./docs/pipes/gitlab.d) integration
14
+ * 🔎 [Stack traces](./docs/stacktrace.md) and error messages
15
+ * 🐙 [GitHub](./docs/pipes/github.md) & [GitLab](./docs/pipes/gitlab.md) integration
16
16
  * 🚅 Realtime reports
17
17
  * 🗃️ Other test frameworks supported via [JUNit XML](./docs/junit.md)
18
18
  * 🚶‍♀️ Steps *(work in progress)*
19
- * 📄 Logger *(work in progress, supports Jest for now)*
19
+ * 📄 [Logger](./docs/logger.md) *(work in progress, supports Jest for now)*
20
20
  * ☁️ Custom properties and metadata *(work in progress)*
21
21
  * 💯 Free & open-source.
22
22
  * 📊 Public and private Run reports on cloud via [Testomat.io App](https://testomat.io) 👇
@@ -128,7 +128,7 @@ Bring this reporter on CI and never lose test results again!
128
128
  * 📓 [JUnit](./docs/junit.md)
129
129
  * 🗄️ [Artifacts](./docs/artifacts.md)
130
130
  * 🔂 [Workflows](./docs/workflows.md)
131
- * 🔂 [Logger](./docs/logger.md)
131
+ * 🖊️ [Logger](./docs/logger.md)
132
132
 
133
133
  ## Development
134
134
 
@@ -1,9 +1,9 @@
1
1
  const debug = require('debug')('@testomatio/reporter:adapter:codeceptjs');
2
2
  const chalk = require('chalk');
3
3
  const TestomatClient = require('../client');
4
- const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE } = require('../constants');
4
+ const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
5
5
  const upload = require('../fileUploader');
6
- const { parseTest: getIdFromTestTitle, fileSystem } = require('../util');
6
+ const { parseTest: getIdFromTestTitle, fileSystem } = require('../utils/utils');
7
7
 
8
8
  if (!global.codeceptjs) {
9
9
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
@@ -56,7 +56,7 @@ function CodeceptReporter(config) {
56
56
  // Listening to events
57
57
  event.dispatcher.on(event.all.before, () => {
58
58
  // clear tmp dir
59
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
59
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
60
60
 
61
61
  recorder.add('Creating new run', () => client.createRun());
62
62
  videos = [];
@@ -73,6 +73,8 @@ function CodeceptReporter(config) {
73
73
  stepShift = 0;
74
74
  });
75
75
 
76
+ if (!global.testomatioDataStore) global.testomatioDataStore = {};
77
+ // reset steps
76
78
  global.testomatioDataStore.steps = [];
77
79
  });
78
80
 
@@ -162,7 +164,7 @@ function CodeceptReporter(config) {
162
164
  message: testObj.message,
163
165
  time: getDuration(test),
164
166
  files,
165
- steps: global.testomatioDataStore.steps.join('\n') || null,
167
+ steps: global.testomatioDataStore?.steps?.join('\n') || null,
166
168
  })
167
169
  .then(pipes => {
168
170
  testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
@@ -219,12 +221,12 @@ function CodeceptReporter(config) {
219
221
  if (!metaSteps[i]) continue;
220
222
  if (metaSteps[i].isBDD()) {
221
223
  // output.push(repeat(stepShift) + chalk.bold(metaSteps[i].toString()) + metaSteps[i].comment);
222
- global.testomatioDataStore.steps.push(
224
+ global.testomatioDataStore?.steps?.push(
223
225
  repeat(stepShift) + chalk.bold(metaSteps[i].toString()) + metaSteps[i].comment,
224
226
  );
225
227
  } else {
226
228
  // output.push(repeat(stepShift) + chalk.green.bold(metaSteps[i].toString()));
227
- global.testomatioDataStore.steps.push(repeat(stepShift) + chalk.green.bold(metaSteps[i].toString()));
229
+ global.testomatioDataStore?.steps?.push(repeat(stepShift) + chalk.green.bold(metaSteps[i].toString()));
228
230
  }
229
231
  }
230
232
  }
@@ -239,16 +241,16 @@ function CodeceptReporter(config) {
239
241
 
240
242
  if (step.status === STATUS.FAILED) {
241
243
  // output.push(repeat(stepShift) + chalk.red(step.toString()) + duration);
242
- global.testomatioDataStore.steps.push(repeat(stepShift) + chalk.red(step.toString()) + duration);
244
+ global.testomatioDataStore?.steps?.push(repeat(stepShift) + chalk.red(step.toString()) + duration);
243
245
  } else {
244
246
  // output.push(repeat(stepShift) + step.toString() + duration);
245
- global.testomatioDataStore.steps.push(repeat(stepShift) + step.toString() + duration);
247
+ global.testomatioDataStore?.steps?.push(repeat(stepShift) + step.toString() + duration);
246
248
  }
247
249
  });
248
250
 
249
251
  event.dispatcher.on(event.step.comment, step => {
250
252
  // output.push(chalk.cyan.bold(step.toString()));
251
- global.testomatioDataStore.steps.push(chalk.cyan.bold(step.toString()));
253
+ global.testomatioDataStore?.steps?.push(chalk.cyan.bold(step.toString()));
252
254
  });
253
255
  }
254
256
 
@@ -2,10 +2,10 @@
2
2
  const { Formatter, formatterHelpers } = require('@cucumber/cucumber');
3
3
  const chalk = require('chalk');
4
4
  const fs = require('fs');
5
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
- const logger = require('../../logger');
8
- const { parseTest, fileSystem } = require('../../util');
7
+ const logger = require('../../storages/logger');
8
+ const { parseTest, fileSystem } = require('../../utils/utils');
9
9
 
10
10
  const { GherkinDocumentParser, PickleParser } = formatterHelpers;
11
11
  const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
@@ -37,7 +37,7 @@ class CucumberReporter extends Formatter {
37
37
 
38
38
  parseEnvelope(envelope) {
39
39
  if (envelope.testRunStarted) {
40
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
40
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
41
41
  }
42
42
  if (envelope.testCaseStarted && this.client) {
43
43
  this.client.createRun();
@@ -1,8 +1,8 @@
1
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 { parseTest, fileSystem } = require('../../util');
5
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
4
+ const { parseTest, fileSystem } = require('../../utils/utils');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
7
 
8
8
  const createTestomatFormatter = apiKey => {
@@ -97,7 +97,7 @@ const createTestomatFormatter = apiKey => {
97
97
 
98
98
  options.eventBroadcaster.on('gherkin-document', addDocument);
99
99
  options.eventBroadcaster.on('test-run-started', () => {
100
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
100
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
101
101
  this?.client?.createRun();
102
102
  });
103
103
  options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
@@ -1,5 +1,5 @@
1
1
  const { STATUS } = require('../../constants');
2
- const { parseTest, parseSuite } = require('../../util');
2
+ const { parseTest, parseSuite } = require('../../utils/utils');
3
3
  const TestomatClient = require('../../client');
4
4
 
5
5
  const testomatioReporter = on => {
@@ -1,5 +1,5 @@
1
1
  const TestomatClient = require('../client');
2
- const { parseTest, ansiRegExp } = require('../util');
2
+ const { parseTest, ansiRegExp } = require('../utils/utils');
3
3
  const { STATUS } = require('../constants');
4
4
 
5
5
  class JasmineReporter {
@@ -1,6 +1,6 @@
1
1
  const TestomatClient = require('../client');
2
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
3
- const { parseTest, ansiRegExp, fileSystem } = require('../util');
2
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
3
+ const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
4
4
 
5
5
  class JestReporter {
6
6
  constructor(globalConfig, options) {
@@ -11,20 +11,9 @@ class JestReporter {
11
11
  this.client.createRun();
12
12
  }
13
13
 
14
- static getIdOfCurrentlyRunningTest() {
15
- if (!process.env.JEST_WORKER_ID) return null;
16
- try {
17
- // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
18
- // eslint-disable-next-line no-undef
19
- if (expect && expect?.getState()?.currentTestName) return parseTest(expect?.getState()?.currentTestName);
20
- } catch (e) {
21
- return null;
22
- }
23
- }
24
-
25
14
  onRunStart() {
26
15
  // clear tmp dir
27
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
16
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
28
17
  }
29
18
 
30
19
  onTestResult(test, testResult) {
@@ -42,12 +31,21 @@ class JestReporter {
42
31
  steps = failureMessages[0];
43
32
  }
44
33
  const testId = parseTest(title);
34
+ let suite_title;
35
+ // this is test without a suite
36
+ if (result.fullName === result.title) {
37
+ suite_title = testResult.testFilePath.split('/').pop();
38
+ } else {
39
+ suite_title = result.fullName.replace(result.title, '');
40
+ }
41
+
45
42
  const deducedStatus = status === 'pending' ? 'skipped' : status;
46
43
  // In jest if test is not matched with test name pattern it is considered as skipped.
47
44
  // So adding a check if it is skipped for real or because of test pattern
48
45
  if (!this._globalConfig.testNamePattern || deducedStatus !== 'skipped') {
49
46
  this.client.addTestRun(deducedStatus, {
50
47
  test_id: testId,
48
+ suite_title,
51
49
  error,
52
50
  steps,
53
51
  title,
@@ -63,9 +61,6 @@ class JestReporter {
63
61
  const { numFailedTests } = results;
64
62
  const status = numFailedTests === 0 ? STATUS.PASSED : STATUS.FAILED;
65
63
  this.client.updateRunStatus(status);
66
-
67
- // clear tmp dir
68
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
69
64
  }
70
65
  }
71
66
 
@@ -1,58 +1,40 @@
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
3
  const chalk = require('chalk');
5
4
  const TestomatClient = require('../client');
6
- const { STATUS } = require('../constants');
7
- const { parseTest, specificTestInfo } = require('../util');
8
- const ArtifactStorage = require('../_ArtifactStorageOld');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
6
+ const { parseTest, fileSystem } = require('../utils/utils');
9
7
 
10
- const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } = Mocha.Runner.constants;
8
+ const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
9
+ Mocha.Runner.constants;
11
10
 
12
11
  function MochaReporter(runner, opts) {
13
12
  Mocha.reporters.Base.call(this, runner);
14
- let passes = 0; let failures = 0; let skipped = 0;
15
- let artifactStore;
13
+ let passes = 0;
14
+ let failures = 0;
15
+ let skipped = 0;
16
+ // let artifactStore;
16
17
 
17
18
  const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
18
19
 
19
- if (!apiKey) {
20
- debug('TESTOMATIO key is empty, ignoring reports');
21
- return;
22
- }
23
20
  const client = new TestomatClient({ apiKey });
24
21
 
25
22
  runner.on(EVENT_RUN_BEGIN, () => {
26
23
  client.createRun();
27
24
 
28
- const params = {
29
- toFile: true
30
- };
31
-
32
- artifactStore = runner._workerReporter !== undefined
33
- ? new ArtifactStorage(params)
34
- : new ArtifactStorage();
25
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
35
26
  });
36
27
 
37
- runner.on(EVENT_TEST_PASS, async(test) => {
28
+ runner.on(EVENT_TEST_PASS, async test => {
38
29
  passes += 1;
39
30
  console.log(chalk.bold.green('✔'), test.fullTitle());
40
31
  const testId = parseTest(test.title);
41
32
 
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
- );
33
+ client.addTestRun(STATUS.PASSED, {
34
+ test_id: testId,
35
+ title: test.title,
36
+ time: test.duration,
37
+ });
56
38
  });
57
39
 
58
40
  runner.on(EVENT_TEST_PENDING, test => {
@@ -66,33 +48,23 @@ function MochaReporter(runner, opts) {
66
48
  });
67
49
  });
68
50
 
69
- runner.on(EVENT_TEST_FAIL, async(test, err) => {
51
+ runner.on(EVENT_TEST_FAIL, async (test, err) => {
70
52
  failures += 1;
71
53
  console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
72
54
  const testId = parseTest(test.title);
73
55
 
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
- );
56
+ client.addTestRun(STATUS.FAILED, {
57
+ error: err,
58
+ test_id: testId,
59
+ title: test.title,
60
+ time: test.duration,
61
+ });
88
62
  });
89
63
 
90
64
  runner.on(EVENT_RUN_END, () => {
91
65
  const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
92
66
  console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
93
67
  client.updateRunStatus(status);
94
-
95
- artifactStore.cleanup();
96
68
  });
97
69
  }
98
70
 
@@ -3,10 +3,10 @@ const crypto = require('crypto');
3
3
  const os = require('os');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE } = require('../constants');
6
+ const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
7
7
  const TestomatioClient = require('../client');
8
8
  const { isArtifactsEnabled } = require('../fileUploader');
9
- const { parseTest, fileSystem } = require('../util');
9
+ const { parseTest, fileSystem } = require('../utils/utils');
10
10
 
11
11
  const reportTestPromises = [];
12
12
 
@@ -19,15 +19,17 @@ class PlaywrightReporter {
19
19
 
20
20
  onBegin(_config, suite) {
21
21
  // clean data storage
22
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
22
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
23
23
  if (!this.client) return;
24
24
  this.suite = suite;
25
25
  this.client.createRun();
26
26
  }
27
27
 
28
- // onTestBegin(test) {
29
- // const testId = parseTest(test.title);
30
- // }
28
+ /*
29
+ onTestBegin(test) {
30
+ // does not work; value is not to the storage context
31
+ global.testTitle = test.title;
32
+ } */
31
33
 
32
34
  onTestEnd(test, result) {
33
35
  if (!this.client) return;
@@ -44,23 +46,33 @@ class PlaywrightReporter {
44
46
  appendStep(step, steps);
45
47
  }
46
48
 
47
- const logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
48
-
49
- const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
50
- error,
51
- test_id: testId,
52
- suite_title,
53
- title,
54
- steps: steps.join('\n'),
55
- time: duration,
56
- stack: logs,
57
- }).then(pipes => {
58
- testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
59
-
60
- this.uploads.push({
61
- testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
49
+ let logs = '';
50
+ if (result.stderr.length || result.stdout.length) {
51
+ logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
52
+ }
53
+
54
+ const reportTestPromise = this.client
55
+ .addTestRun(checkStatus(result.status), {
56
+ error,
57
+ test_id: testId,
58
+ suite_title,
59
+ title,
60
+ steps: steps.join('\n'),
61
+ time: duration,
62
+ stack: logs,
63
+ })
64
+ .then(pipes => {
65
+ testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
66
+
67
+ this.uploads.push({
68
+ testId,
69
+ title,
70
+ suite_title,
71
+ files: result.attachments.filter(a => a.body || a.path),
72
+ });
73
+ // remove empty uploads
74
+ this.uploads = this.uploads.filter(upload => upload.files.length);
62
75
  });
63
- });
64
76
 
65
77
  reportTestPromises.push(reportTestPromise);
66
78
  }
@@ -1,7 +1,7 @@
1
1
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
2
2
  const WDIOReporter = require('@wdio/reporter').default;
3
3
  const TestomatClient = require('../client');
4
- const { parseTest } = require('../util');
4
+ const { parseTest } = require('../utils/utils');
5
5
 
6
6
  class WebdriverReporter extends WDIOReporter {
7
7
  constructor(options) {
@@ -23,6 +23,7 @@ program
23
23
  }
24
24
  let { javaTests, lang } = opts;
25
25
  if (opts.envFile) {
26
+ console.log(APP_PREFIX, 'Loading env file:', opts.envFile);
26
27
  debug('Loading env file: %s', opts.envFile)
27
28
  require('dotenv').config({ path: opts.envFile }); // eslint-disable-line
28
29
  }
@@ -13,9 +13,11 @@ program
13
13
  .option('--launch', 'Start a new run and return its ID')
14
14
  .option('--finish', 'Finish Run by its ID')
15
15
  .option("--env-file <envfile>", "Load environment variables from env file")
16
- .action(opts => {
16
+ .option("--filter <filter>", "Additional execution filter")
17
+ .action(async (opts) => {
18
+ const { launch, finish, filter } = opts;
19
+ let { command } = opts;
17
20
 
18
- const { command, launch, finish } = opts;
19
21
  if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
20
22
 
21
23
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || process.env.TESTOMATIO;
@@ -57,6 +59,27 @@ program
57
59
  return;
58
60
  }
59
61
 
62
+ const client = new TestomatClient({ apiKey, title, parallel: true });
63
+
64
+ if(filter) {
65
+ const [pipe, ...optsArray] = filter.split(":");
66
+ const pipeOptions = optsArray.join(":");
67
+
68
+ try {
69
+ const tests = await client.prepareRun({pipe, pipeOptions});
70
+
71
+ if(!tests || tests.length === 0) {
72
+ return;
73
+ }
74
+
75
+ const grep = ` --grep (${tests.join('|')})`;
76
+ command += grep;
77
+ }
78
+ catch(err) {
79
+ console.log(APP_PREFIX, err);
80
+ }
81
+ }
82
+
60
83
  const testCmds = command.split(' ');
61
84
  console.log(APP_PREFIX, `🚀 Running`, chalk.green(command));
62
85
 
@@ -73,8 +96,6 @@ program
73
96
  return;
74
97
  }
75
98
 
76
- const client = new TestomatClient({ apiKey, title, parallel: true });
77
-
78
99
  client.createRun().then(() => {
79
100
  const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
80
101