From 1c980d503aeae7e3707c33fbcb8a809bea684d7e Mon Sep 17 00:00:00 2001 From: Xinyuan Lin Date: Tue, 11 Aug 2026 21:31:04 -0700 Subject: [PATCH] fix(frontend): report coverage from the browser-mode tests The browser-mode suite runs in CI and uploads its JUnit results, but never its coverage, so every line it exercises is reported as untested. For code-editor.component.ts that is 67 of the 115 lines Codecov calls uncovered - measured by intersecting the two lcov records - and merging them takes the file from 49.8% to 79.0% without writing a test. Passing --coverage alone is not enough: the run fails with "Failed to fetch dynamically imported module: /@id/@vitest/coverage-v8/browser", because the coverage provider is loaded dynamically and is not in the import graph that Vite's scan crawls. The tests still pass while the coverage step errors, which is why this went unnoticed. Adding it to optimizeDeps.include alongside buffer fixes it. Browser coverage is written to coverage-browser/ so it does not overwrite the jsdom run's coverage/gui/lcov.info, and both are uploaded under the frontend flag, which Codecov merges. --- .github/workflows/build.yml | 14 +++++++++++++- frontend/.gitignore | 1 + frontend/vitest.browser.config.ts | 14 +++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c28457b1986..143717b3ea5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -161,7 +161,19 @@ jobs: - name: Install Playwright Chromium run: yarn --cwd frontend playwright install ${{ matrix.os == 'ubuntu-latest' && '--with-deps' || '' }} chromium - name: Run frontend browser-mode tests - run: yarn --cwd frontend ng run gui:test-browser + run: yarn --cwd frontend ng run gui:test-browser --coverage --coverage-reporters=lcovonly + - name: Upload frontend browser-mode coverage to Codecov + # These tests already exercise code the jsdom run cannot reach (Monaco, real + # pointer input); without this upload those lines are reported as uncovered. + # vitest.browser.config.ts writes to coverage-browser/ so this does not collide + # with the jsdom run's coverage/gui/lcov.info. + if: matrix.os == 'ubuntu-latest' && always() + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./frontend/coverage-browser/**/lcov.info + flags: frontend + fail_ci_if_error: false - name: Upload frontend browser-mode test results to Codecov # vitest.browser.config.ts emits junit-browser.xml (distinct from # the unit-test report). Same `frontend` flag — Codecov merges diff --git a/frontend/.gitignore b/frontend/.gitignore index c3db340e81d..93858f17d66 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -10,6 +10,7 @@ # test coverage /coverage +/coverage-browser # vitest browser-mode snapshot baselines **/__screenshots__/ diff --git a/frontend/vitest.browser.config.ts b/frontend/vitest.browser.config.ts index 3fe2c410390..4baa234ded0 100644 --- a/frontend/vitest.browser.config.ts +++ b/frontend/vitest.browser.config.ts @@ -47,8 +47,14 @@ export default defineConfig({ // `require()` calls inside the CJS package crash on first import. // Explicit-include forces esbuild to pre-bundle it (alongside its // `base64-js` + `ieee754` transitive deps) into a browser-runnable ESM. + // + // `@vitest/coverage-v8/browser` is loaded dynamically by the coverage provider once + // `--coverage` is passed, and it is not in the import graph either, so it needs the same + // hint. Without it the run fails with "Failed to fetch dynamically imported module: + // /@id/@vitest/coverage-v8/browser" and no coverage is produced -- the tests still pass, + // which is why the gap went unnoticed. optimizeDeps: { - include: ["buffer"], + include: ["buffer", "@vitest/coverage-v8/browser"], }, test: { // Emit a JUnit-XML report alongside the default console reporter so @@ -56,6 +62,12 @@ export default defineConfig({ // flakies on main. Written to a distinct filename so the upload step // can disambiguate it from the unit-test report. reporters: ["default", ["junit", { outputFile: "junit-browser.xml" }]], + // Written to its own directory so it does not overwrite the jsdom run's + // coverage/gui/lcov.info. Codecov merges multiple uploads under one flag, so both + // records count and the lines these tests already exercise stop reading as uncovered. + coverage: { + reportsDirectory: "coverage-browser", + }, globals: true, // browser-buffer-polyfill must run FIRST: it puts Buffer/process on // globalThis before any test module loads, which is required for