update test sample to finalized coverage api (#1011)

Fixes #1010
Fixes #979
This commit is contained in:
Connor Peet
2024-04-29 09:53:31 -07:00
committed by GitHub
parent 089f47e57d
commit ed42dafa64
4 changed files with 43 additions and 361 deletions

View File

@ -11,14 +11,14 @@
"license": "MIT",
"devDependencies": {
"@types/node": "^18",
"@types/vscode": "^1.88.0",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"@vscode/dts": "^0.4.0",
"eslint": "^8.26.0",
"typescript": "^5.4.2"
},
"engines": {
"vscode": "^1.68.0"
"vscode": "^1.88.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
@ -157,6 +157,12 @@
"integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==",
"dev": true
},
"node_modules/@types/vscode": {
"version": "1.88.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.88.0.tgz",
"integrity": "sha512-rWY+Bs6j/f1lvr8jqZTyp5arRMfovdxolcqGi+//+cPDOh8SBvzXH90e7BiSXct5HJ9HGW6jATchbRTpTJpEkw==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz",
@ -346,20 +352,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@vscode/dts": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@vscode/dts/-/dts-0.4.0.tgz",
"integrity": "sha512-m28fZnyS9PlzVvYHppyC3Q98U2RFIZO2srnBMvyupPBY5QkSxoNIjTV9roLaU7kE+gc+HXczH8XHPETUkt9IAA==",
"dev": true,
"dependencies": {
"https-proxy-agent": "^7.0.0",
"minimist": "^1.2.8",
"prompts": "^2.4.2"
},
"bin": {
"dts": "index.js"
}
},
"node_modules/acorn": {
"version": "8.8.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
@ -381,18 +373,6 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dev": true,
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -988,19 +968,6 @@
"node": ">=8"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz",
"integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==",
"dev": true,
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -1126,15 +1093,6 @@
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -1215,15 +1173,6 @@
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -1361,19 +1310,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
"dev": true,
"dependencies": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -1508,12 +1444,6 @@
"node": ">=8"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"dev": true
},
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -1793,6 +1723,12 @@
"integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==",
"dev": true
},
"@types/vscode": {
"version": "1.88.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.88.0.tgz",
"integrity": "sha512-rWY+Bs6j/f1lvr8jqZTyp5arRMfovdxolcqGi+//+cPDOh8SBvzXH90e7BiSXct5HJ9HGW6jATchbRTpTJpEkw==",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz",
@ -1893,17 +1829,6 @@
"eslint-visitor-keys": "^3.4.1"
}
},
"@vscode/dts": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@vscode/dts/-/dts-0.4.0.tgz",
"integrity": "sha512-m28fZnyS9PlzVvYHppyC3Q98U2RFIZO2srnBMvyupPBY5QkSxoNIjTV9roLaU7kE+gc+HXczH8XHPETUkt9IAA==",
"dev": true,
"requires": {
"https-proxy-agent": "^7.0.0",
"minimist": "^1.2.8",
"prompts": "^2.4.2"
}
},
"acorn": {
"version": "8.8.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
@ -1917,15 +1842,6 @@
"dev": true,
"requires": {}
},
"agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dev": true,
"requires": {
"debug": "^4.3.4"
}
},
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -2381,16 +2297,6 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"https-proxy-agent": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz",
"integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==",
"dev": true,
"requires": {
"agent-base": "^7.0.2",
"debug": "4"
}
},
"ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2489,12 +2395,6 @@
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true
},
"levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -2554,12 +2454,6 @@
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -2658,16 +2552,6 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
"dev": true,
"requires": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
}
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -2740,12 +2624,6 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"dev": true
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",

View File

