src/test.ts
jest.mock('fs');
import * as _fs from 'fs';
import { resolve } from 'path';
import {
CollectCoverageError,
JsonSummaryError,
TimeoutError,
tryOrReject,
} from './errors';
import JestRatchet from './index';
import { findCoveragePath } from './locations';
import { noop } from './noop';
type extFs = typeof _fs & {
__addMockFile: (name: RegExp, value: string) => void;
__resetMockFiles: () => void;
};
const fs: extFs = _fs as any;
const mockConfig = {
collectCoverage: true,
coverageReporters: ['json-summary'],
};
const originalArgv = process.argv;
const originalEnv = process.env;
describe('jest-ratchet', () => {
beforeEach(() => {
jest.resetAllMocks();
fs.__resetMockFiles();
process.cwd = jest.fn().mockReturnValue(resolve('./example'));
process.argv = originalArgv;
process.env = { ...originalEnv };
delete process.env.DISABLE_JEST_RATCHET;
});
it('will ratchet percentages', () => {
const threshold = {
coverageThreshold: {
global: {
branches: 50,
},
},
};
setCoverageSummaryFile(fs, {
total: {
branches: {pct: 100},
},
});
setPackageJson(fs, { ...threshold });
const jestRatchet = new JestRatchet({
...mockConfig,
...threshold,
rootDir: './example',
});
jestRatchet.onRunComplete();
expectCoverageThreshold({
coverageThreshold: {
global: {
branches: 100,
},
},
});
});
it('will ratchet coverage set to zero', () => {
const threshold = {
coverageThreshold: {
global: {
branches: 0,
},
},
};
setCoverageSummaryFile(fs, {
total: {
branches: {pct: 75},
},
});
setPackageJson(fs, { ...threshold });
const jestRatchet = new JestRatchet({
...mockConfig,
...threshold,
rootDir: './example',
});
jestRatchet.onRunComplete();
expectCoverageThreshold({
coverageThreshold: {
global: {
branches: 75,
},
},
});
});
it('will ratchet non-global percentages', () => {
const threshold = {
coverageThreshold: {
global: {
branches: 50,
functions: 50,
lines: 50,
statements: 50,
},
specific: {
branches: 50,
functions: 50,
lines: 50,
statements: 50,
},
},
};
setCoverageSummaryFile(fs, {
specific: {
branches: {pct: 100},
functions: {pct: 100},
lines: {pct: 100},
statements: {pct: 100},
},
total: {
branches: {pct: 100},
functions: {pct: 100},
lines: {pct: 100},
statements: {pct: 100},
},
});
setPackageJson(fs, { ...threshold });
const jestRatchet = new JestRatchet({
...mockConfig,
...threshold,
rootDir: './example',
});
jestRatchet.onRunComplete();
expectCoverageThreshold({
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
specific: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
});
});
it('will round down percentages', () => {
const threshold = {
coverageThreshold: {
global: {
branches: 50,
functions: 50,
lines: 50,
statements: 50,
},
},
};
setCoverageSummaryFile(fs, {
total: {
branches: {pct: 98.7},
functions: {pct: 51.3},
lines: {pct: 70.6},
statements: {pct: 75.8},
},
});
setPackageJson(fs, { ...threshold });
const jestRatchet = new JestRatchet({
...mockConfig,
...threshold,
rootDir: './example',
}, {
roundDown: true,
});
jestRatchet.onRunComplete();
expectCoverageThreshold({
coverageThreshold: {
global: {
branches: 98,
functions: 51,
lines: 70,
statements: 75,
},
},
});
});
it('will pad the ratchet percentages', () => {
const TOLERANCE = 2;
const threshold = {
coverageThreshold: {
global: {
branches: 50,
},
},
};
setCoverageSummaryFile(fs, {
total: {
branches: {pct: 100},
},
});
setPackageJson(fs, { ...threshold });
const jestRatchet = new JestRatchet({
...mockConfig,
...threshold,
}, {
tolerance: TOLERANCE,
});
jestRatchet.onRunComplete();
expectCoverageThreshold({
coverageThreshold: {
global: {
branches: 98,
},
},
});
});
it('will respect the --config flag', () => {
process.argv = ['', '', '--config', 'jestconfig.json'];
const threshold = {
coverageThreshold: {
global: {
branches: -50,
functions: -50,
lines: -50,
statements: -50,
},
},
};
setCoverageSummaryFile(fs, {
total: {
branches: {covered: 100},
functions: {covered: 100},
lines: {covered: 100},
statements: {covered: 100},
},
});
setJestConfig(fs, { ...threshold });
const jestRatchet = new JestRatchet({ ...mockConfig, ...threshold });
jestRatchet.onRunComplete();
expectCoverageThreshold({
coverageThreshold: {
global: {
branches: -100,
functions: -100,
lines: -100,
statements: -100,
},
},
});
});
const setCoverageSummaryFile = (mockfs: extFs, json: any) => {
mockfs.__addMockFile(
/\/coverage-summary\.json$/,
JSON.stringify(json),
);
};
const setPackageJson = (mockfs: extFs, json: any) => {
mockfs.__addMockFile(
/\/package\.json$/,
JSON.stringify(json),
);
};
const setJestConfig = (mockfs: extFs, json: any) => {
mockfs.__addMockFile(
/\/jestconfig\.json$/,
JSON.stringify(json),
);
};
const expectCoverageThreshold = (threshold: any) => {
const writeFileSync = fs.writeFileSync as jest.Mock;
expect(writeFileSync).toHaveBeenCalledWith(
expect.anything(),
JSON.stringify({...threshold}),
expect.anything(),
);
};
it('will initialize without error', () => {
const config = {
...mockConfig,
collectCoverage: true,
coverageReporters: ['json-summary'],
};
const jestRatchet = new JestRatchet(config);
expect(jestRatchet.getLastError).not.toThrowError();
});
it('will throw error when json-summary is not enabled', () => {
const config = {
...mockConfig,
collectCoverage: true,
coverageReporters: undefined,
};
const jestRatchet = new JestRatchet(config);
expect(jestRatchet.getLastError).toThrowError(JsonSummaryError);
});
it('will throw error when collectCoverage is not enabled', () => {
const config = {
...mockConfig,
collectCoverage: false,
};
const jestRatchet = new JestRatchet(config);
expect(jestRatchet.getLastError).toThrowError(CollectCoverageError);
});
it('will do nothing when ratchet is disabled', () => {
process.env.DISABLE_JEST_RATCHET = 'true';
const jestRatchet = new JestRatchet(mockConfig);
expect(jestRatchet.onRunComplete).toBe(noop);
expect(jestRatchet.getLastError).toBe(noop);
});
it('will throw a timeout error', async () => {
fs.watch = () => ({
close: jest.fn(),
on: jest.fn(),
once: jest.fn(),
}) as any;
const jestRatchet = new JestRatchet({ ...mockConfig }, { timeout: 5 });
await expect(jestRatchet.runResult).rejects.toBeInstanceOf(TimeoutError);
expect(jestRatchet.getLastError).toThrowError(TimeoutError);
});
it('will cleanup timeout', () => {
jest.useFakeTimers();
fs.watch = () => ({
close: jest.fn(),
once: jest.fn().mockImplementation((_: string, cb: () => void) => { cb(); }),
}) as any;
const jestRatchet = new JestRatchet({ ...mockConfig }, { timeout: 100 });
jestRatchet.onRunComplete();
expect(clearTimeout).toBeCalled();
});
});
describe('edges', () => {
it('tryOrReject will reject', () => {
const reject = jest.fn();
tryOrReject(reject, () => { throw new Error(); });
expect(reject).toBeCalled();
});
it('findCoveragePath will return coverageDirectory', () => {
const coverageDirectory = 'COVERAGE_DIRECTORY';
expect(findCoveragePath({coverageDirectory})).toBe(coverageDirectory);
});
it('findCoveragePath will return rootDir', () => {
const rootDir = 'COVERAGE_DIRECTORY';
expect(findCoveragePath({rootDir})).toBe(resolve(rootDir, 'coverage'));
});
});