cenfun / monocart-coverage-reports

A code coverage tool to generate native V8 reports or Istanbul reports.
MIT License
31 stars 5 forks source link

[Bug] Branch coverage incorrect in async static class methods #67

Closed acodeninja closed 1 month ago

acodeninja commented 1 month ago

Describe the bug

When using monocart coverage reports, branches inside static async class methods are shown as else uncovered when using monocart

To Reproduce

Expected behavior

In both instances the coverage should be 100% as all lines and branches are in fact covered.

Errors or Screenshots

Any captured error messages, or add screenshots to help explain your problem.

Make a minimal reproduction

package.json

{
  "type": "module",
  "dependencies": {
    "c8": "^10.1.2",
    "monocart-coverage-reports": "^2.10.1"
  }
}

Uncovered.js

export class Uncovered {
    static async uncovered(active) {
        if (active) throw new Error('Active');

        return !active;
    }
}

Uncovered.test.js

import * as assert from 'node:assert';
import {Uncovered} from './Uncovered.js';

assert.equal(await Uncovered.uncovered(false), true);

await assert.rejects(async () => Uncovered.uncovered(true), {
    name: 'Error',
});

Run the following:

npm install
c8 node Uncovered.test.js  # see the 100% coverage output
c8 --experimental-monocart node Uncovered.test.js  # see the 50% branch coverage output

Additional context

While this may be down to some strange interactions with c8 I was unable to trigger this behaviour in the normal c8 coverage checks.

I was unable to reproduce this issue with a normal named async or synchronous function:

async function uncovered(active) {
    if (active) throw new Error('Active');

    return !active;
}

I was unable to reproduce this issue with a synchronous static class method:

export class Uncovered {
    static uncovered(active) {
        if (active) throw new Error('Active');

        return !active;
    }
}

I was unable to reproduce this issue with an async instance method:

export class Uncovered {
    async uncovered(active) {
        if (active) throw new Error('Active');

        return !active;
    }
}
acodeninja commented 1 month ago

In my specific use case I've been able to work around this to achieve 100% branch coverage by moving the if statement into a synchronous static method on the class like so:

export class Uncovered {
    static checkActive(active) {
        if (active) throw new Error('Active');
    }
    static async uncovered(active) {
        this.checkActive(active);

        return !active;
    }
}

This then results in 100% branch coverage:

⇒ c8 --experimental-monocart node Uncovered.test.js
--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |     100 |     100 |                   
 Uncovered.js |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------
cenfun commented 1 month ago

I can reproduce it simply with following

class StaticAsync {
    static async covered(active) {
        // should be covered
        if (active) {
            return 1;
        }
        return 2;
    }
}
await StaticAsync.covered(false);
await StaticAsync.covered(true);

QQ_1722903939742

It should be a issue related to static which is similar to #37 I will figure out how to fix it.

cenfun commented 1 month ago

Please try monocart-coverage-reports@2.10.2

acodeninja commented 1 month ago

Hi @cenfun I can confirm this has resolved the issue, thank you for sorting this out!