@90poe/jest-performant-warnings-stats-reporter
Version:
A flexible jest reporter that provides ability to show statistics of warnings and group them
218 lines (176 loc) • 5.91 kB
text/typescript
import { Context, Test } from '@jest/reporters';
import { AggregatedResult, TestResult } from '@jest/test-result';
import { Config } from '@jest/types';
import { bold, green, red } from 'chalk';
import { table } from 'table';
import {
DEFAULT_MAX_SLOW_TESTS,
DEFAULT_SHOW_SLOWEST,
DEFAULT_SHOW_WARNING_SUMMARY,
DEFAULT_TEST_DURATION,
DEFAULT_WARN_ON_SLOWER_THAN,
DEFAULT_WARNINGS_CONFIG,
TABLE_HEADER_WITH_WARNINGS_DATA,
TABLE_HEADER_WITHOUT_WARNINGS_DATA,
} from './constants';
import { recursiveLogger } from './recursiveLogger';
import {
CustomLogEntry,
IJestReporterConfig,
LogsObj,
SlowTest,
Warning,
} from './types';
import {
addFilesSetToWarningConfig,
getFileName,
log,
omitWarningsData,
prepareGreetingMessage,
prepareTableRow,
prepareWarnings,
} from './utils';
class CustomReporter {
private showWarningSummary: boolean;
private warningsConfig: Array<Warning>;
private maxSlowTests: number;
private globalConfig: Config.InitialOptions;
private logs: LogsObj;
private showSlowest: boolean;
private slowTests: Array<SlowTest>;
private finalTable: Array<Array<string | number>>;
private warnOnSlowerThan: number;
constructor(
public globalConfiguration: Config.InitialOptions,
private options: IJestReporterConfig,
) {
this.globalConfig = globalConfiguration;
this.showWarningSummary =
options.showWarningSummary ?? DEFAULT_SHOW_WARNING_SUMMARY;
this.warningsConfig =
options.warningsConfig?.map(addFilesSetToWarningConfig) ??
DEFAULT_WARNINGS_CONFIG;
this.maxSlowTests = options.maxSlowTests ?? DEFAULT_MAX_SLOW_TESTS;
this.logs = {};
this.showSlowest = options.showSlowest ?? DEFAULT_SHOW_SLOWEST;
this.slowTests = [];
this.warnOnSlowerThan =
options.warnOnSlowerThan ?? DEFAULT_WARN_ON_SLOWER_THAN;
this.finalTable = omitWarningsData(globalConfiguration)
? [TABLE_HEADER_WITHOUT_WARNINGS_DATA]
: [TABLE_HEADER_WITH_WARNINGS_DATA];
}
onRunStart({ numTotalTestSuites }: AggregatedResult): void {
const greetingMessage = prepareGreetingMessage(numTotalTestSuites);
console.log(greetingMessage);
}
onTestResult(_: Test, testResult: TestResult): void {
const fileName = getFileName(testResult.testFilePath);
const warningsPerTest: Record<string, number> = {};
const tableRow = prepareTableRow(
testResult,
this.warnOnSlowerThan,
this.globalConfig,
);
this.logs[fileName] = {
filePath: testResult.testFilePath,
warnings: [],
};
this.finalTable.push(tableRow);
if (testResult.console) {
testResult.console.forEach((value) => {
let isOther = true;
this.warningsConfig.forEach(({ regex, name }) => {
if (regex.test(value.message)) {
warningsPerTest[name] = warningsPerTest[name] + 1 || 1;
isOther = false;
}
});
if (isOther) {
warningsPerTest.Other = warningsPerTest.Other + 1 || 1;
}
this.logs[fileName].warnings.push(value);
});
}
if (this.showSlowest) {
this.collectSlowTests(testResult);
}
if (this.showWarningSummary && !this.globalConfig.verbose) {
const warningsInfo = prepareWarnings(warningsPerTest);
this.finalTable.push(...warningsInfo);
}
}
onRunComplete(
_: Set<Context>,
{ numFailedTests, numPassedTests, testResults }: AggregatedResult,
): void {
testResults.forEach(
({ testFilePath, testResults: innerTestResults, failureMessage }) => {
recursiveLogger(testFilePath, innerTestResults, 0, 0);
if (failureMessage) {
console.log(failureMessage);
}
},
);
if (numPassedTests !== 0) {
console.log(green(`${numPassedTests} passed`));
}
if (numFailedTests !== 0) {
console.log(bold(red(`${numFailedTests} failed`)));
}
if (this.globalConfig.verbose || this.globalConfig.watch) {
Object.entries(this.logs).forEach(([filename, data]) => {
const { warnings } = data;
if (warnings.length) {
console.log(bold(filename));
}
(warnings as CustomLogEntry[]).forEach(log);
});
}
const shouldShowTable = this.finalTable.length > 1;
if (shouldShowTable) {
console.log(table(this.finalTable));
}
if (
this.showSlowest &&
!this.globalConfig.watch &&
!this.globalConfig.watchAll
) {
this.consoleSlowTests();
}
}
// Idea taken from [jest-slow-test-reporter](https://github.com/jodonnell/jest-slow-test-reporter)
private collectSlowTests(result: TestResult): void {
result.testResults.forEach((test) => {
this.slowTests.push({
duration: test.duration ?? DEFAULT_TEST_DURATION,
fullName: test.fullName,
filePath: result.testFilePath,
});
});
}
private consoleSlowTests(): void {
this.slowTests.sort(function sort(a, b) {
return b.duration - a.duration;
});
const rootPathRegex = new RegExp(`^${process.cwd()}`);
const slowestTests = this.slowTests.slice(0, this.maxSlowTests);
const slowTestTime = this.calcTestTime(slowestTests);
const allTestTime = this.calcTestTime(this.slowTests);
const percentTime = (slowTestTime / allTestTime) * 100;
console.log(
`Top ${slowestTests.length} slowest examples (${
slowTestTime / 1000
} seconds, ${percentTime.toFixed(1)}% of total time):`,
);
slowestTests.forEach(({ duration, fullName, filePath }) => {
const path = filePath.replace(rootPathRegex, '.');
console.log(` ${fullName}`);
console.log(` ${duration / 1000} seconds ${path}`);
});
}
private calcTestTime(slowestTests: Array<SlowTest>) {
return slowestTests.reduce((acc, curr) => acc + curr.duration, 0);
}
}
export default CustomReporter;