Switch to using diff-cover for the Coverage gate (#33673)

* Switch to using diff-cover for the Coverage gate

Enabling developers to check their PR coverage without waiting for CI

* Remove sonar-project.properties - in automatic mode Sonar will not read this file

* Add note to CONTRIBUTING.md

* Skip sonar in MQ
This commit is contained in:
Michael Telatynski
2026-06-01 13:26:03 +01:00
committed by GitHub
parent dd2ae6e36c
commit 35484e5183
16 changed files with 83 additions and 194 deletions
-27
View File
@@ -1,27 +0,0 @@
name: SonarQube
on:
# Privilege escalation necessary to call upon SonarCloud
# 🚨 We must not execute any checked out code here.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["Tests"]
types:
- completed
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
permissions: {}
jobs:
sonarqube:
name: 🩻 SonarQube
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'merge_group'
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop # zizmor: ignore[unpinned-uses]
permissions:
actions: read
statuses: write
id-token: write # sonar
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
with:
sharded: true
version-pkg-json-dir: ./apps/web
+58 -24
View File
@@ -80,8 +80,6 @@ jobs:
--shard "$SHARD" \
--cacheDirectory /tmp/jest_cache
env:
JEST_SONAR_UNIQUE_OUTPUT_NAME: true
# tell jest to use coloured output
FORCE_COLOR: true
MAX_WORKERS: ${{ steps.cpu-cores.outputs.count }}
@@ -103,28 +101,6 @@ jobs:
apps/web/coverage
!apps/web/coverage/lcov-report
complete:
name: jest-tests
needs: [jest_ew, vitest]
if: always()
runs-on: ubuntu-24.04
permissions:
statuses: write
steps:
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
- name: Skip SonarCloud in merge queue
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
uses: guibranco/github-status-action-v2@9bfa8773cdbdc6c185747fd43cd7faa9d7c32f09
with:
authToken: ${{ secrets.GITHUB_TOKEN }}
state: success
description: SonarCloud skipped
context: SonarCloud Code Analysis
sha: ${{ github.sha }}
target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
vitest:
name: Vitest
strategy:
@@ -191,3 +167,61 @@ jobs:
path: |
${{ matrix.path }}/coverage
!${{ matrix.path }}/coverage/lcov-report
complete:
name: Tests
needs:
- jest_ew
- vitest
if: always()
runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: read
checks: write
statuses: write
steps:
- name: Skip SonarCloud in merge queue
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
uses: guibranco/github-status-action-v2@9bfa8773cdbdc6c185747fd43cd7faa9d7c32f09
with:
authToken: ${{ secrets.GITHUB_TOKEN }}
state: success
description: SonarCloud skipped
context: SonarCloud Code Analysis
sha: ${{ github.sha }}
target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
if: needs.test.result == 'success' && env.ENABLE_COVERAGE == 'true'
with:
persist-credentials: false
fetch-depth: 0 # Full history, fastest for diff-cover
- name: Download coverage artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
if: needs.test.result == 'success' && env.ENABLE_COVERAGE == 'true'
with:
pattern: coverage-*
path: coverage
- name: Diff Coverage
id: coverage
if: needs.test.result == 'success' && env.ENABLE_COVERAGE == 'true'
uses: Affanmir/diff-cover-action@0d8c98f613bbd2428df50b3109b1e3b1d5ab59d3 # v2.1.0
with:
compare-branch: origin/${{ github.base_ref || 'develop' }}
mode: coverage
coverage-files: coverage/*/*lcov.info
ignore-whitespace: true
show-uncovered: true
post-comment: false
create-annotations: true
annotation-type: warning
fail-on-threshold: ${{ contains(github.event.pull_request.labels.*.name, 'Z-Skip-Coverage') && 'false' || 'true' }}
fail-under: 80
- name: Check status of tests
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
+4
View File
@@ -140,6 +140,10 @@ These are located in `/spec/` in `matrix-js-sdk` or `/test/` in `element-web`.
When writing unit tests, please aim for a high level of test coverage
for new code - 80% or greater. If you cannot achieve that, please document
why it's not possible in your PR.
CI will validate that the coverage reached on your change is sufficient,
you can also assert this locally by installing https://github.com/Bachmann1234/diff_cover,
running the entire test suite in coverage mode `pnpm coverage` then running
`pnpm coverage:diff` to see the coverage of the diff between your HEAD and `develop`.
Some sections of code are not sensible to add coverage for, such as those
which explicitly inhibit noisy logging for tests. Which can be hidden using
+3 -2
View File
@@ -54,6 +54,8 @@
"test:playwright": "nx test:playwright --",
"test:playwright:open": "nx test:playwright -- --ui",
"test:playwright:screenshots": "nx test:playwright:screenshots --",
"coverage": "pnpm test:unit --coverage",
"coverage:diff": "diff-cover --config-file ../../diff-cover.toml coverage/lcov.info",
"sane-postinstall": "electron-builder install-app-deps"
},
"dependencies": {
@@ -106,8 +108,7 @@
"rimraf": "^6.0.0",
"tar": "^7.5.8",
"typescript": "6.0.3",
"vitest": "catalog:",
"vitest-sonar-reporter": "catalog:"
"vitest": "catalog:"
},
"hakDependencies": {
"matrix-seshat": "4.3.0"
-1
View File
@@ -68,7 +68,6 @@ if (env["GITHUB_ACTIONS"] !== undefined) {
config.reporters ??= [];
config.reporters.push(["github-actions", { silent: false }]);
config.reporters.push("summary");
config.reporters.push("@casualbot/jest-sonar-reporter");
// if we're running against the develop branch, also enable the slow test reporter
if (env["GITHUB_REF"] == "refs/heads/develop") {
+1 -6
View File
@@ -34,6 +34,7 @@
"test:playwright:open": "nx test:playwright -- --ui",
"test:playwright:screenshots": "nx test:playwright:screenshots --",
"coverage": "pnpm test --coverage",
"coverage:diff": "diff-cover --config-file ../../diff-cover.toml coverage/lcov.info",
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp"
},
"dependencies": {
@@ -125,7 +126,6 @@
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.12.7",
"@casualbot/jest-sonar-reporter": "2.7.1",
"@element-hq/element-call-embedded": "0.19.4",
"@element-hq/element-web-playwright-common": "workspace:*",
"@fetch-mock/jest": "^0.2.20",
@@ -236,11 +236,6 @@
"webpack-version-file-plugin": "^0.5.0",
"yaml": "^2.3.3"
},
"@casualbot/jest-sonar-reporter": {
"outputDirectory": "coverage",
"outputName": "jest-sonar-report.xml",
"relativePaths": true
},
"engines": {
"node": ">=22.18"
},
+2
View File
@@ -0,0 +1,2 @@
[tool.diff_cover]
compare_branch = "origin/develop"
+5 -1
View File
@@ -1,6 +1,6 @@
import { KnipConfig } from "knip";
// Specify this as knip loads config files which may conditionally add reporters, e.g. `@casualbot/jest-sonar-reporter'
// Specify this as knip loads config files which may conditionally load plugins
process.env.GITHUB_ACTIONS = "1";
export default {
@@ -67,6 +67,10 @@ export default {
"events",
],
ignoreExportsUsedInFile: true,
ignoreBinaries: [
// Optional for coverage:diff development script
"diff-cover",
],
compilers: {
pcss: (text: string) =>
[...text.matchAll(/@import\s+(?:url\()?["']([^"']+)["']\)?[^;]*;/g)]
+2 -1
View File
@@ -22,7 +22,8 @@
"postinstall": "node scripts/pnpm-link.ts && pnpm run -r sane-postinstall",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
"docs:preview": "vitepress preview docs",
"coverage:diff": "diff-cover --config-file diff-cover.toml apps/*/coverage/lcov.info packages/*/coverage/lcov.info"
},
"devDependencies": {
"@action-validator/cli": "^0.6.0",
+4 -3
View File
@@ -27,7 +27,9 @@
"scripts": {
"prepack": "nx build",
"lint:types": "nx lint:types",
"test:unit": "vitest"
"test:unit": "vitest",
"coverage": "pnpm test:unit --coverage",
"coverage:diff": "diff-cover --config-file ../../diff-cover.toml coverage/lcov.info"
},
"devDependencies": {
"@element-hq/vite-common": "workspace:*",
@@ -44,8 +46,7 @@
"typescript": "^6.0.0",
"unplugin-dts": "1.0.1",
"vite": "catalog:",
"vitest": "catalog:",
"vitest-sonar-reporter": "catalog:"
"vitest": "catalog:"
},
"peerDependencies": {
"@matrix-org/react-sdk-module-api": "*",
+3 -2
View File
@@ -51,6 +51,8 @@
"test:unit": "nx test:unit",
"test:storybook": "nx test:storybook",
"test:storybook:update": "nx test:storybook:update",
"coverage": "pnpm test:unit --coverage",
"coverage:diff": "diff-cover --config-file ../../diff-cover.toml coverage/lcov.info",
"build": "nx build",
"prepack": "pnpm run build",
"storybook": "storybook dev -p 6007",
@@ -132,8 +134,7 @@
"unplugin-dts": "1.0.1",
"vite": "catalog:",
"vite-plugin-node-polyfills": "^0.28.0",
"vitest": "catalog:",
"vitest-sonar-reporter": "catalog:"
"vitest": "catalog:"
},
"engines": {
"node": ">=20.0.0"
+1 -2
View File
@@ -13,7 +13,6 @@
"typescript": "catalog:"
},
"peerDependencies": {
"@vitest/coverage-v8": "catalog:",
"vitest-sonar-reporter": "catalog:"
"@vitest/coverage-v8": "catalog:"
}
}
-7
View File
@@ -31,13 +31,6 @@ const slowTestReporter: Reporter = {
// if we're running under GHA, enable the GHA & Sonar reporters
if (env["GITHUB_ACTIONS"] !== undefined) {
reporters.push(["github-actions", { silent: false }]);
reporters.push([
"vitest-sonar-reporter",
{
outputFile: "coverage/sonar-report.xml",
onWritePath: (path): string => `${process.cwd()}/${path}`,
},
]);
// if we're running against the develop branch, also enable the slow test reporter
if (env["GITHUB_REF"] == "refs/heads/develop") {
-49
View File
@@ -241,9 +241,6 @@ catalogs:
vitest:
specifier: 4.1.7
version: 4.1.7
vitest-sonar-reporter:
specifier: 3.0.0
version: 3.0.0
overrides:
pretty-format@30>react-is: 19.2.6
@@ -496,9 +493,6 @@ importers:
vitest:
specifier: 'catalog:'
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.13(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))
vitest-sonar-reporter:
specifier: 'catalog:'
version: 3.0.0(vitest@4.1.7)
apps/web:
dependencies:
@@ -761,9 +755,6 @@ importers:
'@babel/preset-typescript':
specifier: ^7.12.7
version: 7.28.5(@babel/core@7.29.0)
'@casualbot/jest-sonar-reporter':
specifier: 2.7.1
version: 2.7.1
'@element-hq/element-call-embedded':
specifier: 0.19.4
version: 0.19.4
@@ -1146,9 +1137,6 @@ importers:
vitest:
specifier: 'catalog:'
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.13(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))
vitest-sonar-reporter:
specifier: 'catalog:'
version: 3.0.0(vitest@4.1.7)
packages/playwright-common:
dependencies:
@@ -1412,9 +1400,6 @@ importers:
vitest:
specifier: 'catalog:'
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.13(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))
vitest-sonar-reporter:
specifier: 'catalog:'
version: 3.0.0(vitest@4.1.7)
packages/vite-common:
dependencies:
@@ -1424,9 +1409,6 @@ importers:
vitest:
specifier: 'catalog:'
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.13(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))
vitest-sonar-reporter:
specifier: 'catalog:'
version: 3.0.0(vitest@4.1.7)
devDependencies:
typescript:
specifier: 'catalog:'
@@ -2254,10 +2236,6 @@ packages:
'@cacheable/utils@2.4.1':
resolution: {integrity: sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==}
'@casualbot/jest-sonar-reporter@2.7.1':
resolution: {integrity: sha512-C/lzwYEXnHueUufmvSLRKSz8ANVCrFv7HjtGsLoPDUYwugn6CYB1E7gEiCUlWaDeq+DUib2xMUX0nMYpwxUYzA==}
engines: {node: '>=14.0.0'}
'@chevrotain/types@11.1.2':
resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==}
@@ -10544,11 +10522,6 @@ packages:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
mkdirp@3.0.1:
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
engines: {node: '>=10'}
@@ -13447,12 +13420,6 @@ packages:
'@vitest/browser-webdriverio':
optional: true
vitest-sonar-reporter@3.0.0:
resolution: {integrity: sha512-QRT5m9Z/3Kt0WVDVcFHm5LC9Ba89yDP4twlX2QUAJ9PdqfJBkLAh/kXj7m6U3i0k6GxnIEiwCc295bmAYokmZg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
peerDependencies:
vitest: '>=3'
vitest@4.1.7:
resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
@@ -13763,9 +13730,6 @@ packages:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
xml@1.0.1:
resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
xmlbuilder@15.1.1:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'}
@@ -14916,11 +14880,6 @@ snapshots:
hashery: 1.5.1
keyv: 5.6.0
'@casualbot/jest-sonar-reporter@2.7.1':
dependencies:
mkdirp: 1.0.4
xml: 1.0.1
'@chevrotain/types@11.1.2': {}
'@csstools/cascade-layer-name-parser@3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
@@ -24270,8 +24229,6 @@ snapshots:
dependencies:
minimist: 1.2.8
mkdirp@1.0.4: {}
mkdirp@3.0.1: {}
mlly@1.8.2:
@@ -27835,10 +27792,6 @@ snapshots:
- babel-plugin-macros
- typescript
vitest-sonar-reporter@3.0.0(vitest@4.1.7):
dependencies:
vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.13(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))
vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.13(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)):
dependencies:
'@vitest/expect': 4.1.7
@@ -28275,8 +28228,6 @@ snapshots:
xml-name-validator@5.0.0: {}
xml@1.0.1: {}
xmlbuilder@15.1.1: {}
xmlchars@2.2.0: {}
-1
View File
@@ -26,7 +26,6 @@ catalog:
# vite
vite: 8.0.13
vitest: 4.1.7
vitest-sonar-reporter: 3.0.0
"@vitest/coverage-v8": 4.1.7
packageExtensions:
-68
View File
@@ -1,68 +0,0 @@
sonar.projectKey=element-web
sonar.organization=element-hq
# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8
sonar.sources=.
sonar.tests=apps/web/test,apps/web/playwright,apps/desktop,packages
sonar.test.inclusions=\
apps/web/test/*,\
apps/web/playwright/*,\
apps/desktop/playwright/*,\
apps/desktop/src/**/*.test.ts,\
packages/*/src/**/*.test.*,\
packages/*/src/test/**/*
sonar.exclusions=\
apps/web/__mocks__,\
docs,\
apps/web/element.io,\
apps/web/nginx,\
apps/web/src/vector/modernizr.cjs,\
**/*.webm,\
**/*.ogg,\
**/*.mp3,\
**/*.woff2,\
**/*.ttf,\
**/*.webp,\
**/*.jpg,\
**/*.apng,\
**/*.ico,\
**/*.png,\
**/*.gif
# Exclude translations and tests from the duplication check
sonar.cpd.exclusions=\
**/src/i18n/strings/*.json,\
apps/**/test/**,\
apps/**/playwright/**,\
packages/**/test/**,\
packages/shared-components/src/**/*.stories.tsx,\
packages/playwright-common/**
sonar.javascript.lcov.reportPaths=apps/web/coverage/lcov.info,packages/shared-components/coverage/lcov.info
sonar.coverage.exclusions=\
apps/web/test/**/*,\
apps/web/playwright/**/*,\
apps/web/res/**/*,\
apps/web/scripts/**/*,\
apps/web/__mocks__/**/*,\
apps/web/I18nWebpackPlugin.ts,\
apps/web/recorder-worklet-loader.cjs,\
apps/web/src/components/views/dialogs/devtools/**/*,\
apps/web/src/utils/SessionLock.ts,\
apps/web/src/**/*.d.ts,\
apps/web/src/vector/mobile_guide/**/*,\
apps/desktop/electron-builder.ts,\
apps/desktop/scripts/**/*,\
apps/desktop/playwright/**/*,\
apps/desktop/hak/**/*,\
packages/shared-components/src/test/**/*,\
packages/shared-components/src/**/*.stories.tsx,\
packages/shared-components/scripts/**/*,\
packages/playwright-common/**/*,\
scripts/**/*,\
docs/**/*,\
**/*.config.*,\
knip.ts
sonar.testExecutionReportPaths=apps/web/coverage/jest-sonar-report.xml