Skip to content

Commit f0fddeb

Browse files
Merge pull request #3570 from PowerShell/andschwa/fix-paths
Fix path regressions and cover with tests
2 parents 47b2e76 + f1519f8 commit f0fddeb

12 files changed

+175
-62
lines changed

.vsts-ci/templates/ci-general.yml

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ steps:
4646
inputs:
4747
targetType: inline
4848
script: |
49+
Get-Module -ListAvailable Pester
4950
Install-Module InvokeBuild -Scope CurrentUser -Force
5051
Invoke-Build
5152
Write-Host "##vso[task.setvariable variable=vsixPath]$(Resolve-Path powershell-*.vsix)"

src/features/Examples.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22
// Licensed under the MIT License.
33

44
import path = require("path");
5+
import utils = require("../utils")
56
import vscode = require("vscode");
67

78
export class ExamplesFeature implements vscode.Disposable {
89
private command: vscode.Disposable;
9-
private examplesPath: string;
10+
private examplesPath: vscode.Uri;
1011

1112
constructor() {
12-
this.examplesPath = path.resolve(__dirname, "../../examples");
13+
this.examplesPath = vscode.Uri.file(path.resolve(__dirname, "../examples"));
1314
this.command = vscode.commands.registerCommand("PowerShell.OpenExamplesFolder", () => {
14-
vscode.commands.executeCommand(
15-
"vscode.openFolder",
16-
vscode.Uri.file(this.examplesPath),
17-
true);
15+
vscode.commands.executeCommand("vscode.openFolder", this.examplesPath, true);
16+
// Return existence of the path for testing. The `vscode.openFolder`
17+
// command should do this, but doesn't (yet).
18+
return utils.fileExists(this.examplesPath);
1819
});
1920
}
2021

src/features/PesterTests.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -18,48 +18,51 @@ export class PesterTestsFeature implements vscode.Disposable {
1818
private invokePesterStubScriptPath: string;
1919

2020
constructor(private sessionManager: SessionManager) {
21-
this.invokePesterStubScriptPath = path.resolve(__dirname, "../../modules/PowerShellEditorServices/InvokePesterStub.ps1");
21+
this.invokePesterStubScriptPath = path.resolve(__dirname, "../modules/PowerShellEditorServices/InvokePesterStub.ps1");
2222

2323
// File context-menu command - Run Pester Tests
2424
this.command = vscode.commands.registerCommand(
2525
"PowerShell.RunPesterTestsFromFile",
2626
(fileUri) => {
27-
this.launchAllTestsInActiveEditor(LaunchType.Run, fileUri);
27+
return this.launchAllTestsInActiveEditor(LaunchType.Run, fileUri);
2828
});
2929
// File context-menu command - Debug Pester Tests
3030
this.command = vscode.commands.registerCommand(
3131
"PowerShell.DebugPesterTestsFromFile",
3232
(fileUri) => {
33-
this.launchAllTestsInActiveEditor(LaunchType.Debug, fileUri);
33+
return this.launchAllTestsInActiveEditor(LaunchType.Debug, fileUri);
3434
});
3535
// This command is provided for usage by PowerShellEditorServices (PSES) only
3636
this.command = vscode.commands.registerCommand(
3737
"PowerShell.RunPesterTests",
3838
(uriString, runInDebugger, describeBlockName?, describeBlockLineNumber?, outputPath?) => {
39-
this.launchTests(uriString, runInDebugger, describeBlockName, describeBlockLineNumber, outputPath);
39+
return this.launchTests(uriString, runInDebugger, describeBlockName, describeBlockLineNumber, outputPath);
4040
});
4141
}
4242

4343
public dispose() {
4444
this.command.dispose();
4545
}
4646

47-
private launchAllTestsInActiveEditor(launchType: LaunchType, fileUri: vscode.Uri) {
47+
private async launchAllTestsInActiveEditor(
48+
launchType: LaunchType,
49+
fileUri: vscode.Uri): Promise<boolean> {
50+
4851
const uriString = (fileUri || vscode.window.activeTextEditor.document.uri).toString();
4952
const launchConfig = this.createLaunchConfig(uriString, launchType);
50-
this.launch(launchConfig);
53+
return this.launch(launchConfig);
5154
}
5255

5356
private async launchTests(
5457
uriString: string,
5558
runInDebugger: boolean,
5659
describeBlockName?: string,
5760
describeBlockLineNumber?: number,
58-
outputPath?: string) {
61+
outputPath?: string): Promise<boolean> {
5962

6063
const launchType = runInDebugger ? LaunchType.Debug : LaunchType.Run;
6164
const launchConfig = this.createLaunchConfig(uriString, launchType, describeBlockName, describeBlockLineNumber, outputPath);
62-
this.launch(launchConfig);
65+
return this.launch(launchConfig);
6366
}
6467

6568
private createLaunchConfig(
@@ -126,9 +129,9 @@ export class PesterTestsFeature implements vscode.Disposable {
126129
return launchConfig;
127130
}
128131

129-
private launch(launchConfig) {
132+
private async launch(launchConfig): Promise<boolean> {
130133
// Create or show the interactive console
131-
// TODO #367: Check if "newSession" mode is configured
134+
// TODO: #367 Check if "newSession" mode is configured
132135
vscode.commands.executeCommand("PowerShell.ShowSessionConsole", true);
133136

134137
// Write out temporary debug session file
@@ -137,6 +140,10 @@ export class PesterTestsFeature implements vscode.Disposable {
137140
this.sessionManager.getSessionDetails());
138141

139142
// TODO: Update to handle multiple root workspaces.
140-
vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0], launchConfig);
143+
//
144+
// Ensure the necessary script exists (for testing). The debugger will
145+
// start regardless, but we also pass its success along.
146+
return utils.fileExists(this.invokePesterStubScriptPath)
147+
&& vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0], launchConfig);
141148
}
142149
}

