From ed42dafa6471231f8cf556094cd53e10d6699d93 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 29 Apr 2024 09:53:31 -0700 Subject: [PATCH] update test sample to finalized coverage api (#1011) Fixes #1010 Fixes #979 --- test-provider-sample/package-lock.json | 150 ++----------- test-provider-sample/package.json | 10 +- test-provider-sample/src/extension.ts | 43 ++-- .../vscode.proposed.testCoverage.d.ts | 201 ------------------ 4 files changed, 43 insertions(+), 361 deletions(-) delete mode 100644 test-provider-sample/vscode.proposed.testCoverage.d.ts diff --git a/test-provider-sample/package-lock.json b/test-provider-sample/package-lock.json index 6f06da2b..2cc16fa5 100644 --- a/test-provider-sample/package-lock.json +++ b/test-provider-sample/package-lock.json @@ -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", diff --git a/test-provider-sample/package.json b/test-provider-sample/package.json index e66acb20..47ccd6dd 100644 --- a/test-provider-sample/package.json +++ b/test-provider-sample/package.json @@ -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" } } diff --git a/test-provider-sample/src/extension.ts b/test-provider-sample/src/extension.ts index a6d24305..e124f3e0 100644 --- a/test-provider-sample/src/extension.ts +++ b/test-provider-sample/src/extension.ts @@ -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++; + } + } + } +} diff --git a/test-provider-sample/vscode.proposed.testCoverage.d.ts b/test-provider-sample/vscode.proposed.testCoverage.d.ts deleted file mode 100644 index fc6e4964..00000000 --- a/test-provider-sample/vscode.proposed.testCoverage.d.ts +++ /dev/null @@ -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; - } - - 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; - } - - /** - * 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; - -}