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

Coverage All not working in projects (monorepo) #4181

Closed
6 tasks done
polRk opened this issue Sep 26, 2023 · 34 comments
Closed
6 tasks done

Coverage All not working in projects (monorepo) #4181

polRk opened this issue Sep 26, 2023 · 34 comments

Comments

@polRk
Copy link

polRk commented Sep 26, 2023

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

@stackblitz
Copy link

stackblitz bot commented Sep 26, 2023

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

@polRk
Copy link
Author

polRk commented Sep 26, 2023

Run test in workspce:

npm run test:coverage -w @demo/telemetry

@AriPerkkio
Copy link
Member

  • Does -w @demo/telemetry run the vitest command in root of the workspace or in root of the project?
  • Does it work when you run pnpm --filter @demo/monorepo test:coverage?
  • Is this just coverage.all related, or do any workspace level setting work in the project when ran with -w flag?

@polRk
Copy link
Author

polRk commented Sep 27, 2023

  • Does -w @demo/telemetry run the vitest command in root of the workspace or in root of the project?
  • Does it work when you run pnpm --filter @demo/monorepo test:coverage?
  • Is this just coverage.all related, or do any workspace level setting work in the project when ran with -w flag?
  1. Launch in the root of the project and at the same time in the root of the monorepository.
  2. I do not use pnpm
  3. If I run the script in the package root (packages/telemetry) the error remains

@AriPerkkio
Copy link
Member

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

@sheremet-va
Copy link
Member

I don't understand the problem. Does coverage work if you add { coverage: { all: true } } to the project? Do you run a single project and expect coverage for the whole workspace?

The coverage option is never inherited: https://vitest.dev/guide/workspace.html#configuration

When you run vitest from the monorepo root, Vitest will use coverage option from the root config. When you run vitest inside a workspace project, it will use coverage from the project's config.

@polRk
Copy link
Author

polRk commented Sep 28, 2023

I don't understand the problem. Does coverage work if you add { coverage: { all: true } } to the project? Do you run a single project and expect coverage for the whole workspace?

The problem is that when running coverage all in a package, all files in that package are not taken into coverage reports. Only if i define { coverage: { all: true } } in defineProject it working

@polRk
Copy link
Author

polRk commented Sep 28, 2023

I don't understand why no one has added a label bug yet

@polRk
Copy link
Author

polRk commented Sep 28, 2023

When you run vitest inside a workspace project, it will use coverage from the project's config.

No it doesn't. Only shows coverage for affected files, not all files in workspace project

@sheremet-va
Copy link
Member

The problem is that when running coverage all in a package, all files in that package are not taken into coverage reports. Only if i define { coverage: { all: true } } in defineProject it working

This is how it works, yes. What seems to be the problem?

No it doesn't. Only shows coverage for affected files, not all files in workspace project