src/settings.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ export function load(): ISettings {
157157

158158
const defaultDeveloperSettings: IDeveloperSettings = {
159159
featureFlags: [],
160+
// From `<root>/out/main.js` we go to the directory before <root> and
161+
// then into the other repo.
160162
bundledModulesPath: "../../PowerShellEditorServices/module",
161163
editorServicesLogLevel: "Normal",
162164
editorServicesWaitForDebugger: false,
@@ -234,7 +236,7 @@ export function load(): ISettings {
234236
promptToUpdatePackageManagement:
235237
configuration.get<boolean>("promptToUpdatePackageManagement", true),
236238
bundledModulesPath:
237-
"../modules",
239+
"../modules", // Because the extension is always at `<root>/out/main.js`
238240
useX86Host:
239241
configuration.get<boolean>("useX86Host", false),
240242
enableProfileLoading:

src/utils.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
import fs = require("fs");
77
import os = require("os");
88
import path = require("path");
9+
import vscode = require("vscode");
910

1011
export const PowerShellLanguageId = "powershell";
1112

12-
export function ensurePathExists(targetPath: string) {
13+
export function ensurePathExists(targetPath: string): void {
1314
// Ensure that the path exists
1415
try {
16+
// TODO: Use vscode.workspace.fs
1517
fs.mkdirSync(targetPath);
1618
} catch (e) {
1719
// If the exception isn't to indicate that the folder exists already, rethrow it.
@@ -21,6 +23,23 @@ export function ensurePathExists(targetPath: string) {
2123
}
2224
}
2325

26+
// Check that the file exists in an asynchronous manner that relies solely on the VS Code API, not Node's fs library.
27+
export async function fileExists(targetPath: string | vscode.Uri): Promise<boolean> {
28+
try {
29+
await vscode.workspace.fs.stat(
30+
targetPath instanceof vscode.Uri
31+
? targetPath
32+
: vscode.Uri.file(targetPath));
33+
return true;
34+
} catch (e) {
35+
if (e instanceof vscode.FileSystemError.FileNotFound) {
36+
return false;
37+
}
38+
throw e;
39+
}
40+
41+
}
42+
2443
export function getPipePath(pipeName: string) {
2544
if (os.platform() === "win32") {
2645
return "\\\\.\\pipe\\" + pipeName;
@@ -45,7 +64,7 @@ export interface IEditorServicesSessionDetails {
4564

4665
export type IReadSessionFileCallback = (details: IEditorServicesSessionDetails) => void;
4766

48-
const sessionsFolder = path.resolve(__dirname, "..", "..", "sessions/");
67+
const sessionsFolder = path.resolve(__dirname, "../sessions");
4968
const sessionFilePathPrefix = path.resolve(sessionsFolder, "PSES-VSCode-" + process.env.VSCODE_PID);
5069

5170
// Create the sessions path if it doesn't exist already

test/core/paths.test.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import * as assert from "assert";
5+
import * as fs from "fs";
6+
import * as path from "path";
7+
import * as vscode from "vscode";
8+
import { suiteSetup } from "mocha";
9+
import utils = require("../utils");
10+
11+
suite("Path assumptions", () => {
12+
suiteSetup(utils.ensureExtensionIsActivated);
13+
14+
// TODO: This is skipped because it intereferes with other tests. Either
15+
// need to find a way to close the opened folder via a Code API, or find
16+
// another way to test this.
17+
test.skip("The examples folder can be opened (and exists)", async () => {
18+
assert(await vscode.commands.executeCommand("PowerShell.OpenExamplesFolder"));
19+
});
20+
21+
test("The session folder is created in the right place", async () => {
22+
assert(fs.existsSync(path.resolve(utils.rootPath, "sessions")));
23+
});
24+
25+
test("The logs folder is created in the right place", async () => {
26+
assert(fs.existsSync(path.resolve(utils.rootPath, "logs")));
27+
});
28+
});

test/features/CustomViews.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ hello
7070
content: "console.log('asdf');",
7171
},
7272
{
73-
fileName: "../../testCustomViews.js",
73+
fileName: "../testCustomViews.js",
7474
content: "console.log('asdf');",
7575
},
7676
],
7777
cssFiles: [],
7878
expectedHtmlString: `<html><head></head><body>
7979
hello
8080
<script src="${convertToVSCodeResourceScheme(path.join(__dirname, "testCustomViews.js"))}"></script>
81-
<script src="${convertToVSCodeResourceScheme(path.join(__dirname, "../../testCustomViews.js"))}"></script>
81+
<script src="${convertToVSCodeResourceScheme(path.join(__dirname, "../testCustomViews.js"))}"></script>
8282
</body></html>`,
8383
},
8484

@@ -129,7 +129,7 @@ hello
129129
styleSheetPaths: cssPaths,
130130
};
131131
try {
132-
assert.equal(htmlContentView.getContent(), testCase.expectedHtmlString);
132+
assert.strictEqual(htmlContentView.getContent(), testCase.expectedHtmlString);
133133
} finally {
134134
jsPaths.forEach((jsPath) => fs.unlinkSync(vscode.Uri.parse(jsPath).fsPath));
135135
cssPaths.forEach((cssPath) => fs.unlinkSync(vscode.Uri.parse(cssPath).fsPath));

test/features/ExternalApi.test.ts

+15-27
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,19 @@
22
// Licensed under the MIT License.
33

44
import * as assert from "assert";
5-
import * as vscode from "vscode";
6-
import { before, beforeEach, afterEach } from "mocha";
5+
import { suiteSetup, setup, teardown } from "mocha";
6+
import utils = require("../utils");
77
import { IExternalPowerShellDetails, IPowerShellExtensionClient } from "../../src/features/ExternalApi";
88

9-
// tslint:disable-next-line: no-var-requires
10-
const PackageJSON: any = require("../../../package.json");
11-
const testExtensionId = `${PackageJSON.publisher}.${PackageJSON.name}`;
12-
139
suite("ExternalApi feature - Registration API", () => {
1410
let powerShellExtensionClient: IPowerShellExtensionClient;
15-
before(async () => {
16-
const powershellExtension = vscode.extensions.getExtension<IPowerShellExtensionClient>(testExtensionId);
17-
if (!powershellExtension.isActive) {
18-
powerShellExtensionClient = await powershellExtension.activate();
19-
return;
20-
}
11+
suiteSetup(async () => {
12+
const powershellExtension = await utils.ensureExtensionIsActivated();
2113
powerShellExtensionClient = powershellExtension!.exports as IPowerShellExtensionClient;
2214
});
2315

2416
test("It can register and unregister an extension", () => {
25-
const sessionId: string = powerShellExtensionClient.registerExternalExtension(testExtensionId);
17+
const sessionId: string = powerShellExtensionClient.registerExternalExtension(utils.extensionId);
2618
assert.notStrictEqual(sessionId , "");
2719
assert.notStrictEqual(sessionId , null);
2820
assert.strictEqual(
@@ -31,7 +23,7 @@ suite("ExternalApi feature - Registration API", () => {
3123
});
3224

3325
test("It can register and unregister an extension with a version", () => {
34-
const sessionId: string = powerShellExtensionClient.registerExternalExtension(testExtensionId, "v2");
26+
const sessionId: string = powerShellExtensionClient.registerExternalExtension(utils.extensionId, "v2");
3527
assert.notStrictEqual(sessionId , "");
3628
assert.notStrictEqual(sessionId , null);
3729
assert.strictEqual(
@@ -48,12 +40,12 @@ suite("ExternalApi feature - Registration API", () => {
4840
});
4941

5042
test("It can't register the same extension twice", async () => {
51-
const sessionId: string = powerShellExtensionClient.registerExternalExtension(testExtensionId);
43+
const sessionId: string = powerShellExtensionClient.registerExternalExtension(utils.extensionId);
5244
try {
5345
assert.throws(
54-
() => powerShellExtensionClient.registerExternalExtension(testExtensionId),
46+
() => powerShellExtensionClient.registerExternalExtension(utils.extensionId),
5547
{
56-
message: `The extension '${testExtensionId}' is already registered.`
48+
message: `The extension '${utils.extensionId}' is already registered.`
5749
});
5850
} finally {
5951
powerShellExtensionClient.unregisterExternalExtension(sessionId);
@@ -73,20 +65,16 @@ suite("ExternalApi feature - Other APIs", () => {
7365
let sessionId: string;
7466
let powerShellExtensionClient: IPowerShellExtensionClient;
7567

76-
before(async () => {
77-
const powershellExtension = vscode.extensions.getExtension<IPowerShellExtensionClient>(testExtensionId);
78-
if (!powershellExtension.isActive) {
79-
powerShellExtensionClient = await powershellExtension.activate();
80-
return;
81-
}
68+
suiteSetup(async () => {
69+
const powershellExtension = await utils.ensureExtensionIsActivated();
8270
powerShellExtensionClient = powershellExtension!.exports as IPowerShellExtensionClient;
8371
});
8472

85-
beforeEach(() => {
86-
sessionId = powerShellExtensionClient.registerExternalExtension(testExtensionId);
73+
setup(() => {
74+
sessionId = powerShellExtensionClient.registerExternalExtension(utils.extensionId);
8775
});
8876

89-
afterEach(() => {
77+
teardown(() => {
9078
powerShellExtensionClient.unregisterExternalExtension(sessionId);
9179
});
9280

@@ -105,6 +93,6 @@ suite("ExternalApi feature - Other APIs", () => {
10593
assert.notStrictEqual(versionDetails.version, "");
10694
assert.notStrictEqual(versionDetails.version, null);
10795

108-
// Start up can take some time... so set the time out to 30s
96+
// Start up can take some time...so set the timeout to 30 seconds.
10997
}).timeout(30000);
11098
});

0 commit comments

Comments
 (0)