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

chore: add test coverage utils #244

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
48 changes: 31 additions & 17 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,39 @@ jobs:
paths:
- ~/front-config

vulnerabilities_yarn:
test:
<<: *defaults
docker:
- image: << pipeline.parameters.nodejs_base_image >>
- image: cimg/node:20.11.0
environment:
NODE_ENV: test
steps:
- *restore_repo
- run:
name: Critical vulnerabilities test (yarn audit)
command: yarn check:audit
- run: yarn lint
- run: yarn check:audit
- run: yarn test:coverage
- run: yarn test:coverage:group
- persist_to_workspace:
root: ~/front-config
paths:
- coverage

lint:
<<: *defaults
coverage:
docker:
- image: << pipeline.parameters.nodejs_base_image >>
- image: 924484830305.dkr.ecr.eu-west-1.amazonaws.com/coverage:main-latest
environment:
NODE_ENV: test
AWS_S3_BUCKET: welcome-releases
AWS_S3_FOLDER: data/coverage
# Even though we use `vitest` for the tests, we need to specify `jest` because we group the thresholds
COVERAGE_PARSER: jest
KEEP_UNTOUCHED_FILES: 'true'
COVERAGE_VARIATION_THRESHOLD: 0.05
steps:
- *restore_repo
- attach_workspace:
at: /tmp
- run:
name: linter
command: yarn lint
name: Upload coverage and comment PR
command: /app/bin/coverage eval 'Coverage.run("/tmp/coverage/coverage-grouped.json")'

release:
<<: *defaults
Expand All @@ -88,17 +100,20 @@ workflows:
only: /.*/
tags:
only: /.*/
- vulnerabilities_yarn:
- test:
requires:
- checkout
filters:
branches:
only: /.*/
tags:
only: /.*/
- lint:
- coverage:
context:
- aws
- coverage
requires:
- vulnerabilities_yarn
- test
filters:
branches:
only: /.*/
Expand All @@ -108,8 +123,7 @@ workflows:
context:
- wttj-config
requires:
- vulnerabilities_yarn
- lint
- test
filters:
branches:
ignore: /.*/
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ node_modules/

# dotenv environment variables file
.env

coverage/
2 changes: 1 addition & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1 +1 @@
yarn lint && yarn test && yarn check:audit
yarn lint && yarn test run && yarn check:audit
54 changes: 54 additions & 0 deletions lib/scripts/group-coverage.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const getTotalCoverageForPath = (prev, item) => {
// Add up all values for path
prev = {
...prev,
total: (prev.total || 0) + (item?.total || 0),
covered: (prev.covered || 0) + (item?.covered || 0),
skipped: (prev.skipped || 0) + (item?.skipped || 0),
}
// Round to 2 decimal places
prev.pct = Number(((prev.covered / prev.total) * 100).toFixed(2))

return prev
}

export const groupByPath = ({ coverage, thresholds }) => {
try {
// Jest and Vite have different threshold definition patterns so we set them to be the same:
// Vite uses `**/components/SearchNew/**`
// We convert to Jest format `src/components/SearchNew`
const thresholdsPaths = Object.keys(thresholds)
.slice(1)
.map(key => key.replace(/^\*{2}/, 'src').replace(/\/\*{2}$/, ''))

// Jest and Vite have different coverage patterns so we set them to be the same:
// Vite uses `{ lines: 10, 'my_path': { lines: 4 } }`
// We convert to Jest format `{ global: { lines: 10 }, 'my_path': { lines: 4 } }`
if (thresholds.lines) {
thresholds.global = { lines: thresholds.lines }
delete thresholds.lines
}

const { path, ...totalCoverage } = coverage[0]

const coverageByPath = thresholdsPaths.reduce(
(prev, group) => {
const matches = coverage.filter(item => new RegExp(group).test(item.path))

// We're only interested in `lines` (not `statements`, `branches` etc)
prev[group] = {
lines: matches.reduce(getTotalCoverageForPath, {}),
}

return prev
},
{ total: { lines: totalCoverage } }
)

return coverageByPath
} catch (err) {
// eslint-disable-next-line no-console
console.error('groupByPath', err)
return undefined
}
}
84 changes: 84 additions & 0 deletions lib/scripts/group-coverage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, test } from 'vitest'

import { groupByPath } from './group-coverage'