@ -7,11 +7,8 @@
"private": true,
"license": "MIT",
"repository": "https://github.com/Microsoft/vscode-extension-samples",
"enabledApiProposals": [
"testCoverage"
],
"engines": {
"vscode": "^1.68.0"
"vscode": "^1.88.0"
},
"categories": [
"Other"
@ -25,15 +22,14 @@
"compile": "tsc -p ./",
"lint": "eslint \"src/**/*.ts\"",
"watch": "tsc -watch -p ./",
"download-api": "dts main && dts dev",
"postinstall": "npm run download-api"
},
"devDependencies": {
"@types/node": "^18",
"@types/vscode": "^1.88.0",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"eslint": "^8.26.0",
"typescript": "^5.4.2",
"@vscode/dts": "^0.4.0"
"typescript": "^5.4.2"
}
}

View File

@ -66,7 +66,7 @@ export async function activate(context: vscode.ExtensionContext) {
await discoverTests(gatherTestItems(test.children));
}
if (test.uri && !coveredLines.has(test.uri.toString())) {
if (test.uri && !coveredLines.has(test.uri.toString()) && request.profile?.kind === vscode.TestRunProfileKind.Coverage) {
try {
const lines = (await getContentFromFilesystem(test.uri)).split('\n');
coveredLines.set(
@ -96,27 +96,15 @@ export async function activate(context: vscode.ExtensionContext) {
const fileCoverage = coveredLines.get(test.uri!.toString());
const lineInfo = fileCoverage?.[lineNo];
if (lineInfo) {
lineInfo.executionCount++;
(lineInfo.executed as number)++;
}
run.appendOutput(`Completed ${test.id}\r\n`);
}
run.coverageProvider = {
provideFileCoverage() {
const coverage: vscode.FileCoverage[] = [];
for (const [uri, statements] of coveredLines) {
coverage.push(
vscode.FileCoverage.fromDetails(
vscode.Uri.parse(uri),
statements.filter((s): s is vscode.StatementCoverage => !!s)
)
);
}
return coverage;
},
};
for (const [uri, statements] of coveredLines) {
run.addCoverage(new MarkdownFileCoverage(uri, statements));
}
run.end();
};
@ -130,6 +118,15 @@ export async function activate(context: vscode.ExtensionContext) {
ctrl.createRunProfile('Run Tests', vscode.TestRunProfileKind.Run, runHandler, true, undefined, true);
const coverageProfile = ctrl.createRunProfile('Run with Coverage', vscode.TestRunProfileKind.Coverage, runHandler, true, undefined, true);
coverageProfile.loadDetailedCoverage = async (_testRun, coverage) => {
if (coverage instanceof MarkdownFileCoverage) {
return coverage.coveredLines.filter((l): l is vscode.StatementCoverage => !!l);
}
return [];
};
ctrl.resolveHandler = async item => {
if (!item) {
context.subscriptions.push(...startWatchingWorkspace(ctrl, fileChangedEmitter));
@ -226,3 +223,15 @@ function startWatchingWorkspace(controller: vscode.TestController, fileChangedEm
return watcher;
});
}
class MarkdownFileCoverage extends vscode.FileCoverage {
constructor(uri: string, public readonly coveredLines: (vscode.StatementCoverage | undefined)[]) {
super(vscode.Uri.parse(uri), new vscode.TestCoverageCount(0, 0));
for (const line of coveredLines) {
if (line) {
this.statementCoverage.covered += line.executed ? 1 : 0;
this.statementCoverage.total++;
}
}
}
}

View File

@ -1,201 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare module 'vscode' {
// https://github.com/microsoft/vscode/issues/123713
export interface TestRun {
/**
* Adds coverage for a file in the run.
*/
addCoverage(fileCoverage: FileCoverage): void;
/**
* An event fired when the editor is no longer interested in data
* associated with the test run.
*/
onDidDispose: Event<void>;
}
export interface TestRunProfile {
/**
* A function that provides detailed statement and function-level coverage for a file.
*
* The {@link FileCoverage} object passed to this function is the same instance
* emitted on {@link TestRun.addCoverage} calls associated with this profile.
*/
loadDetailedCoverage?: (testRun: TestRun, fileCoverage: FileCoverage, token: CancellationToken) => Thenable<FileCoverageDetail[]>;
}
/**
* A class that contains information about a covered resource. A count can
* be give for lines, branches, and declarations in a file.
*/
export class CoveredCount {
/**
* Number of items covered in the file.
*/
covered: number;
/**
* Total number of covered items in the file.
*/
total: number;
/**
* @param covered Value for {@link CovereredCount.covered}
* @param total Value for {@link CovereredCount.total}
*/
constructor(covered: number, total: number);
}
/**
* Contains coverage metadata for a file.
*/
export class FileCoverage {
/**
* File URI.
*/
readonly uri: Uri;
/**
* Statement coverage information. If the reporter does not provide statement
* coverage information, this can instead be used to represent line coverage.
*/
statementCoverage: CoveredCount;
/**
* Branch coverage information.
*/
branchCoverage?: CoveredCount;
/**
* Declaration coverage information. Depending on the reporter and
* language, this may be types such as functions, methods, or namespaces.
*/
declarationCoverage?: CoveredCount;
/**
* Creates a {@link FileCoverage} instance with counts filled in from
* the coverage details.
* @param uri Covered file URI
* @param detailed Detailed coverage information
*/
static fromDetails(uri: Uri, details: readonly FileCoverageDetail[]): FileCoverage;
/**
* @param uri Covered file URI
* @param statementCoverage Statement coverage information. If the reporter
* does not provide statement coverage information, this can instead be
* used to represent line coverage.
* @param branchCoverage Branch coverage information
* @param declarationCoverage Declaration coverage information
*/
constructor(
uri: Uri,
statementCoverage: CoveredCount,
branchCoverage?: CoveredCount,
declarationCoverage?: CoveredCount,
);
}
/**
* Contains coverage information for a single statement or line.
*/
export class StatementCoverage {
/**
* The number of times this statement was executed, or a boolean indicating
* whether it was executed if the exact count is unknown. If zero or false,
* the statement will be marked as un-covered.
*/
executed: number | boolean;
/**
* Statement location.
*/
location: Position | Range;
/**
* Coverage from branches of this line or statement. If it's not a
* conditional, this will be empty.
*/
branches: BranchCoverage[];
/**
* @param location The statement position.
* @param executed The number of times this statement was executed, or a
* boolean indicating whether it was executed if the exact count is
* unknown. If zero or false, the statement will be marked as un-covered.
* @param branches Coverage from branches of this line. If it's not a
* conditional, this should be omitted.
*/
constructor(executed: number | boolean, location: Position | Range, branches?: BranchCoverage[]);
}
/**
* Contains coverage information for a branch of a {@link StatementCoverage}.
*/
export class BranchCoverage {
/**
* The number of times this branch was executed, or a boolean indicating
* whether it was executed if the exact count is unknown. If zero or false,
* the branch will be marked as un-covered.
*/
executed: number | boolean;
/**
* Branch location.
*/
location?: Position | Range;
/**
* Label for the branch, used in the context of "the ${label} branch was
* not taken," for example.
*/
label?: string;
/**
* @param executed The number of times this branch was executed, or a
* boolean indicating whether it was executed if the exact count is
* unknown. If zero or false, the branch will be marked as un-covered.
* @param location The branch position.
*/
constructor(executed: number | boolean, location?: Position | Range, label?: string);
}
/**
* Contains coverage information for a declaration. Depending on the reporter
* and language, this may be types such as functions, methods, or namespaces.
*/
export class DeclarationCoverage {
/**
* Name of the declaration.
*/
name: string;
/**
* The number of times this declaration was executed, or a boolean
* indicating whether it was executed if the exact count is unknown. If
* zero or false, the declaration will be marked as un-covered.
*/
executed: number | boolean;
/**
* Declaration location.
*/
location: Position | Range;
/**
* @param executed The number of times this declaration was executed, or a
* boolean indicating whether it was executed if the exact count is
* unknown. If zero or false, the declaration will be marked as un-covered.
* @param location The declaration position.
*/
constructor(name: string, executed: number | boolean, location: Position | Range);
}
export type FileCoverageDetail = StatementCoverage | DeclarationCoverage;
}