vitest: Coverage All not working in projects (monorepo)

Describe the bug

vitest --coverage

Does not display coverage for all files

vitest.config.ts

import { defineConfig } from "vitest/config";

const isCI = process.env.CI === "true";
export default defineConfig({
  test: {
    watch: false,
    silent: isCI,
    reporters: isCI ? ["verbose", "json", "junit"] : ["default", "hanging-process"],
    passWithNoTests: true,
    outputFile: {
      json: "test-results.json",
      junit: "test-results.xml",
    },
    coverage: {
      all: true,
      clean: true,
      enabled: isCI,
      provider: "v8",
      include: ["**/*.ts"],
      reporter: isCI ? ["lcov", "json-summary", "text-summary"] : "text",
    },
    benchmark: {
      reporters: isCI ? ["verbose", "json"] : "default",
      outputFile: "benchmark-results.json",
    },
    testTimeout: 300,
  },
});

vitest.workspace.ts

export default ["apps/*", "packages/*"];

packages/telemetry/vitest.config.ts

import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

import { defineProject } from "vitest/config";
import pkg from "./package.json" assert { type: "json" };

export default defineProject({
  test: {
    name: pkg.name,
    root: dirname(fileURLToPath(import.meta.url)),
  },
});

working with

import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

import { defineProject } from "vitest/config";
import pkg from "./package.json" assert { type: "json" };

export default defineProject({
  test: {
    name: pkg.name,
    root: dirname(fileURLToPath(import.meta.url)),
    coverage: {all: true}
  },
});

Reproduction

https://stackblitz.com/edit/vitest-dev-vitest-yorpqm?file=packages%2Ftelemetry%2Fvitest.config.ts&view=editor

System Info

sh: envinfo: command not found

    "@vitest/coverage-v8": "^0.34.3",
    "@vitest/runner": "^0.34.3",
    "@vitest/snapshot": "^0.34.4",
    "vite": "^4.4.9",
    "vitest": "^0.34.3"

Used Package Manager

npm

Validations

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Comments: 34 (13 by maintainers)

Most upvoted comments

I don’t think this is coverage related. When you run npm run <command> -w <project>, it will change the current working directory to that path. Vitest doesn’t traverse parent tree up to see if there are other configurations. It’s same as if you did cd packages/telemetry + npm run test.

You can see this by adding process.cwd() logging in packages/telemetry/vitest.config.ts and running the commands:

console.log("cwd", process.cwd())
$ npm run test:coverage -w @demo/telemetry
> cwd /home/projects/vitest-dev-vitest-yorpqm/packages/telemetry

$ npm run test -- --coverage
> cwd /home/projects/vitest-dev-vitest-yorpqm

To run specific projects, you can use --project flag when #4561 is released:

# run a single project
vitest --project e2e
# run several projects
vitest --project e2e --project unit

Now, I may be wrong at that. What I want is to run a vitest project in the context of a workspace run. I am not sure how to do that from the docs. I have tried both the --root and --dir options, but they do not give what I want.

I think you are looking for non-implemented --project option: #4519

Have a single aggregated test output + coverage at the repository/workspace level for CI integration (not working)

This is working. As I said, for aggregated result Vitest expects a single vitest executable at the root repository/workspace level. It knows what projects you have and their configs and can instrument all your projects from a single command. How else would it know what to merge if you are running completely isolated executables for each project?

All you need is "scripts": { "test": "vitest" } in your root workspace package.json, and vitest.workspace.ts file.

Hey folks - just following through this thread when debugging my own coverage issues, and wondering if we could clarify what the expected configuration is for vitest providing coverage in monorepos? I can explain fully my situation and what I’m struggling with - brain dump incoming!

My monorepo (using package-based Nx and yarn workspaces) looks like this:

├── nx.json
├── tsconfig.json
├── vitest.workspace.ts
├── vitest.shared.ts
├── package.json
├── packages
│   ├── is-even
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── vitest.config.ts
│   │   ├── src
│   │   │   │   ├── App.tsx
│   │   │   │   ├── App.test.tsx
│   │   │   │   ├── isEven.ts
│   │   │   │   ├── isEven.test.ts
│   │   ├── test
│   │   │   │   ├── setupTests.ts
.....
│   ├── is-odd
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── vitest.config.ts
│   │   ├── src
│   │   │   │   ├── App.tsx
│   │   │   │   ├── App.test.tsx
│   │   │   │   ├── isOdd.ts
│   │   │   │   ├── isOdd.test.ts
│   │   ├── test
│   │   │   │   ├── setupTests.ts
....

vitest.workspace.ts:

export default ['packages/*'];

vitest.shared.ts:

import {defineConfig} from 'vitest/config';

