Skip to content

Commit 8cec4c9

Browse files
authored
Vscode coverage support (#1151)
* supports vscode coverage API * update version
1 parent 6daff86 commit 8cec4c9

25 files changed

+3466
-574
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ generated-icons/
99
*.vsix
1010
*.zip
1111
coverage_comparison_report.html
12+
yarn-error.log
13+

__mocks__/vscode.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ const window = {
2222
createWebviewPanel: jest.fn(),
2323
};
2424

25+
const extensions = {
26+
getExtension: jest.fn(),
27+
};
28+
2529
const workspace = {
2630
getConfiguration: jest.fn(),
2731
workspaceFolders: [],
@@ -66,7 +70,6 @@ const commands = {
6670
registerTextEditorCommand: jest.fn(),
6771
};
6872

69-
// eslint-disable-next-line @typescript-eslint/no-empty-function
7073
const CodeLens = function CodeLens() {};
7174

7275
const QuickInputButtons = {
@@ -102,7 +105,15 @@ const QuickPickItemKind = {
102105
Default: 0,
103106
};
104107

108+
// for coverage
109+
const FileCoverage = jest.fn();
110+
const StatementCoverage = jest.fn();
111+
const BranchCoverage = jest.fn();
112+
const DeclarationCoverage = jest.fn();
113+
const TestCoverageCount = jest.fn();
114+
105115
export = {
116+
extensions,
106117
ThemeColor,
107118
CodeLens,
108119
languages,
@@ -128,4 +139,9 @@ export = {
128139
TestRunRequest,
129140
ViewColumn,
130141
QuickPickItemKind,
142+
FileCoverage,
143+
StatementCoverage,
144+
BranchCoverage,
145+
DeclarationCoverage,
146+
TestCoverageCount,
131147
};

jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module.exports = {
1010
},
1111
testEnvironment: 'node',
1212
testRegex: 'tests/.*\\.test\\.ts$',
13-
collectCoverageFrom: ['src/**/*.ts'],
13+
coveragePathIgnorePatterns: ['/node_modules/', '/tests/'],
1414
automock: true,
1515
moduleFileExtensions: ['ts', 'js', 'json'],
1616
unmockedModulePathPatterns: [

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
"name": "vscode-jest",
33
"displayName": "Jest",
44
"description": "Use Facebook's Jest With Pleasure.",
5-
"version": "7.0.0",
5+
"version": "6.3.0",
66
"publisher": "Orta",
77
"engines": {
8-
"vscode": "^1.68.1"
8+
"vscode": "^1.88.1"
99
},
1010
"author": {
1111
"name": "Orta Therox, ConnectDotz & Sean Poulter",

src/JestExt/core.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ import { resultsWithoutAnsiEscapeSequence } from '../TestResults/TestResult';
1818
import { CoverageMapData } from 'istanbul-lib-coverage';
1919
import { Logging } from '../logging';
2020
import { createProcessSession, ProcessSession } from './process-session';
21-
import { JestExtContext, JestSessionEvents, JestExtSessionContext, JestRunEvent } from './types';
21+
import {
22+
JestExtContext,
23+
JestSessionEvents,
24+
JestExtSessionContext,
25+
JestRunEvent,
26+
JestTestDataAvailableEvent,
27+
} from './types';
2228
import { extensionName, SupportedLanguageIds } from '../appGlobals';
2329
import { createJestExtContext, getExtensionResourceSettings, prefixWorkspace } from './helper';
2430
import { PluginResourceSettings } from '../Settings';
@@ -99,6 +105,7 @@ export class JestExt {
99105
onRunEvent: new vscode.EventEmitter<JestRunEvent>(),
100106
onTestSessionStarted: new vscode.EventEmitter<JestExtSessionContext>(),
101107
onTestSessionStopped: new vscode.EventEmitter<void>(),
108+
onTestDataAvailable: new vscode.EventEmitter<JestTestDataAvailableEvent>(),
102109
};
103110
this.setupRunEvents(this.events);
104111

@@ -863,8 +870,10 @@ export class JestExt {
863870
private updateWithData(data: JestTotalResults, process: JestProcessInfo): void {
864871
const noAnsiData = resultsWithoutAnsiEscapeSequence(data);
865872
const normalizedData = resultsWithLowerCaseWindowsDriveLetters(noAnsiData);
866-
this._updateCoverageMap(normalizedData.coverageMap);
867873

874+
this.events.onTestDataAvailable.fire({ data: normalizedData, process });
875+
876+
this._updateCoverageMap(normalizedData.coverageMap);
868877
const statusList = this.testResultProvider.updateTestResults(normalizedData, process);
869878

870879
updateDiagnostics(statusList, this.failDiagnostics);

src/JestExt/helper.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ export const outputFileSuffix = (ws: string, extra?: string): string => {
4646
// replace non-word with '_'
4747
return s.replace(/\W/g, '_');
4848
};
49+
export const collectCoverage = (coverage?: boolean, settings?: PluginResourceSettings) =>
50+
coverage ?? settings?.runMode.config.coverage ?? false;
51+
4952
export const createJestExtContext = (
5053
workspaceFolder: vscode.WorkspaceFolder,
5154
settings: PluginResourceSettings,
@@ -64,7 +67,7 @@ export const createJestExtContext = (
6467
'',
6568
currentJestVersion,
6669
outputFileSuffix(ws, options?.outputFileSuffix),
67-
options?.collectCoverage ?? settings.runMode.config.coverage ?? false,
70+
collectCoverage(options?.collectCoverage, settings),
6871
settings.debugMode,
6972
settings.nodeEnv,
7073
settings.shell.toSetting(),

src/JestExt/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,16 @@ export type JestRunEvent = RunEventBase &
4343
| { type: 'exit'; error?: string; code?: number }
4444
| { type: 'long-run'; threshold: number; numTotalTestSuites?: number }
4545
);
46+
47+
export interface JestTestDataAvailableEvent {
48+
data: JestTotalResults;
49+
process: JestProcessInfo;
50+
}
4651
export interface JestSessionEvents {
4752
onRunEvent: vscode.EventEmitter<JestRunEvent>;
4853
onTestSessionStarted: vscode.EventEmitter<JestExtSessionContext>;
4954
onTestSessionStopped: vscode.EventEmitter<void>;
55+
onTestDataAvailable: vscode.EventEmitter<JestTestDataAvailableEvent>;
5056
}
5157
export interface JestExtProcessContextRaw extends JestExtContext {
5258
updateWithData: (data: JestTotalResults, process: JestProcessInfo) => void;

src/JestProcessManagement/JestProcess.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
22
import { join } from 'path';
33
import { Runner, RunnerEvent, Options } from 'jest-editor-support';
44
import { JestExtContext, WatchMode } from '../JestExt/types';
5+
import { collectCoverage } from '../JestExt/helper';
56
import { extensionId } from '../appGlobals';
67
import { Logging } from '../logging';
78
import { JestProcessInfo, JestProcessRequest, ProcessStatus, UserDataType } from './types';
@@ -34,6 +35,7 @@ export class JestProcess implements JestProcessInfo {
3435
private desc: string;
3536
public readonly request: JestProcessRequest;
3637
public _status: ProcessStatus;
38+
private coverage: boolean | undefined;
3739
private autoStopTimer?: NodeJS.Timeout;
3840

3941
constructor(
@@ -44,9 +46,11 @@ export class JestProcess implements JestProcessInfo {
4446
this.extContext = extContext;
4547
this.request = request;
4648
this.logging = extContext.loggingFactory.create(`JestProcess ${request.type}`);
47-
this.id = `${request.type}-${SEQ++}`;
48-
this.desc = `id: ${this.id}, request: ${requestString(request)}`;
4949
this._status = ProcessStatus.Pending;
50+
this.coverage = collectCoverage(this.getRequestCoverage(), this.extContext.settings);
51+
const extra = (this.coverage ? 'with-coverage:' : '') + `${SEQ++}`;
52+
this.id = `${request.type}:${extra}`;
53+
this.desc = `id: ${this.id}, request: ${requestString(request)}`;
5054
}
5155

5256
public get status(): ProcessStatus {
@@ -122,6 +126,15 @@ export class JestProcess implements JestProcessInfo {
122126
return `"${removeSurroundingQuote(aString)}"`;
123127
}
124128

129+
private getRequestCoverage(): boolean | undefined {
130+
if (this.request.type === 'not-test') {
131+
return;
132+
}
133+
// Note, we are ignoring coverage = false use-case, which doesn't exist yet, by returning undefined
134+
// and let the runMode to decide in createRunnerWorkspace()
135+
return this.request.coverage || undefined;
136+
}
137+
125138
private getTestPathPattern(pattern: string): string[] {
126139
return this.extContext.settings.useJest30
127140
? ['--testPathPatterns', pattern]
@@ -210,6 +223,7 @@ export class JestProcess implements JestProcessInfo {
210223

211224
const runnerWorkspace = this.extContext.createRunnerWorkspace({
212225
outputFileSuffix: this.request.schedule.queue === 'blocking-2' ? '2' : undefined,
226+
collectCoverage: this.coverage,
213227
});
214228

215229
const runner = new Runner(runnerWorkspace, options);

src/JestProcessManagement/types.ts

+29-20
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,8 @@ export interface TaskPredicate {
5353
filterByStatus?: TaskStatus[];
5454
filterByContent?: boolean;
5555
}
56-
/**
57-
* define the eligibility for process scheduling
58-
* @param queue the type of the queue
59-
* @param dedupe a predicate to match the task in queue.
60-
*/
61-
export interface ScheduleStrategy {
62-
queue: QueueType;
63-
dedupe?: TaskPredicate;
64-
}
6556

66-
interface JestProcessRequestCommon {
67-
schedule: ScheduleStrategy;
68-
listener: JestProcessListener;
69-
}
70-
71-
export type JestProcessRequestSimple =
57+
type JestProcessTestRequestBase =
7258
| {
7359
type: Extract<JestTestProcessType, 'watch-tests' | 'watch-all-tests'>;
7460
}
@@ -99,15 +85,38 @@ export type JestProcessRequestSimple =
9985
testFileNamePattern: string;
10086
testNamePattern: TestNamePattern;
10187
updateSnapshot?: boolean;
102-
}
103-
| {
104-
type: Extract<JestTestProcessType, 'not-test'>;
105-
args: string[];
10688
};
10789

90+
type JestProcessTestRequestCommon = {
91+
coverage?: boolean;
92+
};
93+
94+
export type JestProcessTestRequestType = JestProcessTestRequestCommon & JestProcessTestRequestBase;
95+
96+
type JestProcessNonTestRequest = {
97+
type: Extract<JestTestProcessType, 'not-test'>;
98+
args: string[];
99+
};
100+
101+
type JestProcessRequestType = JestProcessTestRequestType | JestProcessNonTestRequest;
102+
103+
/**
104+
* define the eligibility for process scheduling
105+
* @param queue the type of the queue
106+
* @param dedupe a predicate to match the task in queue.
107+
*/
108+
export interface ScheduleStrategy {
109+
queue: QueueType;
110+
dedupe?: TaskPredicate;
111+
}
112+
113+
interface JestProcessRequestCommon {
114+
schedule: ScheduleStrategy;
115+
listener: JestProcessListener;
116+
}
108117
export type JestProcessRequestTransform = (request: JestProcessRequest) => JestProcessRequest;
109118

110-
export type JestProcessRequestBase = JestProcessRequestSimple & {
119+
export type JestProcessRequestBase = JestProcessRequestType & {
111120
transform?: JestProcessRequestTransform;
112121
};
113122
export type JestProcessRequest = JestProcessRequestBase & JestProcessRequestCommon;

src/test-provider/jest-test-run.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import * as vscode from 'vscode';
22
import { JestExtOutput, JestOutputTerminal, OutputOptions } from '../JestExt/output-terminal';
3-
import { JestTestProviderContext } from './test-provider-context';
3+
import { JestTestProviderContext, CreateTestRun } from './test-provider-context';
44
import { JestProcessInfo } from '../JestProcessManagement';
55

66
export type TestRunProtocol = Pick<
77
vscode.TestRun,
88
'name' | 'enqueued' | 'started' | 'errored' | 'failed' | 'passed' | 'skipped' | 'end'
99
>;
1010

11-
export type CreateTestRun = (request: vscode.TestRunRequest, name: string) => vscode.TestRun;
1211
export type EndProcessOption = { process: JestProcessInfo; delay?: number; reason?: string };
1312
export type EndOption = EndProcessOption | { reason: string };
1413
const isEndProcessOption = (arg?: EndOption): arg is EndProcessOption =>
@@ -41,7 +40,7 @@ export class JestTestRun implements JestExtOutput, TestRunProtocol {
4140
private request: vscode.TestRunRequest,
4241
private createRun: CreateTestRun
4342
) {
44-
this.name = `${this.context.ext.workspace.name}:${name}:${SEQ++}`;
43+
this.name = `[${this.context.ext.workspace.name}] ${name}:${SEQ++}`;
4544
this.output = context.output;
4645
this.processes = new Map();
4746
this.verbose = context.ext.settings.debugMode === true;
@@ -89,6 +88,9 @@ export class JestTestRun implements JestExtOutput, TestRunProtocol {
8988
}
9089

9190
// TestRunProtocol
91+
public addCoverage = (fileCoverage: vscode.FileCoverage): void => {
92+
this.vscodeRun()?.addCoverage(fileCoverage);
93+
};
9294
public enqueued = (test: vscode.TestItem): void => {
9395
this.vscodeRun()?.enqueued(test);
9496
};
@@ -129,7 +131,7 @@ export class JestTestRun implements JestExtOutput, TestRunProtocol {
129131
const pid = process.id;
130132
const pInfo = this.processes.get(pid);
131133
if (pInfo?.timeoutId) {
132-
clearTimeout(pInfo?.timeoutId);
134+
clearTimeout(pInfo.timeoutId);
133135
}
134136

135137
if (!delay) {

0 commit comments

Comments
 (0)