vitest-qase-reporter 1.0.0
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/LICENSE +201 -0
- package/README.md +272 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +307 -0
- package/dist/vitest.d.ts +41 -0
- package/dist/vitest.js +183 -0
- package/docs/usage.md +270 -0
- package/package.json +57 -0
- package/tsconfig.build.json +10 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VitestQaseReporter = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
5
|
+
class VitestQaseReporter {
|
|
6
|
+
reporter;
|
|
7
|
+
currentSuite = undefined;
|
|
8
|
+
testMetadata = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* @type {RegExp}
|
|
11
|
+
*/
|
|
12
|
+
static qaseIdRegExp = /\(Qase ID: ([\d,]+)\)/;
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} title
|
|
15
|
+
* @returns {number[]}
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
static getCaseId(title) {
|
|
19
|
+
const [, ids] = title.match(VitestQaseReporter.qaseIdRegExp) ?? [];
|
|
20
|
+
return ids ? ids.split(',').map((id) => Number(id)) : [];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} title
|
|
24
|
+
* @returns {string}
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
static removeQaseIdsFromTitle(title) {
|
|
28
|
+
const matches = title.match(VitestQaseReporter.qaseIdRegExp);
|
|
29
|
+
if (matches) {
|
|
30
|
+
return title.replace(matches[0], '').trimEnd();
|
|
31
|
+
}
|
|
32
|
+
return title;
|
|
33
|
+
}
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
const composedOptions = (0, qase_javascript_commons_1.composeOptions)(options, {});
|
|
36
|
+
this.reporter = qase_javascript_commons_1.QaseReporter.getInstance({
|
|
37
|
+
...composedOptions,
|
|
38
|
+
frameworkPackage: 'vitest',
|
|
39
|
+
frameworkName: 'vitest',
|
|
40
|
+
reporterName: 'vitest-qase-reporter',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Convert Vitest test result status to Qase TestStatusEnum
|
|
45
|
+
*/
|
|
46
|
+
convertStatus(result) {
|
|
47
|
+
switch (result.state) {
|
|
48
|
+
case 'passed':
|
|
49
|
+
return qase_javascript_commons_1.TestStatusEnum.passed;
|
|
50
|
+
case 'failed':
|
|
51
|
+
return qase_javascript_commons_1.TestStatusEnum.failed;
|
|
52
|
+
case 'skipped':
|
|
53
|
+
return qase_javascript_commons_1.TestStatusEnum.skipped;
|
|
54
|
+
case 'pending':
|
|
55
|
+
return qase_javascript_commons_1.TestStatusEnum.skipped;
|
|
56
|
+
default:
|
|
57
|
+
return qase_javascript_commons_1.TestStatusEnum.skipped;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create TestResultType from Vitest TestCase
|
|
62
|
+
*/
|
|
63
|
+
createTestResult(testCase) {
|
|
64
|
+
const result = testCase.result();
|
|
65
|
+
const qaseIds = VitestQaseReporter.getCaseId(testCase.name);
|
|
66
|
+
const diagnostic = testCase.diagnostic();
|
|
67
|
+
const testId = testCase.id ?? testCase.name;
|
|
68
|
+
const metadata = this.testMetadata.get(testId);
|
|
69
|
+
// Use title from metadata if available, otherwise use test name
|
|
70
|
+
const testTitle = metadata?.title ?? VitestQaseReporter.removeQaseIdsFromTitle(testCase.name);
|
|
71
|
+
const testResult = new qase_javascript_commons_1.TestResultType(testTitle);
|
|
72
|
+
testResult.id = testCase.id ?? '';
|
|
73
|
+
testResult.signature = testCase.fullName ?? testCase.name;
|
|
74
|
+
// Set testops_id based on extracted qase IDs
|
|
75
|
+
if (qaseIds.length > 0) {
|
|
76
|
+
testResult.testops_id = qaseIds.length === 1 ? qaseIds[0] ?? null : qaseIds;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
testResult.testops_id = null;
|
|
80
|
+
}
|
|
81
|
+
// Set relations for test suite
|
|
82
|
+
const suiteToUse = metadata?.suite ?? this.currentSuite ?? this.extractSuiteFromTestCase(testCase);
|
|
83
|
+
if (suiteToUse) {
|
|
84
|
+
testResult.relations = {
|
|
85
|
+
suite: {
|
|
86
|
+
data: [],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const suites = suiteToUse.split(' - ');
|
|
90
|
+
suites.forEach((suite) => {
|
|
91
|
+
testResult.relations.suite.data.push({ title: suite.trim(), public_id: null });
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Set execution details
|
|
95
|
+
testResult.execution.status = this.convertStatus(result);
|
|
96
|
+
testResult.execution.start_time = diagnostic?.startTime ? diagnostic.startTime / 1000 : null;
|
|
97
|
+
testResult.execution.end_time = diagnostic?.startTime ? diagnostic.startTime / 1000 + diagnostic.duration : null;
|
|
98
|
+
testResult.execution.duration = Math.round(diagnostic?.duration || 0);
|
|
99
|
+
if (result?.errors && result.errors.length > 0) {
|
|
100
|
+
testResult.execution.stacktrace = result.errors.map((error) => {
|
|
101
|
+
if (error && typeof error === 'object' && 'stack' in error && 'message' in error) {
|
|
102
|
+
return error.stack ?? error.message ?? String(error);
|
|
103
|
+
}
|
|
104
|
+
return String(error);
|
|
105
|
+
}).join('\n');
|
|
106
|
+
testResult.message = result.errors[0] && typeof result.errors[0] === 'object' && 'message' in result.errors[0]
|
|
107
|
+
? String(result.errors[0].message) ?? 'Test failed'
|
|
108
|
+
: 'Test failed';
|
|
109
|
+
}
|
|
110
|
+
if (result.state === 'skipped') {
|
|
111
|
+
testResult.message = result && typeof result === 'object' && 'note' in result ? result.note ?? null : null;
|
|
112
|
+
}
|
|
113
|
+
// Add metadata from annotations
|
|
114
|
+
if (metadata) {
|
|
115
|
+
// Add comment if available - store in message field since execution doesn't have comment
|
|
116
|
+
if (metadata.comment) {
|
|
117
|
+
testResult.message = metadata.comment;
|
|
118
|
+
}
|
|
119
|
+
// Add fields if available
|
|
120
|
+
if (metadata.fields) {
|
|
121
|
+
testResult.fields = metadata.fields;
|
|
122
|
+
}
|
|
123
|
+
// Add parameters if available
|
|
124
|
+
if (metadata.parameters) {
|
|
125
|
+
testResult.params = metadata.parameters;
|
|
126
|
+
}
|
|
127
|
+
// Add group parameters if available
|
|
128
|
+
if (metadata.groupParameters) {
|
|
129
|
+
testResult.group_params = metadata.groupParameters;
|
|
130
|
+
}
|
|
131
|
+
// Add steps if available - create proper TestStepType objects
|
|
132
|
+
if (metadata.steps.length > 0) {
|
|
133
|
+
testResult.steps = metadata.steps.map(step => {
|
|
134
|
+
const stepObj = new qase_javascript_commons_1.TestStepType();
|
|
135
|
+
stepObj.id = Math.random().toString(36).substr(2, 9);
|
|
136
|
+
stepObj.data = {
|
|
137
|
+
action: step.name,
|
|
138
|
+
expected_result: null,
|
|
139
|
+
data: null
|
|
140
|
+
};
|
|
141
|
+
stepObj.execution.status = step.status === 'failed' ? qase_javascript_commons_1.StepStatusEnum.failed : qase_javascript_commons_1.StepStatusEnum.passed;
|
|
142
|
+
return stepObj;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Add attachments if available
|
|
146
|
+
if (metadata.attachments.length > 0) {
|
|
147
|
+
testResult.attachments = metadata.attachments.map(attachment => {
|
|
148
|
+
const attachmentModel = {
|
|
149
|
+
file_name: attachment.name,
|
|
150
|
+
mime_type: attachment.contentType || 'application/octet-stream',
|
|
151
|
+
file_path: attachment.path || null,
|
|
152
|
+
content: attachment.content || '',
|
|
153
|
+
size: attachment.content ? Buffer.byteLength(attachment.content) : 0,
|
|
154
|
+
id: Math.random().toString(36).substr(2, 9)
|
|
155
|
+
};
|
|
156
|
+
return attachmentModel;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Clean up metadata after processing
|
|
161
|
+
this.testMetadata.delete(testId);
|
|
162
|
+
return testResult;
|
|
163
|
+
}
|
|
164
|
+
onTestRunStart() {
|
|
165
|
+
this.reporter.startTestRun();
|
|
166
|
+
}
|
|
167
|
+
async onTestRunEnd() {
|
|
168
|
+
await this.reporter.publish();
|
|
169
|
+
}
|
|
170
|
+
async onTestCaseResult(testCase) {
|
|
171
|
+
const testResult = this.createTestResult(testCase);
|
|
172
|
+
await this.reporter.addTestResult(testResult);
|
|
173
|
+
}
|
|
174
|
+
onTestCaseAnnotate(testCase, annotation) {
|
|
175
|
+
const testId = testCase.id ?? testCase.name;
|
|
176
|
+
// Initialize metadata if not exists
|
|
177
|
+
if (!this.testMetadata.has(testId)) {
|
|
178
|
+
this.testMetadata.set(testId, {
|
|
179
|
+
steps: [],
|
|
180
|
+
attachments: []
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
const metadata = this.testMetadata.get(testId);
|
|
184
|
+
if (!metadata)
|
|
185
|
+
return;
|
|
186
|
+
// Process qase annotations
|
|
187
|
+
// Check if this is a qase annotation by looking at the message pattern
|
|
188
|
+
if (annotation.message && annotation.message.startsWith('Qase ')) {
|
|
189
|
+
const qaseType = annotation.message.split(':')[0]?.replace('Qase ', '').toLowerCase().replace(' ', '-') ?? '';
|
|
190
|
+
switch (qaseType) {
|
|
191
|
+
case 'title': {
|
|
192
|
+
metadata.title = annotation.message.replace('Qase Title: ', '');
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case 'comment': {
|
|
196
|
+
metadata.comment = annotation.message.replace('Qase Comment: ', '');
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case 'suite': {
|
|
200
|
+
metadata.suite = annotation.message.replace('Qase Suite: ', '');
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case 'fields': {
|
|
204
|
+
const fieldsData = annotation.message.replace('Qase Fields: ', '');
|
|
205
|
+
try {
|
|
206
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
207
|
+
const parsed = JSON.parse(fieldsData);
|
|
208
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
209
|
+
metadata.fields = parsed;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
console.warn('Failed to parse qase fields:', fieldsData);
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case 'parameters': {
|
|
218
|
+
const parametersData = annotation.message.replace('Qase Parameters: ', '');
|
|
219
|
+
try {
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
221
|
+
const parsed = JSON.parse(parametersData);
|
|
222
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
223
|
+
metadata.parameters = parsed;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
console.warn('Failed to parse qase parameters:', parametersData);
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
case 'group-parameters': {
|
|
232
|
+
const groupParametersData = annotation.message.replace('Qase Group Parameters: ', '');
|
|
233
|
+
try {
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
235
|
+
const parsed = JSON.parse(groupParametersData);
|
|
236
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
237
|
+
metadata.groupParameters = parsed;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
console.warn('Failed to parse qase group parameters:', groupParametersData);
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case 'step-start': {
|
|
246
|
+
const stepStartData = annotation.message.replace('Qase Step Start: ', '');
|
|
247
|
+
metadata.currentStep = stepStartData;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case 'step-end': {
|
|
251
|
+
if (metadata.currentStep) {
|
|
252
|
+
metadata.steps.push({ name: metadata.currentStep, status: 'end' });
|
|
253
|
+
delete metadata.currentStep;
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case 'step-failed': {
|
|
258
|
+
if (metadata.currentStep) {
|
|
259
|
+
metadata.steps.push({ name: metadata.currentStep, status: 'failed' });
|
|
260
|
+
delete metadata.currentStep;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case 'attachment': {
|
|
265
|
+
const attachmentName = annotation.message.replace('Qase Attachment: ', '');
|
|
266
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
267
|
+
const attachment = {
|
|
268
|
+
name: attachmentName,
|
|
269
|
+
...(annotation.attachment?.path && { path: annotation.attachment.path }),
|
|
270
|
+
...(annotation.attachment?.body && {
|
|
271
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
272
|
+
content: typeof annotation.attachment.body === 'string'
|
|
273
|
+
? annotation.attachment.body
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
275
|
+
: new TextDecoder().decode(annotation.attachment.body)
|
|
276
|
+
}),
|
|
277
|
+
...(annotation.attachment?.contentType && { contentType: annotation.attachment.contentType })
|
|
278
|
+
};
|
|
279
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
280
|
+
metadata.attachments.push(attachment);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
onTestSuiteReady(testSuite) {
|
|
287
|
+
this.currentSuite = testSuite.name;
|
|
288
|
+
}
|
|
289
|
+
onTestSuiteResult() {
|
|
290
|
+
this.currentSuite = undefined;
|
|
291
|
+
}
|
|
292
|
+
extractSuiteFromTestCase(testCase) {
|
|
293
|
+
// Extract suite from testCase.fullName or testCase.name
|
|
294
|
+
// Format is usually "Suite Name > Test Name" or just "Test Name"
|
|
295
|
+
const fullName = testCase.fullName ?? testCase.name;
|
|
296
|
+
const parts = fullName.split(' > ');
|
|
297
|
+
if (parts.length > 1) {
|
|
298
|
+
// Return the suite part (everything except the last part)
|
|
299
|
+
return parts.slice(0, -1).join(' > ');
|
|
300
|
+
}
|
|
301
|
+
// If no suite separator found, return undefined
|
|
302
|
+
// The test will be assigned to the default suite
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
exports.VitestQaseReporter = VitestQaseReporter;
|
|
307
|
+
exports.default = VitestQaseReporter;
|
package/dist/vitest.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type StepFunction = () => Promise<void> | void;
|
|
2
|
+
export declare const addQaseId: (name: string, caseIds: number[]) => string;
|
|
3
|
+
type AnnotateFunction = (message: string, options?: unknown) => Promise<void>;
|
|
4
|
+
export interface QaseWrapper {
|
|
5
|
+
annotate(message: string, options?: unknown): Promise<void>;
|
|
6
|
+
title(value: string): Promise<void>;
|
|
7
|
+
comment(value: string): Promise<void>;
|
|
8
|
+
suite(value: string): Promise<void>;
|
|
9
|
+
fields(values: Record<string, string>): Promise<void>;
|
|
10
|
+
parameters(values: Record<string, string>): Promise<void>;
|
|
11
|
+
groupParameters(values: Record<string, string>): Promise<void>;
|
|
12
|
+
step(name: string, body: StepFunction): Promise<void>;
|
|
13
|
+
attach(attach: {
|
|
14
|
+
name?: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
content?: string;
|
|
17
|
+
paths?: string[];
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface TestContextWithQase {
|
|
21
|
+
qase: QaseWrapper;
|
|
22
|
+
annotate: AnnotateFunction;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Higher-order function that extends test context with qase functions
|
|
26
|
+
* @param testFn - Test function that receives context with qase
|
|
27
|
+
* @returns Wrapped test function
|
|
28
|
+
* @example
|
|
29
|
+
* test('hello world', withQase(async ({ qase, annotate }) => {
|
|
30
|
+
* await qase.title("My Test Title");
|
|
31
|
+
* await qase.comment("This is a test comment");
|
|
32
|
+
*
|
|
33
|
+
* if (condition) {
|
|
34
|
+
* await qase.annotate('this should\'ve errored', 'error');
|
|
35
|
+
* }
|
|
36
|
+
* }));
|
|
37
|
+
*/
|
|
38
|
+
export declare const withQase: <T extends unknown[]>(testFn: (context: TestContextWithQase & T[0]) => unknown) => (context: T[0] & {
|
|
39
|
+
annotate: AnnotateFunction;
|
|
40
|
+
}) => Promise<unknown>;
|
|
41
|
+
export {};
|
package/dist/vitest.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withQase = exports.addQaseId = void 0;
|
|
7
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
// Function to add Qase ID to test name
|
|
10
|
+
const addQaseId = (name, caseIds) => {
|
|
11
|
+
return `${name} (Qase ID: ${caseIds.join(',')})`;
|
|
12
|
+
};
|
|
13
|
+
exports.addQaseId = addQaseId;
|
|
14
|
+
/**
|
|
15
|
+
* Create qase wrapper for annotate function
|
|
16
|
+
* @param annotate - Vitest annotate function
|
|
17
|
+
* @returns QaseWrapper object with all qase methods
|
|
18
|
+
*/
|
|
19
|
+
const createQaseWrapper = (annotate) => {
|
|
20
|
+
return {
|
|
21
|
+
async annotate(message, options) {
|
|
22
|
+
return await annotate(message, options);
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* Set a title for the test case
|
|
26
|
+
* @param {string} value
|
|
27
|
+
* @example
|
|
28
|
+
* test('test', withQase(async ({ qase }) => {
|
|
29
|
+
* await qase.title("Title");
|
|
30
|
+
* expect(true).toBe(true);
|
|
31
|
+
* }));
|
|
32
|
+
*/
|
|
33
|
+
async title(value) {
|
|
34
|
+
return await annotate(`Qase Title: ${value}`, { type: 'qase-title', body: value });
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Add a comment to the test case
|
|
38
|
+
* @param {string} value
|
|
39
|
+
* @example
|
|
40
|
+
* test('test', withQase(async ({ qase }) => {
|
|
41
|
+
* await qase.comment("Comment");
|
|
42
|
+
* expect(true).toBe(true);
|
|
43
|
+
* }));
|
|
44
|
+
*/
|
|
45
|
+
async comment(value) {
|
|
46
|
+
return await annotate(`Qase Comment: ${value}`, { type: 'qase-comment', body: value });
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Set a suite for the test case
|
|
50
|
+
* @param {string} value
|
|
51
|
+
* @example
|
|
52
|
+
* test('test', withQase(async ({ qase }) => {
|
|
53
|
+
* await qase.suite("Suite");
|
|
54
|
+
* expect(true).toBe(true);
|
|
55
|
+
* }));
|
|
56
|
+
*/
|
|
57
|
+
async suite(value) {
|
|
58
|
+
return await annotate(`Qase Suite: ${value}`, { type: 'qase-suite', body: value });
|
|
59
|
+
},
|
|
60
|
+
/**
|
|
61
|
+
* Set fields for the test case
|
|
62
|
+
* @param {Record<string, string>} values
|
|
63
|
+
* @example
|
|
64
|
+
* test('test', withQase(async ({ qase }) => {
|
|
65
|
+
* await qase.fields({field: "value"});
|
|
66
|
+
* expect(true).toBe(true);
|
|
67
|
+
* }));
|
|
68
|
+
*/
|
|
69
|
+
async fields(values) {
|
|
70
|
+
return await annotate(`Qase Fields: ${JSON.stringify(values)}`, { type: 'qase-fields', body: JSON.stringify(values) });
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* Set parameters for the test case
|
|
74
|
+
* @param {Record<string, string>} values
|
|
75
|
+
* @example
|
|
76
|
+
* test('test', withQase(async ({ qase }) => {
|
|
77
|
+
* await qase.parameters({param: "value"});
|
|
78
|
+
* expect(true).toBe(true);
|
|
79
|
+
* }));
|
|
80
|
+
*/
|
|
81
|
+
async parameters(values) {
|
|
82
|
+
return await annotate(`Qase Parameters: ${JSON.stringify(values)}`, { type: 'qase-parameters', body: JSON.stringify(values) });
|
|
83
|
+
},
|
|
84
|
+
/**
|
|
85
|
+
* Set group parameters for the test case
|
|
86
|
+
* @param {Record<string, string>} values
|
|
87
|
+
* @example
|
|
88
|
+
* test('test', withQase(async ({ qase }) => {
|
|
89
|
+
* await qase.groupParameters({param: "value"});
|
|
90
|
+
* expect(true).toBe(true);
|
|
91
|
+
* }));
|
|
92
|
+
*/
|
|
93
|
+
async groupParameters(values) {
|
|
94
|
+
return await annotate(`Qase Group Parameters: ${JSON.stringify(values)}`, { type: 'qase-group-parameters', body: JSON.stringify(values) });
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* Add a step to the test case
|
|
98
|
+
* @param name
|
|
99
|
+
* @param body
|
|
100
|
+
* @example
|
|
101
|
+
* test('test', withQase(async ({ qase }) => {
|
|
102
|
+
* await qase.step("Step", () => {
|
|
103
|
+
* expect(true).toBe(true);
|
|
104
|
+
* });
|
|
105
|
+
* expect(true).toBe(true);
|
|
106
|
+
* }));
|
|
107
|
+
*/
|
|
108
|
+
async step(name, body) {
|
|
109
|
+
await annotate(`Qase Step Start: ${name}`, { type: 'qase-step-start', body: name });
|
|
110
|
+
try {
|
|
111
|
+
const runningStep = new qase_javascript_commons_1.QaseStep(name);
|
|
112
|
+
await runningStep.run(body, async (step) => {
|
|
113
|
+
const stepName = 'action' in step.data ? step.data.action : step.data.name;
|
|
114
|
+
await annotate(`Qase Step: ${stepName}`, { type: 'qase-step', body: stepName });
|
|
115
|
+
});
|
|
116
|
+
await annotate(`Qase Step End: ${name}`, { type: 'qase-step-end', body: name });
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
120
|
+
await annotate(`Qase Step Failed: ${name} - ${errorMessage}`, { type: 'qase-step-failed', body: `${name} - ${errorMessage}` });
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Add an attachment to the test case
|
|
126
|
+
* @param attach
|
|
127
|
+
* @example
|
|
128
|
+
* test('test', withQase(async ({ qase }) => {
|
|
129
|
+
* await qase.attach({ name: 'attachment.txt', content: 'Hello, world!', type: 'text/plain' });
|
|
130
|
+
* await qase.attach({ paths: ['/path/to/file', '/path/to/another/file']});
|
|
131
|
+
* expect(true).toBe(true);
|
|
132
|
+
* }));
|
|
133
|
+
*/
|
|
134
|
+
async attach(attach) {
|
|
135
|
+
if (attach.paths) {
|
|
136
|
+
for (const file of attach.paths) {
|
|
137
|
+
const attachmentName = path_1.default.basename(file);
|
|
138
|
+
const contentType = (0, qase_javascript_commons_1.getMimeTypes)(file);
|
|
139
|
+
await annotate(`Qase Attachment: ${attachmentName}`, {
|
|
140
|
+
type: 'qase-attachment',
|
|
141
|
+
body: attachmentName,
|
|
142
|
+
attachment: {
|
|
143
|
+
path: file,
|
|
144
|
+
contentType: contentType
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (attach.content) {
|
|
151
|
+
await annotate(`Qase Attachment: ${attach.name ?? 'attachment'}`, {
|
|
152
|
+
type: 'qase-attachment',
|
|
153
|
+
body: attach.content,
|
|
154
|
+
attachment: {
|
|
155
|
+
contentType: attach.type ?? 'application/octet-stream',
|
|
156
|
+
body: attach.content
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Higher-order function that extends test context with qase functions
|
|
165
|
+
* @param testFn - Test function that receives context with qase
|
|
166
|
+
* @returns Wrapped test function
|
|
167
|
+
* @example
|
|
168
|
+
* test('hello world', withQase(async ({ qase, annotate }) => {
|
|
169
|
+
* await qase.title("My Test Title");
|
|
170
|
+
* await qase.comment("This is a test comment");
|
|
171
|
+
*
|
|
172
|
+
* if (condition) {
|
|
173
|
+
* await qase.annotate('this should\'ve errored', 'error');
|
|
174
|
+
* }
|
|
175
|
+
* }));
|
|
176
|
+
*/
|
|
177
|
+
const withQase = (testFn) => {
|
|
178
|
+
return async (context) => {
|
|
179
|
+
const qase = createQaseWrapper(context.annotate);
|
|
180
|
+
return await testFn({ ...context, qase });
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
exports.withQase = withQase;
|