It shows what you asked it to show in the config. AGAIN, if you run vitest from the root of the project, it takes the coverage options (where you specify all: true) from the root config. If you run vitest from the project (packages/telemetry), it takes the coverage options from the project config file (where you didn't specify all: true).

@polRk
Copy link
Author

polRk commented Sep 28, 2023

What seems to be the problem?

Yes, because the documentation and typescript types say that I cannot override coverage in defineProject

@polRk
Copy link
Author

polRk commented Sep 28, 2023

It shows what you asked it to show in the config. AGAIN, if you run vitest from the root of the project, it takes the coverage options (where you specify all: true) from the root config. If you run vitest from the project (packages/telemetry), it takes the coverage options from the project config file (where you didn't specify all: true).

AGAIN, i want to see coverage for all files in packages/telemetry. And i can add coverate option in defineProject but typescript shows an error. And i don't know why and why it works.

@polRk
Copy link
Author

polRk commented Sep 28, 2023

What I expect from vitest is the ability to show coverage all files inside the mono repository package.

I thought it was enough to specify coverage all in the root of the monorepository, but it doesn't work that way.

I cannot specify coverage: {all: true} inside defineProject, according to the documentation and typescript, but if I do that, everything starts working.

@sheremet-va
Copy link
Member

AGAIN, i wan't to see coverage for all files in packages/telemetry. And i can add coverate option in defineProject but typescript show an error. And i don't know why and why it works.

defineProject assumes this config file will be used as a workspace project config - when you run vitest command from the root of the project - in this case coverage options are ignored, but you are running tests inside the packages/telemetry folder, so this config takes the role of a workspace config file, not project config.

You can even add another vitest.workspace file close to it, and have nested workspace projects there.

In short, you are meant to run vitest command from the root of the workspace, not inside the project folder.

@sheremet-va
Copy link
Member

What I expect from vitest is the ability to show coverage all files inside the mono repository package.

You are running separate Vitest command for each folder, and you don't use the Vitest workspace feature by doing so, so it cannot work even in theory - each Vitest instance doesn't know about another one.

@polRk
Copy link
Author

polRk commented Sep 28, 2023

In short, you are meant to run vitest command from the root of the workspace, not inside the project folder.

I just need the opposite and with coverage: {all: true} support

@sheremet-va
Copy link
Member

I just need the opposite and with coverage: {all: true} support

Then use defineConfig helper instead of defineProject.

@polRk
Copy link
Author

polRk commented Sep 28, 2023

each Vitest instance doesn't know about another one.

But it seems to me that there should be an inheritance mechanism from the root config

@sheremet-va
Copy link
Member

But it seems to me that there should be an inheritance mechanism from the root config

No, there shouldn't be. With this logic, Vitest will have to traverse all folder to the root of the file system and merge them - which makes zero sense.

@polRk
Copy link
Author

polRk commented Sep 28, 2023

Then use defineConfig helper instead of defineProject.

And then how will vitest workspaces work ?

In the monorepository root, I want to run all the tests (in CI) and with full coverage. But when I'm developing, I want to run tests in a specific directory (package/project) with coverage of all the files for this project.

@sheremet-va
Copy link
Member

But it seems to me that there should be an inheritance mechanism from the root config

When you run "vitest" in a folder, there is only one config - the one in the folder, unless you manually merge it there.

In the monorepository root, I want to run all the tests (in CI) and with full coverage. But when I'm developing, I want to run tests in a specific directory (package/project) with coverage of all the support for this project.

You run vitest from the root of the workspace to filter test files:

vitest packages/telemetry

@polRk
Copy link
Author

polRk commented Sep 28, 2023

You run vitest from the root of the workspace to filter test files:

I would like to add a command to the package.json of the package/project, but what will it look like then?

@polRk
Copy link
Author

polRk commented Sep 28, 2023

You run vitest from the root of the workspace to filter test files:

And there will be support for the coverage: {all:true} option from the root config?

@polRk
Copy link
Author

polRk commented Sep 28, 2023

@sheremet-va It seems that, thanks to your comments, I realized that workspaces work when running from the root of the project. It's great that I understood this, but how can I selectively run tests in a specific package now? Preferably using the package.json of this package?

@polRk polRk closed this as completed Sep 30, 2023
@polRk
Copy link
Author

polRk commented Sep 30, 2023

You run vitest from the root of the workspace to filter test files:

Is it a bug?

 ✓ |@webui/graphql| src/resolvers/groupResolver.test.ts (3)
 · |apps| graphql/src/resolvers/groupResolver.test.ts (3)
 ✓ |@webui/graphql| src/resolvers/areaResolver.test.ts (3)
 · |apps| graphql/src/resolvers/areaResolver.test.ts (3)
 ✓ |@webui/graphql| src/resolvers/groupResolver.test.ts (3)
 ✓ |apps| graphql/src/resolvers/groupResolver.test.ts (3)
 ✓ |@webui/graphql| src/resolvers/areaResolver.test.ts (3)
 ✓ |apps| graphql/src/resolvers/areaResolver.test.ts (3)
 ✓ |@webui/graphql| src/resolvers/userResolver.test.ts (5)
 ✓ |apps| graphql/src/resolvers/userResolver.test.ts (5)
 ✓ |apps| graphql/src/resolvers/standResolver.test.ts (3)
 ✓ |@webui/graphql| src/resolvers/standResolver.test.ts (3)
 ✓ |apps| graphql/src/modules/compute/vmiMapper.test.ts (1)
 ✓ |@webui/graphql| src/modules/compute/vmiMapper.test.ts (1)

@polRk polRk reopened this Sep 30, 2023
@sheremet-va
Copy link
Member

Is it a bug?

What am I looking at?

@polRk
Copy link
Author

polRk commented Oct 2, 2023

Is it a bug?

What am I looking at?

Double test execution

@polRk
Copy link
Author

polRk commented Oct 3, 2023

@sheremet-va Another error. Coverage not working.

 Test Files  28 passed | 1 skipped (29)
      Tests  266 passed | 1 skipped (267)
   Start at  19:25:58
   Duration  8.90s (transform 951ms, setup 4ms, collect 34.47s, tests 7.10s, environment 8ms, prepare 4.88s)

JSON report written to /home/polyakov/test-results.json
JUNIT report written to /home/polyakov/test-results.xml
 % Coverage report from v8

=============================== Coverage summary ===============================
Statements   : Unknown% ( 0/0 )
Branches     : Unknown% ( 0/0 )
Functions    : Unknown% ( 0/0 )
Lines        : Unknown% ( 0/0 )
================================================================================

@ahayes91
Copy link

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 Merging reports and coverage when sharding? #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!

@sheremet-va
Copy link
Member

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.

This should be the way to run coverage for the whole monorepo, yes. I don't really know why it doesn't work from what you described - I would expect it to work. It either doesn't use happy-dom as an environment for your package, or you import something that relies on DOM in another package.

@connorjs
Copy link

connorjs commented Nov 19, 2023

I’m unsure if this is the right issue, but I’ll comment here first. @sheremet-va - let me know if I should create a new issue dedicated to "Vitest Workspace + Nx monorepo."

I think I am running into similar issues to @ahayes91 (I can create a minimal repro repo if desired). TL;DR - Nx wants N runs of vitest, but Vitest workspaces wants 1 run (I think given its workspace docs).


More detail, assuming only bare minimum Nx knowledge. Any additional info is meant to inform, zero condescending tone from me here!

Nx delivers great DX wins for monorepos by (essentially) caching each project. Nx workspace/repo == vitest workspace; Nx project == vitest project.

Nx (and I) want to run tests for projects one at a time. This way a subsequent "full test run" (nx run-many -t test) will have a high cache hit rate for the core packages I haven’t touched.

However, Vitest’s workspace docs suggest (imply?) that there should be a single vitest execution for the entire monorepo. This should happen from the root.

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.

What (I think) I want is

  1. Execute vitest once per project (works)
  2. Have per-project test output (works)
  3. Have a single aggregated test output + coverage at the repository/workspace level for CI integration (not working)

Are these 3 things possible together in Vitest today?

I’m finally getting back into Vitest world and super happy to see some formal workspace support following #256 (I had one comment there before).

Thanks so much in advance and love seeing the 1.0 betas; that's what I've upgraded to.

@sheremet-va
Copy link
Member

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.

@connorjs
Copy link

connorjs commented Nov 19, 2023

I think #4519 represents what I envision. I will follow that.

How else would it know what to merge if you are running completely isolated executables for each project?

I will share thoughts on this in the other issue. TL;DR - workspace-root relative paths to merge.

Thank you so much!

Edit: I started a discussion to gather more thoughts for now: #4544

@sheremet-va
Copy link
Member

sheremet-va commented Nov 24, 2023

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

@github-actions github-actions bot locked and limited conversation to collaborators Dec 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants