Skip to content

Commit

Permalink
feat: Add option to choose mode for display file coverage (#192)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
The File-Coverage will now only report files that were actually changed in the pull-request on default. This is to avoid large projects having problems with the comments on the PR being too big. To get the old behavior (always report all tested files), use the new setting 'file-coverage-mode: all'.

---------

Co-authored-by: David Losert <[email protected]>
  • Loading branch information
sfer23 and davelosert committed Jun 13, 2023
1 parent de9002f commit 148463f
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 132 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ jobs:
This action requires permissions set to `pull-request: write` in order for it to be able to add a comment to your pull-request. If you are using the default `GITHUB_TOKEN`, make sure to include the permissions together with `contents: read` to the the job, so that the `actions/checkout` action is allowed to checkout the repository. This is especially important for new repositories created after [GitHub's announcement](https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/) to change the default permissions to `read-only` for all new `GITHUB_TOKEN`s.


### Options

| Option | Description | Default |
Expand All @@ -75,6 +74,13 @@ This action requires permissions set to `pull-request: write` in order for it to
| `vite-config-path` | The path to the vite config file. Will check the same paths as vite and vitest | Checks pattern `vite[st].config.{t|mt|ct|j|mj|cj}s` |
| `github-token` | A GitHub access token with permissions to write to issues (defaults to `secrets.GITHUB_TOKEN`). | `${{ github.token }}` |
| `working-directory` | Run action within a custom directory (for monorepos). | `./` |
| `file-coverage-mode`| Defines how file-based coverage is reported. Possible values are `all`, `changes` or `none`. | `changes` |

### File Coverage Mode

* `changes` - show Files coverage only for project files changed in that pull request (works only with `pull_request`, `pull_request_review`, `pull_request_review_comment` actions)
* `all` - show it grouped by changed and not changed files in that pull request (works only with `pull_request`, `pull_request_review`, `pull_request_review_comment` actions)
* `none` - do not show any File coverage details (only total Summary)

### Coverage Thresholds

Expand Down
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ inputs:
required: false
description: 'The path to the json summary file. Uses "coverage/coverage-summary.json" by default.'
default: coverage/coverage-summary.json
file-coverage-mode:
required: false
description: 'How to show summary for files coverage. Uses "changes" by default.'
default: changes
json-final-path:
required: false
description: 'The path to the json final file. Uses "coverage/coverage-final.json" by default.'
Expand Down
29 changes: 29 additions & 0 deletions src/FileCoverageMode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, it, expect, vi } from 'vitest';
import * as core from '@actions/core';
import { FileCoverageMode, getCoverageModeFrom } from './FileCoverageMode';

vi.mock('@actions/core', async (importOriginal) => ({
...importOriginal,
warning: vi.fn(),
}));

describe('FileCoverageMode', () => {
it('parses "all" to the right value', () => {
expect(getCoverageModeFrom('all')).toBe(FileCoverageMode.All);
});

it('parses "changes" to the right value', () => {
expect(getCoverageModeFrom('changes')).toBe(FileCoverageMode.Changes);
});

it('defaults to "changes" if the input is not valid', () => {
expect(getCoverageModeFrom('invalid')).toBe(FileCoverageMode.Changes);
});

it('logs a warning if the input is not valid', () => {
const spy = vi.spyOn(core, 'warning');
getCoverageModeFrom('invalid');
expect(spy).toHaveBeenCalledWith('Not valid value "invalid" for summary mode, used "changes"');
spy.mockRestore();
});
});
22 changes: 22 additions & 0 deletions src/FileCoverageMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as core from '@actions/core';

enum FileCoverageMode {
All = 'all',
Changes = 'changes',
None = 'none',
}

function getCoverageModeFrom(input: string): FileCoverageMode {
const allEnums = Object.values(FileCoverageMode) as string[];
const index = allEnums.indexOf(input);
if (index === -1) {
core.warning(`Not valid value "${input}" for summary mode, used "changes"`)
return FileCoverageMode.Changes;
}
return input as FileCoverageMode;
}

export {
FileCoverageMode,
getCoverageModeFrom
}
167 changes: 121 additions & 46 deletions src/generateFileCoverageHtml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,127 @@ import { JsonFinal } from './types/JsonFinal';
import { JsonSummary } from './types/JsonSummary';
import { createMockCoverageReport, createMockJsonSummary, createMockReportNumbers } from './types/JsonSummaryMockFactory';
import { describe, it, expect } from 'vitest';
import { FileCoverageMode } from './FileCoverageMode';
import * as path from 'path';

const workspacePath = process.cwd();
describe('generateFileCoverageHtml()', () => {
it('renders the statements, branches, functions and line coverage-percentage of a file.', () => {
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/generateFileCoverageHtml.ts': {
statements: createMockReportNumbers({ pct: 70, }),
branches: createMockReportNumbers({ pct: 80, }),
functions: createMockReportNumbers({ pct: 90, }),
lines: createMockReportNumbers({ pct: 100, }),
},
});

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal: {}
});

