mocha-qase-reporter 1.0.0-beta.3 → 1.0.0-beta.5

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
@@ -89,7 +89,7 @@ QASE_MODE=testops mocha --parallel
89
89
  After the tests are finished, you can complete the run:
90
90
 
91
91
  ```bash
92
- qli testops run complete --project DEMO --token token --run $(echo $QASE_TESTOPS_RUN_ID)
92
+ qli testops run complete --project DEMO --token token --id $(echo $QASE_TESTOPS_RUN_ID)
93
93
  ```
94
94
 
95
95
  ## Configuration
package/changelog.md CHANGED
@@ -1,3 +1,17 @@
1
+ # qase-mocha@1.0.0-beta.5
2
+
3
+ ## What's new
4
+
5
+ - Added a `qase` function to allow specifying QaseID for tests.
6
+ - Marked the old syntax for QaseID as deprecated.
7
+ - Implemented functionality to capture console logs and include them as attachments in tests.
8
+
9
+ # qase-mocha@1.0.0-beta.4
10
+
11
+ ## What's new
12
+
13
+ Fixed the issue with async tests not being reported correctly.
14
+
1
15
  # qase-mocha@1.0.0-beta.3
2
16
 
3
17
  ## What's new
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { Writable } from 'stream';
4
+ export interface TestOutput {
5
+ stdout: string;
6
+ stderr: string;
7
+ }
8
+ export declare class StreamInterceptor extends Writable {
9
+ private readonly onWrite;
10
+ constructor(onWriteCallback: (data: string) => void);
11
+ _write(chunk: any, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
12
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamInterceptor = void 0;
4
+ const stream_1 = require("stream");
5
+ class StreamInterceptor extends stream_1.Writable {
6
+ constructor(onWriteCallback) {
7
+ super();
8
+ this.onWrite = onWriteCallback;
9
+ }
10
+ _write(chunk, _encoding, callback) {
11
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-call
12
+ this.onWrite(chunk.toString());
13
+ callback();
14
+ }
15
+ }
16
+ exports.StreamInterceptor = StreamInterceptor;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Set IDs for the test case
3
+ *
4
+ * @param caseId
5
+ * @param name
6
+ * @example
7
+ * it(qase(1, 'test'), function() => {
8
+ * // test code
9
+ * });
10
+ * @returns {string}
11
+ */
12
+ export declare const qase: (caseId: number | string | number[] | string[], name: string) => string;
package/dist/mocha.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.qase = void 0;
4
+ /**
5
+ * Set IDs for the test case
6
+ *
7
+ * @param caseId
8
+ * @param name
9
+ * @example
10
+ * it(qase(1, 'test'), function() => {
11
+ * // test code
12
+ * });
13
+ * @returns {string}
14
+ */
15
+ const qase = (caseId, name) => {
16
+ const caseIds = Array.isArray(caseId) ? caseId : [caseId];
17
+ const ids = [];
18
+ for (const id of caseIds) {
19
+ if (typeof id === 'number') {
20
+ ids.push(id);
21
+ continue;
22
+ }
23
+ const parsedId = parseInt(id);
24
+ if (!isNaN(parsedId)) {
25
+ ids.push(parsedId);
26
+ continue;
27
+ }
28
+ console.log(`qase: qase ID ${id} should be a number`);
29
+ }
30
+ return `${name} (Qase ID: ${caseIds.join(',')})`;
31
+ };
32
+ exports.qase = qase;
@@ -3,6 +3,9 @@ import { MochaOptions, reporters, Runner } from 'mocha';
3
3
  import { ConfigLoader, TestStatusEnum } from 'qase-javascript-commons';
4
4
  type MochaState = 'failed' | 'passed' | 'pending';
5
5
  export declare class MochaQaseReporter extends reporters.Base {
6
+ private originalStdoutWrite;
7
+ private originalStderrWrite;
8
+ private testOutputs;
6
9
  /**
7
10
  * @type {Record<CypressState, TestStatusEnum>}
8
11
  */
@@ -23,6 +26,7 @@ export declare class MochaQaseReporter extends reporters.Base {
23
26
  private applyListeners;
24
27
  private onStartRun;
25
28
  private onEndRun;
29
+ private addMethodsToContext;
26
30
  private addMethods;
27
31
  private onStartTest;
28
32
  private onEndTest;
@@ -63,5 +67,21 @@ export declare class MochaQaseReporter extends reporters.Base {
63
67
  }) => void;
64
68
  comment: (message: string) => void;
65
69
  step: (title: string, func: () => void) => void;
70
+ /**
71
+ * @param {string} title
72
+ * @returns {string}
73
+ * @private
74
+ */
75
+ private removeQaseIdsFromTitle;
76
+ /**
77
+ * @type {RegExp}
78
+ */
79
+ static qaseIdRegExp: RegExp;
80
+ /**
81
+ * @param {string} title
82
+ * @returns {number[]}
83
+ * @private
84
+ */
85
+ private static getCaseId;
66
86
  }
67
87
  export {};
package/dist/reporter.js CHANGED
@@ -10,6 +10,7 @@ const qase_javascript_commons_1 = require("qase-javascript-commons");
10
10
  const deasync_promise_1 = __importDefault(require("deasync-promise"));
11
11
  const node_path_1 = require("node:path");
12
12
  const uuid_1 = require("uuid");
13
+ const interceptor_1 = require("./interceptor");
13
14
  const Events = mocha_1.Runner.constants;
14
15
  class currentTest {
15
16
  constructor() {
@@ -33,9 +34,10 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
33
34
  this.applyListeners = () => {
34
35
  this.runner.on(Events.EVENT_RUN_BEGIN, () => this.onStartRun());
35
36
  this.runner.on(Events.EVENT_RUN_END, () => this.onEndRun());
36
- this.runner.on(Events.EVENT_TEST_BEGIN, (test) => this.addMethods(test.ctx));
37
- this.runner.on(Events.EVENT_HOOK_BEGIN, (hook) => this.addMethods(hook.ctx));
37
+ this.runner.on(Events.EVENT_TEST_BEGIN, (test) => this.addMethods(test));
38
+ this.runner.on(Events.EVENT_HOOK_BEGIN, (hook) => this.addMethodsToContext(hook.ctx));
38
39
  this.runner.on(Events.EVENT_TEST_BEGIN, () => this.onStartTest());
40
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
39
41
  this.runner.on(Events.EVENT_TEST_END, (test) => this.onEndTest(test));
40
42
  };
41
43
  this.qaseId = (id) => {
@@ -122,6 +124,9 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
122
124
  else {
123
125
  this.applyListeners();
124
126
  }
127
+ this.originalStdoutWrite = process.stdout.write.bind(process.stdout);
128
+ this.originalStderrWrite = process.stderr.write.bind(process.stderr);
129
+ this.testOutputs = new Map();
125
130
  }
126
131
  onStartRun() {
127
132
  this.reporter.startTestRun();
@@ -129,7 +134,7 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
129
134
  onEndRun() {
130
135
  (0, deasync_promise_1.default)(this.reporter.publish());
131
136
  }
132
- addMethods(ctx) {
137
+ addMethodsToContext(ctx) {
133
138
  if (!ctx)
134
139
  return;
135
140
  ctx.qaseId = this.qaseId;
@@ -143,14 +148,50 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
143
148
  ctx.comment = this.comment;
144
149
  ctx.step = this.step;
145
150
  }
151
+ addMethods(test) {
152
+ const stdoutInterceptor = new interceptor_1.StreamInterceptor((data) => {
153
+ const output = this.testOutputs.get(test.title) ?? { stdout: '', stderr: '' };
154
+ output.stdout += data;
155
+ this.testOutputs.set(test.title, output);
156
+ });
157
+ const stderrInterceptor = new interceptor_1.StreamInterceptor((data) => {
158
+ const output = this.testOutputs.get(test.title) ?? { stdout: '', stderr: '' };
159
+ output.stderr += data;
160
+ this.testOutputs.set(test.title, output);
161
+ });
162
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
163
+ // @ts-ignore
164
+ process.stdout.write = stdoutInterceptor.write.bind(stdoutInterceptor);
165
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
166
+ // @ts-ignore
167
+ process.stderr.write = stderrInterceptor.write.bind(stderrInterceptor);
168
+ this.testOutputs.set(test.title, { stdout: '', stderr: '' });
169
+ this.addMethodsToContext(test.ctx);
170
+ }
146
171
  onStartTest() {
147
172
  this.currentType = 'test';
148
173
  }
149
174
  onEndTest(test) {
175
+ process.stdout.write = this.originalStdoutWrite;
176
+ process.stderr.write = this.originalStderrWrite;
177
+ if (this.reporter.isCaptureLogs()) {
178
+ const output = this.testOutputs.get(test.title);
179
+ if (output?.stdout) {
180
+ this.attach({ name: 'stdout.txt', content: output.stdout, contentType: 'text/plain' });
181
+ }
182
+ if (output?.stderr) {
183
+ this.attach({ name: 'stderr.txt', content: output.stderr, contentType: 'text/plain' });
184
+ }
185
+ }
150
186
  if (this.metadata.ignore) {
187
+ this.metadata.clear();
188
+ this.currentTest = new currentTest();
151
189
  return;
152
190
  }
153
191
  const ids = this.getQaseId();
192
+ if (ids.length === 0) {
193
+ ids.push(...MochaQaseReporter.getCaseId(test.title));
194
+ }
154
195
  const suites = this.getSuites(test);
155
196
  let relations = {};
156
197
  if (suites.length > 0) {
@@ -195,7 +236,7 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
195
236
  thread: null,
196
237
  },
197
238
  testops_id: ids.length > 0 ? ids : null,
198
- title: this.metadata.title && this.metadata.title != '' ? this.metadata.title : test.title,
239
+ title: this.metadata.title && this.metadata.title != '' ? this.metadata.title : this.removeQaseIdsFromTitle(test.title),
199
240
  };
200
241
  (0, deasync_promise_1.default)(this.reporter.addTestResult(result));
201
242
  this.metadata.clear();
@@ -263,6 +304,27 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
263
304
  }
264
305
  return suites;
265
306
  }
307
+ /**
308
+ * @param {string} title
309
+ * @returns {string}
310
+ * @private
311
+ */
312
+ removeQaseIdsFromTitle(title) {
313
+ const matches = title.match(MochaQaseReporter.qaseIdRegExp);
314
+ if (matches) {
315
+ return title.replace(matches[0], '').trimEnd();
316
+ }
317
+ return title;
318
+ }
319
+ /**
320
+ * @param {string} title
321
+ * @returns {number[]}
322
+ * @private
323
+ */
324
+ static getCaseId(title) {
325
+ const [, ids] = title.match(MochaQaseReporter.qaseIdRegExp) ?? [];
326
+ return ids ? ids.split(',').map((id) => Number(id)) : [];
327
+ }
266
328
  }
267
329
  exports.MochaQaseReporter = MochaQaseReporter;
268
330
  /**
@@ -273,3 +335,7 @@ MochaQaseReporter.statusMap = {
273
335
  passed: qase_javascript_commons_1.TestStatusEnum.passed,
274
336
  pending: qase_javascript_commons_1.TestStatusEnum.skipped,
275
337
  };
338
+ /**
339
+ * @type {RegExp}
340
+ */
341
+ MochaQaseReporter.qaseIdRegExp = /\(Qase ID: ([\d,]+)\)/;
package/dist/types.d.ts CHANGED
@@ -13,6 +13,10 @@ export interface Hook extends Mocha.Hook {
13
13
  ctx?: Context;
14
14
  }
15
15
  export interface Methods {
16
+ /**
17
+ * Set IDs for the test case
18
+ * Use `qase()` instead. This method is deprecated and kept for reverse compatibility.
19
+ */
16
20
  qaseId(id: number | number[]): void;
17
21
  title(title: string): void;
18
22
  parameters(values: Record<string, string>): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mocha-qase-reporter",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "Mocha Cypress Reporter",
5
5
  "homepage": "https://github.com/qase-tms/qase-javascript",
6
6
  "sideEffects": false,
@@ -9,7 +9,8 @@
9
9
  "exports": {
10
10
  ".": "./dist/index.js",
11
11
  "./reporter": "./dist/reporter.js",
12
- "./package.json": "./package.json"
12
+ "./package.json": "./package.json",
13
+ "./mocha": "./dist/mocha.js"
13
14
  },
14
15
  "typesVersions": {
15
16
  "*": {