Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report on test coverage #675

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/cli/test/reporters/coverage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { AggregatedResult } from '@jest/test-result';
import istanbulReport from 'istanbul-lib-report';
import istanbulReports from 'istanbul-reports';

export const createWriteOverride = () => {
const chunks: Uint8Array[] = [];

const output = () => Buffer.concat(chunks).toString('utf8');

const write = (
buffer: Uint8Array | string,
encodingOrCb?: BufferEncoding | ((err?: Error) => void),
cb?: (err?: Error) => void,
) => {
chunks.push(
typeof buffer === 'string'
? Buffer.from(
buffer,
typeof encodingOrCb === 'string' ? encodingOrCb : undefined,
)
: buffer,
);

if (typeof encodingOrCb === 'function') {
encodingOrCb();
} else {
cb?.();
}

return true;
};

return {
output,
write,
};
};

/**
* Renders out test coverage using the Istanbul `text` reporter.
*
* This is unfortunately hacky in a couple of ways;
*
* 1. Jest does not support custom coverage reporters (facebook/jest#9112), so
* we rely on the default `CoverageReporter` running before us and use the
* `coverageMap` that it places on the aggregated result state.
*
* 2. `istanbul-reports` does not support writing to a custom stream, so we need
* to temporarily override `process.stdout.write` 😱.
*
* {@link https://github.com/facebook/jest/blob/v27.3.1/packages/jest-reporters/src/CoverageReporter.ts#L103}
//
*/
export const renderCoverageText = (
coverageMap: AggregatedResult['coverageMap'],
) => {
if (!coverageMap) {
// Coverage was not stored on the aggregated result by `CoverageReporter`.
// Maybe `collectCoverage` / `--coverage` was not specified.
return;
}

const reportContext = istanbulReport.createContext({
coverageMap,
});

const overrideWrite = createWriteOverride();

const originalWrite = process.stdout.write.bind(this);
process.stdout.write = overrideWrite.write;

try {
istanbulReports.create('text').execute(reportContext);
} finally {
process.stdout.write = originalWrite;
}

return overrideWrite.output();
};
10 changes: 9 additions & 1 deletion src/cli/test/reporters/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ import { inspect } from 'util';

import type { Reporter, TestContext } from '@jest/reporters';
import type { AggregatedResult } from '@jest/test-result';
import stripAnsi from 'strip-ansi';

import * as Buildkite from '../../../../api/buildkite';
import * as GitHub from '../../../../api/github';
import {
buildNameFromEnvironment,
enabledFromEnvironment,
} from '../../../../api/github/environment';
import { log } from '../../../../utils/logging';
import { throwOnTimeout } from '../../../../utils/wait';
import { renderCoverageText } from '../coverage';

import { generateAnnotationEntries } from './annotations';

export default class GitHubReporter implements Pick<Reporter, 'onRunComplete'> {
async onRunComplete(
_contexts: Set<TestContext>,
{ testResults }: AggregatedResult,
{ coverageMap, testResults }: AggregatedResult,
): Promise<void> {
if (!enabledFromEnvironment()) {
return;
Expand All @@ -27,6 +30,8 @@ export default class GitHubReporter implements Pick<Reporter, 'onRunComplete'> {
let lastCheckRun: CheckRun | undefined;

try {
const coverage = renderCoverageText(coverageMap);

const entries = generateAnnotationEntries(testResults);

const build = buildNameFromEnvironment();
Expand All @@ -47,6 +52,9 @@ export default class GitHubReporter implements Pick<Reporter, 'onRunComplete'> {
annotations,
conclusion: isOk ? 'success' : 'failure',
summary,
text: coverage
? Buildkite.md.terminal(stripAnsi(coverage))
: undefined,
title: `${build} ${isOk ? 'passed' : 'failed'}`,
};

Expand Down