const firstTableLine = getTableLine(1, html);

expect(firstTableLine).toContain('70%');
expect(firstTableLine).toContain('80%');
expect(firstTableLine).toContain('90%');
expect(firstTableLine).toContain('100%');
});

it('renders the line-coverage in the same row as the coverage.', async (): Promise<void> => {
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/exampleFile.ts': createMockCoverageReport({
statements: createMockReportNumbers({ pct: 70, }),
}),
});
const jsonFinal: JsonFinal = {
...createJsonFinalEntry('src/exampleFile.ts', [
{ line: 1, covered: false },
{ line: 2, covered: false }
]),
};

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal
});

const firstTableLine = getTableLine(1, html);

expect(firstTableLine).toContain('70%');
expect(firstTableLine).toContain('1-2');
});
it('renderes only the unchanged files if no changed files exist.', () => {
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/generateFileCoverageHtml.ts': createMockCoverageReport(),
});

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal: {},
fileCoverageMode: FileCoverageMode.All,
pullChanges: []
});

const firstTableLine = getTableLine(1, html);

expect(firstTableLine).toContain('Unchanged Files');
});

it('renders changed files before unchanged files.', () => {
const relativeChangedFilePath = 'src/changedFile.ts'
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/unchangedFile.ts': createMockCoverageReport(),
[path.join(workspacePath, 'src', 'changedFile.ts')]: createMockCoverageReport(),
});

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal: {},
fileCoverageMode: FileCoverageMode.All,
pullChanges: [relativeChangedFilePath]
});

expect(getTableLine(1, html)).toContain('Changed Files');
expect(getTableLine(2, html)).toContain(relativeChangedFilePath);
expect(getTableLine(3, html)).toContain('Unchanged Files');
expect(getTableLine(4, html)).toContain('src/unchangedFile.ts');
});

it('only renders unchanged files if the fileCoverageMode is set to All but only unchanged files exist.', () => {
const changedFileName = 'src/changedFile.ts'
const jsonSummary: JsonSummary = createMockJsonSummary({
[changedFileName]: createMockCoverageReport(),
});

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal: {},
fileCoverageMode: FileCoverageMode.All,
pullChanges: [changedFileName]
});

expect(html).not.toContain('Unchanged Files');
});

it('renders statement that no changed files were found if the fileCoverageMode is set to Changed but no changed files exist.', () => {
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/unchangedFile.ts': createMockCoverageReport(),
});

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal: {},
fileCoverageMode: FileCoverageMode.Changes,
pullChanges: []
});

expect(html).toContain('No changed files found.');
});

it('renders the statements, branches, functions and line coverage-percentage of a file.', () => {
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/generateFileCoverageHtml.ts': {
statements: createMockReportNumbers({ pct: 70, }),
branches: createMockReportNumbers({ pct: 80, }),
functions: createMockReportNumbers({ pct: 90, }),
lines: createMockReportNumbers({ pct: 100, }),
},
});

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal: {},
fileCoverageMode: FileCoverageMode.All,
pullChanges: []
});

const firstTableLine = getTableLine(2, html);

expect(firstTableLine).toContain('70%');
expect(firstTableLine).toContain('80%');
expect(firstTableLine).toContain('90%');
expect(firstTableLine).toContain('100%');
});

it('renders the line-coverage in the same row as the coverage.', async (): Promise<void> => {
const jsonSummary: JsonSummary = createMockJsonSummary({
'src/exampleFile.ts': createMockCoverageReport({
statements: createMockReportNumbers({ pct: 70, }),
}),
});
const jsonFinal: JsonFinal = {
...createJsonFinalEntry('src/exampleFile.ts', [
{ line: 1, covered: false },
{ line: 2, covered: false }
]),
};

const html = generateFileCoverageHtml({
jsonSummary,
jsonFinal,
fileCoverageMode: FileCoverageMode.All,
pullChanges: []
});

const firstTableLine = getTableLine(2, html);

expect(firstTableLine).toContain('70%');
expect(firstTableLine).toContain('1-2');
});
});
Loading

0 comments on commit 148463f

Please sign in to comment.