@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 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.2",
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": "tsc",
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
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
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;