Skip to content

0% coverage for files only containing TypeScript interfaces #494

Open

Description

@Xerillio
  • Version: node v18.7.0 + c8 v8.0.1
  • Platform: 64-bit (Windows)

I'm running c8 using mocha with ts-node/register and I'm wondering if that might cause the issue. Essentially, I have a file task-result.ts that only contains a ITaskResult interface. This is used from task-runner.ts (import { ITaskResult } from "./task-result"). I have tests with 100% coverage for task-runner.ts, but using the --all flag, c8 reports 0% coverage for task-result.ts.

If I move the interface into task-runner.ts instead, task-runner.ts still reports 100% coverage as expected.

Is this an artefact from TS interfaces not being transpiled to JS and/or related to the same underlying issue in #182? Or is there a way to solve it while keeping the interface in its own file (I'd prefer not having to exclude individual files like this)?

// .c8rc.json
{
    "all": true,
    "src": ["./src"],
    "extension": [".ts"],
    "report-dir": "./coverage"
}

// package.json (snippet)
    "scripts": {
        "test": "mocha -r ts-node/register -r mocha-suppress-logs './tests/**/*.test.ts'",
        "coverage": "c8 npm test"
    },

If necessary, I can create a small example to reproduce it.

Activity

  1. bcoe commented on Oct 25, 2023

    @bcoe
    Owner

    @Xerillio could you please provide a reproduction for this issue.

  2. Xerillio commented on Oct 25, 2023

    @Xerillio
    Author

    @bcoe You got it 😉

    /package.json:

    {
      "scripts": {
        "test": "mocha -r ts-node/register './tests/**/*.test.ts'",
        "coverage": "c8 npm test"
      },
      "devDependencies": {
        "@types/chai": "^4.3.9",
        "@types/mocha": "^10.0.3",
        "c8": "^8.0.1",
        "chai": "^4.3.10",
        "mocha": "^10.2.0",
        "ts-node": "^10.9.1",
        "typescript": "^5.2.2"
      }
    }

    /tsconfig.json:

    {
      "compilerOptions": {
        "target": "es6",
        "module": "commonjs",
        "esModuleInterop": true,
        "strict": true
      }
    }

    /.c8rc.json:

    {
        "all": true,
        "src": ["./src"],
        "extension": [".ts"],
        "report-dir": "./coverage"
    }

    /src/power.ts:

    import type { IPower } from "./ipower";
    
    export class Power implements IPower {
      calculate(base: number, exponent: number): number {
        if (!Number.isInteger(exponent)) throw new Error("The exponent must be an integer");
    
        let result = 1;
        for (let i = 0; i < exponent; i++) {
          result *= base;
        }
    
        return result;
      }
    }

    /src/ipower.ts:

    export interface IPower {
      calculate(base: number, exponent: number): number
    }

    /tests/power.test.ts:

    import { Power } from "../src/power";
    import { expect } from "chai";
    
    describe("Power", () => {
      describe("#calculate", () => {
          it("should return 1 when the exponent is 0", () => {
              const sut = new Power();
    
              const result = sut.calculate(9.876, 0);
    
              expect(result).to.equal(1);
          });
    
          it("should throw when the exponent is not an integer", () => {
              const sut = new Power();
    
              expect(() => sut.calculate(9.876, 1.1)).to.throw(
                  Error,
                  "The exponent must be an integer");
          });
      });
    });

    Running coverage then produces this result (0% for ipower.ts):

    $ npm run coverage
    ...
    -----------|---------|----------|---------|---------|-------------------
    File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
    -----------|---------|----------|---------|---------|-------------------
    All files  |   70.58 |       60 |      50 |   70.58 |
     ipower.ts |       0 |        0 |       0 |       0 | 1-3
     power.ts  |   85.71 |       75 |     100 |   85.71 | 9-10
    -----------|---------|----------|---------|---------|-------------------

    Funny thing I noticed now because I didn't add tests for 100% coverage... if you move the contents of ipower.ts into power.ts the coverage of power.ts is strangely enough higher than before:

    $ npm run coverage
    ...
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
    ----------|---------|----------|---------|---------|-------------------
    All files |    87.5 |       75 |     100 |    87.5 |
     power.ts |    87.5 |       75 |     100 |    87.5 | 7-8
    ----------|---------|----------|---------|---------|-------------------
  3. ericmorand commented on Nov 19, 2023

    @ericmorand

    Is there news about this issue?

  4. jamie-tm commented on Nov 28, 2024

    @jamie-tm

    I encounter this too on a regular basis.

    My go-to solution for now is to put my interfaces in one directory and then exclude that whole directory:

    - src/
      - interfaces/
        - widget.ts
      - classes/
        - WidgetFactory.ts
    - test/
      - WidgetFactory.suite.ts
    - .c8rc.yaml
    
    {
      "all": true,
      "src": "./src",
      "exclude": [
        "interfaces/"
      ]
    }

    Or, if you prefer to co-locate your tests and interfaces, you could use file extensions:

    - src/
      - widgets/
        - widget.interface.ts
        - WidgetFactory.spec.ts
        - WidgetFactory.ts
    - .c8rc.yaml
    
    {
      "all": true,
      "src": "./src",
      "exclude": [
        "**/*.interface.ts",
        "**/*.spec.ts"
      ]
    }

    However, both of these share the problem that it's possible to end up with uncovered code if someone accidentally (or intentionally) puts something other than an interface in that directory/file. So, I'd love to see a solution that could somehow recognize and ignore these files automatically.

  5. ericmorand commented on Nov 29, 2024

    @ericmorand

    @jftanner could please give One Double Zero a try?

    https://www.npmjs.com/package/one-double-zero?activeTab=readme

  6. jamie-tm commented on Nov 29, 2024

    @jamie-tm

    @jftanner could please give One Double Zero a try?

    No offense, but I'm not interested in switching to such a new tool. A large part of security in open source comes from having many eyes looking at it. At 11 downloads per week, your tool isn't there yet.

  7. ericmorand commented on Nov 29, 2024

    @ericmorand

    That makes no sense right? How can a tool get downloads if you don't download it because it doesn't have downloads.

    It solves your issue. It is fully tested. It is open source. What you want more?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions