Skip to content

Commit a09707a

Browse files
committed
Fix the vitest reporter being written in cjs and tests
1 parent 192a99f commit a09707a

File tree

10 files changed

+133
-42
lines changed

10 files changed

+133
-42
lines changed

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ export default tseslint.config(
455455
files: [
456456
'lib/buildtools/**/*.ts',
457457
'lib/repotools/**/*.ts',
458+
'lib/vitest-reporter/**/*.ts',
458459
'.github/actions/**/*.ts',
459460
'**/vitest.config.js'
460461
],

lib/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Lib tsconfig.json
22
{
33
"compilerOptions": {
4+
"allowSyntheticDefaultImports": true,
45
"esModuleInterop": true,
56
"forceConsistentCasingInFileNames": true,
67
"module": "ESNext",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"use strict";var m=require("fs"),{ReportBase:p}=require("istanbul-lib-report");function h(s){return s.statements.pct===100&&s.branches.pct===100&&s.functions.pct===100&&s.lines.pct===100}function f(s){if(s.isSummary())return[];let e=s.getCoverageSummary(!1),t=e.isEmpty()?0:e.lines.pct,r,c=s.getFileCoverage();if(t===100){let i=c.getBranchCoverageByLine();r=Object.entries(i).map(([a,{coverage:l}])=>[a,l===100])}else r=Object.entries(c.getLineCoverage());let o=!0;return r.reduce((i,[a,l])=>{if(l)o=!0;else{let u=parseInt(a);o?(i.push([u]),o=!1):i[i.length-1][1]=u}return i},[])}var w=["Statements","Branches","Functions","Lines"];module.exports=class extends p{skipEmpty;skipFull;results={};cw=null;watermarks=null;constructor(e){super(e),this.skipEmpty=!!e.skipEmpty,this.skipFull=!!e.skipFull}onStart(e,n){if(!process.env.GITHUB_STEP_SUMMARY){console.log("Reporter not being executed in Github Actions environment");return}this.cw=m.createWriteStream(process.env.GITHUB_STEP_SUMMARY,{encoding:"utf-8",flags:"a"}),this.watermarks=n.watermarks,this.cw.write("<h2>Test Coverage</h2>"),this.cw.write("<table><thead><tr>");for(let t of["File",...w,"Uncovered Lines"])this.cw.write(`<th>${t}</th>`);this.cw.write("</tr></thead><tbody>")}onSummary(e){let n=e.getRelativeName()||"All Files",t=e.getCoverageSummary(!1),r=t.isEmpty();this.skipEmpty&&r||this.skipFull&&h(t)||(this.results[n]={statements:r?0:t.statements.pct,branches:r?0:t.branches.pct,functions:r?0:t.functions.pct,lines:r?0:t.lines.pct,uncoveredLines:f(e)})}onDetail(e){return this.onSummary(e)}formatter(e,n){if(!this.watermarks)return`<td>${e}%</td>`;let[t,r]=this.watermarks[n];return e<t?`<td><p style="color:red">${e}%</p></td>`:e>r?`<td><p style="color:green">${e}%</p></td>`:`<td><p style="color:yellow">${e}%</p></td>`}onEnd(){if(!this.cw)return;let e=Object.keys(this.results).sort();for(let n of e){let t=this.results[n];if(this.cw.write(`<tr><td><code>${n}</code></td>`),this.cw.write(this.formatter(t.statements,"statements")),this.cw.write(this.formatter(t.branches,"branches")),this.cw.write(this.formatter(t.functions,"functions")),this.cw.write(this.formatter(t.lines,"lines")),t.uncoveredLines.length>0){this.cw.write("<td><details><summary>Expand</summary><ul>");for(let r of t.uncoveredLines)r.length===1?this.cw.write(`<li>${r[0]}</li>`):this.cw.write(`<li>${r[0]}-${r[1]}</li>`);this.cw.write("</ul></details></td>")}else this.cw.write("<td></td>");this.cw.write("</tr>")}this.cw.write("</tbody></table>"),this.cw.close()}};
1+
"use strict";var b=Object.create;var o=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var v=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var S=(t,e)=>{for(var n in e)o(t,n,{get:e[n],enumerable:!0})},p=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of y(e))!k.call(t,s)&&s!==n&&o(t,s,{get:()=>e[s],enumerable:!(r=g(e,s))||r.enumerable});return t};var f=(t,e,n)=>(n=t!=null?b(v(t)):{},p(e||!t||!t.__esModule?o(n,"default",{value:t,enumerable:!0}):n,t)),E=t=>p(o({},"__esModule",{value:!0}),t);var F={};S(F,{default:()=>a});module.exports=E(F);var d=f(require("fs"),1),w=f(require("istanbul-lib-report"),1);function R(t){return t.statements.pct===100&&t.branches.pct===100&&t.functions.pct===100&&t.lines.pct===100}function L(t){if(t.isSummary())return[];let e=t.getCoverageSummary(!1),r=e.isEmpty()?0:e.lines.pct,s,m=t.getFileCoverage();if(r===100){let i=m.getBranchCoverageByLine();s=Object.entries(i).map(([c,{coverage:u}])=>[c,u===100])}else s=Object.entries(m.getLineCoverage());let l=!0;return s.reduce((i,[c,u])=>{if(u)l=!0;else{let h=parseInt(c);l?(i.push([h]),l=!1):i[i.length-1][1]=h}return i},[])}var C=["Statements","Branches","Functions","Lines"],a=class extends w.ReportBase{skipEmpty;skipFull;results={};cw=null;watermarks=null;constructor(e){super(e),this.skipEmpty=!!e.skipEmpty,this.skipFull=!!e.skipFull}onStart(e,n){if(!process.env.GITHUB_STEP_SUMMARY){console.log("Reporter not being executed in Github Actions environment");return}this.cw=d.default.createWriteStream(process.env.GITHUB_STEP_SUMMARY,{encoding:"utf-8",flags:"a"}),this.watermarks=n.watermarks,this.cw.write("<h2>Test Coverage</h2>"),this.cw.write("<table><thead><tr>");for(let r of["File",...C,"Uncovered Lines"])this.cw.write(`<th>${r}</th>`);this.cw.write("</tr></thead><tbody>")}onSummary(e){let n=e.getRelativeName()||"All Files",r=e.getCoverageSummary(!1),s=r.isEmpty();this.skipEmpty&&s||this.skipFull&&R(r)||(this.results[n]={statements:s?0:r.statements.pct,branches:s?0:r.branches.pct,functions:s?0:r.functions.pct,lines:s?0:r.lines.pct,uncoveredLines:L(e)})}onDetail(e){return this.onSummary(e)}formatter(e,n){if(!this.watermarks||this.watermarks[n]===void 0)return`<td>${e}%</td>`;let[r,s]=this.watermarks[n];return e<r?`<td><p style="color:red">${e}%</p></td>`:e>s?`<td><p style="color:green">${e}%</p></td>`:`<td><p style="color:yellow">${e}%</p></td>`}onEnd(){if(!this.cw)return;let e=Object.keys(this.results).sort();for(let n of e){let r=this.results[n];if(this.cw.write(`<tr><td><code>${n}</code></td>`),this.cw.write(this.formatter(r.statements,"statements")),this.cw.write(this.formatter(r.branches,"branches")),this.cw.write(this.formatter(r.functions,"functions")),this.cw.write(this.formatter(r.lines,"lines")),r.uncoveredLines.length>0){this.cw.write("<td><details><summary>Expand</summary><ul>");for(let s of r.uncoveredLines)s.length===1?this.cw.write(`<li>${s[0]}</li>`):this.cw.write(`<li>${s[0]}-${s[1]}</li>`);this.cw.write("</ul></details></td>")}else this.cw.write("<td></td>");this.cw.write("</tr>")}this.cw.write("</tbody></table>"),this.cw.close()}};

lib/vitest-reporter/build/test-reporter.js

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/vitest-reporter/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
},
2020
"scripts": {
2121
"build": "yarn build:coverage-reporter && yarn build:test-reporter",
22-
"build:coverage-reporter": "esbuild --outfile=./build/coverage-reporter.cjs --format=cjs --bundle=true --minify --external:\"istanbul-lib-report\" --platform=node src/coverage-reporter.cts",
23-
"build:test-reporter": "esbuild --outfile=./build/test-reporter.js --format=esm --bundle=true --minify --external:\"vitest*\" --platform=node src/test-reporter.ts"
22+
"build:coverage-reporter": "esbuild --outfile=./build/coverage-reporter.cjs --format=cjs --bundle=true --minify --external:\"istanbul-lib-report\" --platform=node src/coverage-reporter.ts",
23+
"build:test-reporter": "esbuild --outfile=./build/test-reporter.js --format=esm --bundle=true --minify --external:\"vitest*\" --platform=node src/test-reporter.ts",
24+
"test": "vitest"
2425
}
2526
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import fs from 'fs';
2+
import { describe, expect, it as baseIt, vi } from 'vitest';
3+
import CoverageReporter from '../coverage-reporter.js';
4+
5+
vi.spyOn(fs, 'createWriteStream').mockReturnValue({
6+
write: () => true,
7+
close: () => {}
8+
} as any);
9+
10+
describe(CoverageReporter, () => {
11+
const it = baseIt.extend<{ reporter: CoverageReporter }>({
12+
reporter: new CoverageReporter({})
13+
});
14+
15+
it('opens the GITHUB_STEP_SUMMARY file in append mode', ({ reporter }) => {
16+
try {
17+
vi.stubEnv('GITHUB_STEP_SUMMARY', 'oh no');
18+
reporter.onStart({} as any, { watermarks: {} } as any);
19+
20+
expect(fs.createWriteStream).toHaveBeenCalledExactlyOnceWith(
21+
'oh no',
22+
{
23+
encoding: 'utf-8',
24+
flags: 'a'
25+
}
26+
);
27+
} finally {
28+
vi.unstubAllEnvs();
29+
}
30+
});
31+
32+
it('doesn\'t open the summary file if GITHUB_STEP_SUMMARY isn\'t defined', ({ reporter }) => {
33+
reporter.onStart({} as any, { watermarks: {} } as any);
34+
expect(fs.createWriteStream).not.toHaveBeenCalled();
35+
});
36+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import fs from 'fs';
2+
import { describe, expect, it as baseIt, vi } from 'vitest';
3+
import TestReporter from '../test-reporter.js';
4+
5+
vi.spyOn(fs, 'createWriteStream').mockImplementation(() => ({
6+
write: () => {},
7+
close: () => {}
8+
} as any));
9+
10+
describe(TestReporter, () => {
11+
const it = baseIt.extend<{ reporter: TestReporter }>({
12+
reporter: new TestReporter()
13+
});
14+
15+
it('opens the summary file in append mode', ({ reporter }) => {
16+
try {
17+
vi.stubEnv('GITHUB_STEP_SUMMARY', 'oh no');
18+
reporter.onInit();
19+
20+
expect(fs.createWriteStream).toHaveBeenCalledExactlyOnceWith(
21+
'oh no',
22+
{ encoding: 'utf-8', flags: 'a' }
23+
);
24+
} finally {
25+
vi.unstubAllEnvs();
26+
}
27+
});
28+
29+
it('doesn\'t open the summary file if GITHUB_STEP_SUMMARY isn\'t defined', ({ reporter }) => {
30+
reporter.onInit();
31+
expect(fs.createWriteStream).not.toHaveBeenCalled();
32+
});
33+
});

lib/vitest-reporter/src/coverage-reporter.cts renamed to lib/vitest-reporter/src/coverage-reporter.ts

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/**
2-
* For whatever reason, this reporter doesn't really work if its written in ESM,
3-
* so its written in and compiled to CJS
2+
* For whatever reason, this reporter doesn't really work if its compiled to ESM
3+
* so its compiled to CJS
44
*
55
* Heavily based on the default text reporter
66
*/
7-
import type fslib from 'fs';
7+
import fs from 'fs';
88
import type { CoverageSummary } from 'istanbul-lib-coverage';
9-
import type * as report from 'istanbul-lib-report';
10-
11-
const fs: typeof fslib = require('fs');
12-
const { ReportBase }: typeof report = require('istanbul-lib-report');
9+
import * as report from 'istanbul-lib-report';
1310

1411
/**
1512
* Determines if the coverage summary has full coverage
@@ -62,30 +59,30 @@ function getUncoveredLines(node: report.ReportNode) {
6259
}
6360

6461
return acum;
65-
}, [])
62+
}, []);
6663

67-
return ranges
64+
return ranges;
6865
}
6966

70-
const headers = ['Statements', 'Branches', 'Functions', 'Lines']
67+
const headers = ['Statements', 'Branches', 'Functions', 'Lines'];
7168

7269
interface ResultObject {
73-
branches: number
74-
functions: number
75-
lines: number
76-
statements: number
77-
uncoveredLines: ([number] | [number, number])[]
70+
branches: number;
71+
functions: number;
72+
lines: number;
73+
statements: number;
74+
uncoveredLines: ([number] | [number, number])[];
7875
}
7976

8077
/**
8178
* A Vitest coverage reporter that writes to the Github Actions summary
8279
*/
83-
module.exports = class GithubActionsCoverageReporter extends ReportBase {
80+
export default class GithubActionsCoverageReporter extends report.ReportBase {
8481
private readonly skipEmpty: boolean;
8582
private readonly skipFull: boolean;
8683
private readonly results: Record<string, ResultObject> = {};
87-
private cw: fslib.WriteStream | null = null;
88-
private watermarks: report.Watermarks | null = null;
84+
private cw: fs.WriteStream | null = null;
85+
private watermarks: Partial<report.Watermarks> | null = null;
8986

9087
constructor(opts: any) {
9188
super(opts);
@@ -94,16 +91,16 @@ module.exports = class GithubActionsCoverageReporter extends ReportBase {
9491
this.skipFull = Boolean(opts.skipFull);
9592
}
9693

97-
onStart(_node: any, context: report.Context) {
94+
onStart(_node: any, context: report.ContextOptions) {
9895
if (!process.env.GITHUB_STEP_SUMMARY) {
99-
console.log('Reporter not being executed in Github Actions environment')
96+
console.log('Reporter not being executed in Github Actions environment');
10097
return;
10198
}
10299

103-
this.cw = fs.createWriteStream(process.env.GITHUB_STEP_SUMMARY, { encoding: 'utf-8', flags: 'a' })
100+
this.cw = fs.createWriteStream(process.env.GITHUB_STEP_SUMMARY, { encoding: 'utf-8', flags: 'a' });
104101

105102
this.watermarks = context.watermarks;
106-
this.cw.write('<h2>Test Coverage</h2>')
103+
this.cw.write('<h2>Test Coverage</h2>');
107104
this.cw.write('<table><thead><tr>');
108105
for (const heading of ['File', ...headers, 'Uncovered Lines']) {
109106
this.cw.write(`<th>${heading}</th>`);
@@ -129,26 +126,26 @@ module.exports = class GithubActionsCoverageReporter extends ReportBase {
129126
functions: isEmpty ? 0 : rawMetrics.functions.pct,
130127
lines: isEmpty ? 0 : rawMetrics.lines.pct,
131128
uncoveredLines: getUncoveredLines(node),
132-
}
129+
};
133130
}
134131

135132
onDetail(node: report.ReportNode) {
136133
return this.onSummary(node);
137134
}
138135

139136
private formatter(pct: number, watermark: keyof report.Watermarks) {
140-
if (!this.watermarks) return `<td>${pct}%</td>`
137+
if (!this.watermarks || this.watermarks[watermark] === undefined) return `<td>${pct}%</td>`;
141138
const [low, high] = this.watermarks[watermark];
142139

143140
if (pct < low) {
144-
return `<td><p style="color:red">${pct}%</p></td>`
141+
return `<td><p style="color:red">${pct}%</p></td>`;
145142
}
146143

147144
if (pct > high) {
148-
return `<td><p style="color:green">${pct}%</p></td>`
145+
return `<td><p style="color:green">${pct}%</p></td>`;
149146
}
150147

151-
return `<td><p style="color:yellow">${pct}%</p></td>`
148+
return `<td><p style="color:yellow">${pct}%</p></td>`;
152149
}
153150

154151
onEnd() {
@@ -168,9 +165,9 @@ module.exports = class GithubActionsCoverageReporter extends ReportBase {
168165
this.cw.write('<td><details><summary>Expand</summary><ul>');
169166
for (const range of metrics.uncoveredLines) {
170167
if (range.length === 1) {
171-
this.cw.write(`<li>${range[0]}</li>`)
168+
this.cw.write(`<li>${range[0]}</li>`);
172169
} else {
173-
this.cw.write(`<li>${range[0]}-${range[1]}</li>`)
170+
this.cw.write(`<li>${range[0]}-${range[1]}</li>`);
174171
}
175172
}
176173
this.cw.write('</ul></details></td>');

lib/vitest-reporter/src/test-reporter.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ function* formatTestSuite(suite: TestSuite): Generator<string> {
3535
yield '</ul>\n';
3636
}
3737

38+
function getIcon(passed: boolean) {
39+
return passed ? '✅' : '❌';
40+
}
41+
3842
function formatRow(...items: string[]) {
3943
const tds = items.map(item => `<td>${item}</td>`);
4044
return `<tr>${tds.join('')}</tr>\n`;
@@ -86,7 +90,7 @@ export default class GithubActionsSummaryReporter implements Reporter {
8690
const [passCount, failCount] = getTestCount(testModule);
8791
const testCount = passCount + failCount;
8892

89-
this.writeStream.write(`<h3>${passed ? '✅' : '❌'} <code>${relpath}</code> (${testCount} test${testCount === 1 ? '' : 's'})</h3>\n`);
93+
this.writeStream.write(`<h3>${getIcon(passed)} <code>${relpath}</code> (${testCount} test${testCount === 1 ? '' : 's'})</h3>\n`);
9094

9195
this.writeStream.write('<ul>');
9296
for (const child of testModule.children) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @ts-check
2+
// vitest-reporter vitest config
3+
import { defineConfig } from 'vitest/config';
4+
import rootConfig from '../../vitest.config.js';
5+
6+
export default defineConfig({
7+
optimizeDeps: {
8+
include: ['istanbul-lib-report'],
9+
},
10+
test: {
11+
...rootConfig.test,
12+
projects: undefined,
13+
14+
name: 'Vitest Reporters',
15+
root: import.meta.dirname,
16+
include: ['src/**/__tests__/*.test.{ts,tsx}'],
17+
}
18+
});

0 commit comments

Comments
 (0)