@testream/vitest-reporter 0.1.0 → 1.0.1

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 CHANGED
@@ -1,18 +1,15 @@
1
1
  # @testream/vitest-reporter
2
2
 
3
- Vitest reporter that generates [CTRF](https://ctrf.io) test reports and automatically uploads them to [Testream](https://testream.app).
3
+ Bridges Vitest test runs from your codebase into Jira with Testream. Once configured, it uploads results automatically.
4
4
 
5
- ## Installation
5
+ ## Quick Start
6
6
 
7
7
  ```bash
8
8
  npm install --save-dev @testream/vitest-reporter
9
9
  ```
10
10
 
11
- ## Usage
12
-
13
- Add the reporter to your `vitest.config.ts`:
14
-
15
11
  ```ts
12
+ // vitest.config.ts
16
13
  import { defineConfig } from 'vitest/config'
17
14
 
18
15
  export default defineConfig({
@@ -21,59 +18,11 @@ export default defineConfig({
21
18
  'default',
22
19
  ['@testream/vitest-reporter', {
23
20
  apiKey: process.env.TESTREAM_API_KEY,
21
+ uploadEnabled: true,
24
22
  }],
25
23
  ],
26
24
  },
27
25
  })
28
26
  ```
29
27
 
30
- ## Configuration
31
-
32
- | Option | Type | Default | Description |
33
- |--------|------|---------|-------------|
34
- | `apiKey` | `string` | **required** | Testream API key |
35
- | `uploadEnabled` | `boolean` | `true` | Enable/disable upload to Testream |
36
- | `failOnUploadError` | `boolean` | `false` | Fail the test run if upload fails |
37
- | `outputDir` | `string` | `'ctrf'` | Directory for the CTRF report |
38
- | `outputFile` | `string` | `'ctrf-report.json'` | Filename for the CTRF report |
39
- | `testType` | `string` | `'unit'` | Test type identifier |
40
- | `branch` | `string` | auto-detected | Git branch name |
41
- | `commitSha` | `string` | auto-detected | Git commit SHA |
42
- | `repositoryUrl` | `string` | auto-detected | Git repository URL |
43
- | `buildName` | `string` | — | Build name/identifier |
44
- | `buildNumber` | `string` | auto-detected | Build number |
45
- | `buildUrl` | `string` | auto-detected | CI pipeline URL |
46
- | `testEnvironment` | `string` | — | Environment name (e.g., 'staging') |
47
- | `appName` | `string` | — | Application name under test |
48
- | `appVersion` | `string` | — | Application version under test |
49
-
50
- ## CI/CD Setup
51
-
52
- Git context (branch, commit SHA, repository URL) and build metadata are auto-detected for:
53
-
54
- - GitHub Actions
55
- - GitLab CI
56
- - Azure Pipelines
57
- - CircleCI
58
- - Jenkins
59
- - Bitbucket Pipelines
60
-
61
- ## Local Development
62
-
63
- Generate a CTRF report locally without uploading:
64
-
65
- ```ts
66
- ['@testream/vitest-reporter', {
67
- apiKey: 'not-needed',
68
- uploadEnabled: false,
69
- }]
70
- ```
71
-
72
- ## Requirements
73
-
74
- - Vitest >= 3.0.0
75
- - Node.js >= 18.0.0
76
-
77
- ## License
78
-
79
- See [LICENSE](./LICENSE) for details.
28
+ Need more configuration options? See the full guide at https://docs.testream.app/reporters/vitest.
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- (()=>{"use strict";var e={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 o=this&&this.__createBinding||(Object.create?function(e,t,r,o){if(o===undefined)o=r;var s=Object.getOwnPropertyDescriptor(t,r);if(!s||("get"in s?!t.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,o,s)}:function(e,t,r,o){if(o===undefined)o=r;e[o]=t[r]});var s=this&&this.__exportStar||function(e,t){for(var r in e)if(r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r))o(t,e,r)};Object.defineProperty(t,"__esModule",{value:true});t.ensureReportId=t.mapAttachmentsToTestResults=t.uploadArtifacts=t.uploadTestRun=t.detectCIContext=void 0;s(r(827),t);var n=r(406);Object.defineProperty(t,"detectCIContext",{enumerable:true,get:function(){return n.detectCIContext}});var a=r(969);Object.defineProperty(t,"uploadTestRun",{enumerable:true,get:function(){return a.uploadTestRun}});Object.defineProperty(t,"uploadArtifacts",{enumerable:true,get:function(){return a.uploadArtifacts}});Object.defineProperty(t,"mapAttachmentsToTestResults",{enumerable:true,get:function(){return a.mapAttachmentsToTestResults}});Object.defineProperty(t,"ensureReportId",{enumerable:true,get:function(){return a.ensureReportId}})},969:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.ensureReportId=ensureReportId;t.uploadTestRun=uploadTestRun;t.mapAttachmentsToTestResults=mapAttachmentsToTestResults;t.uploadArtifacts=uploadArtifacts;const o="https://test-manager-backend.fly.dev";function ensureReportId(e){if(typeof e.reportId==="string"&&e.reportId.trim().length>0){return e.reportId}const t=crypto.randomUUID();e.reportId=t;return t}function resolveApiUrl(e){const t=normalizeApiUrl(e);if(t){return t}const r=getEnvApiUrl();if(r){return r}return o}function getEnvApiUrl(){if(typeof process==="undefined"||!process?.env){return undefined}return normalizeApiUrl(process.env.TESTREAM_API_URL)}function normalizeApiUrl(e){if(!e){return undefined}const t=e.trim();if(!t){return undefined}return t.replace(/\/+$/,"")}async function uploadTestRun(e){const{report:t,apiKey:r}=e;const o=resolveApiUrl(e.apiUrl);const s=ensureReportId(t);const n={report:t,reportId:s,commitSha:e.commitSha,branch:e.branch,repositoryUrl:e.repositoryUrl,buildName:e.buildName,buildNumber:e.buildNumber,buildUrl:e.buildUrl,testEnvironment:e.testEnvironment,appName:e.appName,appVersion:e.appVersion,testType:e.testType};console.log("Uploading test results...");let a;try{a=await fetch(`${o}/api/v1/ingest`,{method:"POST",headers:{"X-API-KEY":r,"Content-Type":"application/json"},body:JSON.stringify(n)})}catch(e){const t=e instanceof Error?e.message:String(e);return{success:false,reportId:s,error:`Connection failed: ${t}`}}if(a.status===409){console.warn("Report already exists (workflow may have been re-run)");return{success:true,reportId:s,summary:{passed:t.results.summary.passed,failed:t.results.summary.failed,skipped:t.results.summary.skipped,total:t.results.summary.tests},alreadyExists:true}}if(!a.ok){const e=await a.text();return{success:false,reportId:s,error:`Upload failed (HTTP ${a.status}): ${e}`}}const i=await a.json();console.log(`✓ Test results uploaded successfully`);console.log(` Report ID: ${i.reportId}`);console.log(` Test Run ID: ${i.testRunId}`);console.log(` Tests: ${i.summary.passed}/${i.summary.total} passed`);return{success:true,reportId:i.reportId,testRunId:i.testRunId,summary:{passed:i.summary.passed,failed:i.summary.failed,skipped:i.summary.skipped,total:i.summary.total},testResults:i.testResults}}function mapAttachmentsToTestResults(e,t){const r=new Map;for(const e of t){const t=r.get(e.name)??[];t.push(e.id);r.set(e.name,t)}const o=[];for(const t of e){const e=r.get(t.name);const s=e?.shift();if(!t.attachments||t.attachments.length===0){continue}if(!s){console.warn(`Skipping artifact upload: could not match test result for "${t.name}"`);continue}o.push({testResultId:s,attachments:t.attachments})}return o}async function uploadArtifacts(e){const{reportId:t,apiKey:r,testResults:o}=e;const s=resolveApiUrl(e.apiUrl);let n=0;for(const e of o){for(const o of e.attachments){try{const a=await uploadSingleArtifact({testResultId:e.testResultId,attachment:o,reportId:t,apiKey:r,apiUrl:s});if(a)n++}catch(e){const t=e instanceof Error?e.message:String(e);console.error(`Failed to upload artifact ${o.name}: ${t}`)}}}if(n>0){console.log(`✓ Uploaded ${n} artifact(s)`)}return n}async function uploadSingleArtifact(e){const{testResultId:t,attachment:o,reportId:s,apiKey:n,apiUrl:a}=e;const i=await Promise.resolve().then(r.t.bind(r,943,23));const l=await Promise.resolve().then(r.t.bind(r,928,23));const c=l.resolve(o.path);try{await i.access(c)}catch{console.warn(`Artifact not found: ${l.basename(c)}`);return false}let u;let p;const d=l.basename(c);const f=o.contentType||"application/octet-stream";try{u=await i.readFile(c);p=u.byteLength}catch(e){const t=e instanceof Error?e.message:String(e);throw new Error(`Failed to read file: ${t}`)}try{return await uploadWithPresignedUrl({testResultId:t,reportId:s,apiKey:n,apiUrl:a,attachmentName:o.name,fileName:d,contentType:f,sizeBytes:p,fileBuffer:u})}catch(e){if(!(e instanceof LegacyUploadRequiredError)){throw e}return await uploadWithLegacyEndpoint({testResultId:t,attachmentName:o.name,reportId:s,apiKey:n,apiUrl:a,fileName:d,contentType:f,fileBuffer:u})}}class LegacyUploadRequiredError extends Error{constructor(){super("Legacy upload endpoint required")}}async function uploadWithPresignedUrl(e){const{testResultId:t,reportId:r,apiKey:o,apiUrl:s,attachmentName:n,fileName:a,contentType:i,sizeBytes:l,fileBuffer:c}=e;const u={testResultId:t,ctrfAttachmentName:n,fileName:a,contentType:i,sizeBytes:l};const p=await fetch(`${s}/api/v1/artifacts/${r}/upload-url`,{method:"POST",headers:{"X-API-KEY":o,"Content-Type":"application/json"},body:JSON.stringify(u)});if(shouldFallbackToLegacyUpload(p.status)){throw new LegacyUploadRequiredError}if(!p.ok){const e=await p.text();throw new Error(`Failed to create direct upload URL (HTTP ${p.status}): ${e}`)}const d=await p.json();const f={...d.requiredHeaders??{}};if(!hasHeaderIgnoreCase(f,"Content-Type")){f["Content-Type"]=i}const m=await fetch(d.uploadUrl,{method:"PUT",headers:f,body:c});if(!m.ok){const e=await m.text();throw new Error(`Direct artifact upload failed (HTTP ${m.status}): ${e}`)}const h={testResultId:t,ctrfAttachmentName:n,fileName:a,contentType:i,sizeBytes:l,storageKey:d.storageKey};const _=await fetch(`${s}/api/v1/artifacts/${r}/complete`,{method:"POST",headers:{"X-API-KEY":o,"Content-Type":"application/json"},body:JSON.stringify(h)});if(!_.ok){const e=await _.text();throw new Error(`Failed to finalize artifact upload (HTTP ${_.status}): ${e}`)}return true}async function uploadWithLegacyEndpoint(e){const{testResultId:t,attachmentName:r,reportId:o,apiKey:s,apiUrl:n,fileName:a,contentType:i,fileBuffer:l}=e;const c=new Blob([new Uint8Array(l)],{type:i});const u=new FormData;u.append("testResultId",t);u.append("ctrfAttachmentName",r);u.append("file",c,a);const p=await fetch(`${n}/api/v1/artifacts/${o}`,{method:"POST",headers:{"X-API-KEY":s},body:u});if(!p.ok){const e=await p.text();throw new Error(`Legacy artifact upload failed (HTTP ${p.status}): ${e}`)}return true}function shouldFallbackToLegacyUpload(e){return e===404||e===405||e===501}function hasHeaderIgnoreCase(e,t){const r=t.toLowerCase();return Object.keys(e).some((e=>e.toLowerCase()===r))}},943:e=>{e.exports=require("fs/promises")},928:e=>{e.exports=require("path")}};var t={};function __nccwpck_require__(r){var o=t[r];if(o!==undefined){return o.exports}var s=t[r]={exports:{}};var n=true;try{e[r].call(s.exports,s,s.exports,__nccwpck_require__);n=false}finally{if(n)delete t[r]}return s.exports}(()=>{var e=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;var t;__nccwpck_require__.t=function(r,o){if(o&1)r=this(r);if(o&8)return r;if(typeof r==="object"&&r){if(o&4&&r.__esModule)return r;if(o&16&&typeof r.then==="function")return r}var s=Object.create(null);__nccwpck_require__.r(s);var n={};t=t||[null,e({}),e([]),e(e)];for(var a=o&2&&r;typeof a=="object"&&!~t.indexOf(a);a=e(a)){Object.getOwnPropertyNames(a).forEach((e=>n[e]=()=>r[e]))}n["default"]=()=>r;__nccwpck_require__.d(s,n);return s}})();(()=>{__nccwpck_require__.d=(e,t)=>{for(var r in t){if(__nccwpck_require__.o(t,r)&&!__nccwpck_require__.o(e,r)){Object.defineProperty(e,r,{enumerable:true,get:t[r]})}}}})();(()=>{__nccwpck_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})();(()=>{__nccwpck_require__.r=e=>{if(typeof Symbol!=="undefined"&&Symbol.toStringTag){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}Object.defineProperty(e,"__esModule",{value:true})}})();if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};__nccwpck_require__.r(r);__nccwpck_require__.d(r,{TestreamVitestReporter:()=>TestreamVitestReporter,default:()=>TestreamVitestReporter});var o=__nccwpck_require__(943);var s=__nccwpck_require__(928);var n=__nccwpck_require__(322);function mapStatus(e){switch(e){case"passed":return"passed";case"failed":return"failed";case"skipped":return"skipped";default:return"pending"}}function buildSuiteName(e){const t=[];let r=e.parent;while(r&&r.type==="suite"){t.unshift(r.name);r=r.parent}return t.join(" > ")}function collectTests(e){const t=[];for(const r of e){if(r.type==="test"){t.push(r)}else if(r.type==="suite"){t.push(...collectTests(r.children))}}return t}function buildCTRFReport(e,t,r){const o=Date.now();const s=[];const n=new Set;for(const o of e){const e=collectTests(o.children);for(const o of e){const e=o.result();const a=o.diagnostic();const i=e.state;const l=mapStatus(i);const c=Math.round(a?.duration??0);const u=a?.startTime??r;const p=u+c;const d=a?.retryCount??0;const f=a?.flaky??false;const m=buildSuiteName(o);if(m){n.add(m)}let h;let _;const b=e.errors;if(b&&b.length>0){const e=b[0];h=e.message;const t=e.stackTrace??e.stack;_=typeof t==="string"?t:undefined}const y={name:o.name,status:l,duration:c,start:u,stop:p,rawStatus:i,tags:[],type:t.testType||"unit",filePath:o.module.moduleId,retries:d,flaky:f,steps:[],suite:m||undefined,stdout:[],stderr:[],message:h,trace:_};s.push(y)}}const a={tests:s.length,passed:s.filter((e=>e.status==="passed")).length,failed:s.filter((e=>e.status==="failed")).length,skipped:s.filter((e=>e.status==="skipped")).length,pending:s.filter((e=>e.status==="pending")).length,other:s.filter((e=>e.status==="other")).length,suites:n.size,start:r,stop:o};const i={reportFormat:"CTRF",specVersion:"0.0.1",timestamp:new Date(r).toISOString(),generatedBy:"@testream/vitest-reporter",extra:{branch:t.branch,commitSha:t.commitSha},results:{tool:{name:"vitest"},summary:a,tests:s,environment:{...t.testEnvironment?{testEnvironment:t.testEnvironment}:{},...t.appName?{appName:t.appName}:{},...t.appVersion?{appVersion:t.appVersion}:{},...t.buildName?{buildName:t.buildName}:{},...t.buildNumber?{buildNumber:t.buildNumber}:{},...t.buildUrl?{buildUrl:t.buildUrl}:{}}}};return i}class ReportUploader{constructor(e){this.config={apiKey:e.apiKey,branch:e.branch,commitSha:e.commitSha,repositoryUrl:e.repositoryUrl,failOnUploadError:e.failOnUploadError??false,buildName:e.buildName,buildNumber:e.buildNumber,buildUrl:e.buildUrl,testEnvironment:e.testEnvironment,appName:e.appName,appVersion:e.appVersion,testType:e.testType}}async uploadReport(e){try{const t=await(0,n.uploadTestRun)({report:e,apiKey:this.config.apiKey,commitSha:this.config.commitSha,branch:this.config.branch,repositoryUrl:this.config.repositoryUrl,buildName:this.config.buildName,buildNumber:this.config.buildNumber,buildUrl:this.config.buildUrl,testEnvironment:this.config.testEnvironment,appName:this.config.appName,appVersion:this.config.appVersion,testType:this.config.testType});if(!t.success){if(this.config.failOnUploadError){throw new Error(t.error||"Upload failed")}return t}let r=0;if(!t.alreadyExists&&t.testRunId&&t.testResults){const o=(0,n.mapAttachmentsToTestResults)(e.results.tests,t.testResults);if(o.length>0){r=await(0,n.uploadArtifacts)({reportId:t.reportId,apiKey:this.config.apiKey,testResults:o})}}return{...t,artifactCount:r}}catch(t){const r=t instanceof Error?t.message:String(t);if(this.config.failOnUploadError){throw new Error(`Failed to upload report: ${r}`)}return{success:false,reportId:(0,n.ensureReportId)(e),error:r}}}}class TestreamVitestReporter{constructor(e){this.uploader=null;this.startTimestamp=0;if(!e.apiKey){throw new Error("TestreamVitestReporter: apiKey is required")}this.config={uploadEnabled:true,outputFile:"ctrf-report.json",outputDir:"ctrf",failOnUploadError:false,testType:"unit",...e};const t=(0,n.detectCIContext)();if(!this.config.branch&&t.branch){this.config.branch=t.branch}if(!this.config.commitSha&&t.commitSha){this.config.commitSha=t.commitSha}if(!this.config.repositoryUrl&&t.repositoryUrl){this.config.repositoryUrl=t.repositoryUrl}if(!this.config.buildNumber&&t.buildNumber){this.config.buildNumber=t.buildNumber}if(!this.config.buildUrl&&t.buildUrl){this.config.buildUrl=t.buildUrl}this.reportPath=s.join(this.config.outputDir,this.config.outputFile);if(this.config.uploadEnabled){this.uploader=new ReportUploader(this.config)}}onTestRunStart(e){this.startTimestamp=Date.now();console.log("");console.log("=".repeat(60));console.log("Vitest Testream Reporter");console.log("=".repeat(60));if(this.config.uploadEnabled){if(this.config.branch)console.log(`Branch: ${this.config.branch}`);if(this.config.commitSha)console.log(`Commit: ${this.config.commitSha.substring(0,7)}`);if(this.config.repositoryUrl)console.log(`Repository: ${this.config.repositoryUrl}`)}else{console.log("Upload: Disabled")}console.log(`Report: ${this.reportPath}`);console.log("=".repeat(60));console.log("")}async onTestRunEnd(e,t,r){const s=buildCTRFReport(e,this.config,this.startTimestamp);try{await o.mkdir(this.config.outputDir,{recursive:true});await o.writeFile(this.reportPath,JSON.stringify(s,null,2))}catch(e){const t=e instanceof Error?e.message:String(e);console.error(`Failed to write CTRF report: ${t}`)}if(!this.config.uploadEnabled||!this.uploader){console.log("");console.log("CTRF report generated");console.log(` ${this.reportPath}`);return}console.log("");console.log("=".repeat(60));console.log("Uploading Test Results");console.log("=".repeat(60));try{const e=await this.uploader.uploadReport(s);if(e.success){console.log("=".repeat(60));console.log("Upload Complete");console.log("=".repeat(60));console.log("")}else{console.error("Upload completed with errors");console.error("")}}catch(e){const t=e instanceof Error?e.message:String(e);console.error("");console.error("=".repeat(60));console.error("Upload Failed");console.error("=".repeat(60));console.error(`Error: ${t}`);console.error("");if(this.config.failOnUploadError){throw e}}}}const a=null&&TestreamVitestReporter;module.exports=r})();
1
+ (()=>{"use strict";var e={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 o=this&&this.__createBinding||(Object.create?function(e,t,r,o){if(o===undefined)o=r;var s=Object.getOwnPropertyDescriptor(t,r);if(!s||("get"in s?!t.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,o,s)}:function(e,t,r,o){if(o===undefined)o=r;e[o]=t[r]});var s=this&&this.__exportStar||function(e,t){for(var r in e)if(r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r))o(t,e,r)};Object.defineProperty(t,"__esModule",{value:true});t.ensureReportId=t.mapAttachmentsToTestResults=t.uploadArtifacts=t.uploadTestRun=t.detectCIContext=void 0;s(r(827),t);var n=r(406);Object.defineProperty(t,"detectCIContext",{enumerable:true,get:function(){return n.detectCIContext}});var a=r(969);Object.defineProperty(t,"uploadTestRun",{enumerable:true,get:function(){return a.uploadTestRun}});Object.defineProperty(t,"uploadArtifacts",{enumerable:true,get:function(){return a.uploadArtifacts}});Object.defineProperty(t,"mapAttachmentsToTestResults",{enumerable:true,get:function(){return a.mapAttachmentsToTestResults}});Object.defineProperty(t,"ensureReportId",{enumerable:true,get:function(){return a.ensureReportId}})},969:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.ensureReportId=ensureReportId;t.uploadTestRun=uploadTestRun;t.mapAttachmentsToTestResults=mapAttachmentsToTestResults;t.uploadArtifacts=uploadArtifacts;const o=r(406);const s="https://test-manager-backend.fly.dev";function ensureReportId(e){if(typeof e.reportId==="string"&&e.reportId.trim().length>0){return e.reportId}const t=crypto.randomUUID();e.reportId=t;return t}function resolveApiUrl(e){const t=normalizeApiUrl(e);if(t){return t}const r=getEnvApiUrl();if(r){return r}return s}function getEnvApiUrl(){if(typeof process==="undefined"||!process?.env){return undefined}return normalizeApiUrl(process.env.TESTREAM_API_URL)}function normalizeApiUrl(e){if(!e){return undefined}const t=e.trim();if(!t){return undefined}return t.replace(/\/+$/,"")}function isReportAlreadyExistsConflict(e){return e.errorCode==="report_already_exists"}function formatUploadError(e){const t=e.detail||e.title||e.rawBody||"Unknown error";const r=e.errorCode?`, code=${e.errorCode}`:"";return`Upload failed (HTTP ${e.statusCode}${r}): ${t}`}async function parseHttpError(e){const t=await e.text();const r=e.headers.get("content-type")||"";if(r.toLowerCase().includes("json")&&t.trim().length>0){try{const r=JSON.parse(t);return{statusCode:e.status,title:r.title,detail:r.detail||r.details||r.error,errorCode:r.errorCode,rawBody:t}}catch{}}return{statusCode:e.status,rawBody:t}}function resolveUploadContext(e){const t=(0,o.detectCIContext)();return{commitSha:e.commitSha??t.commitSha,branch:e.branch??t.branch,repositoryUrl:e.repositoryUrl??t.repositoryUrl,buildName:e.buildName,buildNumber:e.buildNumber??t.buildNumber,buildUrl:e.buildUrl??t.buildUrl,testEnvironment:e.testEnvironment,appName:e.appName,appVersion:e.appVersion,testType:e.testType}}async function uploadTestRun(e){const{report:t,apiKey:r}=e;const o=resolveApiUrl(e.apiUrl);const s=ensureReportId(t);const n=resolveUploadContext(e);const a={report:t,reportId:s,...n};console.log("Uploading test results...");let i;try{i=await fetch(`${o}/api/v1/ingest`,{method:"POST",headers:{"X-API-KEY":r,"Content-Type":"application/json"},body:JSON.stringify(a)})}catch(e){const t=e instanceof Error?e.message:String(e);const r=`Connection failed: ${t}`;console.error(r);return{success:false,reportId:s,error:r}}if(!i.ok){const e=await parseHttpError(i);if(i.status===409&&isReportAlreadyExistsConflict(e)){console.warn("Report already exists (workflow may have been re-run)");return{success:true,reportId:s,summary:{passed:t.results.summary.passed,failed:t.results.summary.failed,skipped:t.results.summary.skipped,total:t.results.summary.tests},alreadyExists:true,statusCode:e.statusCode,errorCode:e.errorCode}}const r=formatUploadError(e);console.error(r);return{success:false,reportId:s,error:r,statusCode:e.statusCode,errorCode:e.errorCode,errorDetail:e.detail||e.rawBody}}const l=await i.json();console.log(`✓ Test results uploaded successfully`);console.log(` Report ID: ${l.reportId}`);console.log(` Test Run ID: ${l.testRunId}`);console.log(` Tests: ${l.summary.passed}/${l.summary.total} passed`);return{success:true,reportId:l.reportId,testRunId:l.testRunId,summary:{passed:l.summary.passed,failed:l.summary.failed,skipped:l.summary.skipped,total:l.summary.total},testResults:l.testResults}}function mapAttachmentsToTestResults(e,t){const r=new Map;for(const e of t){const t=r.get(e.name)??[];t.push(e.id);r.set(e.name,t)}const o=[];for(const t of e){const e=r.get(t.name);const s=e?.shift();if(!t.attachments||t.attachments.length===0){continue}if(!s){console.warn(`Skipping artifact upload: could not match test result for "${t.name}"`);continue}o.push({testResultId:s,attachments:t.attachments})}return o}async function uploadArtifacts(e){const{reportId:t,apiKey:r,testResults:o}=e;const s=resolveApiUrl(e.apiUrl);let n=0;for(const e of o){for(const o of e.attachments){try{const a=await uploadSingleArtifact({testResultId:e.testResultId,attachment:o,reportId:t,apiKey:r,apiUrl:s});if(a)n++}catch(e){const t=e instanceof Error?e.message:String(e);console.error(`Failed to upload artifact ${o.name}: ${t}`)}}}if(n>0){console.log(`✓ Uploaded ${n} artifact(s)`)}return n}async function uploadSingleArtifact(e){const{testResultId:t,attachment:o,reportId:s,apiKey:n,apiUrl:a}=e;const i=await Promise.resolve().then(r.t.bind(r,943,23));const l=await Promise.resolve().then(r.t.bind(r,928,23));const c=l.resolve(o.path);try{await i.access(c)}catch{console.warn(`Artifact not found: ${l.basename(c)}`);return false}let u;let p;const d=l.basename(c);const f=o.contentType||"application/octet-stream";try{u=await i.readFile(c);p=u.byteLength}catch(e){const t=e instanceof Error?e.message:String(e);throw new Error(`Failed to read file: ${t}`)}try{return await uploadWithPresignedUrl({testResultId:t,reportId:s,apiKey:n,apiUrl:a,attachmentName:o.name,fileName:d,contentType:f,sizeBytes:p,fileBuffer:u})}catch(e){if(!(e instanceof LegacyUploadRequiredError)){throw e}return await uploadWithLegacyEndpoint({testResultId:t,attachmentName:o.name,reportId:s,apiKey:n,apiUrl:a,fileName:d,contentType:f,fileBuffer:u})}}class LegacyUploadRequiredError extends Error{constructor(){super("Legacy upload endpoint required")}}async function uploadWithPresignedUrl(e){const{testResultId:t,reportId:r,apiKey:o,apiUrl:s,attachmentName:n,fileName:a,contentType:i,sizeBytes:l,fileBuffer:c}=e;const u={testResultId:t,ctrfAttachmentName:n,fileName:a,contentType:i,sizeBytes:l};const p=await fetch(`${s}/api/v1/artifacts/${r}/upload-url`,{method:"POST",headers:{"X-API-KEY":o,"Content-Type":"application/json"},body:JSON.stringify(u)});if(shouldFallbackToLegacyUpload(p.status)){throw new LegacyUploadRequiredError}if(!p.ok){const e=await p.text();throw new Error(`Failed to create direct upload URL (HTTP ${p.status}): ${e}`)}const d=await p.json();const f={...d.requiredHeaders??{}};if(!hasHeaderIgnoreCase(f,"Content-Type")){f["Content-Type"]=i}const m=await fetch(d.uploadUrl,{method:"PUT",headers:f,body:c});if(!m.ok){const e=await m.text();throw new Error(`Direct artifact upload failed (HTTP ${m.status}): ${e}`)}const h={testResultId:t,ctrfAttachmentName:n,fileName:a,contentType:i,sizeBytes:l,storageKey:d.storageKey};const _=await fetch(`${s}/api/v1/artifacts/${r}/complete`,{method:"POST",headers:{"X-API-KEY":o,"Content-Type":"application/json"},body:JSON.stringify(h)});if(!_.ok){const e=await _.text();throw new Error(`Failed to finalize artifact upload (HTTP ${_.status}): ${e}`)}return true}async function uploadWithLegacyEndpoint(e){const{testResultId:t,attachmentName:r,reportId:o,apiKey:s,apiUrl:n,fileName:a,contentType:i,fileBuffer:l}=e;const c=new Blob([new Uint8Array(l)],{type:i});const u=new FormData;u.append("testResultId",t);u.append("ctrfAttachmentName",r);u.append("file",c,a);const p=await fetch(`${n}/api/v1/artifacts/${o}`,{method:"POST",headers:{"X-API-KEY":s},body:u});if(!p.ok){const e=await p.text();throw new Error(`Legacy artifact upload failed (HTTP ${p.status}): ${e}`)}return true}function shouldFallbackToLegacyUpload(e){return e===404||e===405||e===501}function hasHeaderIgnoreCase(e,t){const r=t.toLowerCase();return Object.keys(e).some((e=>e.toLowerCase()===r))}},943:e=>{e.exports=require("fs/promises")},928:e=>{e.exports=require("path")}};var t={};function __nccwpck_require__(r){var o=t[r];if(o!==undefined){return o.exports}var s=t[r]={exports:{}};var n=true;try{e[r].call(s.exports,s,s.exports,__nccwpck_require__);n=false}finally{if(n)delete t[r]}return s.exports}(()=>{var e=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;var t;__nccwpck_require__.t=function(r,o){if(o&1)r=this(r);if(o&8)return r;if(typeof r==="object"&&r){if(o&4&&r.__esModule)return r;if(o&16&&typeof r.then==="function")return r}var s=Object.create(null);__nccwpck_require__.r(s);var n={};t=t||[null,e({}),e([]),e(e)];for(var a=o&2&&r;typeof a=="object"&&!~t.indexOf(a);a=e(a)){Object.getOwnPropertyNames(a).forEach((e=>n[e]=()=>r[e]))}n["default"]=()=>r;__nccwpck_require__.d(s,n);return s}})();(()=>{__nccwpck_require__.d=(e,t)=>{for(var r in t){if(__nccwpck_require__.o(t,r)&&!__nccwpck_require__.o(e,r)){Object.defineProperty(e,r,{enumerable:true,get:t[r]})}}}})();(()=>{__nccwpck_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})();(()=>{__nccwpck_require__.r=e=>{if(typeof Symbol!=="undefined"&&Symbol.toStringTag){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}Object.defineProperty(e,"__esModule",{value:true})}})();if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};__nccwpck_require__.r(r);__nccwpck_require__.d(r,{TestreamVitestReporter:()=>TestreamVitestReporter,default:()=>TestreamVitestReporter});var o=__nccwpck_require__(943);var s=__nccwpck_require__(928);var n=__nccwpck_require__(322);function mapStatus(e){switch(e){case"passed":return"passed";case"failed":return"failed";case"skipped":return"skipped";default:return"pending"}}function buildSuiteName(e){const t=[];let r=e.parent;while(r&&r.type==="suite"){t.unshift(r.name);r=r.parent}return t.join(" > ")}function collectTests(e){const t=[];for(const r of e){if(r.type==="test"){t.push(r)}else if(r.type==="suite"){t.push(...collectTests(r.children))}}return t}function buildCTRFReport(e,t,r){const o=Date.now();const s=[];const n=new Set;for(const o of e){const e=collectTests(o.children);for(const o of e){const e=o.result();const a=o.diagnostic();const i=e.state;const l=mapStatus(i);const c=Math.round(a?.duration??0);const u=a?.startTime??r;const p=u+c;const d=a?.retryCount??0;const f=a?.flaky??false;const m=buildSuiteName(o);if(m){n.add(m)}let h;let _;const b=e.errors;if(b&&b.length>0){const e=b[0];h=e.message;const t=e.stackTrace??e.stack;_=typeof t==="string"?t:undefined}const y={name:o.name,status:l,duration:c,start:u,stop:p,rawStatus:i,tags:[],type:t.testType||"unit",filePath:o.module.moduleId,retries:d,flaky:f,steps:[],suite:m||undefined,stdout:[],stderr:[],message:h,trace:_};s.push(y)}}const a={tests:s.length,passed:s.filter((e=>e.status==="passed")).length,failed:s.filter((e=>e.status==="failed")).length,skipped:s.filter((e=>e.status==="skipped")).length,pending:s.filter((e=>e.status==="pending")).length,other:s.filter((e=>e.status==="other")).length,suites:n.size,start:r,stop:o};const i={reportFormat:"CTRF",specVersion:"0.0.1",timestamp:new Date(r).toISOString(),generatedBy:"@testream/vitest-reporter",extra:{branch:t.branch,commitSha:t.commitSha},results:{tool:{name:"vitest"},summary:a,tests:s,environment:{...t.testEnvironment?{testEnvironment:t.testEnvironment}:{},...t.appName?{appName:t.appName}:{},...t.appVersion?{appVersion:t.appVersion}:{},...t.buildName?{buildName:t.buildName}:{},...t.buildNumber?{buildNumber:t.buildNumber}:{},...t.buildUrl?{buildUrl:t.buildUrl}:{}}}};return i}class ReportUploader{constructor(e){this.config={apiKey:e.apiKey,branch:e.branch,commitSha:e.commitSha,repositoryUrl:e.repositoryUrl,failOnUploadError:e.failOnUploadError??false,buildName:e.buildName,buildNumber:e.buildNumber,buildUrl:e.buildUrl,testEnvironment:e.testEnvironment,appName:e.appName,appVersion:e.appVersion,testType:e.testType}}async uploadReport(e){try{const t=await(0,n.uploadTestRun)({report:e,apiKey:this.config.apiKey,commitSha:this.config.commitSha,branch:this.config.branch,repositoryUrl:this.config.repositoryUrl,buildName:this.config.buildName,buildNumber:this.config.buildNumber,buildUrl:this.config.buildUrl,testEnvironment:this.config.testEnvironment,appName:this.config.appName,appVersion:this.config.appVersion,testType:this.config.testType});if(!t.success){if(this.config.failOnUploadError){throw new Error(t.error||"Upload failed")}return t}let r=0;if(!t.alreadyExists&&t.testRunId&&t.testResults){const o=(0,n.mapAttachmentsToTestResults)(e.results.tests,t.testResults);if(o.length>0){r=await(0,n.uploadArtifacts)({reportId:t.reportId,apiKey:this.config.apiKey,testResults:o})}}return{...t,artifactCount:r}}catch(t){const r=t instanceof Error?t.message:String(t);if(this.config.failOnUploadError){throw new Error(`Failed to upload report: ${r}`)}return{success:false,reportId:(0,n.ensureReportId)(e),error:r}}}}class TestreamVitestReporter{constructor(e){this.uploader=null;this.startTimestamp=0;if(!e.apiKey){throw new Error("TestreamVitestReporter: apiKey is required")}this.config={uploadEnabled:true,outputFile:"ctrf-report.json",outputDir:"ctrf",failOnUploadError:false,testType:"unit",...e};const t=(0,n.detectCIContext)();if(!this.config.branch&&t.branch){this.config.branch=t.branch}if(!this.config.commitSha&&t.commitSha){this.config.commitSha=t.commitSha}if(!this.config.repositoryUrl&&t.repositoryUrl){this.config.repositoryUrl=t.repositoryUrl}if(!this.config.buildNumber&&t.buildNumber){this.config.buildNumber=t.buildNumber}if(!this.config.buildUrl&&t.buildUrl){this.config.buildUrl=t.buildUrl}this.reportPath=s.join(this.config.outputDir,this.config.outputFile);if(this.config.uploadEnabled){this.uploader=new ReportUploader(this.config)}}onTestRunStart(e){this.startTimestamp=Date.now();console.log("");console.log("=".repeat(60));console.log("Vitest Testream Reporter");console.log("=".repeat(60));if(this.config.uploadEnabled){if(this.config.branch)console.log(`Branch: ${this.config.branch}`);if(this.config.commitSha)console.log(`Commit: ${this.config.commitSha.substring(0,7)}`);if(this.config.repositoryUrl)console.log(`Repository: ${this.config.repositoryUrl}`)}else{console.log("Upload: Disabled")}console.log(`Report: ${this.reportPath}`);console.log("=".repeat(60));console.log("")}async onTestRunEnd(e,t,r){const s=buildCTRFReport(e,this.config,this.startTimestamp);try{await o.mkdir(this.config.outputDir,{recursive:true});await o.writeFile(this.reportPath,JSON.stringify(s,null,2))}catch(e){const t=e instanceof Error?e.message:String(e);console.error(`Failed to write CTRF report: ${t}`)}if(!this.config.uploadEnabled||!this.uploader){console.log("");console.log("CTRF report generated");console.log(` ${this.reportPath}`);return}console.log("");console.log("=".repeat(60));console.log("Uploading Test Results");console.log("=".repeat(60));try{const e=await this.uploader.uploadReport(s);if(e.success){console.log("=".repeat(60));console.log("Upload Complete");console.log("=".repeat(60));console.log("")}else{console.error("Upload completed with errors");if(e.error){console.error(`Error: ${e.error}`)}if(e.errorCode){console.error(`Error code: ${e.errorCode}`)}console.error("")}}catch(e){const t=e instanceof Error?e.message:String(e);console.error("");console.error("=".repeat(60));console.error("Upload Failed");console.error("=".repeat(60));console.error(`Error: ${t}`);console.error("");if(this.config.failOnUploadError){throw e}}}}const a=null&&TestreamVitestReporter;module.exports=r})();
@@ -1 +1 @@
1
- {"version":3,"file":"","sourceRoot":"","sources":["file:///home/runner/work/jira-test-manager/jira-test-manager/packages/vitest-reporter/src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAI/C;;GAEG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE,oBAAoB;IAuCxC,cAAc,CAAC,eAAe,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAmBjE,YAAY,CAChB,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,EACtC,gBAAgB,EAAE,aAAa,CAAC,eAAe,CAAC,EAChD,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC;CAoDjB;AAED,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"","sourceRoot":"","sources":["file:///home/runner/work/jira-test-manager/jira-test-manager/packages/vitest-reporter/src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAI/C;;GAEG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE,oBAAoB;IAwCxC,cAAc,CAAC,eAAe,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAmBjE,YAAY,CAChB,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,EACtC,gBAAgB,EAAE,aAAa,CAAC,eAAe,CAAC,EAChD,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC;CA0DjB;AAED,eAAe,sBAAsB,CAAC"}
@@ -1 +1 @@
1
- var W=Object.create;var v=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,te=Object.prototype.hasOwnProperty;var b=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var re=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Z(t))!te.call(e,s)&&s!==r&&v(e,s,{get:()=>t[s],enumerable:!(o=Q(t,s))||o.enumerable});return e};var $=(e,t,r)=>(r=e!=null?W(ee(e)):{},re(t||!e||!e.__esModule?v(r,"default",{value:e,enumerable:!0}):r,e));var F=b(D=>{"use strict";Object.defineProperty(D,"__esModule",{value:!0})});var H=b(O=>{"use strict";Object.defineProperty(O,"__esModule",{value:!0});O.detectCIContext=se;function se(){let e=process.env;return e.GITHUB_ACTIONS==="true"?{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}`:void 0,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}`:void 0}:e.GITLAB_CI==="true"?{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}:e.TF_BUILD==="True"?{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}`:void 0}:e.CIRCLECI==="true"?{branch:e.CIRCLE_BRANCH,commitSha:e.CIRCLE_SHA1,repositoryUrl:e.CIRCLE_REPOSITORY_URL,buildNumber:e.CIRCLE_BUILD_NUM,buildUrl:e.CIRCLE_BUILD_URL}:e.JENKINS_URL?{branch:e.GIT_BRANCH?.replace("origin/",""),commitSha:e.GIT_COMMIT,repositoryUrl:e.GIT_URL,buildNumber:e.BUILD_NUMBER,buildUrl:e.BUILD_URL}:e.BITBUCKET_BUILD_NUMBER?{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}`:void 0}:{}}});var G=b(y=>{"use strict";Object.defineProperty(y,"__esModule",{value:!0});y.ensureReportId=K;y.uploadTestRun=ae;y.mapAttachmentsToTestResults=ie;y.uploadArtifacts=le;var oe="https://test-manager-backend.fly.dev";function K(e){if(typeof e.reportId=="string"&&e.reportId.trim().length>0)return e.reportId;let t=crypto.randomUUID();return e.reportId=t,t}function V(e){let t=x(e);if(t)return t;let r=ne();return r||oe}function ne(){if(!(typeof process>"u"||!process?.env))return x(process.env.TESTREAM_API_URL)}function x(e){if(!e)return;let t=e.trim();if(t)return t.replace(/\/+$/,"")}async function ae(e){let{report:t,apiKey:r}=e,o=V(e.apiUrl),s=K(t),a={report:t,reportId:s,commitSha:e.commitSha,branch:e.branch,repositoryUrl:e.repositoryUrl,buildName:e.buildName,buildNumber:e.buildNumber,buildUrl:e.buildUrl,testEnvironment:e.testEnvironment,appName:e.appName,appVersion:e.appVersion,testType:e.testType};console.log("Uploading test results...");let i;try{i=await fetch(`${o}/api/v1/ingest`,{method:"POST",headers:{"X-API-KEY":r,"Content-Type":"application/json"},body:JSON.stringify(a)})}catch(n){let d=n instanceof Error?n.message:String(n);return{success:!1,reportId:s,error:`Connection failed: ${d}`}}if(i.status===409)return console.warn("Report already exists (workflow may have been re-run)"),{success:!0,reportId:s,summary:{passed:t.results.summary.passed,failed:t.results.summary.failed,skipped:t.results.summary.skipped,total:t.results.summary.tests},alreadyExists:!0};if(!i.ok){let n=await i.text();return{success:!1,reportId:s,error:`Upload failed (HTTP ${i.status}): ${n}`}}let l=await i.json();return console.log("\u2713 Test results uploaded successfully"),console.log(` Report ID: ${l.reportId}`),console.log(` Test Run ID: ${l.testRunId}`),console.log(` Tests: ${l.summary.passed}/${l.summary.total} passed`),{success:!0,reportId:l.reportId,testRunId:l.testRunId,summary:{passed:l.summary.passed,failed:l.summary.failed,skipped:l.summary.skipped,total:l.summary.total},testResults:l.testResults}}function ie(e,t){let r=new Map;for(let s of t){let a=r.get(s.name)??[];a.push(s.id),r.set(s.name,a)}let o=[];for(let s of e){let i=r.get(s.name)?.shift();if(!(!s.attachments||s.attachments.length===0)){if(!i){console.warn(`Skipping artifact upload: could not match test result for "${s.name}"`);continue}o.push({testResultId:i,attachments:s.attachments})}}return o}async function le(e){let{reportId:t,apiKey:r,testResults:o}=e,s=V(e.apiUrl),a=0;for(let i of o)for(let l of i.attachments)try{await ue({testResultId:i.testResultId,attachment:l,reportId:t,apiKey:r,apiUrl:s})&&a++}catch(n){let d=n instanceof Error?n.message:String(n);console.error(`Failed to upload artifact ${l.name}: ${d}`)}return a>0&&console.log(`\u2713 Uploaded ${a} artifact(s)`),a}async function ue(e){let{testResultId:t,attachment:r,reportId:o,apiKey:s,apiUrl:a}=e,i=await import("fs/promises"),l=await import("path"),n=l.resolve(r.path);try{await i.access(n)}catch{return console.warn(`Artifact not found: ${l.basename(n)}`),!1}let d,c,p=l.basename(n),f=r.contentType||"application/octet-stream";try{d=await i.readFile(n),c=d.byteLength}catch(m){let T=m instanceof Error?m.message:String(m);throw new Error(`Failed to read file: ${T}`)}try{return await ce({testResultId:t,reportId:o,apiKey:s,apiUrl:a,attachmentName:r.name,fileName:p,contentType:f,sizeBytes:c,fileBuffer:d})}catch(m){if(!(m instanceof g))throw m;return await pe({testResultId:t,attachmentName:r.name,reportId:o,apiKey:s,apiUrl:a,fileName:p,contentType:f,fileBuffer:d})}}var g=class extends Error{constructor(){super("Legacy upload endpoint required")}};async function ce(e){let{testResultId:t,reportId:r,apiKey:o,apiUrl:s,attachmentName:a,fileName:i,contentType:l,sizeBytes:n,fileBuffer:d}=e,c={testResultId:t,ctrfAttachmentName:a,fileName:i,contentType:l,sizeBytes:n},p=await fetch(`${s}/api/v1/artifacts/${r}/upload-url`,{method:"POST",headers:{"X-API-KEY":o,"Content-Type":"application/json"},body:JSON.stringify(c)});if(de(p.status))throw new g;if(!p.ok){let R=await p.text();throw new Error(`Failed to create direct upload URL (HTTP ${p.status}): ${R}`)}let f=await p.json(),m={...f.requiredHeaders??{}};fe(m,"Content-Type")||(m["Content-Type"]=l);let T=await fetch(f.uploadUrl,{method:"PUT",headers:m,body:d});if(!T.ok){let R=await T.text();throw new Error(`Direct artifact upload failed (HTTP ${T.status}): ${R}`)}let I={testResultId:t,ctrfAttachmentName:a,fileName:i,contentType:l,sizeBytes:n,storageKey:f.storageKey},U=await fetch(`${s}/api/v1/artifacts/${r}/complete`,{method:"POST",headers:{"X-API-KEY":o,"Content-Type":"application/json"},body:JSON.stringify(I)});if(!U.ok){let R=await U.text();throw new Error(`Failed to finalize artifact upload (HTTP ${U.status}): ${R}`)}return!0}async function pe(e){let{testResultId:t,attachmentName:r,reportId:o,apiKey:s,apiUrl:a,fileName:i,contentType:l,fileBuffer:n}=e,d=new Blob([new Uint8Array(n)],{type:l}),c=new FormData;c.append("testResultId",t),c.append("ctrfAttachmentName",r),c.append("file",d,i);let p=await fetch(`${a}/api/v1/artifacts/${o}`,{method:"POST",headers:{"X-API-KEY":s},body:c});if(!p.ok){let f=await p.text();throw new Error(`Legacy artifact upload failed (HTTP ${p.status}): ${f}`)}return!0}function de(e){return e===404||e===405||e===501}function fe(e,t){let r=t.toLowerCase();return Object.keys(e).some(o=>o.toLowerCase()===r)}});var P=b(u=>{"use strict";var me=u&&u.__createBinding||(Object.create?function(e,t,r,o){o===void 0&&(o=r);var s=Object.getOwnPropertyDescriptor(t,r);(!s||("get"in s?!t.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,o,s)}:function(e,t,r,o){o===void 0&&(o=r),e[o]=t[r]}),he=u&&u.__exportStar||function(e,t){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r)&&me(t,e,r)};Object.defineProperty(u,"__esModule",{value:!0});u.ensureReportId=u.mapAttachmentsToTestResults=u.uploadArtifacts=u.uploadTestRun=u.detectCIContext=void 0;he(F(),u);var Te=H();Object.defineProperty(u,"detectCIContext",{enumerable:!0,get:function(){return Te.detectCIContext}});var E=G();Object.defineProperty(u,"uploadTestRun",{enumerable:!0,get:function(){return E.uploadTestRun}});Object.defineProperty(u,"uploadArtifacts",{enumerable:!0,get:function(){return E.uploadArtifacts}});Object.defineProperty(u,"mapAttachmentsToTestResults",{enumerable:!0,get:function(){return E.mapAttachmentsToTestResults}});Object.defineProperty(u,"ensureReportId",{enumerable:!0,get:function(){return E.ensureReportId}})});var q=$(P());import*as N from"fs/promises";import*as Y from"path";function Re(e){switch(e){case"passed":return"passed";case"failed":return"failed";case"skipped":return"skipped";default:return"pending"}}function Ue(e){let t=[],r=e.parent;for(;r&&r.type==="suite";)t.unshift(r.name),r=r.parent;return t.join(" > ")}function j(e){let t=[];for(let r of e)r.type==="test"?t.push(r):r.type==="suite"&&t.push(...j(r.children));return t}function k(e,t,r){let o=Date.now(),s=[],a=new Set;for(let n of e){let d=j(n.children);for(let c of d){let p=c.result(),f=c.diagnostic(),m=p.state,T=Re(m),I=Math.round(f?.duration??0),U=f?.startTime??r,R=U+I,J=f?.retryCount??0,z=f?.flaky??!1,S=Ue(c);S&&a.add(S);let A,L,B=p.errors;if(B&&B.length>0){let w=B[0];A=w.message;let M=w.stackTrace??w.stack;L=typeof M=="string"?M:void 0}let X={name:c.name,status:T,duration:I,start:U,stop:R,rawStatus:m,tags:[],type:t.testType||"unit",filePath:c.module.moduleId,retries:J,flaky:z,steps:[],suite:S||void 0,stdout:[],stderr:[],message:A,trace:L};s.push(X)}}let i={tests:s.length,passed:s.filter(n=>n.status==="passed").length,failed:s.filter(n=>n.status==="failed").length,skipped:s.filter(n=>n.status==="skipped").length,pending:s.filter(n=>n.status==="pending").length,other:s.filter(n=>n.status==="other").length,suites:a.size,start:r,stop:o};return{reportFormat:"CTRF",specVersion:"0.0.1",timestamp:new Date(r).toISOString(),generatedBy:"@testream/vitest-reporter",extra:{branch:t.branch,commitSha:t.commitSha},results:{tool:{name:"vitest"},summary:i,tests:s,environment:{...t.testEnvironment?{testEnvironment:t.testEnvironment}:{},...t.appName?{appName:t.appName}:{},...t.appVersion?{appVersion:t.appVersion}:{},...t.buildName?{buildName:t.buildName}:{},...t.buildNumber?{buildNumber:t.buildNumber}:{},...t.buildUrl?{buildUrl:t.buildUrl}:{}}}}}var h=$(P()),_=class{constructor(t){this.config={apiKey:t.apiKey,branch:t.branch,commitSha:t.commitSha,repositoryUrl:t.repositoryUrl,failOnUploadError:t.failOnUploadError??!1,buildName:t.buildName,buildNumber:t.buildNumber,buildUrl:t.buildUrl,testEnvironment:t.testEnvironment,appName:t.appName,appVersion:t.appVersion,testType:t.testType}}async uploadReport(t){try{let r=await(0,h.uploadTestRun)({report:t,apiKey:this.config.apiKey,commitSha:this.config.commitSha,branch:this.config.branch,repositoryUrl:this.config.repositoryUrl,buildName:this.config.buildName,buildNumber:this.config.buildNumber,buildUrl:this.config.buildUrl,testEnvironment:this.config.testEnvironment,appName:this.config.appName,appVersion:this.config.appVersion,testType:this.config.testType});if(!r.success){if(this.config.failOnUploadError)throw new Error(r.error||"Upload failed");return r}let o=0;if(!r.alreadyExists&&r.testRunId&&r.testResults){let s=(0,h.mapAttachmentsToTestResults)(t.results.tests,r.testResults);s.length>0&&(o=await(0,h.uploadArtifacts)({reportId:r.reportId,apiKey:this.config.apiKey,testResults:s}))}return{...r,artifactCount:o}}catch(r){let o=r instanceof Error?r.message:String(r);if(this.config.failOnUploadError)throw new Error(`Failed to upload report: ${o}`);return{success:!1,reportId:(0,h.ensureReportId)(t),error:o}}}};var C=class{constructor(t){this.uploader=null;this.startTimestamp=0;if(!t.apiKey)throw new Error("TestreamVitestReporter: apiKey is required");this.config={uploadEnabled:!0,outputFile:"ctrf-report.json",outputDir:"ctrf",failOnUploadError:!1,testType:"unit",...t};let r=(0,q.detectCIContext)();!this.config.branch&&r.branch&&(this.config.branch=r.branch),!this.config.commitSha&&r.commitSha&&(this.config.commitSha=r.commitSha),!this.config.repositoryUrl&&r.repositoryUrl&&(this.config.repositoryUrl=r.repositoryUrl),!this.config.buildNumber&&r.buildNumber&&(this.config.buildNumber=r.buildNumber),!this.config.buildUrl&&r.buildUrl&&(this.config.buildUrl=r.buildUrl),this.reportPath=Y.join(this.config.outputDir,this.config.outputFile),this.config.uploadEnabled&&(this.uploader=new _(this.config))}onTestRunStart(t){this.startTimestamp=Date.now(),console.log(""),console.log("=".repeat(60)),console.log("Vitest Testream Reporter"),console.log("=".repeat(60)),this.config.uploadEnabled?(this.config.branch&&console.log(`Branch: ${this.config.branch}`),this.config.commitSha&&console.log(`Commit: ${this.config.commitSha.substring(0,7)}`),this.config.repositoryUrl&&console.log(`Repository: ${this.config.repositoryUrl}`)):console.log("Upload: Disabled"),console.log(`Report: ${this.reportPath}`),console.log("=".repeat(60)),console.log("")}async onTestRunEnd(t,r,o){let s=k(t,this.config,this.startTimestamp);try{await N.mkdir(this.config.outputDir,{recursive:!0}),await N.writeFile(this.reportPath,JSON.stringify(s,null,2))}catch(a){let i=a instanceof Error?a.message:String(a);console.error(`Failed to write CTRF report: ${i}`)}if(!this.config.uploadEnabled||!this.uploader){console.log(""),console.log("CTRF report generated"),console.log(` ${this.reportPath}`);return}console.log(""),console.log("=".repeat(60)),console.log("Uploading Test Results"),console.log("=".repeat(60));try{(await this.uploader.uploadReport(s)).success?(console.log("=".repeat(60)),console.log("Upload Complete"),console.log("=".repeat(60)),console.log("")):(console.error("Upload completed with errors"),console.error(""))}catch(a){let i=a instanceof Error?a.message:String(a);if(console.error(""),console.error("=".repeat(60)),console.error("Upload Failed"),console.error("=".repeat(60)),console.error(`Error: ${i}`),console.error(""),this.config.failOnUploadError)throw a}}};export{C as TestreamVitestReporter,C as default};
1
+ var W=Object.create;var v=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,te=Object.prototype.hasOwnProperty;var b=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var re=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Z(t))!te.call(e,o)&&o!==r&&v(e,o,{get:()=>t[o],enumerable:!(s=Q(t,o))||s.enumerable});return e};var D=(e,t,r)=>(r=e!=null?W(ee(e)):{},re(t||!e||!e.__esModule?v(r,"default",{value:e,enumerable:!0}):r,e));var H=b(F=>{"use strict";Object.defineProperty(F,"__esModule",{value:!0})});var P=b(O=>{"use strict";Object.defineProperty(O,"__esModule",{value:!0});O.detectCIContext=oe;function oe(){let e=process.env;return e.GITHUB_ACTIONS==="true"?{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}`:void 0,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}`:void 0}:e.GITLAB_CI==="true"?{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}:e.TF_BUILD==="True"?{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}`:void 0}:e.CIRCLECI==="true"?{branch:e.CIRCLE_BRANCH,commitSha:e.CIRCLE_SHA1,repositoryUrl:e.CIRCLE_REPOSITORY_URL,buildNumber:e.CIRCLE_BUILD_NUM,buildUrl:e.CIRCLE_BUILD_URL}:e.JENKINS_URL?{branch:e.GIT_BRANCH?.replace("origin/",""),commitSha:e.GIT_COMMIT,repositoryUrl:e.GIT_URL,buildNumber:e.BUILD_NUMBER,buildUrl:e.BUILD_URL}:e.BITBUCKET_BUILD_NUMBER?{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}`:void 0}:{}}});var j=b(U=>{"use strict";Object.defineProperty(U,"__esModule",{value:!0});U.ensureReportId=x;U.uploadTestRun=pe;U.mapAttachmentsToTestResults=de;U.uploadArtifacts=fe;var se=P(),ne="https://test-manager-backend.fly.dev";function x(e){if(typeof e.reportId=="string"&&e.reportId.trim().length>0)return e.reportId;let t=crypto.randomUUID();return e.reportId=t,t}function K(e){let t=V(e);if(t)return t;let r=ae();return r||ne}function ae(){if(!(typeof process>"u"||!process?.env))return V(process.env.TESTREAM_API_URL)}function V(e){if(!e)return;let t=e.trim();if(t)return t.replace(/\/+$/,"")}function ie(e){return e.errorCode==="report_already_exists"}function le(e){let t=e.detail||e.title||e.rawBody||"Unknown error",r=e.errorCode?`, code=${e.errorCode}`:"";return`Upload failed (HTTP ${e.statusCode}${r}): ${t}`}async function ce(e){let t=await e.text();if((e.headers.get("content-type")||"").toLowerCase().includes("json")&&t.trim().length>0)try{let s=JSON.parse(t);return{statusCode:e.status,title:s.title,detail:s.detail||s.details||s.error,errorCode:s.errorCode,rawBody:t}}catch{}return{statusCode:e.status,rawBody:t}}function ue(e){let t=(0,se.detectCIContext)();return{commitSha:e.commitSha??t.commitSha,branch:e.branch??t.branch,repositoryUrl:e.repositoryUrl??t.repositoryUrl,buildName:e.buildName,buildNumber:e.buildNumber??t.buildNumber,buildUrl:e.buildUrl??t.buildUrl,testEnvironment:e.testEnvironment,appName:e.appName,appVersion:e.appVersion,testType:e.testType}}async function pe(e){let{report:t,apiKey:r}=e,s=K(e.apiUrl),o=x(t),a=ue(e),l={report:t,reportId:o,...a};console.log("Uploading test results...");let c;try{c=await fetch(`${s}/api/v1/ingest`,{method:"POST",headers:{"X-API-KEY":r,"Content-Type":"application/json"},body:JSON.stringify(l)})}catch(i){let p=`Connection failed: ${i instanceof Error?i.message:String(i)}`;return console.error(p),{success:!1,reportId:o,error:p}}if(!c.ok){let i=await ce(c);if(c.status===409&&ie(i))return console.warn("Report already exists (workflow may have been re-run)"),{success:!0,reportId:o,summary:{passed:t.results.summary.passed,failed:t.results.summary.failed,skipped:t.results.summary.skipped,total:t.results.summary.tests},alreadyExists:!0,statusCode:i.statusCode,errorCode:i.errorCode};let u=le(i);return console.error(u),{success:!1,reportId:o,error:u,statusCode:i.statusCode,errorCode:i.errorCode,errorDetail:i.detail||i.rawBody}}let n=await c.json();return console.log("\u2713 Test results uploaded successfully"),console.log(` Report ID: ${n.reportId}`),console.log(` Test Run ID: ${n.testRunId}`),console.log(` Tests: ${n.summary.passed}/${n.summary.total} passed`),{success:!0,reportId:n.reportId,testRunId:n.testRunId,summary:{passed:n.summary.passed,failed:n.summary.failed,skipped:n.summary.skipped,total:n.summary.total},testResults:n.testResults}}function de(e,t){let r=new Map;for(let o of t){let a=r.get(o.name)??[];a.push(o.id),r.set(o.name,a)}let s=[];for(let o of e){let l=r.get(o.name)?.shift();if(!(!o.attachments||o.attachments.length===0)){if(!l){console.warn(`Skipping artifact upload: could not match test result for "${o.name}"`);continue}s.push({testResultId:l,attachments:o.attachments})}}return s}async function fe(e){let{reportId:t,apiKey:r,testResults:s}=e,o=K(e.apiUrl),a=0;for(let l of s)for(let c of l.attachments)try{await me({testResultId:l.testResultId,attachment:c,reportId:t,apiKey:r,apiUrl:o})&&a++}catch(n){let i=n instanceof Error?n.message:String(n);console.error(`Failed to upload artifact ${c.name}: ${i}`)}return a>0&&console.log(`\u2713 Uploaded ${a} artifact(s)`),a}async function me(e){let{testResultId:t,attachment:r,reportId:s,apiKey:o,apiUrl:a}=e,l=await import("fs/promises"),c=await import("path"),n=c.resolve(r.path);try{await l.access(n)}catch{return console.warn(`Artifact not found: ${c.basename(n)}`),!1}let i,u,p=c.basename(n),f=r.contentType||"application/octet-stream";try{i=await l.readFile(n),u=i.byteLength}catch(m){let T=m instanceof Error?m.message:String(m);throw new Error(`Failed to read file: ${T}`)}try{return await he({testResultId:t,reportId:s,apiKey:o,apiUrl:a,attachmentName:r.name,fileName:p,contentType:f,sizeBytes:u,fileBuffer:i})}catch(m){if(!(m instanceof g))throw m;return await Te({testResultId:t,attachmentName:r.name,reportId:s,apiKey:o,apiUrl:a,fileName:p,contentType:f,fileBuffer:i})}}var g=class extends Error{constructor(){super("Legacy upload endpoint required")}};async function he(e){let{testResultId:t,reportId:r,apiKey:s,apiUrl:o,attachmentName:a,fileName:l,contentType:c,sizeBytes:n,fileBuffer:i}=e,u={testResultId:t,ctrfAttachmentName:a,fileName:l,contentType:c,sizeBytes:n},p=await fetch(`${o}/api/v1/artifacts/${r}/upload-url`,{method:"POST",headers:{"X-API-KEY":s,"Content-Type":"application/json"},body:JSON.stringify(u)});if(Re(p.status))throw new g;if(!p.ok){let R=await p.text();throw new Error(`Failed to create direct upload URL (HTTP ${p.status}): ${R}`)}let f=await p.json(),m={...f.requiredHeaders??{}};ye(m,"Content-Type")||(m["Content-Type"]=c);let T=await fetch(f.uploadUrl,{method:"PUT",headers:m,body:i});if(!T.ok){let R=await T.text();throw new Error(`Direct artifact upload failed (HTTP ${T.status}): ${R}`)}let I={testResultId:t,ctrfAttachmentName:a,fileName:l,contentType:c,sizeBytes:n,storageKey:f.storageKey},y=await fetch(`${o}/api/v1/artifacts/${r}/complete`,{method:"POST",headers:{"X-API-KEY":s,"Content-Type":"application/json"},body:JSON.stringify(I)});if(!y.ok){let R=await y.text();throw new Error(`Failed to finalize artifact upload (HTTP ${y.status}): ${R}`)}return!0}async function Te(e){let{testResultId:t,attachmentName:r,reportId:s,apiKey:o,apiUrl:a,fileName:l,contentType:c,fileBuffer:n}=e,i=new Blob([new Uint8Array(n)],{type:c}),u=new FormData;u.append("testResultId",t),u.append("ctrfAttachmentName",r),u.append("file",i,l);let p=await fetch(`${a}/api/v1/artifacts/${s}`,{method:"POST",headers:{"X-API-KEY":o},body:u});if(!p.ok){let f=await p.text();throw new Error(`Legacy artifact upload failed (HTTP ${p.status}): ${f}`)}return!0}function Re(e){return e===404||e===405||e===501}function ye(e,t){let r=t.toLowerCase();return Object.keys(e).some(s=>s.toLowerCase()===r)}});var L=b(d=>{"use strict";var Ue=d&&d.__createBinding||(Object.create?function(e,t,r,s){s===void 0&&(s=r);var o=Object.getOwnPropertyDescriptor(t,r);(!o||("get"in o?!t.__esModule:o.writable||o.configurable))&&(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){s===void 0&&(s=r),e[s]=t[r]}),Ie=d&&d.__exportStar||function(e,t){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r)&&Ue(t,e,r)};Object.defineProperty(d,"__esModule",{value:!0});d.ensureReportId=d.mapAttachmentsToTestResults=d.uploadArtifacts=d.uploadTestRun=d.detectCIContext=void 0;Ie(H(),d);var be=P();Object.defineProperty(d,"detectCIContext",{enumerable:!0,get:function(){return be.detectCIContext}});var E=j();Object.defineProperty(d,"uploadTestRun",{enumerable:!0,get:function(){return E.uploadTestRun}});Object.defineProperty(d,"uploadArtifacts",{enumerable:!0,get:function(){return E.uploadArtifacts}});Object.defineProperty(d,"mapAttachmentsToTestResults",{enumerable:!0,get:function(){return E.mapAttachmentsToTestResults}});Object.defineProperty(d,"ensureReportId",{enumerable:!0,get:function(){return E.ensureReportId}})});var q=D(L());import*as N from"fs/promises";import*as Y from"path";function ge(e){switch(e){case"passed":return"passed";case"failed":return"failed";case"skipped":return"skipped";default:return"pending"}}function Ee(e){let t=[],r=e.parent;for(;r&&r.type==="suite";)t.unshift(r.name),r=r.parent;return t.join(" > ")}function G(e){let t=[];for(let r of e)r.type==="test"?t.push(r):r.type==="suite"&&t.push(...G(r.children));return t}function k(e,t,r){let s=Date.now(),o=[],a=new Set;for(let n of e){let i=G(n.children);for(let u of i){let p=u.result(),f=u.diagnostic(),m=p.state,T=ge(m),I=Math.round(f?.duration??0),y=f?.startTime??r,R=y+I,J=f?.retryCount??0,z=f?.flaky??!1,S=Ee(u);S&&a.add(S);let A,$,B=p.errors;if(B&&B.length>0){let w=B[0];A=w.message;let M=w.stackTrace??w.stack;$=typeof M=="string"?M:void 0}let X={name:u.name,status:T,duration:I,start:y,stop:R,rawStatus:m,tags:[],type:t.testType||"unit",filePath:u.module.moduleId,retries:J,flaky:z,steps:[],suite:S||void 0,stdout:[],stderr:[],message:A,trace:$};o.push(X)}}let l={tests:o.length,passed:o.filter(n=>n.status==="passed").length,failed:o.filter(n=>n.status==="failed").length,skipped:o.filter(n=>n.status==="skipped").length,pending:o.filter(n=>n.status==="pending").length,other:o.filter(n=>n.status==="other").length,suites:a.size,start:r,stop:s};return{reportFormat:"CTRF",specVersion:"0.0.1",timestamp:new Date(r).toISOString(),generatedBy:"@testream/vitest-reporter",extra:{branch:t.branch,commitSha:t.commitSha},results:{tool:{name:"vitest"},summary:l,tests:o,environment:{...t.testEnvironment?{testEnvironment:t.testEnvironment}:{},...t.appName?{appName:t.appName}:{},...t.appVersion?{appVersion:t.appVersion}:{},...t.buildName?{buildName:t.buildName}:{},...t.buildNumber?{buildNumber:t.buildNumber}:{},...t.buildUrl?{buildUrl:t.buildUrl}:{}}}}}var h=D(L()),C=class{constructor(t){this.config={apiKey:t.apiKey,branch:t.branch,commitSha:t.commitSha,repositoryUrl:t.repositoryUrl,failOnUploadError:t.failOnUploadError??!1,buildName:t.buildName,buildNumber:t.buildNumber,buildUrl:t.buildUrl,testEnvironment:t.testEnvironment,appName:t.appName,appVersion:t.appVersion,testType:t.testType}}async uploadReport(t){try{let r=await(0,h.uploadTestRun)({report:t,apiKey:this.config.apiKey,commitSha:this.config.commitSha,branch:this.config.branch,repositoryUrl:this.config.repositoryUrl,buildName:this.config.buildName,buildNumber:this.config.buildNumber,buildUrl:this.config.buildUrl,testEnvironment:this.config.testEnvironment,appName:this.config.appName,appVersion:this.config.appVersion,testType:this.config.testType});if(!r.success){if(this.config.failOnUploadError)throw new Error(r.error||"Upload failed");return r}let s=0;if(!r.alreadyExists&&r.testRunId&&r.testResults){let o=(0,h.mapAttachmentsToTestResults)(t.results.tests,r.testResults);o.length>0&&(s=await(0,h.uploadArtifacts)({reportId:r.reportId,apiKey:this.config.apiKey,testResults:o}))}return{...r,artifactCount:s}}catch(r){let s=r instanceof Error?r.message:String(r);if(this.config.failOnUploadError)throw new Error(`Failed to upload report: ${s}`);return{success:!1,reportId:(0,h.ensureReportId)(t),error:s}}}};var _=class{constructor(t){this.uploader=null;this.startTimestamp=0;if(!t.apiKey)throw new Error("TestreamVitestReporter: apiKey is required");this.config={uploadEnabled:!0,outputFile:"ctrf-report.json",outputDir:"ctrf",failOnUploadError:!1,testType:"unit",...t};let r=(0,q.detectCIContext)();!this.config.branch&&r.branch&&(this.config.branch=r.branch),!this.config.commitSha&&r.commitSha&&(this.config.commitSha=r.commitSha),!this.config.repositoryUrl&&r.repositoryUrl&&(this.config.repositoryUrl=r.repositoryUrl),!this.config.buildNumber&&r.buildNumber&&(this.config.buildNumber=r.buildNumber),!this.config.buildUrl&&r.buildUrl&&(this.config.buildUrl=r.buildUrl),this.reportPath=Y.join(this.config.outputDir,this.config.outputFile),this.config.uploadEnabled&&(this.uploader=new C(this.config))}onTestRunStart(t){this.startTimestamp=Date.now(),console.log(""),console.log("=".repeat(60)),console.log("Vitest Testream Reporter"),console.log("=".repeat(60)),this.config.uploadEnabled?(this.config.branch&&console.log(`Branch: ${this.config.branch}`),this.config.commitSha&&console.log(`Commit: ${this.config.commitSha.substring(0,7)}`),this.config.repositoryUrl&&console.log(`Repository: ${this.config.repositoryUrl}`)):console.log("Upload: Disabled"),console.log(`Report: ${this.reportPath}`),console.log("=".repeat(60)),console.log("")}async onTestRunEnd(t,r,s){let o=k(t,this.config,this.startTimestamp);try{await N.mkdir(this.config.outputDir,{recursive:!0}),await N.writeFile(this.reportPath,JSON.stringify(o,null,2))}catch(a){let l=a instanceof Error?a.message:String(a);console.error(`Failed to write CTRF report: ${l}`)}if(!this.config.uploadEnabled||!this.uploader){console.log(""),console.log("CTRF report generated"),console.log(` ${this.reportPath}`);return}console.log(""),console.log("=".repeat(60)),console.log("Uploading Test Results"),console.log("=".repeat(60));try{let a=await this.uploader.uploadReport(o);a.success?(console.log("=".repeat(60)),console.log("Upload Complete"),console.log("=".repeat(60)),console.log("")):(console.error("Upload completed with errors"),a.error&&console.error(`Error: ${a.error}`),a.errorCode&&console.error(`Error code: ${a.errorCode}`),console.error(""))}catch(a){let l=a instanceof Error?a.message:String(a);if(console.error(""),console.error("=".repeat(60)),console.error("Upload Failed"),console.error("=".repeat(60)),console.error(`Error: ${l}`),console.error(""),this.config.failOnUploadError)throw a}}};export{_ as TestreamVitestReporter,_ as default};
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAI/C;;GAEG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE,oBAAoB;IAuCxC,cAAc,CAAC,eAAe,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAmBjE,YAAY,CAChB,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,EACtC,gBAAgB,EAAE,aAAa,CAAC,eAAe,CAAC,EAChD,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC;CAoDjB;AAED,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAI/C;;GAEG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE,oBAAoB;IAwCxC,cAAc,CAAC,eAAe,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAmBjE,YAAY,CAChB,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,EACtC,gBAAgB,EAAE,aAAa,CAAC,eAAe,CAAC,EAChD,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC;CA0DjB;AAED,eAAe,sBAAsB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testream/vitest-reporter",
3
- "version": "0.1.0",
3
+ "version": "1.0.1",
4
4
  "description": "Vitest reporter to upload test results to Testream",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.mjs",