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

Browser mode: Istanbul coverage report incomplete #3514

Closed
6 tasks done
lsim opened this issue Jun 5, 2023 · 12 comments
Closed
6 tasks done

Browser mode: Istanbul coverage report incomplete #3514

lsim opened this issue Jun 5, 2023 · 12 comments
Labels
feat: browser Issues and PRs related to the browser runner feat: coverage Issues and PRs related to the coverage feature

Comments

@lsim
Copy link

lsim commented Jun 5, 2023

Describe the bug

The following test file causes trouble with vitest in browser mode (chrome). Notice the nested describe calls:

import { describe, it, expect } from 'vitest';
import { foo, bar, baz } from '../src/stuff';

describe('stuff 1', () => {
  it('should baz', () => {
    expect(baz()).toBe('doo');
  });

  describe('stuff 2', () => {
    describe('stuff 3', () => {
      it('should foo', () => {
        expect(foo()).toBe('bar');
      });
    });
    it('should bar', () => {
      expect(bar()).toBe('foo');
    });
  });
});

What seems to happen is that all three tests are run (and pass), but only the should baz and should foo count towards the coverage report. In the repro repo, this means line coverage is reported as 66%.

The issue only occurs when the tests are browser based. With the jsdom environment, things work as expected and the coverage is reported as 100%.

Reproduction

Reproduction repo is here

System Info

System:
    OS: macOS 13.4
    CPU: (10) arm64 Apple M1 Max
    Memory: 1.84 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.16.0 - ~/.nvm/versions/node/v18.16.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.16.0/bin/yarn
    npm: 9.5.1 - ~/.nvm/versions/node/v18.16.0/bin/npm
  Browsers:
    Chrome: 113.0.5672.126
    Firefox: 107.0.1
    Safari: 16.5
  npmPackages:
    @vitest/browser: ^0.31.1 => 0.31.4
    @vitest/coverage-istanbul: ^0.31.4 => 0.31.4
    @vitest/ui: ^0.31.1 => 0.31.4
    vite: ^4.3.2 => 4.3.9
    vitest: ^0.31.1 => 0.31.4

Used Package Manager

yarn

Validations

@elby22
Copy link
Contributor

elby22 commented Jun 15, 2023

@lsim are you using the browser.slowHijackESM config option? I also had this issue, and when I was digging into it I found that the source maps weren't accounting for the transformations done with that option.

@sheremet-va sheremet-va added feat: coverage Issues and PRs related to the coverage feature feat: browser Issues and PRs related to the browser runner labels Jun 15, 2023
@lsim
Copy link
Author

lsim commented Jun 16, 2023

Thanks for your input! It's something to look into.

That option is on by default and I hadn't disabled it. So yes - I am using that. If I'm reading the docs right, I don't need it. Disabling it stops all my browser tests from running though.

No errors, no output.

I'll do a bit more experimenting

@lsim
Copy link
Author

lsim commented Jun 16, 2023

Not sure if it's helpful, but with browser.slowHijackESM disabled, I get the following in the console of the browser used for running the tests:

CleanShot 2023-06-16 at 09 56 21@2x

@lsim
Copy link
Author

lsim commented Jun 16, 2023

Weirdly with browser.headless = false, my tests fail with

TypeError: Cannot redefine property: chrome

Presumably from this line:

vi.stubGlobal('chrome', chrome);

Same tests work when running in headless chrome. This endeavour is a bit like walking blindfolded through a mine field :)

I guess for now I'll settle for the path that gives me working tests and broken coverage metrics.

@elby22
Copy link
Contributor

elby22 commented Jun 16, 2023

I can't get my tests to run without the browser.slowHijackESM option either, so I'm in the same boat.

It seems like the instrumented code that the test is running on is mapping incorrectly to the source code. In packages/coverage-istanbul/src/provider.ts, onFileTransform does seem to be using the getCombinedSourcemap from the rollup plugin, but the mappings provided with the instrumented version of the file is still wrong.

Here is an example

@sheremet-va
Copy link
Member

I can't get my tests to run without the browser.slowHijackESM option either, so I'm in the same boat.

This should be fixed in 0.32.1

It seems like the instrumented code that the test is running on is mapping incorrectly to the source code. In packages/coverage-istanbul/src/provider.ts, onFileTransform does seem to be using the getCombinedSourcemap from the rollup plugin, but the mappings provided with the instrumented version of the file is still wrong.

Maybe we need to provide order: 'post' in the plugin so it runs after all transformations 🤔

@elby22
Copy link
Contributor

elby22 commented Jun 16, 2023

@sheremet-va I added enforce: 'post' to the CoverageTransform plugin function and it did produce a different transformation, just not the correct one.
Here is the output to compare against my previous.

@elby22
Copy link
Contributor

elby22 commented Jun 16, 2023

Also just updated to 0.32.2 with browser.slowHijackESM = false and the tests run. However I'm still getting incorrect coverage.

@elby22
Copy link
Contributor

elby22 commented Jul 20, 2023

@lsim I explored this some more and I believe the issue is not due to coverage-istanbul but how the browser runner differs from the standard runner. Here is a reproduction to demonstrate .

@sheremet-va I'm happy to make a PR if you can point me in the right direction. Do tests for this belong in test/coverage-test/coverage-report-tests?

From the readme:

While debugging, I noticed that takeCoverage() in the coverage-istanbul package gets called multiple times in browser mode, and only once with node. It seems like the browser is calling onAfterRunSuite in packages/browser/src/client/runner.ts 4 times.

Removing globalThis[COVERAGE_STORE_KEY] = {} in packages/coverage-istanbul/src/index.ts: takeCoverage() does produce 100% coverage, but with higher counts for coverage hits.

So far I don't think there is an issue with the coverage-istanbul package, but with how the browser runner is handling suites. Running without any describe() blocks (find commented-out in the test file) gives 100% coverage for both.

@elby22
Copy link
Contributor

elby22 commented Jul 24, 2023

@sheremet-va I've added a PR (#3806). First time contributing to this repo and I left some notes for the reviewer. Thanks.

@AriPerkkio
Copy link
Member

AriPerkkio commented Jul 25, 2023

@elby22 your analysis on the root cause sounds correct. The browser runner is using different hook when compared to NodeJS's worker_thread/child_process runner.

async onAfterRunSuite() {
await super.onAfterRunSuite?.()
const coverage = await coverageModule?.takeCoverage?.()
await rpc().onAfterSuiteRun({ coverage })
}

const originalOnAfterRun = testRunner.onAfterRun
testRunner.onAfterRun = async (files) => {
const coverage = await takeCoverageInsideWorker(config.coverage, executor)
rpc().onAfterSuiteRun({ coverage })
await originalOnAfterRun?.call(testRunner, files)
}

I think the rpc.onAfterSuiteRun is poorly named as it should actually be something like onAfterTestFileRun. But let's not change that at this point.

@AriPerkkio
Copy link
Member

Fixed in #3806.

@github-actions github-actions bot locked and limited conversation to collaborators Aug 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feat: browser Issues and PRs related to the browser runner feat: coverage Issues and PRs related to the coverage feature
Projects
None yet
Development

No branches or pull requests

4 participants