diff --git a/src/test-provider/test-coverage.ts b/src/test-provider/test-coverage.ts index e065bc03b..2d302f72c 100644 --- a/src/test-provider/test-coverage.ts +++ b/src/test-provider/test-coverage.ts @@ -24,6 +24,9 @@ export class JestFileCoverage extends vscode.FileCoverage { return this.iCoverage; } + private isInvalidRange = (range: Range): boolean => + range.start.line === undefined || range.end.line === undefined; + public loadDetails(): vscode.FileCoverageDetail[] { if (this.details) { return this.details; @@ -40,9 +43,14 @@ export class JestFileCoverage extends vscode.FileCoverage { return statement.location.end.character; } }; + // transform istanbul line number from 1-based to vscode 0-based // and replace null end-column with the end of line, if available - const transformRange = (range: Range): vscode.Range => { + const transformRange = (range: Range): vscode.Range | undefined => { + if (this.isInvalidRange(range)) { + return; + } + const endColumn = range.end.column ?? getEOL(range.end.line) ?? range.start.column; return new vscode.Range( range.start.line - 1, @@ -57,6 +65,9 @@ export class JestFileCoverage extends vscode.FileCoverage { Object.entries(transformed.statementMap).forEach(([statementId, range]) => { const executionCount = transformed.s[statementId]; const vRange = transformRange(range); + if (!vRange) { + return; + } const statementCoverage = new vscode.StatementCoverage(executionCount, vRange); details.push(statementCoverage); statementByLine[vRange.start.line] = statementCoverage; @@ -67,6 +78,9 @@ export class JestFileCoverage extends vscode.FileCoverage { branch.locations.forEach((location, index) => { const branchExecutionCount = transformed.b[branchId][index]; const vRange = transformRange(location); + if (!vRange) { + return; + } const branchCoverage = new vscode.BranchCoverage( branchExecutionCount > 0, vRange, @@ -83,6 +97,9 @@ export class JestFileCoverage extends vscode.FileCoverage { Object.entries(transformed.fnMap).forEach(([functionId, func]) => { const executionCount = transformed.f[functionId]; const vRange = transformRange(func.loc); + if (!vRange) { + return; + } details.push(new vscode.DeclarationCoverage(func.name, executionCount, vRange)); }); } catch (e) { diff --git a/tests/test-provider/test-coverage.test.ts b/tests/test-provider/test-coverage.test.ts index 01c5ed2ed..c55287987 100644 --- a/tests/test-provider/test-coverage.test.ts +++ b/tests/test-provider/test-coverage.test.ts @@ -169,6 +169,32 @@ describe('test-coverage', () => { expect(console.error).toHaveBeenCalled(); console.error = saved; }); + describe('will skip empty ranges', () => { + it('for statements', () => { + fileCoverageMock = createFileCoverageMock(); + fileCoverageMock.statementMap['3'] = { + start: {}, + end: {}, + }; + jestFileCoverage = new JestFileCoverage(fileCoverageMock); + jestFileCoverage.loadDetails(); + expect(vscode.StatementCoverage).toHaveBeenCalledTimes(2); + }); + it('for branches', () => { + fileCoverageMock = createFileCoverageMock(); + fileCoverageMock.branchMap['1'].locations[1] = { start: {}, end: {} }; + jestFileCoverage = new JestFileCoverage(fileCoverageMock); + jestFileCoverage.loadDetails(); + expect(vscode.BranchCoverage).toHaveBeenCalledTimes(1); + }); + it('for functions', () => { + fileCoverageMock = createFileCoverageMock(); + fileCoverageMock.fnMap['1'].loc = { start: {}, end: {} }; + jestFileCoverage = new JestFileCoverage(fileCoverageMock); + jestFileCoverage.loadDetails(); + expect(vscode.DeclarationCoverage).toHaveBeenCalledTimes(0); + }); + }); }); });