diff --git a/README.md b/README.md
index a5c711b..e723c25 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ Some examples for the highlight colour are as follows:
https://developer.mozilla.org/en/docs/Web/CSS/background-color#Syntax
## Known Issues
-- can only reliably handle one lcov file per workspace [#35](https://github.com/ryanluker/vscode-coverage-gutters/issues/35)
+### [Open Bugs](https://github.com/ryanluker/vscode-coverage-gutters/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
## Release Notes
### [Changelog](https://github.com/ryanluker/vscode-coverage-gutters/releases)
diff --git a/package.json b/package.json
index 13a4706..1d8e1f3 100644
--- a/package.json
+++ b/package.json
@@ -1,229 +1,233 @@
{
- "name": "vscode-coverage-gutters",
- "displayName": "Coverage Gutters",
- "description": "Display test coverage generated by lcov - works with many languages",
- "version": "0.3.0",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "https://github.com/ryanluker/vscode-coverage-gutters"
- },
- "icon": "images/icon.svg",
- "galleryBanner": {
- "color": "#24381b",
- "theme": "dark"
- },
- "bugs": "https://github.com/ryanluker/vscode-coverage-gutters/issues",
- "publisher": "ryanluker",
- "engines": {
- "vscode": "^1.9.0"
+ "name": "vscode-coverage-gutters",
+ "displayName": "Coverage Gutters",
+ "description": "Display test coverage generated by lcov - works with many languages",
+ "version": "0.4.0",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ryanluker/vscode-coverage-gutters"
+ },
+ "icon": "images/icon.svg",
+ "galleryBanner": {
+ "color": "#24381b",
+ "theme": "dark"
+ },
+ "bugs": "https://github.com/ryanluker/vscode-coverage-gutters/issues",
+ "publisher": "ryanluker",
+ "engines": {
+ "vscode": "^1.9.0"
+ },
+ "categories": [
+ "Other"
+ ],
+ "keywords": [
+ "gutters",
+ "code coverage",
+ "lcov",
+ "vscode",
+ "extension"
+ ],
+ "main": "./out/src/extension",
+ "contributes": {
+ "configuration": {
+ "title": "coverage-gutters",
+ "properties": {
+ "coverage-gutters.lcovname": {
+ "type": "string",
+ "default": "lcov.info",
+ "description": "name of your lcov file"
+ },
+ "coverage-gutters.altSfCompare": {
+ "type": "boolean",
+ "default": false,
+ "description": "uses a relative method of comparing lcov source file paths"
+ },
+ "coverage-gutters.highlightlight": {
+ "type": "string",
+ "default": "rgba(166, 220, 142, 0.75)",
+ "description": "light themed highlight for code coverage"
+ },
+ "coverage-gutters.highlightdark": {
+ "type": "string",
+ "default": "rgba(36, 56, 27, 0.75)",
+ "description": "dark themed highlight for code coverage"
+ },
+ "coverage-gutters.partialHighlightLight": {
+ "type": "string",
+ "default": "rgba(220, 213, 143, 0.75)",
+ "description": "light theme partial highlight for code coverage"
+ },
+ "coverage-gutters.partialHighlightDark": {
+ "type": "string",
+ "default": "rgba(57, 50, 27, 0.75)",
+ "description": "dark theme partial highlight for code coverage"
+ },
+ "coverage-gutters.noHighlightLight": {
+ "type": "string",
+ "default": "rgba(220, 143, 143, 0.75)",
+ "description": "light theme partial highlight for code coverage"
+ },
+ "coverage-gutters.noHighlightDark": {
+ "type": "string",
+ "default": "rgba(57, 27, 27, 0.75)",
+ "description": "dark theme partial highlight for code coverage"
+ },
+ "coverage-gutters.gutterIconPathLight": {
+ "type": "string",
+ "default": "./images/gutter-icon-light.svg",
+ "description": "path to an icon (svg, png, etc) for displaying in the gutter for full coverage"
+ },
+ "coverage-gutters.gutterIconPathDark": {
+ "type": "string",
+ "default": "./images/gutter-icon-dark.svg",
+ "description": "path to an icon (svg, png, etc) for displaying in the gutter for full coverage"
+ },
+ "coverage-gutters.partialGutterIconPathLight": {
+ "type": "string",
+ "default": "./images/partial-gutter-icon-light.svg",
+ "description": "path to an icon (svg, png, etc) for displaying in the gutter for partial coverage"
+ },
+ "coverage-gutters.partialGutterIconPathDark": {
+ "type": "string",
+ "default": "./images/partial-gutter-icon-dark.svg",
+ "description": "path to an icon (svg, png, etc) for displaying in the gutter for partial coverage"
+ },
+ "coverage-gutters.noGutterIconPathLight": {
+ "type": "string",
+ "default": "./images/no-gutter-icon-light.svg",
+ "description": "path to an icon (svg, png, etc) for displaying in the gutter for no coverage"
+ },
+ "coverage-gutters.noGutterIconPathDark": {
+ "type": "string",
+ "default": "./images/no-gutter-icon-dark.svg",
+ "description": "path to an icon (svg, png, etc) for displaying in the gutter for no coverage"
+ },
+ "coverage-gutters.showLineCoverage": {
+ "type": "boolean",
+ "default": true,
+ "description": "show or hide the line coverage"
+ },
+ "coverage-gutters.showRulerCoverage": {
+ "type": "boolean",
+ "default": true,
+ "description": "show or hide the ruler coverage"
+ },
+ "coverage-gutters.showGutterCoverage": {
+ "type": "boolean",
+ "default": true,
+ "description": "show or hide the gutter coverage"
+ },
+ "coverage-gutters.customizable.menus-editor-context-displayCoverage-enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "enable or disable the displayCoverage command in the editor/context menu"
+ },
+ "coverage-gutters.customizable.menus-editor-context-watchLcovFile-enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "enable or disable the watchLcovFile command in the editor/context menu"
+ },
+ "coverage-gutters.customizable.menus-editor-context-removeCoverage-enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "enable or disable the removeCoverage command in the editor/context menu"
+ },
+ "coverage-gutters.customizable.keybindings-displayCoverage-enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "enable or disable the keybinding shortcut for enabling coverage on the active file"
+ },
+ "coverage-gutters.customizable.keybindings-watchLcovFile-enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "enable or disable the keybinding shortcut for watching the lcov file"
+ },
+ "coverage-gutters.customizable.keybindings-removeCoverage-enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "enable or disable the keybinding shortcut for removing the coverage on the active file"
+ }
+ }
},
- "categories": [
- "Other"
+ "commands": [
+ {
+ "command": "extension.displayCoverage",
+ "title": "Coverage Gutters: Display File Coverage"
+ },
+ {
+ "command": "extension.watchLcovFile",
+ "title": "Coverage Gutters: Watch Lcov and Render Changes"
+ },
+ {
+ "command": "extension.removeCoverage",
+ "title": "Coverage Gutters: Remove File Coverage"
+ }
],
- "keywords": [
- "gutters",
- "code coverage",
- "lcov",
- "vscode",
- "extension"
+ "keybindings": [
+ {
+ "command": "extension.displayCoverage",
+ "key": "ctrl+shift+7",
+ "mac": "shift+cmd+7",
+ "when": "config.coverage-gutters.customizable.keybindings-displayCoverage-enabled"
+ },
+ {
+ "command": "extension.watchLcovFile",
+ "key": "ctrl+shift+8",
+ "mac": "shift+cmd+8",
+ "when": "config.coverage-gutters.customizable.keybindings-watchLcovFile-enabled"
+ },
+ {
+ "command": "extension.removeCoverage",
+ "key": "ctrl+shift+9",
+ "mac": "shift+cmd+9",
+ "when": "config.coverage-gutters.customizable.keybindings-removeCoverage-enabled"
+ }
],
- "main": "./out/src/extension",
- "contributes": {
- "configuration": {
- "title": "coverage-gutters",
- "properties": {
- "coverage-gutters.lcovname": {
- "type": "string",
- "default": "lcov.info",
- "description": "name of your lcov file"
- },
- "coverage-gutters.altSfCompare": {
- "type": "boolean",
- "default": false,
- "description": "uses a relative method of comparing lcov source file paths"
- },
- "coverage-gutters.highlightlight": {
- "type": "string",
- "default": "rgba(166, 220, 142, 0.75)",
- "description": "light themed highlight for code coverage"
- },
- "coverage-gutters.highlightdark": {
- "type": "string",
- "default": "rgba(36, 56, 27, 0.75)",
- "description": "dark themed highlight for code coverage"
- },
- "coverage-gutters.partialHighlightLight": {
- "type": "string",
- "default": "rgba(220, 213, 143, 0.75)",
- "description": "light theme partial highlight for code coverage"
- },
- "coverage-gutters.partialHighlightDark": {
- "type": "string",
- "default": "rgba(57, 50, 27, 0.75)",
- "description": "dark theme partial highlight for code coverage"
- },
- "coverage-gutters.noHighlightLight": {
- "type": "string",
- "default": "rgba(220, 143, 143, 0.75)",
- "description": "light theme partial highlight for code coverage"
- },
- "coverage-gutters.noHighlightDark": {
- "type": "string",
- "default": "rgba(57, 27, 27, 0.75)",
- "description": "dark theme partial highlight for code coverage"
- },
- "coverage-gutters.gutterIconPathLight": {
- "type": "string",
- "default": "./images/gutter-icon-light.svg",
- "description": "path to an icon (svg, png, etc) for displaying in the gutter for full coverage"
- },
- "coverage-gutters.gutterIconPathDark": {
- "type": "string",
- "default": "./images/gutter-icon-dark.svg",
- "description": "path to an icon (svg, png, etc) for displaying in the gutter for full coverage"
- },
- "coverage-gutters.partialGutterIconPathLight": {
- "type": "string",
- "default": "./images/partial-gutter-icon-light.svg",
- "description": "path to an icon (svg, png, etc) for displaying in the gutter for partial coverage"
- },
- "coverage-gutters.partialGutterIconPathDark": {
- "type": "string",
- "default": "./images/partial-gutter-icon-dark.svg",
- "description": "path to an icon (svg, png, etc) for displaying in the gutter for partial coverage"
- },
- "coverage-gutters.noGutterIconPathLight": {
- "type": "string",
- "default": "./images/no-gutter-icon-light.svg",
- "description": "path to an icon (svg, png, etc) for displaying in the gutter for no coverage"
- },
- "coverage-gutters.noGutterIconPathDark": {
- "type": "string",
- "default": "./images/no-gutter-icon-dark.svg",
- "description": "path to an icon (svg, png, etc) for displaying in the gutter for no coverage"
- },
- "coverage-gutters.showLineCoverage": {
- "type": "boolean",
- "default": true,
- "description": "show or hide the line coverage"
- },
- "coverage-gutters.showRulerCoverage": {
- "type": "boolean",
- "default": true,
- "description": "show or hide the ruler coverage"
- },
- "coverage-gutters.showGutterCoverage": {
- "type": "boolean",
- "default": true,
- "description": "show or hide the gutter coverage"
- },
- "coverage-gutters.customizable.menus-editor-context-displayCoverage-enabled": {
- "type": "boolean",
- "default": true,
- "description": "enable or disable the displayCoverage command in the editor/context menu"
- },
- "coverage-gutters.customizable.menus-editor-context-watchLcovFile-enabled": {
- "type": "boolean",
- "default": true,
- "description": "enable or disable the watchLcovFile command in the editor/context menu"
- },
- "coverage-gutters.customizable.menus-editor-context-removeCoverage-enabled": {
- "type": "boolean",
- "default": true,
- "description": "enable or disable the removeCoverage command in the editor/context menu"
- },
- "coverage-gutters.customizable.keybindings-displayCoverage-enabled": {
- "type": "boolean",
- "default": true,
- "description": "enable or disable the keybinding shortcut for enabling coverage on the active file"
- },
- "coverage-gutters.customizable.keybindings-watchLcovFile-enabled": {
- "type": "boolean",
- "default": true,
- "description": "enable or disable the keybinding shortcut for watching the lcov file"
- },
- "coverage-gutters.customizable.keybindings-removeCoverage-enabled": {
- "type": "boolean",
- "default": true,
- "description": "enable or disable the keybinding shortcut for removing the coverage on the active file"
- }
- }
- },
- "commands": [
- {
- "command": "extension.displayCoverage",
- "title": "Coverage Gutters: Display File Coverage"
- },
- {
- "command": "extension.watchLcovFile",
- "title": "Coverage Gutters: Watch Lcov and Render Changes"
- },
- {
- "command": "extension.removeCoverage",
- "title": "Coverage Gutters: Remove File Coverage"
- }
- ],
- "keybindings":[
- {
- "command": "extension.displayCoverage",
- "key": "ctrl+shift+7",
- "mac": "shift+cmd+7",
- "when": "config.coverage-gutters.customizable.keybindings-displayCoverage-enabled"
- },
- {
- "command": "extension.watchLcovFile",
- "key": "ctrl+shift+8",
- "mac": "shift+cmd+8",
- "when": "config.coverage-gutters.customizable.keybindings-watchLcovFile-enabled"
- },
- {
- "command": "extension.removeCoverage",
- "key": "ctrl+shift+9",
- "mac": "shift+cmd+9",
- "when": "config.coverage-gutters.customizable.keybindings-removeCoverage-enabled"
- }
- ],
- "menus": {
- "editor/context": [
- {
- "when": "config.coverage-gutters.customizable.menus-editor-context-displayCoverage-enabled",
- "command": "extension.displayCoverage",
- "group": "Coverage-Gutters@1"
- },
- {
- "when": "config.coverage-gutters.customizable.menus-editor-context-watchLcovFile-enabled",
- "command": "extension.watchLcovFile",
- "group": "Coverage-Gutters@2"
- },
- {
- "when": "config.coverage-gutters.customizable.menus-editor-context-removeCoverage-enabled",
- "command": "extension.removeCoverage",
- "group": "Coverage-Gutters@3"
- }
- ]
+ "menus": {
+ "editor/context": [
+ {
+ "when": "config.coverage-gutters.customizable.menus-editor-context-displayCoverage-enabled",
+ "command": "extension.displayCoverage",
+ "group": "Coverage-Gutters@1"
+ },
+ {
+ "when": "config.coverage-gutters.customizable.menus-editor-context-watchLcovFile-enabled",
+ "command": "extension.watchLcovFile",
+ "group": "Coverage-Gutters@2"
+ },
+ {
+ "when": "config.coverage-gutters.customizable.menus-editor-context-removeCoverage-enabled",
+ "command": "extension.removeCoverage",
+ "group": "Coverage-Gutters@3"
}
- },
- "activationEvents": [
- "*"
- ],
- "scripts": {
- "lint": "tslint './src/**/*.ts' './test/**/*.ts'",
- "test": "node ./node_modules/vscode/bin/test",
- "test-ci": "npm run lint && npm test --silent",
- "vscode:prepublish": "tsc -p ./",
- "compile": "tsc -watch -p ./",
- "postinstall": "node ./node_modules/vscode/bin/install"
- },
- "devDependencies": {
- "typescript": "^2.0.3",
- "vscode": "^1.0.0",
- "mocha": "^2.3.3",
- "tslint": "^4.5.1",
- "@types/node": "^6.0.40",
- "@types/mocha": "^2.2.32"
- },
- "dependencies": {
- "lcov-parse": "0.0.10"
+ ]
}
-}
\ No newline at end of file
+ },
+ "activationEvents": [
+ "*"
+ ],
+ "scripts": {
+ "lint": "tslint './src/**/*.ts' './test/**/*.ts'",
+ "test": "node ./node_modules/vscode/bin/test",
+ "test-ci": "npm run lint && npm test --silent",
+ "vscode:prepublish": "tsc -p ./",
+ "compile": "tsc -watch -p ./",
+ "postinstall": "node ./node_modules/vscode/bin/install"
+ },
+ "devDependencies": {
+ "typescript": "^2.2.2",
+ "vscode": "^1.1.0",
+ "mocha": "^2.3.3",
+ "tslint": "^5.0.0",
+ "@types/node": "^6.0.40",
+ "@types/mocha": "^2.2.32",
+ "@types/request": "0.0.42",
+ "@types/uuid": "2.0.29"
+ },
+ "dependencies": {
+ "lcov-parse": "0.0.10",
+ "request": "2.81.0",
+ "uuid": "3.0.1"
+ }
+}
diff --git a/src/config.ts b/src/config.ts
index d5db5e8..664eefb 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -3,25 +3,23 @@ import {
ExtensionContext,
OverviewRulerLane,
TextEditorDecorationType,
+ WorkspaceConfiguration,
} from "vscode";
+import {Reporter} from "./reporter";
import {InterfaceVscode} from "./wrappers/vscode";
-export type ConfigStore = {
+export interface IConfigStore {
lcovFileName: string;
fullCoverageDecorationType: TextEditorDecorationType;
partialCoverageDecorationType: TextEditorDecorationType;
noCoverageDecorationType: TextEditorDecorationType;
altSfCompare: boolean;
-};
-
-export interface InterfaceConfig {
- get(): ConfigStore;
- setup(): ConfigStore;
}
-export class Config implements InterfaceConfig {
+export class Config {
private vscode: InterfaceVscode;
private context: ExtensionContext;
+ private reporter: Reporter;
private lcovFileName: string;
private fullCoverageDecorationType: TextEditorDecorationType;
@@ -29,12 +27,13 @@ export class Config implements InterfaceConfig {
private noCoverageDecorationType: TextEditorDecorationType;
private altSfCompare: boolean;
- constructor(vscode: InterfaceVscode, context: ExtensionContext) {
+ constructor(vscode: InterfaceVscode, context: ExtensionContext, reporter: Reporter) {
this.vscode = vscode;
this.context = context;
+ this.reporter = reporter;
}
- public get(): ConfigStore {
+ public get(): IConfigStore {
return {
altSfCompare: this.altSfCompare,
fullCoverageDecorationType: this.fullCoverageDecorationType,
@@ -44,19 +43,23 @@ export class Config implements InterfaceConfig {
};
}
- public setup(): ConfigStore {
- // Customizable UI configurations
+ public setup(): IConfigStore {
const rootCustomConfig = this.vscode.getConfiguration("coverage-gutters.customizable");
+ this.sendConfigMetrics(rootCustomConfig, "customConfig");
+
+ // Customizable UI configurations
const configsCustom = Object.keys(rootCustomConfig);
- for (let element of configsCustom) {
+ for (const element of configsCustom) {
this.vscode.executeCommand(
"setContext",
"config.coverage-gutters.customizable." + element,
rootCustomConfig.get(element));
}
- // Basic configurations
const rootConfig = this.vscode.getConfiguration("coverage-gutters");
+ this.sendConfigMetrics(rootConfig, "config");
+
+ // Basic configurations
this.lcovFileName = rootConfig.get("lcovname") as string;
this.altSfCompare = rootConfig.get("altSfCompare") as boolean;
@@ -130,4 +133,10 @@ export class Config implements InterfaceConfig {
return this.get();
}
+
+ private sendConfigMetrics(config: WorkspaceConfiguration, category: string) {
+ Object.keys(config).forEach((configElement) => {
+ this.reporter.sendEvent(category, configElement, config.get(configElement) as string);
+ });
+ }
}
diff --git a/src/extension.ts b/src/extension.ts
index ef45ae8..f5225f1 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,18 +1,23 @@
import * as vscode from "vscode";
import {Gutters} from "./gutters";
+import {Reporter} from "./reporter";
+import {Request} from "./wrappers/request";
+import {Uuid} from "./wrappers/uuid";
export function activate(context: vscode.ExtensionContext) {
- let gutters = new Gutters(context);
+ const enableMetrics = vscode.workspace.getConfiguration("telemetry").get("enableTelemetry") as boolean;
+ const reporter = new Reporter(new Request(), new Uuid(), enableMetrics);
+ const gutters = new Gutters(context, reporter);
- let display = vscode.commands.registerCommand("extension.displayCoverage", () => {
+ const display = vscode.commands.registerCommand("extension.displayCoverage", () => {
gutters.displayCoverageForActiveFile();
});
- let watchLcovFile = vscode.commands.registerCommand("extension.watchLcovFile", () => {
+ const watchLcovFile = vscode.commands.registerCommand("extension.watchLcovFile", () => {
gutters.watchLcovFile();
});
- let remove = vscode.commands.registerCommand("extension.removeCoverage", () => {
+ const remove = vscode.commands.registerCommand("extension.removeCoverage", () => {
gutters.removeCoverageForActiveFile();
});
diff --git a/src/gutters.ts b/src/gutters.ts
index bff79d2..85c0957 100644
--- a/src/gutters.ts
+++ b/src/gutters.ts
@@ -4,30 +4,35 @@ import {
TextEditor,
window,
} from "vscode";
+
import {Fs} from "./wrappers/fs";
import {LcovParse} from "./wrappers/lcov-parse";
import {Vscode} from "./wrappers/vscode";
-import {Config, ConfigStore} from "./config";
-import {Indicators, InterfaceIndicators} from "./indicators";
-import {InterfaceLcov, Lcov} from "./lcov";
+import {Config, IConfigStore} from "./config";
+import {Indicators} from "./indicators";
+import {Lcov} from "./lcov";
+import {Reporter} from "./reporter";
const vscodeImpl = new Vscode();
const fsImpl = new Fs();
const parseImpl = new LcovParse();
export class Gutters {
- private configStore: ConfigStore;
+ private configStore: IConfigStore;
private fileWatcher: FileSystemWatcher;
- private lcov: InterfaceLcov;
- private indicators: InterfaceIndicators;
+ private lcov: Lcov;
+ private indicators: Indicators;
+ private reporter: Reporter;
private textEditors: TextEditor[];
- constructor(context: ExtensionContext) {
- this.configStore = new Config(vscodeImpl, context).setup();
+ constructor(context: ExtensionContext, reporter: Reporter) {
+ this.configStore = new Config(vscodeImpl, context, reporter).setup();
this.lcov = new Lcov(this.configStore, vscodeImpl, fsImpl);
this.indicators = new Indicators(parseImpl, vscodeImpl, this.configStore);
+ this.reporter = reporter;
this.textEditors = [];
+ this.reporter.sendEvent("user", "start");
}
public async displayCoverageForActiveFile() {
@@ -36,6 +41,7 @@ export class Gutters {
try {
const lcovPath = await this.lcov.find();
await this.loadAndRenderCoverage(textEditor, lcovPath);
+ this.reporter.sendEvent("user", "display-coverage");
} catch (error) {
this.handleError(error);
}
@@ -60,6 +66,7 @@ export class Gutters {
}
});
});
+ this.reporter.sendEvent("user", "watch-lcov");
} catch (error) {
this.handleError(error);
}
@@ -69,11 +76,13 @@ export class Gutters {
const activeEditor = window.activeTextEditor;
this.removeTextEditorFromCache(activeEditor);
this.removeDecorationsForTextEditor(activeEditor);
+ this.reporter.sendEvent("user", "remove-coverage");
}
public dispose() {
this.fileWatcher.dispose();
this.textEditors.forEach(this.removeDecorationsForTextEditor);
+ this.reporter.sendEvent("cleanup", "dispose");
}
public getTextEditors(): TextEditor[] {
@@ -83,6 +92,7 @@ export class Gutters {
private handleError(error: Error) {
const message = error.message ? error.message : error;
window.showErrorMessage(message.toString());
+ this.reporter.sendEvent("error", message.toString());
}
private addTextEditorToCache(editor: TextEditor) {
diff --git a/src/indicators.ts b/src/indicators.ts
index 74d680c..f6752b2 100644
--- a/src/indicators.ts
+++ b/src/indicators.ts
@@ -1,30 +1,25 @@
-import {ConfigStore} from "./config";
+import {IConfigStore} from "./config";
import {InterfaceLcovParse} from "./wrappers/lcov-parse";
import {InterfaceVscode} from "./wrappers/vscode";
import {LcovSection} from "lcov-parse";
import {Range, TextEditor} from "vscode";
-export interface InterfaceIndicators {
- renderToTextEditor(lines: LcovSection, textEditor: TextEditor): Promise;
- extract(lcovFile: string, file: string): Promise;
-}
-
-export type CoverageLines = {
+export interface ICoverageLines {
full: Range[];
partial: Range[];
none: Range[];
-};
+}
-export class Indicators implements InterfaceIndicators {
+export class Indicators {
private parse: InterfaceLcovParse;
private vscode: InterfaceVscode;
- private configStore: ConfigStore;
+ private configStore: IConfigStore;
constructor(
parse: InterfaceLcovParse,
vscode: InterfaceVscode,
- configStore: ConfigStore,
+ configStore: IConfigStore,
) {
this.parse = parse;
this.vscode = vscode;
@@ -33,7 +28,7 @@ export class Indicators implements InterfaceIndicators {
public renderToTextEditor(section: LcovSection, textEditor: TextEditor): Promise {
return new Promise((resolve, reject) => {
- let coverageLines: CoverageLines = {
+ const coverageLines: ICoverageLines = {
full: [],
none: [],
partial: [],
@@ -50,7 +45,7 @@ export class Indicators implements InterfaceIndicators {
return new Promise((resolve, reject) => {
this.parse.source(lcovFile, (err, data) => {
if (err) { return reject(err); }
- let section = data.find((lcovSection) => {
+ const section = data.find((lcovSection) => {
return this.compareFilePaths(lcovSection.file, file);
});
@@ -60,7 +55,7 @@ export class Indicators implements InterfaceIndicators {
});
}
- private setDecorationsForEditor(editor: TextEditor, coverage: CoverageLines) {
+ private setDecorationsForEditor(editor: TextEditor, coverage: ICoverageLines) {
// remove coverage first to prevent graphical conflicts
editor.setDecorations(this.configStore.fullCoverageDecorationType, []);
editor.setDecorations(this.configStore.noCoverageDecorationType, []);
@@ -71,7 +66,7 @@ export class Indicators implements InterfaceIndicators {
editor.setDecorations(this.configStore.partialCoverageDecorationType, coverage.partial);
}
- private filterCoverage(section: LcovSection, coverageLines: CoverageLines): CoverageLines {
+ private filterCoverage(section: LcovSection, coverageLines: ICoverageLines): ICoverageLines {
section.lines.details.forEach((detail) => {
const lineRange = new Range(detail.line - 1, 0, detail.line - 1, 0);
if (detail.hit > 0) {
diff --git a/src/lcov.ts b/src/lcov.ts
index 82ef2d2..0c5d6e5 100644
--- a/src/lcov.ts
+++ b/src/lcov.ts
@@ -1,19 +1,14 @@
-import {ConfigStore} from "./config";
+import {IConfigStore} from "./config";
import {InterfaceFs} from "./wrappers/fs";
import {InterfaceVscode} from "./wrappers/vscode";
-export interface InterfaceLcov {
- find(): Promise;
- load(lcovPath: string): Promise;
-}
-
-export class Lcov implements InterfaceLcov {
- private configStore: ConfigStore;
+export class Lcov {
+ private configStore: IConfigStore;
private vscode: InterfaceVscode;
private fs: InterfaceFs;
constructor(
- configStore: ConfigStore,
+ configStore: IConfigStore,
vscode: InterfaceVscode,
fs: InterfaceFs,
) {
diff --git a/src/reporter.ts b/src/reporter.ts
new file mode 100644
index 0000000..416dd9c
--- /dev/null
+++ b/src/reporter.ts
@@ -0,0 +1,44 @@
+import {platform} from "os";
+import {Request} from "./wrappers/request";
+import {Uuid} from "./wrappers/uuid";
+
+const PLATFORM = platform();
+const GA_TRACKING_ID = ""; // add before a release;
+const EXT_NAME = "vscode-coverage-gutters";
+const EXT_VERSION = "0.4.0";
+
+export class Reporter {
+ private readonly cid: string;
+ private readonly enableMetrics: boolean;
+ private readonly request: Request;
+
+ constructor(request: Request, uuid: Uuid, enableMetrics: boolean) {
+ this.request = request;
+ this.cid = uuid.get();
+ this.enableMetrics = enableMetrics;
+ }
+
+ public sendEvent(
+ category: string,
+ action: string,
+ label?: string,
+ value?: number,
+ ) {
+ if (!this.enableMetrics) { return; }
+ const data = {
+ an: EXT_NAME,
+ av: EXT_VERSION,
+ cid: this.cid,
+ ea: action,
+ ec: category,
+ el: label,
+ ev: value,
+ t: "event",
+ tid: GA_TRACKING_ID,
+ ua: PLATFORM,
+ v: "1",
+ };
+
+ return this.request.post("https://www.google-analytics.com/collect", { form: data });
+ }
+}
diff --git a/src/wrappers/request.ts b/src/wrappers/request.ts
new file mode 100644
index 0000000..f716dc2
--- /dev/null
+++ b/src/wrappers/request.ts
@@ -0,0 +1,10 @@
+import {post} from "request";
+
+export interface IOptions { form?: object; }
+
+export class Request {
+ public post(uri: string, options?: IOptions): void {
+ post(uri, options);
+ return;
+ }
+}
diff --git a/src/wrappers/uuid.ts b/src/wrappers/uuid.ts
new file mode 100644
index 0000000..4c49ee7
--- /dev/null
+++ b/src/wrappers/uuid.ts
@@ -0,0 +1,7 @@
+import {v4} from "uuid";
+
+export class Uuid {
+ public get(): string {
+ return v4();
+ }
+}
diff --git a/test/config.test.ts b/test/config.test.ts
index 08bf924..04af4b0 100644
--- a/test/config.test.ts
+++ b/test/config.test.ts
@@ -1,7 +1,4 @@
-"use strict";
-
import * as assert from "assert";
-
import * as vscode from "vscode";
import {Config} from "../src/config";
diff --git a/test/gutters.test.ts b/test/gutters.test.ts
index 40bc905..66f7ba7 100644
--- a/test/gutters.test.ts
+++ b/test/gutters.test.ts
@@ -1,21 +1,25 @@
-"use strict";
-
import * as assert from "assert";
-
import * as vscode from "vscode";
import {Gutters} from "../src/gutters";
+import {Reporter} from "../src/reporter";
suite("Gutters Tests", function() {
test("Should setup gutters based on config values with no errors", function(done) {
this.timeout(12000);
try {
- let ctx: vscode.ExtensionContext = {
+ const ctx: vscode.ExtensionContext = {
asAbsolutePath() {
return "test";
},
subscriptions: [],
- };
- const gutters = new Gutters(ctx);
+ } as any;
+ const reporter: Reporter = {
+ sendEvent() {
+ return;
+ },
+ } as any;
+
+ const gutters = new Gutters(ctx, reporter);
assert.equal(gutters.getTextEditors().length, 0);
return done();
} catch (e) {
@@ -25,13 +29,19 @@ suite("Gutters Tests", function() {
test("Should remove the activeEditor from the textEditors array", async function() {
this.timeout(12000);
- let ctx: vscode.ExtensionContext = {
+ const ctx: vscode.ExtensionContext = {
asAbsolutePath() {
return "test";
},
subscriptions: [],
- };
- const gutters = new Gutters(ctx);
+ } as any;
+ const reporter: Reporter = {
+ sendEvent() {
+ return;
+ },
+ } as any;
+
+ const gutters = new Gutters(ctx, reporter);
await gutters.displayCoverageForActiveFile();
assert.equal(gutters.getTextEditors().length, 1);
gutters.removeCoverageForActiveFile();
diff --git a/test/indicators.test.ts b/test/indicators.test.ts
index 918aca4..f2dc42e 100644
--- a/test/indicators.test.ts
+++ b/test/indicators.test.ts
@@ -1,5 +1,3 @@
-"use strict";
-
import * as assert from "assert";
import {BranchDetail, LcovSection} from "lcov-parse";
import {TextEditor} from "vscode";
diff --git a/test/lcov.test.ts b/test/lcov.test.ts
index c91a540..ac445fa 100644
--- a/test/lcov.test.ts
+++ b/test/lcov.test.ts
@@ -1,7 +1,4 @@
-"use strict";
-
import * as assert from "assert";
-
import {Lcov} from "../src/lcov";
import {Fs} from "../src/wrappers/fs";
import {Vscode} from "../src/wrappers/vscode";
diff --git a/test/reporter.test.ts b/test/reporter.test.ts
new file mode 100644
index 0000000..20d87ab
--- /dev/null
+++ b/test/reporter.test.ts
@@ -0,0 +1,61 @@
+import * as assert from "assert";
+import {Reporter} from "../src/reporter";
+import {IOptions} from "../src/wrappers/request";
+
+suite("Reporter Tests", function() {
+ test("Should not report metrics if enabledMetrics false", function() {
+ const fakeRequest = {
+ post(uri: string, options?: IOptions) {
+ assert.equal(1, 2);
+ return;
+ },
+ };
+
+ const fakeUuid = {
+ get() {
+ return "fakeuuidhere";
+ },
+ };
+
+ const reporter = new Reporter(fakeRequest, fakeUuid, false);
+ reporter.sendEvent("test", "action");
+ });
+
+ test("Should send metrics if enabledMetrics is true", function() {
+ const fakeRequest = {
+ post(uri: string, options?: IOptions) {
+ // tslint:disable-next-line:no-string-literal
+ assert.equal(options.form["ec"], "test");
+ return;
+ },
+ };
+
+ const fakeUuid = {
+ get() {
+ return "fakeuuidhere";
+ },
+ };
+
+ const reporter = new Reporter(fakeRequest, fakeUuid, true);
+ reporter.sendEvent("test", "action");
+ });
+
+ test("GA tracking id should not be set in code", function() {
+ const fakeRequest = {
+ post(uri: string, options?: IOptions) {
+ // tslint:disable-next-line:no-string-literal
+ assert.equal(options.form["tid"], "");
+ return;
+ },
+ };
+
+ const fakeUuid = {
+ get() {
+ return "fakeuuidhere";
+ },
+ };
+
+ const reporter = new Reporter(fakeRequest, fakeUuid, true);
+ reporter.sendEvent("test", "action");
+ });
+});