@testream/jest-reporter 0.1.2 → 0.1.3
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/dist/index.js +1 -13
- package/package.json +5 -2
- package/dist/reporter.js +0 -187
- package/dist/types.js +0 -2
- package/dist/uploader.js +0 -165
package/dist/index.js
CHANGED
|
@@ -1,13 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @testream/jest-reporter
|
|
4
|
-
*
|
|
5
|
-
* Jest reporter that generates CTRF-format test reports and
|
|
6
|
-
* automatically uploads them to the Testream backend.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.TestreamJestReporter = exports.default = void 0;
|
|
10
|
-
var reporter_1 = require("./reporter");
|
|
11
|
-
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return reporter_1.TestreamJestReporter; } });
|
|
12
|
-
var reporter_2 = require("./reporter");
|
|
13
|
-
Object.defineProperty(exports, "TestreamJestReporter", { enumerable: true, get: function () { return reporter_2.TestreamJestReporter; } });
|
|
1
|
+
(()=>{"use strict";var e={580:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});const i=r(896);const n=r(928);class GenerateCtrfReport{constructor(e,t,r){var n,o,s,u,a,l,d,f,p,c,m,h,_,v,b,R,T,I,U;this.reporterName="jest-ctrf-json-reporter";this.defaultOutputFile="ctrf-report.json";this.defaultOutputDir="ctrf";this.filename=this.defaultOutputFile;this.reporterConfigOptions={outputFile:(n=t===null||t===void 0?void 0:t.outputFile)!==null&&n!==void 0?n:this.defaultOutputFile,outputDir:(o=t===null||t===void 0?void 0:t.outputDir)!==null&&o!==void 0?o:this.defaultOutputDir,minimal:(s=t===null||t===void 0?void 0:t.minimal)!==null&&s!==void 0?s:false,testType:(u=t.testType)!==null&&u!==void 0?u:"unit",appName:(a=t===null||t===void 0?void 0:t.appName)!==null&&a!==void 0?a:undefined,appVersion:(l=t===null||t===void 0?void 0:t.appVersion)!==null&&l!==void 0?l:undefined,osPlatform:(d=t===null||t===void 0?void 0:t.osPlatform)!==null&&d!==void 0?d:undefined,osRelease:(f=t===null||t===void 0?void 0:t.osRelease)!==null&&f!==void 0?f:undefined,osVersion:(p=t===null||t===void 0?void 0:t.osVersion)!==null&&p!==void 0?p:undefined,buildName:(c=t===null||t===void 0?void 0:t.buildName)!==null&&c!==void 0?c:undefined,buildNumber:(m=t===null||t===void 0?void 0:t.buildNumber)!==null&&m!==void 0?m:undefined,buildUrl:(h=t===null||t===void 0?void 0:t.buildUrl)!==null&&h!==void 0?h:undefined,repositoryName:(_=t===null||t===void 0?void 0:t.repositoryName)!==null&&_!==void 0?_:undefined,repositoryUrl:(v=t===null||t===void 0?void 0:t.repositoryUrl)!==null&&v!==void 0?v:undefined,branchName:(b=t===null||t===void 0?void 0:t.branchName)!==null&&b!==void 0?b:undefined,testEnvironment:(R=t===null||t===void 0?void 0:t.testEnvironment)!==null&&R!==void 0?R:undefined};this.ctrfReport={results:{tool:{name:"jest"},summary:{tests:0,passed:0,failed:0,pending:0,skipped:0,other:0,start:0,stop:0},tests:[]}};this.ctrfEnvironment={};if(((T=this.reporterConfigOptions)===null||T===void 0?void 0:T.outputFile)!==undefined)this.setFilename(this.reporterConfigOptions.outputFile);if(!i.existsSync((I=this.reporterConfigOptions.outputDir)!==null&&I!==void 0?I:this.defaultOutputDir)){i.mkdirSync((U=this.reporterConfigOptions.outputDir)!==null&&U!==void 0?U:this.defaultOutputDir,{recursive:true})}}onRunStart(){var e;this.ctrfReport.results.summary.start=Date.now();this.setEnvironmentDetails((e=this.reporterConfigOptions)!==null&&e!==void 0?e:{});if(this.hasEnvironmentDetails(this.ctrfEnvironment)){this.ctrfReport.results.environment=this.ctrfEnvironment}}onTestStart(){}onTestResult(e,t){this.updateCtrfTestResultsFromTestResult(t);this.updateTotalsFromTestResult(t)}onRunComplete(){this.ctrfReport.results.summary.stop=Date.now();this.writeReportToFile(this.ctrfReport)}setFilename(e){if(e.endsWith(".json")){this.filename=e}else{this.filename=`${e}.json`}}updateCtrfTestResultsFromTestResult(e){e.testResults.forEach((t=>{var r,i,n,o;const s={name:t.fullName,duration:(r=t.duration)!==null&&r!==void 0?r:0,status:this.mapStatus(t.status)};if(this.reporterConfigOptions.minimal===false){s.message=this.extractFailureDetails(t).message;s.trace=this.extractFailureDetails(t).trace;s.rawStatus=t.status;s.type=(i=this.reporterConfigOptions.testType)!==null&&i!==void 0?i:"unit";s.filePath=e.testFilePath;s.retries=((n=t.invocations)!==null&&n!==void 0?n:1)-1;s.flaky=t.status==="passed"&&((o=t.invocations)!==null&&o!==void 0?o:1)-1>0;s.suite=this.buildSuitePath(e,t)}this.ctrfReport.results.tests.push(s)}))}extractFailureDetails(e){var t;const r=/^\s{4}at/mu;const i=/\x1b\[\d+m/gmu;if(e.status==="failed"&&e.failureMessages!==undefined){const n={};if(e.failureMessages!==undefined){const t=e.failureMessages.join("\n");const o=t.match(r);n.message=t.slice(0,o===null||o===void 0?void 0:o.index).replace(i,"");n.trace=t.slice(o===null||o===void 0?void 0:o.index).split("\n").map((e=>e.trim())).join("\n")}if(e.failureDetails!==undefined){n.trace=(t=n.trace)===null||t===void 0?void 0:t.concat("\n\n",e.failureDetails.join("\n"))}return n}return{}}updateTotalsFromTestResult(e){e.testResults.forEach((e=>{const t=this.mapStatus(e.status);this.ctrfReport.results.summary[t]++;this.ctrfReport.results.summary.tests++}))}mapStatus(e){switch(e){case"passed":return"passed";case"failed":return"failed";case"skipped":return"skipped";case"pending":return"pending";case"todo":case"disabled":case"focused":default:return"other"}}setEnvironmentDetails(e){if(e.appName!==undefined){this.ctrfEnvironment.appName=e.appName}if(e.appVersion!==undefined){this.ctrfEnvironment.appVersion=e.appVersion}if(e.osPlatform!==undefined){this.ctrfEnvironment.osPlatform=e.osPlatform}if(e.osRelease!==undefined){this.ctrfEnvironment.osRelease=e.osRelease}if(e.osVersion!==undefined){this.ctrfEnvironment.osVersion=e.osVersion}if(e.buildName!==undefined){this.ctrfEnvironment.buildName=e.buildName}if(e.buildNumber!==undefined){this.ctrfEnvironment.buildNumber=e.buildNumber}if(e.buildUrl!==undefined){this.ctrfEnvironment.buildUrl=e.buildUrl}if(e.repositoryName!==undefined){this.ctrfEnvironment.repositoryName=e.repositoryName}if(e.repositoryUrl!==undefined){this.ctrfEnvironment.repositoryUrl=e.repositoryUrl}if(e.branchName!==undefined){this.ctrfEnvironment.branchName=e.branchName}if(e.testEnvironment!==undefined){this.ctrfEnvironment.testEnvironment=e.testEnvironment}}hasEnvironmentDetails(e){return Object.keys(e).length>0}buildSuitePath(e,t){var r;const i=(r=e.testFilePath.split("/").pop())!==null&&r!==void 0?r:"";const n=[i,...t.ancestorTitles];return n.join(" > ")}writeReportToFile(e){var t;const r=n.join((t=this.reporterConfigOptions.outputDir)!==null&&t!==void 0?t:this.defaultOutputDir,this.filename);const o=JSON.stringify(e,null,2);try{i.writeFileSync(r,o+"\n");console.log(`${this.reporterName}: successfully written ctrf json to %s/%s`,this.reporterConfigOptions.outputDir,this.filename)}catch(e){console.error(`Error writing ctrf json report:, ${String(e)}`)}}}t["default"]=GenerateCtrfReport},764:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var i=r(580);Object.defineProperty(t,"default",{enumerable:true,get:function(){return i.default}})},406:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.detectCIContext=detectCIContext;function detectCIContext(){const e=process.env;if(e.GITHUB_ACTIONS==="true"){return{branch:e.GITHUB_HEAD_REF||e.GITHUB_REF_NAME||e.GITHUB_REF?.replace("refs/heads/",""),commitSha:e.GITHUB_SHA,repositoryUrl:e.GITHUB_SERVER_URL&&e.GITHUB_REPOSITORY?`${e.GITHUB_SERVER_URL}/${e.GITHUB_REPOSITORY}`:undefined,buildNumber:e.GITHUB_RUN_NUMBER,buildUrl:e.GITHUB_SERVER_URL&&e.GITHUB_REPOSITORY&&e.GITHUB_RUN_ID?`${e.GITHUB_SERVER_URL}/${e.GITHUB_REPOSITORY}/actions/runs/${e.GITHUB_RUN_ID}`:undefined}}if(e.GITLAB_CI==="true"){return{branch:e.CI_COMMIT_BRANCH||e.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME,commitSha:e.CI_COMMIT_SHA,repositoryUrl:e.CI_PROJECT_URL,buildNumber:e.CI_PIPELINE_IID,buildUrl:e.CI_PIPELINE_URL}}if(e.TF_BUILD==="True"){return{branch:e.BUILD_SOURCEBRANCH?.replace("refs/heads/",""),commitSha:e.BUILD_SOURCEVERSION,repositoryUrl:e.BUILD_REPOSITORY_URI,buildNumber:e.BUILD_BUILDNUMBER,buildUrl:e.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI&&e.SYSTEM_TEAMPROJECT&&e.BUILD_BUILDID?`${e.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}${e.SYSTEM_TEAMPROJECT}/_build/results?buildId=${e.BUILD_BUILDID}`:undefined}}if(e.CIRCLECI==="true"){return{branch:e.CIRCLE_BRANCH,commitSha:e.CIRCLE_SHA1,repositoryUrl:e.CIRCLE_REPOSITORY_URL,buildNumber:e.CIRCLE_BUILD_NUM,buildUrl:e.CIRCLE_BUILD_URL}}if(e.JENKINS_URL){return{branch:e.GIT_BRANCH?.replace("origin/",""),commitSha:e.GIT_COMMIT,repositoryUrl:e.GIT_URL,buildNumber:e.BUILD_NUMBER,buildUrl:e.BUILD_URL}}if(e.BITBUCKET_BUILD_NUMBER){return{branch:e.BITBUCKET_BRANCH,commitSha:e.BITBUCKET_COMMIT,repositoryUrl:e.BITBUCKET_GIT_HTTP_ORIGIN,buildNumber:e.BITBUCKET_BUILD_NUMBER,buildUrl:e.BITBUCKET_REPO_FULL_NAME&&e.BITBUCKET_BUILD_NUMBER?`https://bitbucket.org/${e.BITBUCKET_REPO_FULL_NAME}/pipelines/results/${e.BITBUCKET_BUILD_NUMBER}`:undefined}}return{}}},827:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true})},322:function(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){if(i===undefined)i=r;var n=Object.getOwnPropertyDescriptor(t,r);if(!n||("get"in n?!t.__esModule:n.writable||n.configurable)){n={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,i,n)}:function(e,t,r,i){if(i===undefined)i=r;e[i]=t[r]});var n=this&&this.__exportStar||function(e,t){for(var r in e)if(r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r))i(t,e,r)};Object.defineProperty(t,"__esModule",{value:true});t.detectCIContext=void 0;n(r(827),t);var o=r(406);Object.defineProperty(t,"detectCIContext",{enumerable:true,get:function(){return o.detectCIContext}})},665:function(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){if(i===undefined)i=r;var n=Object.getOwnPropertyDescriptor(t,r);if(!n||("get"in n?!t.__esModule:n.writable||n.configurable)){n={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,i,n)}:function(e,t,r,i){if(i===undefined)i=r;e[i]=t[r]});var n=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var o=this&&this.__importStar||function(){var ownKeys=function(e){ownKeys=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))t[t.length]=r;return t};return ownKeys(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var r=ownKeys(e),o=0;o<r.length;o++)if(r[o]!=="default")i(t,e,r[o]);n(t,e);return t}}();var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.TestreamJestReporter=void 0;const u=s(r(764));const a=o(r(943));const l=o(r(928));const d=r(322);const f=r(10);class TestreamJestReporter{constructor(e,t={apiKey:"",projectKey:"",uploadEnabled:true},r={}){this.outputDir="ctrf";this.outputFile="ctrf-report.json";this.config={uploadEnabled:true,failOnUploadError:false,...t};const i={outputDir:this.outputDir,outputFile:this.outputFile};if(this.config.testEnvironment)i.testEnvironment=this.config.testEnvironment;if(this.config.appName)i.appName=this.config.appName;if(this.config.appVersion)i.appVersion=this.config.appVersion;if(this.config.buildName)i.buildName=this.config.buildName;if(this.config.buildNumber)i.buildNumber=this.config.buildNumber;if(this.config.buildUrl)i.buildUrl=this.config.buildUrl;if(this.config.testType)i.testType=this.config.testType;if(this.config.branch)i.branchName=this.config.branch;if(this.config.commitSha)i.commit=this.config.commitSha;this.ctrfReporter=new u.default(e,i,r)}onRunStart(e,t){if(this.ctrfReporter.onRunStart){return this.ctrfReporter.onRunStart()}}onTestStart(e){if(this.ctrfReporter.onTestStart){return this.ctrfReporter.onTestStart()}}onTestResult(e,t,r){if(this.ctrfReporter.onTestResult){return this.ctrfReporter.onTestResult(e,t)}}async onRunComplete(e,t){if(this.ctrfReporter.onRunComplete){await this.ctrfReporter.onRunComplete()}const r=await this.readCTRFReport();if(!r){f.defaultLogger.error("Failed to read CTRF report, skipping upload");return}f.defaultLogger.info(`CTRF report written to: ${l.join(this.outputDir,this.outputFile)}`);if(this.config.uploadEnabled&&this.config.apiKey&&this.config.projectKey){await this.uploadReport(r)}}async readCTRFReport(){const e=l.join(this.outputDir,this.outputFile);try{const t=await a.readFile(e,"utf-8");const r=JSON.parse(t);r.results.tool.name="jest";return r}catch(t){f.defaultLogger.error(`Failed to read CTRF report from ${e}: ${t instanceof Error?t.message:String(t)}`);return null}}async uploadReport(e){const t=(0,d.detectCIContext)();const r=this.config.branch||t.branch;const i=this.config.commitSha||t.commitSha;const n=this.config.repositoryUrl||t.repositoryUrl;const o=this.config.buildNumber||t.buildNumber;const s=this.config.buildUrl||t.buildUrl;const u=await(0,f.uploadToApi)({report:e,projectKey:this.config.projectKey,apiKey:this.config.apiKey,branch:r,commitSha:i,repositoryUrl:n,buildName:this.config.buildName,buildNumber:o,buildUrl:s,testEnvironment:this.config.testEnvironment,appName:this.config.appName,appVersion:this.config.appVersion,testType:this.config.testType,logger:f.defaultLogger});if(!u.success&&this.config.failOnUploadError){throw new Error(`Upload failed: ${u.error}`)}}}t.TestreamJestReporter=TestreamJestReporter;t["default"]=TestreamJestReporter},10:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.detectGitContext=t.defaultLogger=void 0;t.uploadToApi=uploadToApi;const i=r(322);const n=r(982);const o="https://test-manager-backend.fly.dev";t.defaultLogger={info:e=>console.log(e),warning:e=>console.warn(e),error:e=>console.error(e)};async function uploadToApi(e){const{report:r,projectKey:i,apiKey:s,commitSha:u="",branch:a="",repositoryUrl:l="",buildName:d,buildNumber:f,buildUrl:p,testEnvironment:c,appName:m,appVersion:h,testType:_,logger:v=t.defaultLogger}=e;if(!r.reportId){r.reportId=(0,n.randomUUID)()}try{v.info("=".repeat(60));v.info("Testream - Jest Test Results Upload");v.info("=".repeat(60));v.info(`Test Summary:`);v.info(` Total: ${r.results.summary.tests}`);v.info(` Passed: ${r.results.summary.passed}`);v.info(` Failed: ${r.results.summary.failed}`);v.info(` Skipped: ${r.results.summary.skipped}`);v.info("");if(a||u||l){v.info(`Git Context:`);if(a)v.info(` Branch: ${a}`);if(u)v.info(` Commit: ${u.substring(0,7)}`);if(l)v.info(` Repository: ${l}`);v.info("")}if(d||f||p){v.info(`Build Info:`);if(d)v.info(` Name: ${d}`);if(f)v.info(` Number: ${f}`);if(p)v.info(` URL: ${p}`);v.info("")}if(c||m||h||_){v.info(`Environment:`);if(c)v.info(` Environment: ${c}`);if(m)v.info(` App: ${m}`);if(h)v.info(` Version: ${h}`);if(_)v.info(` Test Type: ${_}`);v.info("")}const e={report:r,reportId:r.reportId,projectKey:i,commitSha:u,branch:a,repositoryUrl:l,buildName:d,buildNumber:f,buildUrl:p,testEnvironment:c,appName:m,appVersion:h,testType:_};v.info(`Uploading test results...`);let t;try{t=await fetch(`${o}/api/v1/ingest`,{method:"POST",headers:{"X-API-KEY":s,"Content-Type":"application/json"},body:JSON.stringify(e)})}catch(e){const t=e instanceof Error?e.message:String(e);throw new Error(`Failed to connect to API: ${t}`)}if(!t.ok){const e=await t.text();let i=null;try{i=JSON.parse(e)}catch{}if(t.status===409){v.warning(`Report already exists: ${i?.reportId||"unknown"}`);v.info("This is expected if the workflow was re-run");return{success:true,reportId:i?.reportId||r.reportId,summary:{passed:r.results.summary.passed,failed:r.results.summary.failed,skipped:r.results.summary.skipped,total:r.results.summary.tests}}}const n=i?.error?`${i.error}${i.details?`: ${i.details}`:""}`:e||`HTTP ${t.status}`;throw new Error(`API ingest failed (${t.status}): ${n}`)}const n=await t.json();v.info("");v.info("=".repeat(60));v.info("Upload completed successfully");v.info(` Report ID: ${n.reportId}`);v.info(` Test Run ID: ${n.testRunId}`);v.info("=".repeat(60));return{success:true,reportId:n.reportId,testRunId:n.testRunId,summary:n.summary}}catch(e){const t=e instanceof Error?e.message:String(e);v.error(`Upload failed: ${t}`);return{success:false,reportId:"",error:t}}}t.detectGitContext=i.detectCIContext},982:e=>{e.exports=require("crypto")},896:e=>{e.exports=require("fs")},943:e=>{e.exports=require("fs/promises")},928:e=>{e.exports=require("path")}};var t={};function __nccwpck_require__(r){var i=t[r];if(i!==undefined){return i.exports}var n=t[r]={exports:{}};var o=true;try{e[r].call(n.exports,n,n.exports,__nccwpck_require__);o=false}finally{if(o)delete t[r]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:true});e.TestreamJestReporter=e["default"]=void 0;var t=__nccwpck_require__(665);Object.defineProperty(e,"default",{enumerable:true,get:function(){return t.TestreamJestReporter}});var i=__nccwpck_require__(665);Object.defineProperty(e,"TestreamJestReporter",{enumerable:true,get:function(){return i.TestreamJestReporter}})})();module.exports=r})();
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testream/jest-reporter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Jest CTRF reporter with automatic upload to Testream",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "
|
|
8
|
+
"build": "npm run build:bundle && npm run build:types",
|
|
9
|
+
"build:bundle": "ncc build src/index.ts -o dist --minify",
|
|
10
|
+
"build:types": "tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
9
11
|
"format": "prettier --write '**/*.ts'",
|
|
10
12
|
"lint": "eslint src/**/*.ts",
|
|
11
13
|
"prepublishOnly": "npm run build",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
"@types/node": "^20.10.6",
|
|
45
47
|
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
|
46
48
|
"@typescript-eslint/parser": "^6.17.0",
|
|
49
|
+
"@vercel/ncc": "^0.38.1",
|
|
47
50
|
"eslint": "^8.56.0",
|
|
48
51
|
"prettier": "^3.1.1",
|
|
49
52
|
"typescript": "^5.3.3"
|
package/dist/reporter.js
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.TestreamJestReporter = void 0;
|
|
40
|
-
const jest_ctrf_json_reporter_1 = __importDefault(require("jest-ctrf-json-reporter"));
|
|
41
|
-
const fs = __importStar(require("fs/promises"));
|
|
42
|
-
const path = __importStar(require("path"));
|
|
43
|
-
const shared_types_1 = require("@jira-test-manager/shared-types");
|
|
44
|
-
const uploader_1 = require("./uploader");
|
|
45
|
-
/**
|
|
46
|
-
* Testream Jest Reporter
|
|
47
|
-
* Wraps jest-ctrf-json-reporter to generate CTRF reports and upload them to Testream
|
|
48
|
-
*/
|
|
49
|
-
class TestreamJestReporter {
|
|
50
|
-
constructor(globalConfig, options = {
|
|
51
|
-
apiKey: '',
|
|
52
|
-
projectKey: '',
|
|
53
|
-
uploadEnabled: true,
|
|
54
|
-
}, context = {}) {
|
|
55
|
-
this.outputDir = 'ctrf';
|
|
56
|
-
this.outputFile = 'ctrf-report.json';
|
|
57
|
-
this.config = {
|
|
58
|
-
uploadEnabled: true,
|
|
59
|
-
failOnUploadError: false,
|
|
60
|
-
...options,
|
|
61
|
-
};
|
|
62
|
-
// Initialize jest-ctrf-json-reporter with fixed output path and metadata
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
-
const ctrfConfig = {
|
|
65
|
-
outputDir: this.outputDir,
|
|
66
|
-
outputFile: this.outputFile,
|
|
67
|
-
};
|
|
68
|
-
// Add environment metadata (same pattern as playwright-reporter)
|
|
69
|
-
if (this.config.testEnvironment)
|
|
70
|
-
ctrfConfig.testEnvironment = this.config.testEnvironment;
|
|
71
|
-
if (this.config.appName)
|
|
72
|
-
ctrfConfig.appName = this.config.appName;
|
|
73
|
-
if (this.config.appVersion)
|
|
74
|
-
ctrfConfig.appVersion = this.config.appVersion;
|
|
75
|
-
if (this.config.buildName)
|
|
76
|
-
ctrfConfig.buildName = this.config.buildName;
|
|
77
|
-
if (this.config.buildNumber)
|
|
78
|
-
ctrfConfig.buildNumber = this.config.buildNumber;
|
|
79
|
-
if (this.config.buildUrl)
|
|
80
|
-
ctrfConfig.buildUrl = this.config.buildUrl;
|
|
81
|
-
if (this.config.testType)
|
|
82
|
-
ctrfConfig.testType = this.config.testType;
|
|
83
|
-
if (this.config.branch)
|
|
84
|
-
ctrfConfig.branchName = this.config.branch;
|
|
85
|
-
if (this.config.commitSha)
|
|
86
|
-
ctrfConfig.commit = this.config.commitSha;
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
-
this.ctrfReporter = new jest_ctrf_json_reporter_1.default(globalConfig, ctrfConfig, context);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Called when test run starts
|
|
92
|
-
*/
|
|
93
|
-
onRunStart(_aggregatedResult, _options) {
|
|
94
|
-
if (this.ctrfReporter.onRunStart) {
|
|
95
|
-
return this.ctrfReporter.onRunStart();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Called when an individual test starts
|
|
100
|
-
*/
|
|
101
|
-
onTestStart(_test) {
|
|
102
|
-
if (this.ctrfReporter.onTestStart) {
|
|
103
|
-
return this.ctrfReporter.onTestStart();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Called when a test file completes
|
|
108
|
-
*/
|
|
109
|
-
onTestResult(test, testResult, _aggregatedResult) {
|
|
110
|
-
if (this.ctrfReporter.onTestResult) {
|
|
111
|
-
return this.ctrfReporter.onTestResult(test, testResult);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Called when entire test run completes
|
|
116
|
-
*/
|
|
117
|
-
async onRunComplete(_testContexts, _results) {
|
|
118
|
-
// Let jest-ctrf-json-reporter complete and write the CTRF file
|
|
119
|
-
if (this.ctrfReporter.onRunComplete) {
|
|
120
|
-
await this.ctrfReporter.onRunComplete();
|
|
121
|
-
}
|
|
122
|
-
// Read the generated CTRF report
|
|
123
|
-
const report = await this.readCTRFReport();
|
|
124
|
-
if (!report) {
|
|
125
|
-
uploader_1.defaultLogger.error('Failed to read CTRF report, skipping upload');
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
uploader_1.defaultLogger.info(`CTRF report written to: ${path.join(this.outputDir, this.outputFile)}`);
|
|
129
|
-
// Upload to API if enabled
|
|
130
|
-
if (this.config.uploadEnabled && this.config.apiKey && this.config.projectKey) {
|
|
131
|
-
await this.uploadReport(report);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Read CTRF report from file
|
|
136
|
-
*/
|
|
137
|
-
async readCTRFReport() {
|
|
138
|
-
const reportPath = path.join(this.outputDir, this.outputFile);
|
|
139
|
-
try {
|
|
140
|
-
const content = await fs.readFile(reportPath, 'utf-8');
|
|
141
|
-
const report = JSON.parse(content);
|
|
142
|
-
// Force tool name to 'jest'
|
|
143
|
-
report.results.tool.name = 'jest';
|
|
144
|
-
return report;
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
uploader_1.defaultLogger.error(`Failed to read CTRF report from ${reportPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Upload report to Testream API
|
|
153
|
-
*/
|
|
154
|
-
async uploadReport(report) {
|
|
155
|
-
const ciContext = (0, shared_types_1.detectCIContext)();
|
|
156
|
-
// Merge CLI options with auto-detected CI context (config takes precedence)
|
|
157
|
-
const branch = this.config.branch || ciContext.branch;
|
|
158
|
-
const commitSha = this.config.commitSha || ciContext.commitSha;
|
|
159
|
-
const repositoryUrl = this.config.repositoryUrl || ciContext.repositoryUrl;
|
|
160
|
-
const buildNumber = this.config.buildNumber || ciContext.buildNumber;
|
|
161
|
-
const buildUrl = this.config.buildUrl || ciContext.buildUrl;
|
|
162
|
-
const result = await (0, uploader_1.uploadToApi)({
|
|
163
|
-
report,
|
|
164
|
-
projectKey: this.config.projectKey,
|
|
165
|
-
apiKey: this.config.apiKey,
|
|
166
|
-
// Git context
|
|
167
|
-
branch,
|
|
168
|
-
commitSha,
|
|
169
|
-
repositoryUrl,
|
|
170
|
-
// Build metadata
|
|
171
|
-
buildName: this.config.buildName,
|
|
172
|
-
buildNumber,
|
|
173
|
-
buildUrl,
|
|
174
|
-
// Environment metadata
|
|
175
|
-
testEnvironment: this.config.testEnvironment,
|
|
176
|
-
appName: this.config.appName,
|
|
177
|
-
appVersion: this.config.appVersion,
|
|
178
|
-
testType: this.config.testType,
|
|
179
|
-
logger: uploader_1.defaultLogger,
|
|
180
|
-
});
|
|
181
|
-
if (!result.success && this.config.failOnUploadError) {
|
|
182
|
-
throw new Error(`Upload failed: ${result.error}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
exports.TestreamJestReporter = TestreamJestReporter;
|
|
187
|
-
exports.default = TestreamJestReporter;
|
package/dist/types.js
DELETED
package/dist/uploader.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.detectGitContext = exports.defaultLogger = void 0;
|
|
4
|
-
exports.uploadToApi = uploadToApi;
|
|
5
|
-
const shared_types_1 = require("@jira-test-manager/shared-types");
|
|
6
|
-
const crypto_1 = require("crypto");
|
|
7
|
-
/**
|
|
8
|
-
* Hardcoded API URL for Testream backend
|
|
9
|
-
*/
|
|
10
|
-
const API_URL = 'https://test-manager-backend.fly.dev';
|
|
11
|
-
/**
|
|
12
|
-
* Default console logger
|
|
13
|
-
*/
|
|
14
|
-
exports.defaultLogger = {
|
|
15
|
-
info: (msg) => console.log(msg),
|
|
16
|
-
warning: (msg) => console.warn(msg),
|
|
17
|
-
error: (msg) => console.error(msg),
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Upload CTRF report to Testream API
|
|
21
|
-
*/
|
|
22
|
-
async function uploadToApi(options) {
|
|
23
|
-
const { report, projectKey, apiKey, commitSha = '', branch = '', repositoryUrl = '', buildName, buildNumber, buildUrl, testEnvironment, appName, appVersion, testType, logger = exports.defaultLogger, } = options;
|
|
24
|
-
// Ensure reportId exists
|
|
25
|
-
if (!report.reportId) {
|
|
26
|
-
report.reportId = (0, crypto_1.randomUUID)();
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
logger.info('='.repeat(60));
|
|
30
|
-
logger.info('Testream - Jest Test Results Upload');
|
|
31
|
-
logger.info('='.repeat(60));
|
|
32
|
-
logger.info(`Test Summary:`);
|
|
33
|
-
logger.info(` Total: ${report.results.summary.tests}`);
|
|
34
|
-
logger.info(` Passed: ${report.results.summary.passed}`);
|
|
35
|
-
logger.info(` Failed: ${report.results.summary.failed}`);
|
|
36
|
-
logger.info(` Skipped: ${report.results.summary.skipped}`);
|
|
37
|
-
logger.info('');
|
|
38
|
-
// Log git context if provided
|
|
39
|
-
if (branch || commitSha || repositoryUrl) {
|
|
40
|
-
logger.info(`Git Context:`);
|
|
41
|
-
if (branch)
|
|
42
|
-
logger.info(` Branch: ${branch}`);
|
|
43
|
-
if (commitSha)
|
|
44
|
-
logger.info(` Commit: ${commitSha.substring(0, 7)}`);
|
|
45
|
-
if (repositoryUrl)
|
|
46
|
-
logger.info(` Repository: ${repositoryUrl}`);
|
|
47
|
-
logger.info('');
|
|
48
|
-
}
|
|
49
|
-
// Log build info if provided
|
|
50
|
-
if (buildName || buildNumber || buildUrl) {
|
|
51
|
-
logger.info(`Build Info:`);
|
|
52
|
-
if (buildName)
|
|
53
|
-
logger.info(` Name: ${buildName}`);
|
|
54
|
-
if (buildNumber)
|
|
55
|
-
logger.info(` Number: ${buildNumber}`);
|
|
56
|
-
if (buildUrl)
|
|
57
|
-
logger.info(` URL: ${buildUrl}`);
|
|
58
|
-
logger.info('');
|
|
59
|
-
}
|
|
60
|
-
// Log environment info if provided
|
|
61
|
-
if (testEnvironment || appName || appVersion || testType) {
|
|
62
|
-
logger.info(`Environment:`);
|
|
63
|
-
if (testEnvironment)
|
|
64
|
-
logger.info(` Environment: ${testEnvironment}`);
|
|
65
|
-
if (appName)
|
|
66
|
-
logger.info(` App: ${appName}`);
|
|
67
|
-
if (appVersion)
|
|
68
|
-
logger.info(` Version: ${appVersion}`);
|
|
69
|
-
if (testType)
|
|
70
|
-
logger.info(` Test Type: ${testType}`);
|
|
71
|
-
logger.info('');
|
|
72
|
-
}
|
|
73
|
-
// Prepare payload with all metadata
|
|
74
|
-
const ingestPayload = {
|
|
75
|
-
report,
|
|
76
|
-
reportId: report.reportId,
|
|
77
|
-
projectKey,
|
|
78
|
-
// Git context
|
|
79
|
-
commitSha,
|
|
80
|
-
branch,
|
|
81
|
-
repositoryUrl,
|
|
82
|
-
// Build metadata
|
|
83
|
-
buildName,
|
|
84
|
-
buildNumber,
|
|
85
|
-
buildUrl,
|
|
86
|
-
// Environment metadata
|
|
87
|
-
testEnvironment,
|
|
88
|
-
appName,
|
|
89
|
-
appVersion,
|
|
90
|
-
testType,
|
|
91
|
-
};
|
|
92
|
-
// Upload to API
|
|
93
|
-
logger.info(`Uploading test results...`);
|
|
94
|
-
let response;
|
|
95
|
-
try {
|
|
96
|
-
response = await fetch(`${API_URL}/api/v1/ingest`, {
|
|
97
|
-
method: 'POST',
|
|
98
|
-
headers: {
|
|
99
|
-
'X-API-KEY': apiKey,
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
},
|
|
102
|
-
body: JSON.stringify(ingestPayload),
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
107
|
-
throw new Error(`Failed to connect to API: ${errorMessage}`);
|
|
108
|
-
}
|
|
109
|
-
// Handle errors
|
|
110
|
-
if (!response.ok) {
|
|
111
|
-
const responseText = await response.text();
|
|
112
|
-
let errorData = null;
|
|
113
|
-
try {
|
|
114
|
-
errorData = JSON.parse(responseText);
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Not JSON
|
|
118
|
-
}
|
|
119
|
-
// Handle 409 (already exists)
|
|
120
|
-
if (response.status === 409) {
|
|
121
|
-
logger.warning(`Report already exists: ${errorData?.reportId || 'unknown'}`);
|
|
122
|
-
logger.info('This is expected if the workflow was re-run');
|
|
123
|
-
return {
|
|
124
|
-
success: true,
|
|
125
|
-
reportId: errorData?.reportId || report.reportId,
|
|
126
|
-
summary: {
|
|
127
|
-
passed: report.results.summary.passed,
|
|
128
|
-
failed: report.results.summary.failed,
|
|
129
|
-
skipped: report.results.summary.skipped,
|
|
130
|
-
total: report.results.summary.tests,
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
// Other errors
|
|
135
|
-
const errorMessage = errorData?.error
|
|
136
|
-
? `${errorData.error}${errorData.details ? `: ${errorData.details}` : ''}`
|
|
137
|
-
: responseText || `HTTP ${response.status}`;
|
|
138
|
-
throw new Error(`API ingest failed (${response.status}): ${errorMessage}`);
|
|
139
|
-
}
|
|
140
|
-
const result = (await response.json());
|
|
141
|
-
logger.info('');
|
|
142
|
-
logger.info('='.repeat(60));
|
|
143
|
-
logger.info('Upload completed successfully');
|
|
144
|
-
logger.info(` Report ID: ${result.reportId}`);
|
|
145
|
-
logger.info(` Test Run ID: ${result.testRunId}`);
|
|
146
|
-
logger.info('='.repeat(60));
|
|
147
|
-
return {
|
|
148
|
-
success: true,
|
|
149
|
-
reportId: result.reportId,
|
|
150
|
-
testRunId: result.testRunId,
|
|
151
|
-
summary: result.summary,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
156
|
-
logger.error(`Upload failed: ${errorMessage}`);
|
|
157
|
-
return {
|
|
158
|
-
success: false,
|
|
159
|
-
reportId: '',
|
|
160
|
-
error: errorMessage,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// Keep old function name for backwards compatibility
|
|
165
|
-
exports.detectGitContext = shared_types_1.detectCIContext;
|