yarn-audit-html
Version:
Generate a HTML report for Yarn Audit
182 lines (151 loc) • 4.42 kB
text/typescript
import fs from "node:fs";
import { Readable } from "node:stream";
import childProcess, { SpawnSyncReturns } from "node:child_process";
import { describe, test, beforeEach, afterEach } from "node:test";
import chai from "chai";
import esmock from "esmock";
import sinon from "sinon";
import sinonChai from "sinon-chai";
chai.should();
chai.use(sinonChai);
const yarnV1Audit = getFixture("yarn1.jsonl");
const yarnV2Audit = getFixture("yarn2.json");
const yarnV3Audit = getFixture("yarn3.json");
describe("cli", () => {
let fstatSync: sinon.SinonStub;
let processExit: sinon.SinonStub;
let spawnSync: sinon.SinonStub;
let processStdout: sinon.SinonSpy;
let processStderr: sinon.SinonSpy;
let programOutputHelp: sinon.SinonStub;
let consoleInfo: sinon.SinonStub;
let bailWithError: sinon.SinonStub;
let generateReport: sinon.SinonStub;
let programMock: Record<
string,
sinon.SinonStub | (() => typeof programMock) | (() => Record<string, string | boolean>)
>;
const stdinMock1 = new Readable({
read() {
this.push(yarnV1Audit);
this.push(null);
},
});
const stdinMock2 = new Readable({
read() {
this.push(yarnV2Audit);
this.push(null);
},
});
const stdinMock3 = new Readable({
read() {
this.push(yarnV3Audit);
this.push(null);
},
});
beforeEach(async () => {
fstatSync = sinon.stub().returns({
isFIFO: () => true,
});
spawnSync = sinon.stub(childProcess, "spawnSync").returns({ stdout: "1.0.0" } as SpawnSyncReturns<string>);
processExit = sinon.stub(process, "exit");
processStdout = sinon.spy(process.stdout, "write");
processStderr = sinon.spy(process.stderr, "write");
programOutputHelp = sinon.stub();
consoleInfo = sinon.stub(console, "info");
bailWithError = sinon.stub();
generateReport = sinon.stub();
programMock = {
opts: () => ({
output: "",
template: "",
theme: "materia",
fatalExitCode: true,
}),
option: () => programMock,
addOption: () => programMock,
parse: () => programMock,
outputHelp: programOutputHelp,
};
});
test("should work properly when executed with no piped in data", async () => {
fstatSync.returns({
isFIFO: () => false,
});
const run = await mockModule();
await run([], process.stdin);
consoleInfo.should.be.calledOnce;
fstatSync.should.be.calledOnce;
programOutputHelp.should.be.calledOnce;
processExit.should.be.calledOnce.and.calledWithExactly(1);
});
test("should work properly when executed without arguments and with piped in data", async () => {
const run = await mockModule();
await run([], stdinMock1);
consoleInfo.should.be.calledOnce;
fstatSync.should.be.calledOnce;
processStderr.should.not.be.called;
spawnSync.should.be.calledOnce;
// generateReport.should.be.called;
});
test("should work properly for yarn v2", async () => {
spawnSync.returns({ stdout: "2.0.0" });
const run = await mockModule();
await run([], stdinMock2);
consoleInfo.should.be.calledOnce;
fstatSync.should.be.calledOnce;
processStderr.should.not.be.called;
processExit.should.not.be.called;
});
test("should work properly for yarn v3", async () => {
spawnSync.returns({ stdout: "3.0.0" });
const run = await mockModule();
await run([], stdinMock3);
consoleInfo.should.be.calledOnce;
fstatSync.should.be.calledOnce;
processStderr.should.not.be.called;
processExit.should.not.be.called;
});
// TODO: enable
test("should work properly there is an error in generation", { skip: true }, async () => {
generateReport.returns(() => Promise.reject(""));
const run = await mockModule();
await run([], stdinMock1);
consoleInfo.should.be.calledOnce;
fstatSync.should.be.calledOnce;
fstatSync.should.be.calledOnce;
generateReport.should.be.calledOnce;
bailWithError.should.be.calledOnce;
});
afterEach(() => {
sinon.restore();
});
async function mockModule() {
return (
await esmock(
"./cli.ts",
{
"./index.ts": {
bailWithError,
generateReport,
},
},
{
"node:child_process": { spawnSync },
"node:fs": {
fstatSync,
},
"node:process": { exit: processExit, stdout: processStdout },
commander: {
program: programMock,
},
},
)
).run;
}
});
function getFixture(name: string) {
return fs.readFileSync(new URL(`../fixtures/${name}`, import.meta.url).pathname, {
encoding: "utf8",
});
}