@testream/dotnet-reporter 0.4.7 → 0.5.2
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 +1 -2
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +265 -195
- package/dist/index.js.map +1 -1
- package/dist/uploader.d.ts +2 -30
- package/dist/uploader.d.ts.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
Run .NET tests and send the results from your codebase to Jira via Testream.
|
|
4
4
|
|
|
5
5
|
- **Docs:** https://testream.github.io/docs/reporters/dotnet
|
|
6
|
-
- **Docs repository:** https://github.com/testream/docs
|
|
7
6
|
|
|
8
7
|
## Quick Start
|
|
9
8
|
|
|
10
9
|
```bash
|
|
11
|
-
npx @testream/dotnet-reporter -
|
|
10
|
+
npx @testream/dotnet-reporter -k $TESTREAM_API_KEY
|
|
12
11
|
```
|
|
13
12
|
|
|
14
13
|
This command runs your tests and uploads the results so they show up in Jira.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { parseTrxFile, parseTrxFiles } from './trx-parser';
|
|
2
|
-
export { uploadToApi, detectGitContext
|
|
3
|
-
export type { Logger, UploadToApiOptions } from './uploader';
|
|
2
|
+
export { uploadToApi, detectGitContext } from './uploader';
|
|
4
3
|
export type { DotnetReporterConfig } from './types';
|
|
5
|
-
export type { CTRFReport, CTRFTest, CTRFSummary, CTRFResults, UploadResult, } from '@jira-test-manager/shared-types';
|
|
4
|
+
export type { CTRFReport, CTRFTest, CTRFSummary, CTRFResults, UploadResult, UploadTestRunOptions, } from '@jira-test-manager/shared-types';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"","sourceRoot":"","sources":["file:///home/runner/work/jira-test-manager/jira-test-manager/packages/dotnet-reporter/src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"","sourceRoot":"","sources":["file:///home/runner/work/jira-test-manager/jira-test-manager/packages/dotnet-reporter/src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC3D,YAAY,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACpD,YAAY,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACX,WAAW,EACX,YAAY,EACZ,oBAAoB,GACrB,MAAM,iCAAiC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2275,15 +2275,194 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
2275
2275
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
2276
2276
|
};
|
|
2277
2277
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2278
|
-
exports.detectCIContext = void 0;
|
|
2278
|
+
exports.uploadArtifacts = exports.uploadTestRun = exports.detectCIContext = void 0;
|
|
2279
2279
|
// CTRF types
|
|
2280
2280
|
__exportStar(__nccwpck_require__(827), exports);
|
|
2281
2281
|
var ci_detection_1 = __nccwpck_require__(406);
|
|
2282
2282
|
Object.defineProperty(exports, "detectCIContext", ({ enumerable: true, get: function () { return ci_detection_1.detectCIContext; } }));
|
|
2283
|
+
var upload_1 = __nccwpck_require__(969);
|
|
2284
|
+
Object.defineProperty(exports, "uploadTestRun", ({ enumerable: true, get: function () { return upload_1.uploadTestRun; } }));
|
|
2285
|
+
Object.defineProperty(exports, "uploadArtifacts", ({ enumerable: true, get: function () { return upload_1.uploadArtifacts; } }));
|
|
2283
2286
|
//# sourceMappingURL=index.js.map
|
|
2284
2287
|
|
|
2285
2288
|
/***/ }),
|
|
2286
2289
|
|
|
2290
|
+
/***/ 969:
|
|
2291
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
2292
|
+
|
|
2293
|
+
"use strict";
|
|
2294
|
+
|
|
2295
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2296
|
+
exports.uploadTestRun = uploadTestRun;
|
|
2297
|
+
exports.uploadArtifacts = uploadArtifacts;
|
|
2298
|
+
const DEFAULT_API_URL = 'https://test-manager-backend.fly.dev';
|
|
2299
|
+
/**
|
|
2300
|
+
* Upload test run to Testream backend API
|
|
2301
|
+
*/
|
|
2302
|
+
async function uploadTestRun(options) {
|
|
2303
|
+
const { report, apiKey, apiUrl = DEFAULT_API_URL } = options;
|
|
2304
|
+
// Ensure reportId exists
|
|
2305
|
+
if (!report.reportId) {
|
|
2306
|
+
report.reportId = crypto.randomUUID();
|
|
2307
|
+
}
|
|
2308
|
+
// Build type-safe IngestRequest payload
|
|
2309
|
+
const ingestPayload = {
|
|
2310
|
+
report,
|
|
2311
|
+
reportId: report.reportId,
|
|
2312
|
+
commitSha: options.commitSha,
|
|
2313
|
+
branch: options.branch,
|
|
2314
|
+
repositoryUrl: options.repositoryUrl,
|
|
2315
|
+
buildName: options.buildName,
|
|
2316
|
+
buildNumber: options.buildNumber,
|
|
2317
|
+
buildUrl: options.buildUrl,
|
|
2318
|
+
testEnvironment: options.testEnvironment,
|
|
2319
|
+
appName: options.appName,
|
|
2320
|
+
appVersion: options.appVersion,
|
|
2321
|
+
testType: options.testType,
|
|
2322
|
+
};
|
|
2323
|
+
// Professional logging
|
|
2324
|
+
console.log('Uploading test results...');
|
|
2325
|
+
let response;
|
|
2326
|
+
try {
|
|
2327
|
+
response = await fetch(`${apiUrl}/api/v1/ingest`, {
|
|
2328
|
+
method: 'POST',
|
|
2329
|
+
headers: {
|
|
2330
|
+
'X-API-KEY': apiKey,
|
|
2331
|
+
'Content-Type': 'application/json',
|
|
2332
|
+
},
|
|
2333
|
+
body: JSON.stringify(ingestPayload),
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
catch (error) {
|
|
2337
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2338
|
+
return {
|
|
2339
|
+
success: false,
|
|
2340
|
+
reportId: report.reportId,
|
|
2341
|
+
error: `Connection failed: ${errorMessage}`,
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
// Handle 409 (report already exists) - idempotent
|
|
2345
|
+
if (response.status === 409) {
|
|
2346
|
+
console.warn('Report already exists (workflow may have been re-run)');
|
|
2347
|
+
return {
|
|
2348
|
+
success: true,
|
|
2349
|
+
reportId: report.reportId,
|
|
2350
|
+
summary: {
|
|
2351
|
+
passed: report.results.summary.passed,
|
|
2352
|
+
failed: report.results.summary.failed,
|
|
2353
|
+
skipped: report.results.summary.skipped,
|
|
2354
|
+
total: report.results.summary.tests,
|
|
2355
|
+
},
|
|
2356
|
+
alreadyExists: true,
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
// Handle other errors
|
|
2360
|
+
if (!response.ok) {
|
|
2361
|
+
const errorText = await response.text();
|
|
2362
|
+
return {
|
|
2363
|
+
success: false,
|
|
2364
|
+
reportId: report.reportId,
|
|
2365
|
+
error: `Upload failed (HTTP ${response.status}): ${errorText}`,
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
// Success
|
|
2369
|
+
const result = (await response.json());
|
|
2370
|
+
console.log(`✓ Test results uploaded successfully`);
|
|
2371
|
+
console.log(` Report ID: ${result.reportId}`);
|
|
2372
|
+
console.log(` Test Run ID: ${result.testRunId}`);
|
|
2373
|
+
console.log(` Tests: ${result.summary.passed}/${result.summary.total} passed`);
|
|
2374
|
+
return {
|
|
2375
|
+
success: true,
|
|
2376
|
+
reportId: result.reportId,
|
|
2377
|
+
testRunId: result.testRunId,
|
|
2378
|
+
summary: {
|
|
2379
|
+
passed: result.summary.passed,
|
|
2380
|
+
failed: result.summary.failed,
|
|
2381
|
+
skipped: result.summary.skipped,
|
|
2382
|
+
total: result.summary.total,
|
|
2383
|
+
},
|
|
2384
|
+
testResults: result.testResults,
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Upload test artifacts (screenshots, videos, etc.) to Testream backend API
|
|
2389
|
+
*/
|
|
2390
|
+
async function uploadArtifacts(options) {
|
|
2391
|
+
const { reportId, apiKey, testResults, apiUrl = DEFAULT_API_URL } = options;
|
|
2392
|
+
let uploadedCount = 0;
|
|
2393
|
+
for (const testResult of testResults) {
|
|
2394
|
+
for (const attachment of testResult.attachments) {
|
|
2395
|
+
try {
|
|
2396
|
+
const success = await uploadSingleArtifact({
|
|
2397
|
+
testResultId: testResult.testResultId,
|
|
2398
|
+
attachment,
|
|
2399
|
+
reportId,
|
|
2400
|
+
apiKey,
|
|
2401
|
+
apiUrl,
|
|
2402
|
+
});
|
|
2403
|
+
if (success)
|
|
2404
|
+
uploadedCount++;
|
|
2405
|
+
}
|
|
2406
|
+
catch (error) {
|
|
2407
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2408
|
+
console.error(`Failed to upload artifact ${attachment.name}: ${errorMessage}`);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
if (uploadedCount > 0) {
|
|
2413
|
+
console.log(`✓ Uploaded ${uploadedCount} artifact(s)`);
|
|
2414
|
+
}
|
|
2415
|
+
return uploadedCount;
|
|
2416
|
+
}
|
|
2417
|
+
async function uploadSingleArtifact(options) {
|
|
2418
|
+
const { testResultId, attachment, reportId, apiKey, apiUrl } = options;
|
|
2419
|
+
// Dynamically import fs for Node.js environments
|
|
2420
|
+
const fs = await Promise.resolve(/* import() */).then(__nccwpck_require__.t.bind(__nccwpck_require__, 943, 23));
|
|
2421
|
+
const path = await Promise.resolve(/* import() */).then(__nccwpck_require__.t.bind(__nccwpck_require__, 928, 23));
|
|
2422
|
+
const filePath = path.resolve(attachment.path);
|
|
2423
|
+
// Check if file exists
|
|
2424
|
+
try {
|
|
2425
|
+
await fs.access(filePath);
|
|
2426
|
+
}
|
|
2427
|
+
catch {
|
|
2428
|
+
console.warn(`Artifact not found: ${path.basename(filePath)}`);
|
|
2429
|
+
return false;
|
|
2430
|
+
}
|
|
2431
|
+
// Read file
|
|
2432
|
+
let fileBuffer;
|
|
2433
|
+
try {
|
|
2434
|
+
fileBuffer = await fs.readFile(filePath);
|
|
2435
|
+
}
|
|
2436
|
+
catch (error) {
|
|
2437
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2438
|
+
throw new Error(`Failed to read file: ${errorMessage}`);
|
|
2439
|
+
}
|
|
2440
|
+
// Create native FormData with Blob
|
|
2441
|
+
const blob = new Blob([fileBuffer], { type: attachment.contentType });
|
|
2442
|
+
const formData = new FormData();
|
|
2443
|
+
formData.append('testResultId', testResultId);
|
|
2444
|
+
formData.append('ctrfAttachmentName', attachment.name);
|
|
2445
|
+
formData.append('file', blob, path.basename(filePath));
|
|
2446
|
+
// Upload to API
|
|
2447
|
+
const uploadUrl = `${apiUrl}/api/v1/artifacts/${reportId}`;
|
|
2448
|
+
const response = await fetch(uploadUrl, {
|
|
2449
|
+
method: 'POST',
|
|
2450
|
+
headers: {
|
|
2451
|
+
'X-API-KEY': apiKey,
|
|
2452
|
+
// Don't set Content-Type - fetch sets it with boundary
|
|
2453
|
+
},
|
|
2454
|
+
body: formData,
|
|
2455
|
+
});
|
|
2456
|
+
if (!response.ok) {
|
|
2457
|
+
const errorText = await response.text();
|
|
2458
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
2459
|
+
}
|
|
2460
|
+
return true;
|
|
2461
|
+
}
|
|
2462
|
+
//# sourceMappingURL=upload.js.map
|
|
2463
|
+
|
|
2464
|
+
/***/ }),
|
|
2465
|
+
|
|
2287
2466
|
/***/ 328:
|
|
2288
2467
|
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
2289
2468
|
|
|
@@ -2335,10 +2514,9 @@ function printUsage() {
|
|
|
2335
2514
|
@testream/dotnet-reporter - Run .NET tests and upload results to Testream
|
|
2336
2515
|
|
|
2337
2516
|
Usage:
|
|
2338
|
-
npx @testream/dotnet-reporter --
|
|
2517
|
+
npx @testream/dotnet-reporter --api-key <key> [options]
|
|
2339
2518
|
|
|
2340
2519
|
Required arguments:
|
|
2341
|
-
--project-key, -p Project key
|
|
2342
2520
|
--api-key, -k API key for authentication
|
|
2343
2521
|
|
|
2344
2522
|
Optional arguments:
|
|
@@ -2371,25 +2549,25 @@ Other:
|
|
|
2371
2549
|
|
|
2372
2550
|
Examples:
|
|
2373
2551
|
# Run tests and upload (simplest usage)
|
|
2374
|
-
npx @testream/dotnet-reporter -
|
|
2552
|
+
npx @testream/dotnet-reporter -k your-api-key
|
|
2375
2553
|
|
|
2376
2554
|
# Run tests for a specific project
|
|
2377
|
-
npx @testream/dotnet-reporter -
|
|
2555
|
+
npx @testream/dotnet-reporter -k your-api-key --project ./MyTests
|
|
2378
2556
|
|
|
2379
2557
|
# With environment metadata
|
|
2380
|
-
npx @testream/dotnet-reporter -
|
|
2558
|
+
npx @testream/dotnet-reporter -k your-api-key \\
|
|
2381
2559
|
--test-environment staging --app-name MyApp --app-version 1.0.0
|
|
2382
2560
|
|
|
2383
2561
|
# Pass additional arguments to dotnet test
|
|
2384
|
-
npx @testream/dotnet-reporter -
|
|
2562
|
+
npx @testream/dotnet-reporter -k your-api-key -- --filter "Category=Unit"
|
|
2385
2563
|
|
|
2386
2564
|
# Use existing TRX file (skip running tests)
|
|
2387
|
-
npx @testream/dotnet-reporter -
|
|
2565
|
+
npx @testream/dotnet-reporter -k your-api-key --trx-path TestResults/*.trx
|
|
2388
2566
|
|
|
2389
2567
|
How it works:
|
|
2390
2568
|
1. Runs 'dotnet test' with TRX logging enabled
|
|
2391
2569
|
2. Parses the TRX output and converts to CTRF format
|
|
2392
|
-
3. Uploads to Testream
|
|
2570
|
+
3. Uploads to Testream (project key inferred from API key)
|
|
2393
2571
|
|
|
2394
2572
|
No manual steps required - just run and upload!
|
|
2395
2573
|
`);
|
|
@@ -2419,11 +2597,6 @@ function parseArgs(args) {
|
|
|
2419
2597
|
printUsage();
|
|
2420
2598
|
process.exit(0);
|
|
2421
2599
|
break;
|
|
2422
|
-
case '--project-key':
|
|
2423
|
-
case '-p':
|
|
2424
|
-
options.projectKey = nextArg;
|
|
2425
|
-
i++;
|
|
2426
|
-
break;
|
|
2427
2600
|
case '--api-key':
|
|
2428
2601
|
case '-k':
|
|
2429
2602
|
options.apiKey = nextArg;
|
|
@@ -2493,13 +2666,8 @@ function parseArgs(args) {
|
|
|
2493
2666
|
options.dotnetArgs = dotnetArgs;
|
|
2494
2667
|
}
|
|
2495
2668
|
// Validate required options
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
missing.push('--project-key');
|
|
2499
|
-
if (!options.apiKey && options.uploadEnabled)
|
|
2500
|
-
missing.push('--api-key');
|
|
2501
|
-
if (missing.length > 0) {
|
|
2502
|
-
console.error(`Error: Missing required arguments: ${missing.join(', ')}`);
|
|
2669
|
+
if (!options.apiKey && options.uploadEnabled) {
|
|
2670
|
+
console.error('Error: Missing required argument: --api-key');
|
|
2503
2671
|
console.error('Run with --help for usage information.');
|
|
2504
2672
|
return null;
|
|
2505
2673
|
}
|
|
@@ -2521,7 +2689,7 @@ async function findTrxFiles(pattern) {
|
|
|
2521
2689
|
* Run dotnet test and return the path to generated TRX file
|
|
2522
2690
|
*/
|
|
2523
2691
|
async function runDotnetTest(projectPath, additionalArgs = []) {
|
|
2524
|
-
const logger =
|
|
2692
|
+
const logger = console;
|
|
2525
2693
|
// Create temp directory for TRX output
|
|
2526
2694
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dotnet-reporter-'));
|
|
2527
2695
|
const trxFileName = `results-${Date.now()}.trx`;
|
|
@@ -2579,7 +2747,7 @@ async function main() {
|
|
|
2579
2747
|
if (!options) {
|
|
2580
2748
|
process.exit(1);
|
|
2581
2749
|
}
|
|
2582
|
-
const logger =
|
|
2750
|
+
const logger = console;
|
|
2583
2751
|
try {
|
|
2584
2752
|
let trxPath;
|
|
2585
2753
|
// Either use provided TRX path or run dotnet test
|
|
@@ -2631,7 +2799,6 @@ async function main() {
|
|
|
2631
2799
|
// Upload to API
|
|
2632
2800
|
const result = await (0, uploader_1.uploadToApi)({
|
|
2633
2801
|
report,
|
|
2634
|
-
projectKey: options.projectKey,
|
|
2635
2802
|
apiKey: options.apiKey,
|
|
2636
2803
|
// Git context
|
|
2637
2804
|
branch,
|
|
@@ -2646,14 +2813,13 @@ async function main() {
|
|
|
2646
2813
|
appName: options.appName,
|
|
2647
2814
|
appVersion: options.appVersion,
|
|
2648
2815
|
testType: options.testType,
|
|
2649
|
-
logger,
|
|
2650
2816
|
});
|
|
2651
2817
|
if (!result.success) {
|
|
2652
2818
|
if (options.failOnUploadError) {
|
|
2653
2819
|
process.exit(1);
|
|
2654
2820
|
}
|
|
2655
2821
|
else {
|
|
2656
|
-
logger.
|
|
2822
|
+
logger.warn('Upload failed but continuing (use --fail-on-error to fail on upload errors)');
|
|
2657
2823
|
}
|
|
2658
2824
|
}
|
|
2659
2825
|
}
|
|
@@ -2981,173 +3147,27 @@ async function parseTrxFiles(trxPaths) {
|
|
|
2981
3147
|
"use strict";
|
|
2982
3148
|
|
|
2983
3149
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2984
|
-
exports.detectGitContext =
|
|
3150
|
+
exports.detectGitContext = void 0;
|
|
2985
3151
|
exports.uploadToApi = uploadToApi;
|
|
2986
3152
|
const shared_types_1 = __nccwpck_require__(322);
|
|
2987
|
-
const crypto_1 = __nccwpck_require__(982);
|
|
2988
|
-
/**
|
|
2989
|
-
* Hardcoded API URL for Testream backend
|
|
2990
|
-
*/
|
|
2991
|
-
const API_URL = 'https://test-manager-backend.fly.dev';
|
|
2992
|
-
/**
|
|
2993
|
-
* Default console logger
|
|
2994
|
-
*/
|
|
2995
|
-
exports.defaultLogger = {
|
|
2996
|
-
info: (msg) => console.log(msg),
|
|
2997
|
-
warning: (msg) => console.warn(msg),
|
|
2998
|
-
error: (msg) => console.error(msg),
|
|
2999
|
-
};
|
|
3000
3153
|
/**
|
|
3001
3154
|
* Upload CTRF report to Testream API
|
|
3002
3155
|
*/
|
|
3003
3156
|
async function uploadToApi(options) {
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
logger.info(` Total: ${report.results.summary.tests}`);
|
|
3019
|
-
logger.info(` Passed: ${report.results.summary.passed}`);
|
|
3020
|
-
logger.info(` Failed: ${report.results.summary.failed}`);
|
|
3021
|
-
logger.info(` Skipped: ${report.results.summary.skipped}`);
|
|
3022
|
-
logger.info('');
|
|
3023
|
-
// Log git context if provided
|
|
3024
|
-
if (branch || commitSha || repositoryUrl) {
|
|
3025
|
-
logger.info(`Git Context:`);
|
|
3026
|
-
if (branch)
|
|
3027
|
-
logger.info(` Branch: ${branch}`);
|
|
3028
|
-
if (commitSha)
|
|
3029
|
-
logger.info(` Commit: ${commitSha.substring(0, 7)}`);
|
|
3030
|
-
if (repositoryUrl)
|
|
3031
|
-
logger.info(` Repository: ${repositoryUrl}`);
|
|
3032
|
-
logger.info('');
|
|
3033
|
-
}
|
|
3034
|
-
// Log build info if provided
|
|
3035
|
-
if (buildName || buildNumber || buildUrl) {
|
|
3036
|
-
logger.info(`Build Info:`);
|
|
3037
|
-
if (buildName)
|
|
3038
|
-
logger.info(` Name: ${buildName}`);
|
|
3039
|
-
if (buildNumber)
|
|
3040
|
-
logger.info(` Number: ${buildNumber}`);
|
|
3041
|
-
if (buildUrl)
|
|
3042
|
-
logger.info(` URL: ${buildUrl}`);
|
|
3043
|
-
logger.info('');
|
|
3044
|
-
}
|
|
3045
|
-
// Log environment info if provided
|
|
3046
|
-
if (testEnvironment || appName || appVersion || testType) {
|
|
3047
|
-
logger.info(`Environment:`);
|
|
3048
|
-
if (testEnvironment)
|
|
3049
|
-
logger.info(` Environment: ${testEnvironment}`);
|
|
3050
|
-
if (appName)
|
|
3051
|
-
logger.info(` App: ${appName}`);
|
|
3052
|
-
if (appVersion)
|
|
3053
|
-
logger.info(` Version: ${appVersion}`);
|
|
3054
|
-
if (testType)
|
|
3055
|
-
logger.info(` Test Type: ${testType}`);
|
|
3056
|
-
logger.info('');
|
|
3057
|
-
}
|
|
3058
|
-
// Prepare payload with all metadata
|
|
3059
|
-
const ingestPayload = {
|
|
3060
|
-
report,
|
|
3061
|
-
reportId: report.reportId,
|
|
3062
|
-
projectKey,
|
|
3063
|
-
// Git context
|
|
3064
|
-
commitSha,
|
|
3065
|
-
branch,
|
|
3066
|
-
repositoryUrl,
|
|
3067
|
-
// Build metadata
|
|
3068
|
-
buildName,
|
|
3069
|
-
buildNumber,
|
|
3070
|
-
buildUrl,
|
|
3071
|
-
// Environment metadata
|
|
3072
|
-
testEnvironment,
|
|
3073
|
-
appName,
|
|
3074
|
-
appVersion,
|
|
3075
|
-
testType,
|
|
3076
|
-
};
|
|
3077
|
-
// Upload to API
|
|
3078
|
-
logger.info(`Uploading test results...`);
|
|
3079
|
-
logger.info(` Report ID: ${report.reportId}`);
|
|
3080
|
-
let response;
|
|
3081
|
-
try {
|
|
3082
|
-
response = await fetch(`${API_URL}/api/v1/ingest`, {
|
|
3083
|
-
method: 'POST',
|
|
3084
|
-
headers: {
|
|
3085
|
-
'X-API-KEY': apiKey,
|
|
3086
|
-
'Content-Type': 'application/json',
|
|
3087
|
-
},
|
|
3088
|
-
body: JSON.stringify(ingestPayload),
|
|
3089
|
-
});
|
|
3090
|
-
}
|
|
3091
|
-
catch (error) {
|
|
3092
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3093
|
-
throw new Error(`Failed to connect to API: ${errorMessage}`);
|
|
3094
|
-
}
|
|
3095
|
-
// Log response status for debugging
|
|
3096
|
-
logger.info(` Response status: ${response.status}`);
|
|
3097
|
-
// Handle errors
|
|
3098
|
-
if (!response.ok) {
|
|
3099
|
-
const responseText = await response.text();
|
|
3100
|
-
let errorData = null;
|
|
3101
|
-
try {
|
|
3102
|
-
errorData = JSON.parse(responseText);
|
|
3103
|
-
}
|
|
3104
|
-
catch {
|
|
3105
|
-
// Not JSON
|
|
3106
|
-
}
|
|
3107
|
-
// Handle 409 (already exists)
|
|
3108
|
-
if (response.status === 409) {
|
|
3109
|
-
logger.warning(`Report already exists: ${errorData?.reportId || 'unknown'}`);
|
|
3110
|
-
logger.info('This is expected if the workflow was re-run');
|
|
3111
|
-
return {
|
|
3112
|
-
success: true,
|
|
3113
|
-
reportId: errorData?.reportId || report.reportId,
|
|
3114
|
-
summary: {
|
|
3115
|
-
passed: report.results.summary.passed,
|
|
3116
|
-
failed: report.results.summary.failed,
|
|
3117
|
-
skipped: report.results.summary.skipped,
|
|
3118
|
-
total: report.results.summary.tests,
|
|
3119
|
-
},
|
|
3120
|
-
};
|
|
3121
|
-
}
|
|
3122
|
-
// Other errors
|
|
3123
|
-
const errorMessage = errorData?.error
|
|
3124
|
-
? `${errorData.error}${errorData.details ? `: ${errorData.details}` : ''}`
|
|
3125
|
-
: responseText || `HTTP ${response.status}`;
|
|
3126
|
-
throw new Error(`API ingest failed (${response.status}): ${errorMessage}`);
|
|
3127
|
-
}
|
|
3128
|
-
const result = (await response.json());
|
|
3129
|
-
logger.info('');
|
|
3130
|
-
logger.info('='.repeat(60));
|
|
3131
|
-
logger.info('Upload completed successfully');
|
|
3132
|
-
logger.info(` Report ID: ${result.reportId}`);
|
|
3133
|
-
logger.info(` Test Run ID: ${result.testRunId}`);
|
|
3134
|
-
logger.info('='.repeat(60));
|
|
3135
|
-
return {
|
|
3136
|
-
success: true,
|
|
3137
|
-
reportId: result.reportId,
|
|
3138
|
-
testRunId: result.testRunId,
|
|
3139
|
-
summary: result.summary,
|
|
3140
|
-
};
|
|
3141
|
-
}
|
|
3142
|
-
catch (error) {
|
|
3143
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3144
|
-
logger.error(`Upload failed: ${errorMessage}`);
|
|
3145
|
-
return {
|
|
3146
|
-
success: false,
|
|
3147
|
-
reportId: '',
|
|
3148
|
-
error: errorMessage,
|
|
3149
|
-
};
|
|
3150
|
-
}
|
|
3157
|
+
return (0, shared_types_1.uploadTestRun)({
|
|
3158
|
+
report: options.report,
|
|
3159
|
+
apiKey: options.apiKey,
|
|
3160
|
+
commitSha: options.commitSha,
|
|
3161
|
+
branch: options.branch,
|
|
3162
|
+
repositoryUrl: options.repositoryUrl,
|
|
3163
|
+
buildName: options.buildName,
|
|
3164
|
+
buildNumber: options.buildNumber,
|
|
3165
|
+
buildUrl: options.buildUrl,
|
|
3166
|
+
testEnvironment: options.testEnvironment,
|
|
3167
|
+
appName: options.appName,
|
|
3168
|
+
appVersion: options.appVersion,
|
|
3169
|
+
testType: options.testType,
|
|
3170
|
+
});
|
|
3151
3171
|
}
|
|
3152
3172
|
// Keep old function name for backwards compatibility
|
|
3153
3173
|
exports.detectGitContext = shared_types_1.detectCIContext;
|
|
@@ -3163,14 +3183,6 @@ module.exports = require("child_process");
|
|
|
3163
3183
|
|
|
3164
3184
|
/***/ }),
|
|
3165
3185
|
|
|
3166
|
-
/***/ 982:
|
|
3167
|
-
/***/ ((module) => {
|
|
3168
|
-
|
|
3169
|
-
"use strict";
|
|
3170
|
-
module.exports = require("crypto");
|
|
3171
|
-
|
|
3172
|
-
/***/ }),
|
|
3173
|
-
|
|
3174
3186
|
/***/ 896:
|
|
3175
3187
|
/***/ ((module) => {
|
|
3176
3188
|
|
|
@@ -11526,6 +11538,64 @@ exports.PathScurry = process.platform === 'win32' ? PathScurryWin32
|
|
|
11526
11538
|
/******/ }
|
|
11527
11539
|
/******/
|
|
11528
11540
|
/************************************************************************/
|
|
11541
|
+
/******/ /* webpack/runtime/create fake namespace object */
|
|
11542
|
+
/******/ (() => {
|
|
11543
|
+
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
|
|
11544
|
+
/******/ var leafPrototypes;
|
|
11545
|
+
/******/ // create a fake namespace object
|
|
11546
|
+
/******/ // mode & 1: value is a module id, require it
|
|
11547
|
+
/******/ // mode & 2: merge all properties of value into the ns
|
|
11548
|
+
/******/ // mode & 4: return value when already ns object
|
|
11549
|
+
/******/ // mode & 16: return value when it's Promise-like
|
|
11550
|
+
/******/ // mode & 8|1: behave like require
|
|
11551
|
+
/******/ __nccwpck_require__.t = function(value, mode) {
|
|
11552
|
+
/******/ if(mode & 1) value = this(value);
|
|
11553
|
+
/******/ if(mode & 8) return value;
|
|
11554
|
+
/******/ if(typeof value === 'object' && value) {
|
|
11555
|
+
/******/ if((mode & 4) && value.__esModule) return value;
|
|
11556
|
+
/******/ if((mode & 16) && typeof value.then === 'function') return value;
|
|
11557
|
+
/******/ }
|
|
11558
|
+
/******/ var ns = Object.create(null);
|
|
11559
|
+
/******/ __nccwpck_require__.r(ns);
|
|
11560
|
+
/******/ var def = {};
|
|
11561
|
+
/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
|
|
11562
|
+
/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
|
|
11563
|
+
/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
|
|
11564
|
+
/******/ }
|
|
11565
|
+
/******/ def['default'] = () => (value);
|
|
11566
|
+
/******/ __nccwpck_require__.d(ns, def);
|
|
11567
|
+
/******/ return ns;
|
|
11568
|
+
/******/ };
|
|
11569
|
+
/******/ })();
|
|
11570
|
+
/******/
|
|
11571
|
+
/******/ /* webpack/runtime/define property getters */
|
|
11572
|
+
/******/ (() => {
|
|
11573
|
+
/******/ // define getter functions for harmony exports
|
|
11574
|
+
/******/ __nccwpck_require__.d = (exports, definition) => {
|
|
11575
|
+
/******/ for(var key in definition) {
|
|
11576
|
+
/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) {
|
|
11577
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
11578
|
+
/******/ }
|
|
11579
|
+
/******/ }
|
|
11580
|
+
/******/ };
|
|
11581
|
+
/******/ })();
|
|
11582
|
+
/******/
|
|
11583
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
11584
|
+
/******/ (() => {
|
|
11585
|
+
/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
11586
|
+
/******/ })();
|
|
11587
|
+
/******/
|
|
11588
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
11589
|
+
/******/ (() => {
|
|
11590
|
+
/******/ // define __esModule on exports
|
|
11591
|
+
/******/ __nccwpck_require__.r = (exports) => {
|
|
11592
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
11593
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
11594
|
+
/******/ }
|
|
11595
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
11596
|
+
/******/ };
|
|
11597
|
+
/******/ })();
|
|
11598
|
+
/******/
|
|
11529
11599
|
/******/ /* webpack/runtime/compat */
|
|
11530
11600
|
/******/
|
|
11531
11601
|
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
|