@appliqation/automation-sdk 2.1.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 +21 -0
- package/README.md +441 -0
- package/package.json +107 -0
- package/src/AppliqationClient.js +562 -0
- package/src/constants.js +245 -0
- package/src/core/AuthManager.js +353 -0
- package/src/core/HttpClient.js +475 -0
- package/src/index.d.ts +333 -0
- package/src/index.js +26 -0
- package/src/playwright/JwtBrowserAuth.js +240 -0
- package/src/playwright/fixture.js +92 -0
- package/src/playwright/global-setup.js +243 -0
- package/src/playwright/helpers/jwt-browser-auth.js +227 -0
- package/src/playwright/index.js +16 -0
- package/src/reporters/cypress/CypressReporter.js +387 -0
- package/src/reporters/cypress/UuidExtractor.js +139 -0
- package/src/reporters/cypress/index.js +30 -0
- package/src/reporters/jest/JestReporter.js +361 -0
- package/src/reporters/jest/UuidExtractor.js +174 -0
- package/src/reporters/jest/index.js +28 -0
- package/src/reporters/playwright/AppliqationReporter.js +654 -0
- package/src/reporters/playwright/helpers/DeviceOsDetector.js +435 -0
- package/src/reporters/playwright/helpers/UuidExtractor.js +290 -0
- package/src/reporters/playwright/index.d.ts +96 -0
- package/src/reporters/playwright/index.js +14 -0
- package/src/services/OrphanTestService.js +74 -0
- package/src/services/ResultService.js +252 -0
- package/src/services/RunMatrixService.js +309 -0
- package/src/utils/PayloadBuilder.js +280 -0
- package/src/utils/RunDataNormalizer.js +335 -0
- package/src/utils/UuidValidator.js +102 -0
- package/src/utils/errors.js +217 -0
- package/src/utils/index.js +17 -0
- package/src/utils/logger.js +124 -0
- package/src/utils/mapAppqUuid.js +83 -0
- package/src/utils/validator.js +157 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
const AppliqationClient = require('../../AppliqationClient');
|
|
2
|
+
const UuidExtractor = require('./UuidExtractor');
|
|
3
|
+
const logger = require('../../utils/logger');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Appliqation Reporter for Jest
|
|
7
|
+
*
|
|
8
|
+
* Custom Jest reporter that automatically reports test results
|
|
9
|
+
* to the Appliqation platform.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // jest.config.js
|
|
13
|
+
* const { JestReporter } = require('@appliqation/automation-sdk/jest');
|
|
14
|
+
*
|
|
15
|
+
* module.exports = {
|
|
16
|
+
* reporters: [
|
|
17
|
+
* 'default',
|
|
18
|
+
* [JestReporter, {
|
|
19
|
+
* baseUrl: process.env.APPLIQATION_BASE_URL,
|
|
20
|
+
* apiKey: process.env.APPLIQATION_API_KEY,
|
|
21
|
+
* projectKey: process.env.APPLIQATION_PROJECT_KEY,
|
|
22
|
+
* scenarioId: parseInt(process.env.APPLIQATION_SCENARIO_ID),
|
|
23
|
+
* environment: process.env.APPLIQATION_ENVIRONMENT || 'Local',
|
|
24
|
+
* title: process.env.APPLIQATION_RUN_TITLE
|
|
25
|
+
* }]
|
|
26
|
+
* ]
|
|
27
|
+
* };
|
|
28
|
+
*/
|
|
29
|
+
class JestReporter {
|
|
30
|
+
constructor(globalConfig, options) {
|
|
31
|
+
this.globalConfig = globalConfig;
|
|
32
|
+
this.config = {
|
|
33
|
+
baseUrl: options.baseUrl,
|
|
34
|
+
apiKey: options.apiKey,
|
|
35
|
+
projectKey: options.projectKey,
|
|
36
|
+
scenarioId: options.scenarioId,
|
|
37
|
+
testSetId: options.testSetId,
|
|
38
|
+
environment: options.environment || 'Local',
|
|
39
|
+
title: options.title || process.env.APPLIQATION_RUN_TITLE,
|
|
40
|
+
autoCreateRun: options.autoCreateRun !== false,
|
|
41
|
+
logLevel: options.logLevel || 'info',
|
|
42
|
+
submitOrphans: options.submitOrphans !== false
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Validate required config
|
|
46
|
+
this.validateConfig();
|
|
47
|
+
|
|
48
|
+
// Set logger level
|
|
49
|
+
logger.setLevel(this.config.logLevel);
|
|
50
|
+
|
|
51
|
+
// Initialize Appliqation client
|
|
52
|
+
this.client = new AppliqationClient({
|
|
53
|
+
baseUrl: this.config.baseUrl,
|
|
54
|
+
apiKey: this.config.apiKey,
|
|
55
|
+
projectKey: this.config.projectKey,
|
|
56
|
+
title: this.config.title,
|
|
57
|
+
options: {
|
|
58
|
+
logLevel: this.config.logLevel
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Test result storage
|
|
63
|
+
this.testResults = [];
|
|
64
|
+
this.orphanTests = [];
|
|
65
|
+
this.runId = null;
|
|
66
|
+
this.runCreated = false;
|
|
67
|
+
|
|
68
|
+
logger.info('Appliqation Jest Reporter initialized', {
|
|
69
|
+
environment: this.config.environment,
|
|
70
|
+
scenarioId: this.config.scenarioId,
|
|
71
|
+
autoCreateRun: this.config.autoCreateRun
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Called once at the start of the test run
|
|
77
|
+
*/
|
|
78
|
+
async onRunStart(results, options) {
|
|
79
|
+
try {
|
|
80
|
+
logger.info('Jest test run starting...', {
|
|
81
|
+
numTotalTestSuites: results.numTotalTestSuites
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (this.config.autoCreateRun) {
|
|
85
|
+
await this.createRun();
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logger.error('Error in onRunStart', { error: error.message });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create Appliqation run matrix
|
|
94
|
+
*/
|
|
95
|
+
async createRun() {
|
|
96
|
+
try {
|
|
97
|
+
const os = this.detectOS();
|
|
98
|
+
|
|
99
|
+
const runOptions = {
|
|
100
|
+
scenarioId: this.config.scenarioId,
|
|
101
|
+
testSetId: this.config.testSetId,
|
|
102
|
+
environment: this.config.environment,
|
|
103
|
+
browsers: ['Node.js'],
|
|
104
|
+
device: 'Server',
|
|
105
|
+
os: os,
|
|
106
|
+
title: this.config.title || `Automation Run - ${new Date().toISOString()}`
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
logger.info('Creating run matrix...', runOptions);
|
|
110
|
+
|
|
111
|
+
const run = await this.client.createRun(runOptions);
|
|
112
|
+
this.runId = run.runId;
|
|
113
|
+
this.runCreated = true;
|
|
114
|
+
|
|
115
|
+
logger.info('Run matrix created successfully', {
|
|
116
|
+
runId: this.runId
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
logger.error('Failed to create run matrix', { error: error.message });
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Called after each test file completes
|
|
126
|
+
*/
|
|
127
|
+
async onTestFileResult(test, testResult, results) {
|
|
128
|
+
try {
|
|
129
|
+
logger.debug('Processing test file results', {
|
|
130
|
+
testFilePath: testResult.testFilePath,
|
|
131
|
+
numTests: testResult.testResults.length
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Process all tests in this file
|
|
135
|
+
for (const result of testResult.testResults) {
|
|
136
|
+
await this.processTestResult(result, testResult);
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
logger.error('Error processing test file results', {
|
|
140
|
+
error: error.message,
|
|
141
|
+
file: testResult.testFilePath
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Process individual test result
|
|
148
|
+
*/
|
|
149
|
+
async processTestResult(testResult, fileResult) {
|
|
150
|
+
try {
|
|
151
|
+
// Extract UUID from test
|
|
152
|
+
const uuid = UuidExtractor.extractUuid(testResult);
|
|
153
|
+
|
|
154
|
+
if (!uuid) {
|
|
155
|
+
// Track orphan test
|
|
156
|
+
this.orphanTests.push({
|
|
157
|
+
title: testResult.fullName || testResult.title,
|
|
158
|
+
status: this.mapJestStatus(testResult.status),
|
|
159
|
+
file: fileResult.testFilePath,
|
|
160
|
+
timestamp: new Date().toISOString()
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
logger.warn('No UUID found for test', {
|
|
164
|
+
title: testResult.fullName || testResult.title
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Prepare result
|
|
170
|
+
const result = {
|
|
171
|
+
uuid: uuid,
|
|
172
|
+
runId: this.runId,
|
|
173
|
+
status: this.mapJestStatus(testResult.status),
|
|
174
|
+
browser: 'Jest',
|
|
175
|
+
environment: this.config.environment,
|
|
176
|
+
comment: this.buildComment(testResult),
|
|
177
|
+
parent_uuid: null // Jest doesn't have nested tests in the same way
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
this.testResults.push(result);
|
|
181
|
+
|
|
182
|
+
logger.debug('Test result prepared', {
|
|
183
|
+
uuid: uuid,
|
|
184
|
+
status: result.status,
|
|
185
|
+
title: testResult.fullName
|
|
186
|
+
});
|
|
187
|
+
} catch (error) {
|
|
188
|
+
logger.error('Error processing test result', {
|
|
189
|
+
title: testResult.fullName,
|
|
190
|
+
error: error.message
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Called after all tests complete
|
|
197
|
+
*/
|
|
198
|
+
async onRunComplete(contexts, results) {
|
|
199
|
+
try {
|
|
200
|
+
logger.info('Jest test run completed', {
|
|
201
|
+
tests: this.testResults.length,
|
|
202
|
+
orphans: this.orphanTests.length,
|
|
203
|
+
numTotalTests: results.numTotalTests,
|
|
204
|
+
numPassedTests: results.numPassedTests,
|
|
205
|
+
numFailedTests: results.numFailedTests
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Submit all test results
|
|
209
|
+
if (this.testResults.length > 0 && this.runId) {
|
|
210
|
+
await this.submitResults();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Submit orphan tests
|
|
214
|
+
if (this.orphanTests.length > 0 && this.config.submitOrphans && this.runId) {
|
|
215
|
+
await this.submitOrphanTests();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Print summary
|
|
219
|
+
this.printSummary(results);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
logger.error('Error in onRunComplete', { error: error.message });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Submit test results to Appliqation
|
|
227
|
+
*/
|
|
228
|
+
async submitResults() {
|
|
229
|
+
try {
|
|
230
|
+
logger.info(`Submitting ${this.testResults.length} test results...`);
|
|
231
|
+
|
|
232
|
+
const summary = await this.client.submitBatch(this.testResults, {
|
|
233
|
+
batchSize: 50,
|
|
234
|
+
retryFailures: true
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
logger.info('Results submitted successfully', {
|
|
238
|
+
success: summary.success,
|
|
239
|
+
failed: summary.failed,
|
|
240
|
+
total: summary.total
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return summary;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
logger.error('Failed to submit results', { error: error.message });
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Submit orphan tests
|
|
252
|
+
*/
|
|
253
|
+
async submitOrphanTests() {
|
|
254
|
+
try {
|
|
255
|
+
logger.info(`Logging ${this.orphanTests.length} orphan tests...`);
|
|
256
|
+
|
|
257
|
+
await this.client.logOrphanTests(this.runId, this.orphanTests);
|
|
258
|
+
|
|
259
|
+
logger.info('Orphan tests logged successfully');
|
|
260
|
+
} catch (error) {
|
|
261
|
+
logger.error('Failed to log orphan tests', { error: error.message });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Map Jest test status to Appliqation status
|
|
267
|
+
*/
|
|
268
|
+
mapJestStatus(jestStatus) {
|
|
269
|
+
const statusMap = {
|
|
270
|
+
'passed': 'passed',
|
|
271
|
+
'failed': 'failed',
|
|
272
|
+
'skipped': 'skipped',
|
|
273
|
+
'pending': 'skipped',
|
|
274
|
+
'todo': 'skipped',
|
|
275
|
+
'disabled': 'skipped'
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return statusMap[jestStatus] || 'skipped';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Build comment from test details
|
|
283
|
+
*/
|
|
284
|
+
buildComment(testResult) {
|
|
285
|
+
const comments = [];
|
|
286
|
+
|
|
287
|
+
if (testResult.failureMessages && testResult.failureMessages.length > 0) {
|
|
288
|
+
// Get first failure message (Jest can have multiple)
|
|
289
|
+
const errorMsg = testResult.failureMessages[0];
|
|
290
|
+
// Truncate to first 200 chars to avoid too long comments
|
|
291
|
+
const truncated = errorMsg.substring(0, 200);
|
|
292
|
+
comments.push(`Error: ${truncated}${errorMsg.length > 200 ? '...' : ''}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (testResult.duration) {
|
|
296
|
+
comments.push(`Duration: ${testResult.duration}ms`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (testResult.numPassingAsserts) {
|
|
300
|
+
comments.push(`Assertions: ${testResult.numPassingAsserts}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return comments.length > 0 ? comments.join(' | ') : null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Detect OS
|
|
308
|
+
*/
|
|
309
|
+
detectOS() {
|
|
310
|
+
const platform = process.platform;
|
|
311
|
+
|
|
312
|
+
if (platform === 'darwin') return 'macOS';
|
|
313
|
+
if (platform === 'win32') return 'Windows';
|
|
314
|
+
if (platform === 'linux') return 'Linux';
|
|
315
|
+
|
|
316
|
+
return platform;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Print test summary
|
|
321
|
+
*/
|
|
322
|
+
printSummary(results) {
|
|
323
|
+
console.log('\n' + '='.repeat(60));
|
|
324
|
+
console.log('📊 Appliqation Jest Reporter Summary');
|
|
325
|
+
console.log('='.repeat(60));
|
|
326
|
+
console.log(`Environment: ${this.config.environment}`);
|
|
327
|
+
console.log(`Run ID: ${this.runId || 'N/A'}`);
|
|
328
|
+
console.log(`Tests Submitted: ${this.testResults.length}`);
|
|
329
|
+
console.log(`Orphan Tests: ${this.orphanTests.length}`);
|
|
330
|
+
console.log(`Total Tests: ${results.numTotalTests}`);
|
|
331
|
+
console.log(`Passed: ${results.numPassedTests}`);
|
|
332
|
+
console.log(`Failed: ${results.numFailedTests}`);
|
|
333
|
+
console.log(`Skipped: ${results.numPendingTests}`);
|
|
334
|
+
console.log(`Duration: ${(results.startTime && ((Date.now() - results.startTime) / 1000).toFixed(2))}s`);
|
|
335
|
+
console.log('='.repeat(60) + '\n');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Validate configuration
|
|
340
|
+
*/
|
|
341
|
+
validateConfig() {
|
|
342
|
+
const required = ['baseUrl', 'apiKey', 'projectKey'];
|
|
343
|
+
const missing = required.filter(key => !this.config[key]);
|
|
344
|
+
|
|
345
|
+
if (missing.length > 0) {
|
|
346
|
+
throw new Error(`Missing required config: ${missing.join(', ')}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Note: scenarioId and testSetId are now optional
|
|
350
|
+
// If neither is provided, will default to 0 for generic automation runs
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get last error (Jest API requirement)
|
|
355
|
+
*/
|
|
356
|
+
getLastError() {
|
|
357
|
+
return this.lastError;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
module.exports = JestReporter;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const UuidValidator = require('../../utils/UuidValidator');
|
|
2
|
+
const logger = require('../../utils/logger');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* UUID Extractor for Jest Tests
|
|
6
|
+
*
|
|
7
|
+
* Extracts Appliqation test case UUIDs from Jest tests.
|
|
8
|
+
* Supports multiple UUID assignment methods.
|
|
9
|
+
*/
|
|
10
|
+
class UuidExtractor {
|
|
11
|
+
/**
|
|
12
|
+
* Extract UUID from Jest test result
|
|
13
|
+
*
|
|
14
|
+
* Supports multiple UUID formats:
|
|
15
|
+
* 1. Test title with UUID prefix: test('1154-... - should login', () => {})
|
|
16
|
+
* 2. Docblock with @uuid tag: /** @uuid 1154-... *\/
|
|
17
|
+
* 3. Custom test metadata (if available)
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} testResult - Jest test result object
|
|
20
|
+
* @returns {string|null} - Extracted UUID or null
|
|
21
|
+
*/
|
|
22
|
+
static extractUuid(testResult) {
|
|
23
|
+
try {
|
|
24
|
+
// Method 1: Extract from test title/fullName
|
|
25
|
+
const fullName = testResult.fullName || testResult.title;
|
|
26
|
+
if (fullName) {
|
|
27
|
+
const uuidFromTitle = this.extractUuidFromString(fullName);
|
|
28
|
+
if (uuidFromTitle) {
|
|
29
|
+
logger.debug('UUID extracted from test title', { uuid: uuidFromTitle });
|
|
30
|
+
return uuidFromTitle;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Method 2: Extract from ancestorTitles + title
|
|
35
|
+
if (testResult.ancestorTitles && testResult.title) {
|
|
36
|
+
const combinedTitle = [...testResult.ancestorTitles, testResult.title].join(' ');
|
|
37
|
+
const uuidFromCombined = this.extractUuidFromString(combinedTitle);
|
|
38
|
+
if (uuidFromCombined) {
|
|
39
|
+
logger.debug('UUID extracted from combined titles', { uuid: uuidFromCombined });
|
|
40
|
+
return uuidFromCombined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Method 3: Check each ancestor title individually
|
|
45
|
+
if (testResult.ancestorTitles) {
|
|
46
|
+
for (const ancestorTitle of testResult.ancestorTitles) {
|
|
47
|
+
const uuidFromAncestor = this.extractUuidFromString(ancestorTitle);
|
|
48
|
+
if (uuidFromAncestor) {
|
|
49
|
+
logger.debug('UUID extracted from ancestor title', { uuid: uuidFromAncestor });
|
|
50
|
+
return uuidFromAncestor;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Method 4: Check test title alone
|
|
56
|
+
if (testResult.title) {
|
|
57
|
+
const uuidFromTestTitle = this.extractUuidFromString(testResult.title);
|
|
58
|
+
if (uuidFromTestTitle) {
|
|
59
|
+
logger.debug('UUID extracted from test title alone', { uuid: uuidFromTestTitle });
|
|
60
|
+
return uuidFromTestTitle;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// No UUID found
|
|
65
|
+
return null;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
logger.error('Error extracting UUID from Jest test', {
|
|
68
|
+
error: error.message,
|
|
69
|
+
test: testResult.title
|
|
70
|
+
});
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extract UUID from string using regex pattern
|
|
77
|
+
*
|
|
78
|
+
* @param {string} str - String to search
|
|
79
|
+
* @returns {string|null} - Extracted UUID or null
|
|
80
|
+
*/
|
|
81
|
+
static extractUuidFromString(str) {
|
|
82
|
+
if (!str || typeof str !== 'string') {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Pattern: nid-uuid format (e.g., "1154-7a17b809-0ff9-4ba1-9322-4eb2a49abfc5")
|
|
87
|
+
const uuidPattern = /(\d+)-([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
|
|
88
|
+
const match = str.match(uuidPattern);
|
|
89
|
+
|
|
90
|
+
if (match && match[0]) {
|
|
91
|
+
const uuid = match[0];
|
|
92
|
+
if (UuidValidator.validate(uuid)) {
|
|
93
|
+
return uuid;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extract UUID from Jest docblock comment
|
|
102
|
+
*
|
|
103
|
+
* Example: /** @uuid 1154-7a17b809-0ff9-4ba1-9322-4eb2a49abfc5 *\/
|
|
104
|
+
*
|
|
105
|
+
* @param {string} docblock - Docblock comment string
|
|
106
|
+
* @returns {string|null} - Extracted UUID or null
|
|
107
|
+
*/
|
|
108
|
+
static extractUuidFromDocblock(docblock) {
|
|
109
|
+
if (!docblock || typeof docblock !== 'string') {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Pattern: @uuid tag in docblock
|
|
114
|
+
const docblockPattern = /@uuid\s+(\d+-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
|
|
115
|
+
const match = docblock.match(docblockPattern);
|
|
116
|
+
|
|
117
|
+
if (match && match[1]) {
|
|
118
|
+
const uuid = match[1];
|
|
119
|
+
if (UuidValidator.validate(uuid)) {
|
|
120
|
+
return uuid;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extract all UUIDs from a collection of tests
|
|
129
|
+
*
|
|
130
|
+
* @param {Array} testResults - Array of Jest test result objects
|
|
131
|
+
* @returns {Object} - { uuids: [...], orphans: [...] }
|
|
132
|
+
*/
|
|
133
|
+
static extractFromTests(testResults) {
|
|
134
|
+
const results = {
|
|
135
|
+
uuids: [],
|
|
136
|
+
orphans: []
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
if (!Array.isArray(testResults)) {
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const testResult of testResults) {
|
|
144
|
+
const uuid = this.extractUuid(testResult);
|
|
145
|
+
|
|
146
|
+
if (uuid) {
|
|
147
|
+
results.uuids.push({
|
|
148
|
+
uuid: uuid,
|
|
149
|
+
test: testResult
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
results.orphans.push({
|
|
153
|
+
title: testResult.fullName || testResult.title,
|
|
154
|
+
test: testResult
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validate if test has a valid UUID
|
|
164
|
+
*
|
|
165
|
+
* @param {Object} testResult - Jest test result object
|
|
166
|
+
* @returns {boolean} - True if test has valid UUID
|
|
167
|
+
*/
|
|
168
|
+
static hasValidUuid(testResult) {
|
|
169
|
+
const uuid = this.extractUuid(testResult);
|
|
170
|
+
return uuid !== null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = UuidExtractor;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Appliqation Jest Reporter
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* // jest.config.js
|
|
6
|
+
* const { JestReporter } = require('@appliqation/automation-sdk/jest');
|
|
7
|
+
*
|
|
8
|
+
* module.exports = {
|
|
9
|
+
* reporters: [
|
|
10
|
+
* 'default',
|
|
11
|
+
* [JestReporter, {
|
|
12
|
+
* baseUrl: process.env.APPLIQATION_BASE_URL,
|
|
13
|
+
* apiKey: process.env.APPLIQATION_API_KEY,
|
|
14
|
+
* projectKey: process.env.APPLIQATION_PROJECT_KEY,
|
|
15
|
+
* scenarioId: parseInt(process.env.APPLIQATION_SCENARIO_ID),
|
|
16
|
+
* environment: process.env.APPLIQATION_ENVIRONMENT || 'Local',
|
|
17
|
+
* title: process.env.APPLIQATION_RUN_TITLE
|
|
18
|
+
* }]
|
|
19
|
+
* ]
|
|
20
|
+
* };
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const JestReporter = require('./JestReporter');
|
|
24
|
+
const UuidExtractor = require('./UuidExtractor');
|
|
25
|
+
|
|
26
|
+
module.exports = JestReporter;
|
|
27
|
+
module.exports.JestReporter = JestReporter;
|
|
28
|
+
module.exports.UuidExtractor = UuidExtractor;
|