Skip to content

Commit bb4d09b

Browse files
Gerrit0ryanluker
andauthored
Make ms-vscode.live-server an optional dependency (#480)
Resolves #392 Co-authored-by: Ryan Luker <[email protected]>
1 parent a0d4844 commit bb4d09b

File tree

7 files changed

+83
-63
lines changed

7 files changed

+83
-63
lines changed

QUICKSTART/QUICKSTART.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ You need two extensions to get started. Installation via CLI or Quick Open work
6565
</details>
6666

6767
<details>
68-
<summary>Live Preview - ms-vscode.live-server</summary>
68+
<summary>Optional - Live Preview - ms-vscode.live-server</summary>
6969

7070
-----
7171

@@ -104,7 +104,7 @@ After the extensions are installed, you need to get your coverage files.
104104
* `coverage.xml`
105105
* `jacoco.xml`
106106
* `coverage.cobertura.xml`
107-
107+
108108
2. Customizable file name. You may use any filename you like, but the file must be a Clover, Cobertura, Jacoco, or LCov format file.
109109

110110
3. Generating a coverage file is language specific.
@@ -155,9 +155,9 @@ After the extensions are installed, you need to get your coverage files.
155155

156156
-----
157157

158-
* Directory where the **coverage file** is located.
158+
* Directory where the **coverage file** is located.
159159

160-
* Default is "`**`" which is the entire workspace.
160+
* Default is "`**`" which is the entire workspace.
161161

162162
* To prevent searching through the entire workspace, you can limit **coverage gutters* to a specific folder.
163163

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ See [examples directory](example) on how to setup a project.
5555
## Requirements
5656
- vscode 1.27.0 and up
5757
- macos, linux or windows
58-
- Requires Live Preview extension
58+
- Optional: Live Preview extension
5959
- vscode extension id: ms-vscode.live-server
6060
- v0.2.12 or higher
6161

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,5 @@
294294
"lcov-parse": "1.0.0",
295295
"tslib": "2.4.0",
296296
"uuid": "8.3.2"
297-
},
298-
"extensionDependencies": [
299-
"ms-vscode.live-server"
300-
]
297+
}
301298
}

src/extension/gutters.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { commands, extensions } from "vscode";
2-
import { OutputChannel, window } from "vscode";
1+
import { commands, extensions, window, OutputChannel } from "vscode";
32
import { Coverage } from "../coverage-system/coverage";
43
import { CoverageService } from "../coverage-system/coverageservice";
54
import { Config } from "./config";
65
import { StatusBarToggler } from "./statusbartoggler";
76

