From 02f8b3b0b60ec4214fed55c29ec7f511e6e793f6 Mon Sep 17 00:00:00 2001 From: ajiron Date: Sat, 14 Mar 2020 23:19:27 -0700 Subject: [PATCH 1/5] added github pull request uncovered line annotations --- README.md | 24 ++++++++++++- package.json | 21 +++++++++-- src/create-check.ts | 74 ++++++++++++++++++++++++++++++++++---- src/groupSequences.test.ts | 16 +++++++++ src/groupSequences.ts | 20 +++++++++++ src/index.ts | 17 +++++++-- tsconfig.json | 17 ++++++--- 7 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 src/groupSequences.test.ts create mode 100644 src/groupSequences.ts diff --git a/README.md b/README.md index 7903fea..400e016 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,32 @@ Then just run jest and it will test errors PRs! ```json { - "reporters": ["default", "./dist/index.js"] + "reporters": [ + "default", + "jest-github-reporter" + ] } ``` +### Additional Options + +You may pass in additional options the the github reporter like so + +```json +{ + "reporters": [ + "default", + ["jest-github-reporter", { "failOnUncoveredLines": true }] + ] +} +``` + +#### failOnUncoveredLines + +If set to true, the reporter will comment on lines which were added in the pull request but +have no code coverage. If false, then uncovered lines will not fail the build. + + ```sh jest file.js ``` diff --git a/package.json b/package.json index a54a2d4..5465a38 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "build": "tsc -p tsconfig.json", "lint": "eslint src --ext .ts", + "test": "jest", "test:example": "jest --testLocationInResults example/", "release": "auto shipit" }, @@ -22,6 +23,7 @@ "dependencies": { "@octokit/rest": "^16.28.7", "create-check": "^0.5.0", + "istanbul-gh-pr-uncovered": "^1.0.0-beta.1", "strip-ansi": "^5.2.0" }, "devDependencies": { @@ -43,6 +45,7 @@ "jest": "24.9.0", "lint-staged": "9.4.1", "prettier": "1.18.2", + "ts-jest": "^25.2.1", "typescript": "3.6.3" }, "lint-staged": { @@ -56,11 +59,23 @@ }, "jest": { "testLocationInResults": true, - "reporters": [ - "./dist/index.js" + "roots": [ + "/src" + ], + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" ] }, "auto": { "extends": "hipstersmoothie" } -} +} \ No newline at end of file diff --git a/src/create-check.ts b/src/create-check.ts index 0c79f3a..23557a4 100644 --- a/src/create-check.ts +++ b/src/create-check.ts @@ -2,6 +2,8 @@ import path from 'path'; import stripAnsi from 'strip-ansi'; import Octokit from '@octokit/rest'; import createCheck from 'create-check'; +import getUncoveredPrFiles from 'istanbul-gh-pr-uncovered'; +import groupSequences from './groupSequences'; const APP_ID = 38833; /** @@ -51,6 +53,15 @@ interface Location { line: number; } +function getAppId() { + return process.env.JEST_APP_ID ? Number(process.env.JEST_APP_ID) : APP_ID; +} + +function getPrivatekey() { + return process.env.JEST_PRIVATE_KEY || PRIVATE_KEY; +} + + function createAnnotations(results: jest.TestResult[]) { const annotations: Octokit.ChecksCreateParamsOutputAnnotations[] = []; @@ -87,12 +98,63 @@ function createAnnotations(results: jest.TestResult[]) { return annotations; } -export default (results: ReturnType) => - createCheck({ +async function createUncoveredLinesAnnotations(results: ReturnType, config: GithubReporterConfig) { + if (!config.failOnUncoveredLines) { + return []; + } + + const annotations: Octokit.ChecksCreateParamsOutputAnnotations[] = []; + + const uncoveredPRFiles = await getUncoveredPrFiles({ + coverageMap: results.coverageMap, + appId: getAppId(), + privateKey: getPrivatekey() + }); + + + uncoveredPRFiles.forEach((ghPrFile: IstanbulGhPRUncovered.UncoveredFile) => { + const sequences = groupSequences(ghPrFile.lines); + + // Group the lines together so that we don't post an annotation for lines that are adjacent to + // each other. + sequences.forEach(sequenceArray => { + const startLine = sequenceArray[0]; + const endLine = sequenceArray[sequenceArray.length - 1]; + + annotations.push({ + path: path.relative(process.cwd(), ghPrFile.filename), + start_line: startLine, + end_line: endLine, + annotation_level: 'failure', + message: startLine === endLine ? `This line is uncovered by tests.` + : `Lines ${startLine}-${endLine} are uncovered by tests.` + }); + + }); + }); + + return annotations; +} + + + +export default async (results: ReturnType, config: GithubReporterConfig) => { + + const failureAnnotations = createAnnotations(results.testResults); + const uncoveredLinesAnnotations = await createUncoveredLinesAnnotations(results, config); + + const annotations: Octokit.ChecksCreateParamsOutputAnnotations[] = [ + ...failureAnnotations, + ...uncoveredLinesAnnotations + ]; + + return createCheck({ tool: 'Jest', name: 'Test', - annotations: createAnnotations(results.testResults), - errorCount: results.numFailedTests, - appId: process.env.JEST_APP_ID ? Number(process.env.JEST_APP_ID) : APP_ID, - privateKey: process.env.JEST_PRIVATE_KEY || PRIVATE_KEY + annotations, + errorCount: annotations.length, + appId: getAppId(), + privateKey: getPrivatekey() }); +} + diff --git a/src/groupSequences.test.ts b/src/groupSequences.test.ts new file mode 100644 index 0000000..089c8d6 --- /dev/null +++ b/src/groupSequences.test.ts @@ -0,0 +1,16 @@ +import groupSequences from './groupSequences'; + +describe('groupSequences', () => { + it('should return an empty array when array is empty', () => { + expect(groupSequences([])).toEqual([]); + }); + it('should return grouped sequences', () => { + const array = [1, 2, 3, 10, 11, 12, 13, 42]; + + expect(groupSequences(array)).toEqual([ + [1, 2, 3], + [10, 11, 12, 13], + [42] + ]); + }); +}) \ No newline at end of file diff --git a/src/groupSequences.ts b/src/groupSequences.ts new file mode 100644 index 0000000..a620e88 --- /dev/null +++ b/src/groupSequences.ts @@ -0,0 +1,20 @@ + +/** + * @param {Number[]} numbers an array of sorted numbers + * @returns {Array[]} an array of array of numbers grouped by sequences + */ +export default (numbers: ReadonlyArray) => { + if (numbers.length === 0) { + return []; + } + + return numbers.reduce((array: Array>, number, index) => { + if (index === 0 || number !== numbers[index - 1] + 1) { + array.push([]); + } + + array[array.length - 1].push(number); + + return array; + }, []); +}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 9d9814c..5e818f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,25 @@ import createCheck from './create-check'; +interface GithubReporterConfig { + /** + * If set to true, the reporter will comment on lines which were added in the pull request but + * have no code coverage. If false, then uncovered lines will not fail the build. + */ + failOnUncoveredLines?: boolean; +}; + class GitHubReporter { - // eslint-disable-next-line class-methods-use-this + private config: GithubReporterConfig; + + constructor(globalConfig: jest.GlobalConfig, config: GithubReporterConfig) { + this.config = config; + } + async onRunComplete( contexts: Set, testResult: ReturnType ) { - await createCheck(testResult); + await createCheck(testResult, this.config); } } diff --git a/tsconfig.json b/tsconfig.json index bfda479..c5a43ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,19 @@ { - "include": ["src/**/*", "test/**/*", "typings/**/*"], - + "include": [ + "src/**/*", + "test/**/*", + "typings/**/*" + ], + "exclude": [ + "node_modules", + "**/*.test.ts" + ], "compilerOptions": { "target": "es5", "module": "commonjs", - "lib": ["esnext"], + "lib": [ + "esnext" + ], "outDir": "./dist", "rootDir": "./src", "declaration": true, @@ -17,4 +26,4 @@ "resolveJsonModule": true, "downlevelIteration": true } -} +} \ No newline at end of file From d890c56db2a638bb6a256698f015275d07904cff Mon Sep 17 00:00:00 2001 From: Joey Jiron Date: Sun, 15 Mar 2020 14:28:28 -0700 Subject: [PATCH 2/5] minor cleanup * wip * wip * wip * wip * wip * wip * wip * removed dist Co-authored-by: ajiron --- package.json | 2 +- src/create-check.ts | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 5465a38..88fe04d 100644 --- a/package.json +++ b/package.json @@ -78,4 +78,4 @@ "auto": { "extends": "hipstersmoothie" } -} \ No newline at end of file +} diff --git a/src/create-check.ts b/src/create-check.ts index 23557a4..58c4123 100644 --- a/src/create-check.ts +++ b/src/create-check.ts @@ -98,11 +98,7 @@ function createAnnotations(results: jest.TestResult[]) { return annotations; } -async function createUncoveredLinesAnnotations(results: ReturnType, config: GithubReporterConfig) { - if (!config.failOnUncoveredLines) { - return []; - } - +async function createUncoveredLinesAnnotations(results: ReturnType) { const annotations: Octokit.ChecksCreateParamsOutputAnnotations[] = []; const uncoveredPRFiles = await getUncoveredPrFiles({ @@ -111,7 +107,6 @@ async function createUncoveredLinesAnnotations(results: ReturnType { const sequences = groupSequences(ghPrFile.lines); @@ -139,14 +134,12 @@ async function createUncoveredLinesAnnotations(results: ReturnType, config: GithubReporterConfig) => { + const annotations: Octokit.ChecksCreateParamsOutputAnnotations[] = createAnnotations(results.testResults); - const failureAnnotations = createAnnotations(results.testResults); - const uncoveredLinesAnnotations = await createUncoveredLinesAnnotations(results, config); - - const annotations: Octokit.ChecksCreateParamsOutputAnnotations[] = [ - ...failureAnnotations, - ...uncoveredLinesAnnotations - ]; + if (config.failOnUncoveredLines) { + const uncoveredLinesAnnotations = await createUncoveredLinesAnnotations(results); + annotations.push(...uncoveredLinesAnnotations); + } return createCheck({ tool: 'Jest', From a170fbb0e8698f5b2b96c2c384e209030b8d0055 Mon Sep 17 00:00:00 2001 From: Joey Jiron Date: Wed, 15 Jul 2020 10:57:15 -0700 Subject: [PATCH 3/5] Update src/groupSequences.ts Co-authored-by: Andrew Lisowski --- src/groupSequences.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groupSequences.ts b/src/groupSequences.ts index a620e88..8473429 100644 --- a/src/groupSequences.ts +++ b/src/groupSequences.ts @@ -8,7 +8,7 @@ export default (numbers: ReadonlyArray) => { return []; } - return numbers.reduce((array: Array>, number, index) => { + return numbers.reduce((array: number[][], number, index) => { if (index === 0 || number !== numbers[index - 1] + 1) { array.push([]); } @@ -17,4 +17,4 @@ export default (numbers: ReadonlyArray) => { return array; }, []); -}; \ No newline at end of file +}; From 99d6ce1532553b4d5cf0b6501c67260063516d02 Mon Sep 17 00:00:00 2001 From: ajiron Date: Wed, 15 Jul 2020 11:26:07 -0700 Subject: [PATCH 4/5] fixed PR issues --- package.json | 11 +++++++---- src/create-check.ts | 10 +++++----- src/index.ts | 9 +-------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index d11dd29..38e47b2 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ ], "dependencies": { "@octokit/rest": "^16.28.7", - "istanbul-gh-pr-uncovered": "^1.0.0-beta.1", - "create-check": "^0.6.1", + "create-check": "^0.6.36", + "istanbul-gh-pr-uncovered": "^1.0.0-beta.2", "strip-ansi": "^6.0.0" }, "devDependencies": { @@ -41,9 +41,10 @@ "eslint-plugin-jsx-a11y": "6.2.3", "eslint-plugin-prettier": "3.1.3", "husky": "4.2.5", - "jest": "26.0.1", + "jest": "^26.1.0", "lint-staged": "10.2.2", "prettier": "2.0.5", + "ts-jest": "^26.1.2", "typescript": "3.8.3" }, "peerDependencies": { @@ -60,6 +61,8 @@ }, "jest": { "testLocationInResults": true, + "preset": "ts-jest", + "testEnvironment": "node", "reporters": [ "default", "./dist/index.js" @@ -68,4 +71,4 @@ "auto": { "extends": "hipstersmoothie" } -} +} \ No newline at end of file diff --git a/src/create-check.ts b/src/create-check.ts index 822c967..281a94e 100644 --- a/src/create-check.ts +++ b/src/create-check.ts @@ -4,10 +4,10 @@ import { TestResult, AggregatedResult } from '@jest/reporters'; import path from 'path'; import stripAnsi from 'strip-ansi'; -import Octokit from '@octokit/rest'; import getUncoveredPrFiles from 'istanbul-gh-pr-uncovered'; -import groupSequences from './groupSequences'; import createCheck, { Annotation } from 'create-check'; +import groupSequences from './groupSequences'; +import { GithubReporterConfig } from './types'; const APP_ID = 38833; /** @@ -105,7 +105,7 @@ function createAnnotations(results: TestResult[]) { return annotations; } -async function createUncoveredLinesAnnotations(results: ReturnType) { +async function createUncoveredLinesAnnotations(results: AggregatedResult) { const annotations: Annotation[] = []; const uncoveredPRFiles = await getUncoveredPrFiles({ @@ -140,7 +140,7 @@ async function createUncoveredLinesAnnotations(results: ReturnType, config: GithubReporterConfig) => { +export default async (results: AggregatedResult, config: GithubReporterConfig) => { const annotations: Annotation[] = createAnnotations(results.testResults); if (config.failOnUncoveredLines) { @@ -148,7 +148,7 @@ export default async (results: ReturnType, config: Gi annotations.push(...uncoveredLinesAnnotations); } - return createCheck({ + return createCheck({ tool: 'Jest', name: 'Test', annotations, diff --git a/src/index.ts b/src/index.ts index 5eb7296..41df5c8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,14 +3,7 @@ import { AggregatedResult, Test } from '@jest/reporters'; import createCheck from './create-check'; - -interface GithubReporterConfig { - /** - * If set to true, the reporter will comment on lines which were added in the pull request but - * have no code coverage. If false, then uncovered lines will not fail the build. - */ - failOnUncoveredLines?: boolean; -}; +import { GithubReporterConfig } from './types'; class GitHubReporter { private config: GithubReporterConfig; From 093eeb133c41332adae89f37354eafbee3fdee7c Mon Sep 17 00:00:00 2001 From: ajiron Date: Wed, 15 Jul 2020 11:26:15 -0700 Subject: [PATCH 5/5] fixed PR issues --- src/types.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/types.ts diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..b3c126d --- /dev/null +++ b/src/types.ts @@ -0,0 +1,7 @@ +export interface GithubReporterConfig { + /** + * If set to true, the reporter will comment on lines which were added in the pull request but + * have no code coverage. If false, then uncovered lines will not fail the build. + */ + failOnUncoveredLines?: boolean; +};