export default defineConfig({
  test: {
    // The cache setting improves test performance by caching test artifacts.
    // Storing the cache in the node_modules folder keeps it ignored by version control systems and simplifies project management.
    cache: {
      // When this runs it is relative to the projects in `packages`, hence the `../../` to get to node_modules at the root of the repo.
      dir: '../../node_modules/.vitest/assets',
    },
    // The coverage setting enables test coverage reporting, helping you understand how much of your code is covered by tests.
    // This information can be used to identify areas of the code that require additional testing.
    coverage: {
      // Specify the minimum percentage of statement coverage.
      // Statement coverage measures the percentage of code statements that are executed by your tests.
      // A statement is any individual line of code in your JavaScript or TypeScript file.
      statements: 80,

      // Specify the minimum percentage of branch coverage.
      // Branch coverage measures the percentage of code branches that are executed by your tests.
      // A branch is any conditional statement (such as an `if` statement or a `switch` statement)
      // that can result in multiple possible outcomes.
      branches: 70,

      // Specify the minimum percentage of function coverage.
      // Function coverage measures the percentage of code functions that are executed by your tests.
      // A function is a reusable block of code that performs a specific task.
      functions: 70,

      // Specify the minimum percentage of line coverage.
      // Line coverage measures the percentage of code lines that are executed by your tests.
      // A line is any individual line of code in your JavaScript or TypeScript file, including blank lines and comments.
      lines: 80,

      // These reporters are required for displaying code coverage information:
      // text to show the results in the console
      // lcov to produce reports for use in SonarQube
      // html to allow devs to view interactive reports in their browsers with custom test:report commands
      reporter: ['text', 'lcov', 'html'],

      // Generates the coverage reports even if the test run fails - allows us to use test:report to view reports with up to date results.
      reportOnFailure: true,

      // Exclude .stories files from coverage checks
      exclude: ['**/*.stories*'],
    },
    testTimeout: 30000,

    // Sets the print limit for errors in the console to a high value so that we can see all errors from React Testing Library.
    globalSetup: '../../setDebugPrintLimit.ts',

    // Sets the test reporter format to 'verbose', which gives detailed information about each test run.
    // Other options include 'default' and 'tap', depending on your preferences and requirements.
    reporters: ['verbose'],

    // We don't want vitest to fail if it encounters a package that has no tests
    passWithNoTests: true,
  },
});

packages/is-even/vitest.config.ts:

import {defineProject, mergeConfig} from 'vitest/config';

import baseTestConfig from '../../vitest.shared';

const config = defineProject({
  test: {
    // The environment setting determines the environment to run tests in.
    // 'happy-dom' is chosen for its better performance and lightweight DOM emulation compared to other environments like 'jsdom'.
    environment: 'happy-dom',

    // The globals setting enables global variables in tests.
    // This is useful when using testing libraries that rely on global variables, such as Jest's 'expect' function.
    globals: true,

    // Will call .mockClear() on all spies before each test.
    // .mockClear() will clear all recorded calls to a mocked function or object, but does not reset any other behavior or implementation details.
    clearMocks: true,

    // The include setting ensures that only test files are executed, avoiding accidental execution of non-test code.
    include: ['**/*.test.[t]s?(x)'],

    // The setupFiles setting allows you to execute specific files before running tests.
    // This is useful for setting up global configurations, mocks, or utilities required by your test suite.
    setupFiles: ['./test/setupTests.ts'],
  },
});

export default mergeConfig(baseTestConfig, config);

What I have been doing to generate lcov.info files for use in SonarQube code coverage is to run a vitest command in each package, with a command like this from the root package.json:

    "testc": "nx run-many --target=testc"

packages/is-even/package.json:

    "test": "vitest --run",
    "test:report": "nx run testc; open ./coverage/index.html",
    "testc": "vitest --run --coverage"

sonar-project.properties:

...
# Reports
sonar.javascript.lcov.reportPaths=packages/**/coverage/lcov.info

When I run yarn testc from the root, a folder like packages/is-even/coverage is generated in each package:

├── nx.json
├── tsconfig.json
├── vitest.workspace.ts
├── vitest.shared.ts
├── package.json
├── packages
│   ├── is-even
│   │   ├── coverage
│   │   │   │   ├── lcov-report
│   │   │   │    │   ├── src
│   │   │   │    │   ├── index.html
...
│   │   │   │   ├── src
│   │   │   │   ├── index.html
│   │   │   │   ├── lcov.info
...
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── vitest.config.ts
│   │   ├── src
│   │   │   │   ├── App.tsx
│   │   │   │   ├── App.test.tsx
│   │   │   │   ├── isEven.ts
│   │   │   │   ├── isEven.test.ts
│   │   ├── test
│   │   │   │   ├── setupTests.ts
...

The trouble I have now though is for the App.tsx file, which has the same name and directory structure in both packages, SonarQube coverage isn’t reported correctly on one of those files (I think because the name conflicts happen when the lcov.info reports are loaded by SonarQube)

packages/is-even/coverage/lcov.info

....
TN:
SF:src/App.tsx
FN:11,App
FNF:1
FNH:1
FNDA:3,App
....

So I need a way to either:

  • Combine the reports into one lcov.info (not yet supported by vitest according to the answer on https://github.com/vitest-dev/vitest/discussions/4227 - is there another way folks have found to do this?)
  • Generate just one report for the whole repo (which seems to be supported looking at the comments on this thread, but just not sure exactly how)

When I tried adding a "test": "vitest --coverage" command in the root package.json of the monorepo (like what I gather should be done from the comments), I got a lot of runtime errors in the tests like ReferenceError: window is not defined and those sorts of things, and I wasn’t able to fix all those errors to get the command working.

Apologies for the essay - any feedback is very welcome! Thank you!