7+
export const PREVIEW_COMMAND = "livePreview.start.internalPreview.atFile";
8+
89
export class Gutters {
910
private coverage: Coverage;
1011
private outputChannel: OutputChannel;
@@ -29,6 +30,15 @@ export class Gutters {
2930

3031
public async previewCoverageReport() {
3132
try {
33+
const livePreview = this.getLiveServerExtension();
34+
if (!livePreview) {
35+
await window.showErrorMessage("Live Preview extension not installed", {
36+
modal: true,
37+
detail: "The ms-vscode.live-server extension must be installed to preview the coverage report."
38+
}, "Ok");
39+
return;
40+
}
41+
3242
const coverageReports = await this.coverage.findReports();
3343
const pickedReport = await this.coverage.pickFile(
3444
coverageReports,
@@ -39,27 +49,18 @@ export class Gutters {
3949
return;
4050
}
4151

52+
// is the ext loaded and ready?
53+
if (livePreview.isActive === false) {
54+
await livePreview.activate();
55+
}
56+
4257
// TODO: Figure out how to convert pickedReport to a workspace relative filename.
4358
// Right now the livePreview.start.internalPreview.atFile is called with "false" as
4459
// the second parameter. This means that the file specified has an absolute path.
4560
// See the Live Preview extension source code:
4661
// https://github.com/microsoft/vscode-livepreview/blob/
4762
// 3be1e2eb5c8a7b51aa4a88275ad73bb4d923432b/src/extension.ts#L169
48-
const livePreview = extensions.getExtension("ms-vscode.live-server");
49-
// is the ext loaded and ready?
50-
if (livePreview?.isActive === false) {
51-
livePreview.activate().then(
52-
function() {
53-
console.log("Extension activated");
54-
commands.executeCommand("livePreview.start.internalPreview.atFile", pickedReport, false);
55-
},
56-
function() {
57-
console.log("Extension activation failed");
58-
},
59-
);
60-
} else {
61-
commands.executeCommand("livePreview.start.internalPreview.atFile", pickedReport, false);
62-
}
63+
await commands.executeCommand(PREVIEW_COMMAND, pickedReport, false);
6364

6465
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6566
} catch (error: any) {
@@ -125,6 +126,10 @@ export class Gutters {
125126
}
126127
}
127128

129+
getLiveServerExtension() {
130+
return extensions.getExtension("ms-vscode.live-server");
131+
}
132+
128133
private handleError(area: string, error: Error) {
129134
const message = error.message ? error.message : error;
130135
const stackTrace = error.stack;

test/extension.test.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,69 @@
11
import { expect } from "chai";
22
import { exec } from "child_process";
33
import sinon from "sinon";
4-
import { after } from "mocha";
4+
import { after, afterEach } from "mocha";
55
import * as vscode from "vscode";
66
import { ICoverageLines, Renderer } from "../src/coverage-system/renderer";
77
import { StatusBarToggler } from "../src/extension/statusbartoggler";
8+
import { Gutters, PREVIEW_COMMAND } from "../src/extension/gutters";
89

910
suite("Extension Tests", function () {
11+
const disposables: vscode.Disposable[] = [];
12+
13+
afterEach(() => {
14+
// Clear mocks after each test to avoid cascading failures due to one test failing
15+
sinon.restore();
16+
17+
// Also clear any disposables
18+
disposables.forEach(d => d.dispose());
19+
disposables.length = 0;
20+
});
21+
1022
after(() => {
1123
vscode.window.showInformationMessage('All tests done!');
1224
});
1325

14-
test("Preview the coverage report in a new webview tab @integration", async () => {
26+
test("Preview the coverage report without live server extension @integration", async function() {
27+
const getLiveServerStub = sinon.stub(Gutters.prototype, "getLiveServerExtension");
28+
getLiveServerStub.returns(undefined);
29+
30+
const toastStub = sinon.stub(vscode.window, "showErrorMessage");
31+
32+
await vscode.commands.executeCommand("coverage-gutters.previewCoverageReport");
33+
34+
expect(toastStub.callCount).to.equal(1);
35+
const call = toastStub.getCall(0);
36+
expect(call.args[0]).to.equal("Live Preview extension not installed");
37+
});
38+
39+
test("Preview the coverage report with live server installed but inactive @integration", async function() {
40+
const getLiveServerStub = sinon.stub(Gutters.prototype, "getLiveServerExtension");
41+
const liveServer = { isActive: false, activate: () => { liveServer.isActive = true; } };
42+
getLiveServerStub.returns(liveServer as never);
43+
44+
const doPreviewStub = sinon.stub();
45+
disposables.push(vscode.commands.registerCommand(PREVIEW_COMMAND, doPreviewStub));
46+
47+
// Note: depends on "coverage-gutters.coverageReportFileName": "index.html",
48+
// being set in the example.code-workspace setting file as the coverage report
49+
// is in the root of the node folder and not inside the default /coverage
50+
await vscode.commands.executeCommand("coverage-gutters.previewCoverageReport");
51+
expect(liveServer.isActive).to.equal(true);
52+
expect(doPreviewStub.callCount).to.equal(1);
53+
});
54+
55+
test("Preview the coverage report with live server @integration", async function() {
56+
const getLiveServerStub = sinon.stub(Gutters.prototype, "getLiveServerExtension");
57+
getLiveServerStub.returns({ isActive: true } as never);
58+
59+
const doPreviewStub = sinon.stub();
60+
disposables.push(vscode.commands.registerCommand(PREVIEW_COMMAND, doPreviewStub));
61+
1562
// Note: depends on "coverage-gutters.coverageReportFileName": "index.html",
1663
// being set in the example.code-workspace setting file as the coverage report
1764
// is in the root of the node folder and not inside the default /coverage
1865
await vscode.commands.executeCommand("coverage-gutters.previewCoverageReport");
19-
// Look to see if the webview is open and showing preview coverage
20-
await checkCoverage(() => {
21-
const livePreview = vscode.extensions.getExtension("ms-vscode.live-server");
22-
expect(livePreview?.isActive).to.equal(true);
23-
});
66+
expect(doPreviewStub.callCount).to.equal(1);
2467
});
2568

2669
test("Run display coverage on a test file that has coverages generated remotely @integration", async () => {

test/runTest.ts

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,23 @@
1-
import * as cp from "child_process";
21
import * as path from "path";
3-
import {
4-
downloadAndUnzipVSCode,
5-
resolveCliArgsFromVSCodeExecutablePath,
6-
runTests,
7-
} from "@vscode/test-electron";
2+
import { runTests } from "@vscode/test-electron";
83

94
async function main() {
105
try {
116
const extensionDevelopmentPath = path.resolve(__dirname, "../../out");
127
const extensionTestsPath = path.resolve(__dirname, "index");
138

14-
// Add the dependent extension for test coverage preview functionality
15-
const vscodeExecutablePath = await downloadAndUnzipVSCode("insiders");
16-
const [cliPath, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);
17-
18-
// Use cp.spawn / cp.exec for custom setup
19-
// Note: shell true is needed to fix an issue with install-extension (on windows)
20-
// https://github.com/microsoft/vscode-test/issues/266#issuecomment-2085723194
21-
const output = cp.spawnSync(
22-
cliPath,
23-
[...args, "--install-extension", "ms-vscode.live-server"],
24-
{
25-
shell: process.platform === 'win32',
26-
encoding: 'utf-8',
27-
stdio: 'inherit'
28-
},
29-
);
30-
31-
// Useful for debugging failing dependant extension installs
32-
console.info(output);
33-
34-
// Default test options for gutters testing
359
await runTests({
36-
vscodeExecutablePath,
10+
version: "insiders",
3711
extensionDevelopmentPath,
3812
extensionTestsPath,
3913
launchArgs: ["example/example.code-workspace"],
40-
});
14+
})
4115

4216
console.info("Success!");
4317
process.exit(0);
4418
} catch (err) {
4519
console.error("Failed to run tests");
20+
console.error(err);
4621
process.exit(1);
4722
}
4823
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"alwaysStrict": true,
88
"importHelpers": true,
99
"forceConsistentCasingInFileNames": true,
10-
"esModuleInterop": true,
10+
"esModuleInterop": true,
1111
"lib": [
1212
"es6",
1313
"dom",

0 commit comments

Comments
 (0)