mocha-qase-reporter 1.0.0-beta.4 → 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,11 @@
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
+
1
9
  # qase-mocha@1.0.0-beta.4
2
10
 
3
11
  ## 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,11 +34,11 @@ 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());
39
40
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
40
- this.runner.on(Events.EVENT_TEST_END, async (test) => await this.onEndTest(test));
41
+ this.runner.on(Events.EVENT_TEST_END, (test) => this.onEndTest(test));
41
42
  };
42
43
  this.qaseId = (id) => {
43
44
  this.metadata.addQaseId(id);
@@ -123,6 +124,9 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
123
124
  else {
124
125
  this.applyListeners();
125
126
  }
127
+ this.originalStdoutWrite = process.stdout.write.bind(process.stdout);
128
+ this.originalStderrWrite = process.stderr.write.bind(process.stderr);
129
+ this.testOutputs = new Map();
126
130
  }
127
131
  onStartRun() {
128
132
  this.reporter.startTestRun();
@@ -130,7 +134,7 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
130
134
  onEndRun() {
131
135
  (0, deasync_promise_1.default)(this.reporter.publish());
132
136
  }
133
- addMethods(ctx) {
137
+ addMethodsToContext(ctx) {
134
138
  if (!ctx)
135
139
  return;
136
140
  ctx.qaseId = this.qaseId;
@@ -144,14 +148,50 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
144
148
  ctx.comment = this.comment;
145
149
  ctx.step = this.step;
146
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
+ }
147
171
  onStartTest() {
148
172
  this.currentType = 'test';
149
173
  }
150
- async onEndTest(test) {
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
+ }
151
186
  if (this.metadata.ignore) {
187
+ this.metadata.clear();
188
+ this.currentTest = new currentTest();
152
189
  return;
153
190
  }
154
191
  const ids = this.getQaseId();
192
+ if (ids.length === 0) {
193
+ ids.push(...MochaQaseReporter.getCaseId(test.title));
194
+ }
155
195
  const suites = this.getSuites(test);
156
196
  let relations = {};
157
197
  if (suites.length > 0) {
@@ -196,9 +236,9 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
196
236
  thread: null,
197
237
  },
198
238
  testops_id: ids.length > 0 ? ids : null,
199
- 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),
200
240
  };
201
- await this.reporter.addTestResult(result);
241
+ (0, deasync_promise_1.default)(this.reporter.addTestResult(result));
202
242
  this.metadata.clear();
203
243
  this.currentTest = new currentTest();
204
244
  }
@@ -264,6 +304,27 @@ class MochaQaseReporter extends mocha_1.reporters.Base {
264
304
  }
265
305
  return suites;
266
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
+ }
267
328
  }
268
329
  exports.MochaQaseReporter = MochaQaseReporter;
269
330
  /**
@@ -274,3 +335,7 @@ MochaQaseReporter.statusMap = {
274
335
  passed: qase_javascript_commons_1.TestStatusEnum.passed,
275
336
  pending: qase_javascript_commons_1.TestStatusEnum.skipped,
276
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.4",
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
  "*": {