const jestThresholds = {
global: {
lines: 70,
},
'src/components/SearchNew': {
lines: 89,
},
'src/pages/Jobs/Show': {
lines: 70,
},
'src/pages/Home/CandidateHub': {
lines: 70,
},
'src/pages/Organizations/Show': {
lines: 75,
},
'src/pages/Organizations/CompanyNews': {
lines: 25,
},
'src/utils/search': {
lines: 90,
},
}

const viteThresholds = {
lines: 70,
'**/components/SearchNew/**': {
lines: 89,
},
'**/pages/Jobs/Show/**': {
lines: 70,
},
'**/pages/Home/CandidateHub/**': {
lines: 70,
},
'**/pages/Organizations/Show/**': {
lines: 75,
},
'**/pages/Organizations/CompanyNews/**': {
lines: 25,
},
'**/utils/search/**': {
lines: 90,
},
}

const coverage = [
{ path: 'global', total: 8106, covered: 5925, skipped: 0, pct: 73.09 },
{ path: 'src/components/SearchNew', total: 774, covered: 701, skipped: 0, pct: 90.57 },
{ path: 'src/components/SearchNew/pages', total: 26, covered: 13, skipped: 0, pct: 50 },
{ path: 'src/pages/Jobs/Show', total: 555, covered: 413, skipped: 0, pct: 74.41 },
{ path: 'src/pages/Home/CandidateHub', total: 168, covered: 129, skipped: 0, pct: 76.79 },
{ path: 'src/pages/Organizations/Show', total: 506, covered: 357, skipped: 0, pct: 70.55 },
{ path: 'src/pages/Organizations/CompanyNews', total: 99, covered: 27, skipped: 0, pct: 27.27 },
{ path: 'src/utils/search', total: 786, covered: 748, skipped: 0, pct: 95.17 },
]

const groupedCoverage = {
total: { lines: { total: 8106, covered: 5925, skipped: 0, pct: 73.09 } },
'src/components/SearchNew': { lines: { total: 800, covered: 714, skipped: 0, pct: 89.25 } },
'src/pages/Jobs/Show': { lines: { total: 555, covered: 413, skipped: 0, pct: 74.41 } },
'src/pages/Home/CandidateHub': { lines: { total: 168, covered: 129, skipped: 0, pct: 76.79 } },
'src/pages/Organizations/Show': { lines: { total: 506, covered: 357, skipped: 0, pct: 70.55 } },
'src/pages/Organizations/CompanyNews': {
lines: { total: 99, covered: 27, skipped: 0, pct: 27.27 },
},
'src/utils/search': { lines: { total: 786, covered: 748, skipped: 0, pct: 95.17 } },
}

describe('groupByPath', () => {
test('should group Jest coverage by threshold path', () => {
expect(groupByPath({ coverage, thresholds: jestThresholds })).toEqual(groupedCoverage)
})
test('should group Vite coverage by threshold path', () => {
expect(groupByPath({ coverage, thresholds: viteThresholds })).toEqual(groupedCoverage)
})
test('should return undefined if error', () => {
expect(groupByPath({ coverage, thresholds: null })).toBeUndefined()
})
})
1 change: 1 addition & 0 deletions lib/scripts/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {
'sort-translations': require('./sort-translations'),
'group-coverage': require('./group-coverage'),
}
2 changes: 1 addition & 1 deletion lib/scripts/sort-keys.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, test } from '@jest/globals'
import { describe, expect, test } from 'vitest'

import { sortKeys } from './sort-keys'

Expand Down
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
],
"scripts": {
"audit:fix": "npm_config_yes=true npx yarn-audit-fix",
"check:audit": "/bin/bash -c 'yarn audit --level critical; [[ $? -ge 16 ]] && exit 1 || exit 0'",
"check:audit": "/bin/bash -c 'yarn audit --level info; [[ $? -ge 1 ]] && exit 1 || exit 0'",
"lint": "eslint lib --max-warnings 0",
"prepare": "husky",
"release": "release-it",
"test": "jest"
"test": "vitest",
"test:coverage": "vitest run --coverage",
"test:coverage:group": "node lib/scripts/group-coverage.js"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -69,11 +71,11 @@
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"babel-jest": "^29.7.0",
"@vitest/coverage-v8": "^1.6.0",
"husky": "^9.0.11",
"jest": "^29.7.0",
"postcss": "^8.4.35",
"release-it": "^17.1.1",
"typescript": "^5.3.3"
"typescript": "^5.3.3",
"vitest": "^1.6.0"
}
}
11 changes: 11 additions & 0 deletions vitest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
globals: true,
coverage: {
include: ['**/lib/scripts/**'],
reporter: ['json-summary', 'text'],
},
},
})
Loading