diff --git a/package.json b/package.json index 7d9fc01..3af96e3 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,21 @@ "description": "Search location for lcov files" } } - } + }, + "activationEvents": [ + "onCommand:code-coverage.hide", + "onCommand:code-coverage.show" + ], + "commands": [ + { + "command": "code-coverage.hide", + "title": "Hide Code Coverage" + }, + { + "command": "code-coverage.show", + "title": "Show Code Coverage" + } + ] }, "eslintConfig": { "env": { @@ -91,10 +105,10 @@ "vscode:prepublish": "tsc -p ./", "compile": "tsc -p ./", "compile:watch": "tsc -watch -p ./", - "preformat": "prettier --write src/**/*.ts", - "format": "eslint --fix src/**/*.ts", - "prelint": "prettier --check src/**/*.ts", - "lint": "eslint src/**/*.ts", + "preformat": "prettier --write src/**/*.ts test/**/*.ts", + "format": "eslint --fix src/**/*.ts test/**/*.ts", + "prelint": "prettier --check src/**/*.ts test/**/*.ts", + "lint": "eslint src/**/*.ts test/**/*.ts", "pretest": "npm run compile", "test": "node ./out/test/run-test.js" }, diff --git a/src/extension.ts b/src/extension.ts index 3bf50cd..6e0195f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,6 @@ import { isAbsolute, join } from "path"; import { + commands, Diagnostic, DiagnosticSeverity, ExtensionContext, @@ -21,11 +22,26 @@ import { parse as parseLcov } from "./parse-lcov"; const DEFAULT_SEARCH_CRITERIA = "coverage/lcov*.info"; +export let onCommand: (cmd: string) => Promise = noop; + +export async function deactivate() { + onCommand = noop; +} + export async function activate(context: ExtensionContext) { const packageInfo = require(join(context.extensionPath, "package.json")); const diagnostics = languages.createDiagnosticCollection("coverage"); const statusBar = window.createStatusBarItem(); + const hideCommand = commands.registerCommand( + `${packageInfo.name}.hide`, + onHideCoverage + ); + const showCommand = commands.registerCommand( + `${packageInfo.name}.show`, + onShowCoverage + ); const coverageByFile = new Map(); + let showCoverage = true; const config = workspace.getConfiguration("markiscodecoverage"); const configSearchCriteria = @@ -47,7 +63,7 @@ export async function activate(context: ExtensionContext) { } } - context.subscriptions.push(diagnostics, statusBar); + context.subscriptions.push(diagnostics, statusBar, showCommand, hideCommand); // Update status bar on changes to any open file workspace.onDidChangeTextDocument((e) => { @@ -67,8 +83,31 @@ export async function activate(context: ExtensionContext) { }); // Run the main routine at activation time as well - if (workspaceFolders) { - await Promise.all(workspaceFolders.map(findDiagnostics)); + await findDiagnosticsInWorkspace(); + + onCommand = async function onCommand(cmd: string) { + switch (cmd) { + case `${packageInfo.name}.hide`: + return onHideCoverage(); + case `${packageInfo.name}.show`: + return onShowCoverage(); + } + }; + + async function onHideCoverage() { + showCoverage = false; + diagnostics.clear(); + } + + async function onShowCoverage() { + showCoverage = true; + await findDiagnosticsInWorkspace(); + } + + async function findDiagnosticsInWorkspace() { + if (workspaceFolders) { + await Promise.all(workspaceFolders.map(findDiagnostics)); + } } // Finds VSCode diagnostics to display based on a coverage file specified by the search pattern in each workspace folder @@ -123,6 +162,8 @@ export async function activate(context: ExtensionContext) { coverages: CoverageCollection, workspaceFolder: string ) { + if (!showCoverage) return; // do nothing + for (const coverage of coverages) { if (coverage && coverage.lines && coverage.lines.details) { const fileName = !isAbsolute(coverage.file) @@ -147,14 +188,14 @@ export async function activate(context: ExtensionContext) { details: LineCoverageInfo[], fileName: string ) { - const currentFile = window.activeTextEditor?.document.uri.fsPath; + const doc = window.activeTextEditor?.document; + const currentFile = doc?.uri.fsPath; const diagnosticsForFiles: Diagnostic[] = []; for (const detail of details) { const line = detail.line - 1; if (detail.hit === 0) { const range = - (currentFile === fileName && - window.activeTextEditor?.document.lineAt(line).range) || + (currentFile === fileName && doc?.lineAt(line).range) || new Range(new Position(line, 0), new Position(line, 1000)); diagnosticsForFiles.push( new Diagnostic( @@ -169,5 +210,7 @@ export async function activate(context: ExtensionContext) { } // exports - accessible to tests - return { statusBar }; + return { onCommand, statusBar }; } + +async function noop() {} diff --git a/test/run-test.ts b/test/run-test.ts index cccf87d..2920435 100644 --- a/test/run-test.ts +++ b/test/run-test.ts @@ -1,24 +1,24 @@ -import * as path from 'path'; +import * as path from "path"; -import { runTests } from '@vscode/test-electron'; +import { runTests } from "@vscode/test-electron"; async function main() { try { // The folder containing the Extension Manifest package.json // Passed to `--extensionDevelopmentPath` - const extensionDevelopmentPath = path.resolve(__dirname, '../../../'); + const extensionDevelopmentPath = path.resolve(__dirname, "../../../"); // The path to the extension test runner script // Passed to --extensionTestsPath - const extensionTestsPath = path.resolve(__dirname, './suite/'); + const extensionTestsPath = path.resolve(__dirname, "./suite/"); // Download VS Code, unzip it and run the integration test await runTests({ extensionDevelopmentPath, extensionTestsPath }); } catch (err) { console.error(err); - console.error('Failed to run tests'); + console.error("Failed to run tests"); process.exit(1); } } -main(); \ No newline at end of file +main(); diff --git a/test/suite/extension.test.ts b/test/suite/extension.test.ts index 1a7cc42..f97b3ea 100644 --- a/test/suite/extension.test.ts +++ b/test/suite/extension.test.ts @@ -1,38 +1,66 @@ -import * as assert from 'assert'; -import { join } from 'path'; -import { commands, extensions, languages, StatusBarItem, Uri, window, workspace } from 'vscode'; - -suite('code-coverage', () => { - const exampleWorkspace = join(__dirname, '../../../', 'example'); - const exampleWorkspaceUri = Uri.file(exampleWorkspace); - const exampleIndexFile = join(exampleWorkspace, 'index.ts'); - const extension = extensions.getExtension("markis.code-coverage"); - - setup(async () => { - // Open the example workspace and open the example index file - await commands.executeCommand('vscode.openFolder', exampleWorkspaceUri); - const doc = await workspace.openTextDocument(exampleIndexFile); - await window.showTextDocument(doc); - }); - - teardown(async () => { - // All done, close the editor - commands.executeCommand('workbench.action.closeActiveEditor'); - }); - - test('check diagnostics exist', async () => { - // Check to see if the diagnostics exist for the example file - // there should only be one line not covered, and so only one diagnostic should exist - const diagnostics = languages.getDiagnostics(Uri.file(exampleIndexFile)); - assert.strictEqual(diagnostics.length, 1); - assert.strictEqual(diagnostics[0].range.start.line, 5); - }); - - test('check status bar', async () => { - // Check to see if the status bar is updated correctly - // the example coverage should cover 3 out of 4 lines - "3/4" - const statusBar: StatusBarItem = extension?.exports.statusBar; - assert.ok(statusBar.text); - assert.ok(statusBar.text.includes('3/4')) - }); -}); +import * as assert from "assert"; +import { join } from "path"; +import { + commands, + extensions, + languages, + StatusBarItem, + Uri, + window, + workspace, + Extension, +} from "vscode"; + +suite("code-coverage", function () { + this.timeout(10000); + const exampleWorkspace = join(__dirname, "../../../", "example"); + const exampleWorkspaceUri = Uri.file(exampleWorkspace); + const exampleIndexUri = Uri.file(join(exampleWorkspace, "index.ts")); + let extension: Extension | undefined; + let onCommand: (cmd: string) => Promise | undefined; + + setup(async () => { + // Open the example workspace and open the example index file + await commands.executeCommand("vscode.openFolder", exampleWorkspaceUri); + const doc = await workspace.openTextDocument(exampleIndexUri); + await window.showTextDocument(doc); + extension = extensions.getExtension("markis.code-coverage"); + onCommand = extension?.exports.onCommand; + }); + + teardown(async () => { + // All done, close the editor + commands.executeCommand("workbench.action.closeActiveEditor"); + }); + + test("check diagnostics exist", async () => { + // Check to see if the diagnostics exist for the example file + // there should only be one line not covered, and so only one diagnostic should exist + const diagnostics = languages.getDiagnostics(exampleIndexUri); + assert.strictEqual(diagnostics.length, 1); + assert.strictEqual(diagnostics[0].range.start.line, 5); + }); + + test("check status bar", async () => { + // Check to see if the status bar is updated correctly + // the example coverage should cover 3 out of 4 lines - "3/4" + const statusBar: StatusBarItem = extension?.exports.statusBar; + assert.ok(statusBar.text); + assert.ok(statusBar.text.includes("3/4")); + }); + + test("test commands hide coverage", async () => { + // Ensure coverage exists + assert.strictEqual(languages.getDiagnostics(exampleIndexUri).length, 1); + + // Hide coverage + await onCommand("code-coverage.hide"); + assert.strictEqual(languages.getDiagnostics(exampleIndexUri).length, 0); + }); + + test("test commands show coverage", async () => { + // Show coverage + await onCommand("code-coverage.show"); + assert.strictEqual(languages.getDiagnostics(exampleIndexUri).length, 1); + }); +}); diff --git a/test/suite/index.ts b/test/suite/index.ts index 8cb6761..6e6baae 100644 --- a/test/suite/index.ts +++ b/test/suite/index.ts @@ -1,31 +1,33 @@ +import * as path from "path"; +import * as Mocha from "mocha"; +import * as glob from "glob"; -import * as path from 'path'; -import * as Mocha from 'mocha'; -import * as glob from 'glob'; +export function run( + testsRoot: string, + cb: (error: any, failures?: number) => void +): void { + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + }); + mocha.useColors(true); -export function run(testsRoot: string, cb: (error: any, failures?: number) => void): void { - // Create the mocha test - const mocha = new Mocha({ - ui: 'tdd' - }); - mocha.useColors(true); + glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { + if (err) { + return cb(err); + } - glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { - if (err) { - return cb(err); - } + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); - // Add files to the test suite - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); - - try { - // Run the mocha test - mocha.run(failures => { - cb(null, failures); - }); - } catch (err) { - console.error(err); - cb(err); - } - }); -} \ No newline at end of file + try { + // Run the mocha test + mocha.run((failures) => { + cb(null, failures); + }); + } catch (err) { + console.error(err); + cb(err); + } + }); +}