Skip to content

Commit 723433b

Browse files
authored
Migrate Pester version detection into an InovkePester stub script (#1776)
* Migrate Pester version detection into an InovkePester stub script * Move TestName warning into script * Improve stub script, move module loaded check to beginning * Address Codacy issue * Address PR feedback * Move TestName check before LineNumber in case of multiple blocks/line
1 parent 5962cb4 commit 723433b

File tree

2 files changed

+110
-30
lines changed

2 files changed

+110
-30
lines changed

InvokePesterStub.ps1

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env pwsh
2+
3+
<#
4+
.SYNOPSIS
5+
Stub around Invoke-Pester command used by VSCode PowerShell extension.
6+
.DESCRIPTION
7+
The stub checks the version of Pester and if >= 4.6.0, invokes Pester
8+
using the LineNumber parameter (if specified). Otherwise, it invokes
9+
using the TestName parameter (if specified). If the All parameter
10+
is specified, then all the tests are invoked in the specifed file.
11+
Finally, if none of these three parameters are specified, all tests
12+
are invoked and a warning is issued indicating what the user can do
13+
to allow invocation of individual Describe blocks.
14+
.EXAMPLE
15+
PS C:\> .\InvokePesterStub.ps1 ~\project\test\foo.tests.ps1 -LineNumber 14
16+
Invokes a specific test by line number in the specified file.
17+
.EXAMPLE
18+
PS C:\> .\InvokePesterStub.ps1 ~\project\test\foo.tests.ps1 -TestName 'Foo Tests'
19+
Invokes a specific test by test name in the specified file.
20+
.EXAMPLE
21+
PS C:\> .\InvokePesterStub.ps1 ~\project\test\foo.tests.ps1 -All
22+
Invokes all tests in the specified file.
23+
.INPUTS
24+
None
25+
.OUTPUTS
26+
None
27+
#>
28+
param(
29+
# Specifies the path to the test script.
30+
[Parameter(Position=0, Mandatory)]
31+
[ValidateNotNullOrEmpty()]
32+
[string]
33+
$ScriptPath,
34+
35+
# Specifies the name of the test taken from the Describe block's name.
36+
[Parameter()]
37+
[string]
38+
$TestName,
39+
40+
# Specifies the starting line number of the DescribeBlock. This feature requires
41+
# Pester 4.6.0 or higher.
42+
[Parameter()]
43+
[ValidatePattern('\d*')]
44+
[string]
45+
$LineNumber,
46+
47+
# If specified, executes all the tests in the specified test script.
48+
[Parameter()]
49+
[switch]
50+
$All
51+
)
52+
53+
$pesterModule = Microsoft.PowerShell.Core\Get-Module Pester
54+
if (!$pesterModule) {
55+
Write-Output "Importing Pester module..."
56+
$pesterModule = Microsoft.PowerShell.Core\Import-Module Pester -ErrorAction Ignore -PassThru
57+
if (!$pesterModule) {
58+
# If we still don't have an imported Pester module, that is (most likely) because Pester is not installed.
59+
Write-Warning "Failed to import the Pester module. You must install Pester to run or debug Pester tests."
60+
Write-Warning "You can install Pester by executing: Install-Module Pester -Scope CurrentUser -Force"
61+
return
62+
}
63+
}
64+
65+
if ($All) {
66+
Pester\Invoke-Pester -Script $ScriptPath -PesterOption @{IncludeVSCodeMarker=$true}
67+
}
68+
elseif ($TestName) {
69+
Pester\Invoke-Pester -Script $ScriptPath -PesterOption @{IncludeVSCodeMarker=$true} -TestName $TestName
70+
}
71+
elseif (($LineNumber -match '\d+') -and ($pesterModule.Version -ge '4.6.0')) {
72+
Pester\Invoke-Pester -Script $ScriptPath -PesterOption (New-PesterOption -ScriptBlockFilter @{
73+
IncludeVSCodeMarker=$true; Line=$LineNumber; Path=$ScriptPath})
74+
}
75+
else {
76+
# We get here when the TestName expression is of type ExpandableStringExpressionAst.
77+
# PSES will not attempt to "evaluate" the expression so it returns null for the TestName.
78+
Write-Warning "The Describe block's TestName cannot be evaluated. EXECUTING ALL TESTS instead."
79+
Write-Warning "To avoid this, install Pester >= 4.6.0 or remove any expressions in the TestName."
80+
81+
Pester\Invoke-Pester -Script $ScriptPath -PesterOption @{IncludeVSCodeMarker=$true}
82+
}

src/features/PesterTests.ts

+28-30
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ export class PesterTestsFeature implements IFeature {
1818

1919
private command: vscode.Disposable;
2020
private languageClient: LanguageClient;
21+
private invokePesterStubScriptPath: string;
2122

2223
constructor(private sessionManager: SessionManager) {
24+
this.invokePesterStubScriptPath = path.resolve(__dirname, "../../../InvokePesterStub.ps1");
25+
2326
// File context-menu command - Run Pester Tests
2427
this.command = vscode.commands.registerCommand(
2528
"PowerShell.RunPesterTestsFromFile",
@@ -35,8 +38,8 @@ export class PesterTestsFeature implements IFeature {
3538
// This command is provided for usage by PowerShellEditorServices (PSES) only
3639
this.command = vscode.commands.registerCommand(
3740
"PowerShell.RunPesterTests",
38-
(uriString, runInDebugger, describeBlockName?) => {
39-
this.launchTests(uriString, runInDebugger, describeBlockName);
41+
(uriString, runInDebugger, describeBlockName?, describeBlockLineNumber?) => {
42+
this.launchTests(uriString, runInDebugger, describeBlockName, describeBlockLineNumber);
4043
});
4144
}
4245

@@ -51,38 +54,22 @@ export class PesterTestsFeature implements IFeature {
5154
private launchAllTestsInActiveEditor(launchType: LaunchType) {
5255
const uriString = vscode.window.activeTextEditor.document.uri.toString();
5356
const launchConfig = this.createLaunchConfig(uriString, launchType);
57+
launchConfig.args.push("-All");
5458
this.launch(launchConfig);
5559
}
5660

57-
private async launchTests(uriString: string, runInDebugger: boolean, describeBlockName?: string) {
58-
// PSES passes null for the describeBlockName to signal that it can't evaluate the TestName.
59-
if (!describeBlockName) {
60-
const answer = await vscode.window.showErrorMessage(
61-
"This Describe block's TestName parameter cannot be evaluated. " +
62-
`Would you like to ${runInDebugger ? "debug" : "run"} all the tests in this file?`,
63-
"Yes", "No");
64-
65-
if (answer !== "Yes") {
66-
return;
67-
}
68-
}
61+
private async launchTests(
62+
uriString: string,
63+
runInDebugger: boolean,
64+
describeBlockName?: string,
65+
describeBlockLineNumber?: number) {
6966

7067
const launchType = runInDebugger ? LaunchType.Debug : LaunchType.Run;
71-
const launchConfig = this.createLaunchConfig(uriString, launchType);
72-
73-
if (describeBlockName) {
74-
launchConfig.args.push("-TestName");
75-
// Escape single quotes inside double quotes by doubling them up
76-
if (describeBlockName.includes("'")) {
77-
describeBlockName = describeBlockName.replace(/'/g, "''");
78-
}
79-
launchConfig.args.push(`'${describeBlockName}'`);
80-
}
81-
68+
const launchConfig = this.createLaunchConfig(uriString, launchType, describeBlockName, describeBlockLineNumber);
8269
this.launch(launchConfig);
8370
}
8471

85-
private createLaunchConfig(uriString: string, launchType: LaunchType) {
72+
private createLaunchConfig(uriString: string, launchType: LaunchType, testName?: string, lineNum?: number) {
8673
const uri = vscode.Uri.parse(uriString);
8774
const currentDocument = vscode.window.activeTextEditor.document;
8875
const settings = Settings.load();
@@ -95,12 +82,10 @@ export class PesterTestsFeature implements IFeature {
9582
request: "launch",
9683
type: "PowerShell",
9784
name: "PowerShell Launch Pester Tests",
98-
script: "Invoke-Pester",
85+
script: this.invokePesterStubScriptPath,
9986
args: [
100-
"-Script",
87+
"-ScriptPath",
10188
`'${scriptPath}'`,
102-
"-PesterOption",
103-
"@{IncludeVSCodeMarker=$true}",
10489
],
10590
internalConsoleOptions: "neverOpen",
10691
noDebug: (launchType === LaunchType.Run),
@@ -111,6 +96,19 @@ export class PesterTestsFeature implements IFeature {
11196
: path.dirname(currentDocument.fileName),
11297
};
11398

99+
if (lineNum) {
100+
launchConfig.args.push("-LineNumber", `${lineNum}`);
101+
}
102+
103+
if (testName) {
104+
// Escape single quotes inside double quotes by doubling them up
105+
if (testName.includes("'")) {
106+
testName = testName.replace(/'/g, "''");
107+
}
108+
109+
launchConfig.args.push("-TestName", `'${testName}'`);
110+
}
111+
114112
return launchConfig;
115113
}
116114

0 commit comments

Comments
 (0)