-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInvoke-Tests.ps1
More file actions
executable file
·395 lines (334 loc) · 12.5 KB
/
Invoke-Tests.ps1
File metadata and controls
executable file
·395 lines (334 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Runs Pester tests for the PowerShell Profile project with cross-version compatibility.
.DESCRIPTION
This script runs unit and integration tests using Pester, automatically detecting
the installed Pester version and using the appropriate syntax for compatibility
with both Pester 4.x and Pester 5+.
Requirements:
- Pester 4.0 or higher (Pester 3.x is not supported)
- Test files must be compatible with the installed Pester version
Features:
- Cross-platform compatibility (Windows, macOS, Linux)
- Automatic Pester version detection and syntax adaptation
- Support for unit and integration test separation
- Configurable output verbosity
- NUnit XML test results generation
- CI/CD pipeline friendly with proper exit codes
.PARAMETER TestType
Specifies which types of tests to run.
- 'Unit': Run only unit tests from ./Tests/Unit/
- 'Integration': Run only integration tests from ./Tests/Integration/
- 'All': Run both unit and integration tests (default)
.PARAMETER OutputFormat
Controls the verbosity of test output.
- 'Normal': Standard test output with basic information
- 'Detailed': Comprehensive output including test names and timing (default)
- 'Diagnostic': Maximum verbosity for debugging
Note: The actual available output formats depend on the Pester version installed.
.PARAMETER PassThru
When specified, returns the Pester test results object for further processing
instead of just displaying the summary.
.PARAMETER ShowTimingSummary
When specified, writes a Markdown timing summary from the generated NUnit XML
test results after the test run completes.
.PARAMETER TimingSummaryTop
Specifies how many slow test files and test cases to include in the timing
summary. The default is 10.
.PARAMETER TimingSummaryTitle
Specifies the Markdown heading text for the timing summary.
.PARAMETER TimingSummaryOutputPath
Specifies where to append the Markdown timing summary. Use an empty string
to write the summary to the console. The default is an empty string.
.EXAMPLE
./Invoke-Tests.ps1
Runs all tests with detailed output.
.EXAMPLE
./Invoke-Tests.ps1 -TestType Unit
Runs only unit tests with detailed output.
.EXAMPLE
./Invoke-Tests.ps1 -TestType Integration -OutputFormat Normal
Runs integration tests with normal verbosity.
.EXAMPLE
./Invoke-Tests.ps1 -TestType All -OutputFormat Diagnostic -PassThru
Runs all tests with maximum verbosity and returns results object.
.EXAMPLE
./Invoke-Tests.ps1 -TestType Unit -ShowTimingSummary
Runs unit tests and writes a Markdown summary of the slowest test files and
test cases to the console.
.EXAMPLE
./Invoke-Tests.ps1 -ShowTimingSummary -TimingSummaryOutputPath test-timing-summary.md
Runs all tests and appends the timing summary to test-timing-summary.md.
.NOTES
Requires Pester module to be installed. The script will automatically detect
the Pester version and use the appropriate syntax:
- Pester 5+: Uses PesterConfiguration object
- Pester 4.x: Uses parameter-based syntax
#>
[CmdletBinding()]
param(
[Parameter()]
[ValidateSet('Unit', 'Integration', 'All')]
[string]$TestType = 'All',
[Parameter()]
[string]$OutputFormat = 'Detailed',
[Parameter()]
[switch]$PassThru,
[Parameter()]
[switch]$ShowTimingSummary,
[Parameter()]
[ValidateRange(1, 100)]
[int]$TimingSummaryTop = 10,
[Parameter()]
[string]$TimingSummaryTitle = 'Pester timing summary',
[Parameter()]
[AllowEmptyString()]
[string]$TimingSummaryOutputPath = ''
)
# Ensure we're in the script directory
$ScriptDirectory = $PSScriptRoot
if (-not $ScriptDirectory)
{
$ScriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path
}
Set-Location $ScriptDirectory
# ---- Path helpers (PowerShell 5.1-safe) ----
function Join-Parts
{
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')]
param(
[Parameter(Mandatory)] [string]$BasePath,
[Parameter(Mandatory)] [string[]]$PathSegments
)
$path = $BasePath
foreach ($segment in $PathSegments)
{
$path = Join-Path -Path $path -ChildPath $segment
}
return $path
}
$UnitTestsPath = Join-Parts -BasePath $ScriptDirectory -PathSegments @('Tests', 'Unit')
$IntegrationTestsPath = Join-Parts -BasePath $ScriptDirectory -PathSegments @('Tests', 'Integration')
$NUnitResultsPath = Join-Path -Path $ScriptDirectory -ChildPath 'testresults.xml'
$TestTimingSummaryScriptPath = Join-Parts -BasePath $ScriptDirectory -PathSegments @('Tests', 'Write-TestTimingSummary.ps1')
# Import Pester if not already loaded
if (-not (Get-Module Pester -ListAvailable))
{
Write-Error 'Pester module is not installed. Please install Pester 5.x first: Install-Module Pester -Force'
exit 1
}
# Ensure we're using Pester 4+ by importing the latest available version
$availablePesterModules = Get-Module Pester -ListAvailable | Sort-Object Version -Descending
$latestPesterModule = $availablePesterModules[0]
if ($latestPesterModule.Version.Major -lt 4)
{
Write-Error @"
The latest available Pester version ($($latestPesterModule.Version.ToString())) is too old.
This test suite requires Pester 4.0 or higher. Please update Pester:
Install-Module -Name Pester -Force -SkipPublisherCheck
Available Pester versions:
$($availablePesterModules | ForEach-Object { " - $($_.Version.ToString()) at $($_.ModuleBase)" } | Out-String)
"@
exit 1
}
# Import the latest compatible Pester version
Import-Module Pester -RequiredVersion $latestPesterModule.Version -Force
# Determine which tests to run based on TestType parameter
$testPathsToRun = switch ($TestType)
{
'Unit' { @($UnitTestsPath) }
'Integration' { @($IntegrationTestsPath) }
'All' { @($UnitTestsPath, $IntegrationTestsPath) }
}
# Filter paths to only existing directories
$testPathsToRun = $testPathsToRun | Where-Object { Test-Path $_ }
if (-not $testPathsToRun)
{
Write-Warning "No test directories found for test type: $TestType"
exit 1
}
Write-Host "Running $TestType tests from: $($testPathsToRun -join ', ')" -ForegroundColor Green
# Check Pester version and configure accordingly
$installedPesterVersion = (Get-Module Pester).Version
$isPesterVersion5OrHigher = $installedPesterVersion -and $installedPesterVersion.Major -ge 5
# Check for Pester 3.x which is not supported
if ($installedPesterVersion -and $installedPesterVersion.Major -lt 4)
{
Write-Error @"
Pester version $($installedPesterVersion.ToString()) is not supported.
This test suite requires Pester 4.0 or higher due to the following features:
- BeforeAll/BeforeEach blocks (introduced in Pester 4.0)
- Improved parameter validation
- Better cross-platform support
The script attempted to use the latest available version but it's still too old.
Please update Pester:
Install-Module -Name Pester -Force -SkipPublisherCheck
Current Pester installation: $($latestPesterModule.ModuleBase)
"@
exit 1
}
# Validate and map OutputFormat based on Pester version capabilities
$ValidOutputFormats = if ($isPesterVersion5OrHigher)
{
@('Normal', 'Detailed', 'Diagnostic')
}
else
{
@('Normal', 'Detailed')
}
if ($OutputFormat -notin $ValidOutputFormats)
{
Write-Error "Invalid OutputFormat '$OutputFormat'. Valid values for Pester $($installedPesterVersion.ToString()) are: $($ValidOutputFormats -join ', ')"
exit 1
}
# Determine which Pester syntax to use based on version and available types
if ($isPesterVersion5OrHigher -and ([System.Management.Automation.PSTypeName]'PesterConfiguration').Type)
{
# Pester 5+ syntax
Write-Verbose "Using Pester $($installedPesterVersion.ToString()) with configuration object syntax"
$PesterConfiguration = [PesterConfiguration]::Default
$PesterConfiguration.Run.Path = $testPathsToRun
$PesterConfiguration.Run.Exit = $false
$PesterConfiguration.Run.PassThru = $true
$PesterConfiguration.Output.Verbosity = $OutputFormat
# NUnit XML results
$PesterConfiguration.TestResult.Enabled = $true
$PesterConfiguration.TestResult.OutputFormat = 'NUnitXml'
$PesterConfiguration.TestResult.OutputPath = $NUnitResultsPath
# Run tests
$previousProgressPreference = $global:ProgressPreference
try
{
$global:ProgressPreference = 'SilentlyContinue'
$pesterTestResults = Invoke-Pester -Configuration $PesterConfiguration
}
catch
{
Write-Error "Error running tests: $($_.Exception.Message)"
exit 1
}
finally
{
$global:ProgressPreference = $previousProgressPreference
}
}
else
{
# Pester 4.x syntax
Write-Verbose "Using Pester $($installedPesterVersion.ToString()) with parameter-based syntax"
$invokePesterParams = @{
Path = $testPathsToRun
PassThru = $true
}
# Handle OutputFormat for Pester 4.x when available
if ($installedPesterVersion -and $installedPesterVersion.Major -ge 4)
{
try
{
$invokePesterCommand = Get-Command Invoke-Pester -Module Pester
$outputFormatParameter = $invokePesterCommand.Parameters['OutputFormat']
if ($outputFormatParameter -and $outputFormatParameter.Attributes.ValidateSet)
{
$validOutputFormatSet = $outputFormatParameter.Attributes.ValidateSet.ValidValues
if ($OutputFormat -in $validOutputFormatSet)
{
$invokePesterParams.OutputFormat = $OutputFormat
}
else
{
Write-Warning "OutputFormat '$OutputFormat' not supported in Pester $($installedPesterVersion.ToString()). Valid values: $($validOutputFormatSet -join ', '). Using default."
}
}
elseif ($outputFormatParameter)
{
$invokePesterParams.OutputFormat = $OutputFormat
}
}
catch
{
Write-Warning "Could not determine OutputFormat support in Pester $($installedPesterVersion.ToString()). Using default output format."
}
}
# NUnit XML results (Pester 4.x)
if ($installedPesterVersion -and $installedPesterVersion.Major -ge 4)
{
$invokePesterParams.OutputFile = $NUnitResultsPath
$invokePesterParams.OutputFormat = 'NUnitXml'
}
# Run tests
$previousProgressPreference = $global:ProgressPreference
try
{
$global:ProgressPreference = 'SilentlyContinue'
$pesterTestResults = Invoke-Pester @invokePesterParams
}
catch
{
Write-Error "Error running tests: $($_.Exception.Message)"
exit 1
}
finally
{
$global:ProgressPreference = $previousProgressPreference
}
}
# Output results summary
Write-Host ''
Write-Host 'Test Results Summary:' -ForegroundColor Yellow
Write-Host " Total Tests: $($pesterTestResults.TotalCount)" -ForegroundColor White
Write-Host " Passed: $($pesterTestResults.PassedCount)" -ForegroundColor Green
Write-Host " Failed: $($pesterTestResults.FailedCount)" -ForegroundColor Red
Write-Host " Skipped: $($pesterTestResults.SkippedCount)" -ForegroundColor Yellow
Write-Host " Duration: $($pesterTestResults.Duration)" -ForegroundColor White
# Show failed tests if any
if ($pesterTestResults.FailedCount -gt 0)
{
Write-Host ''
Write-Host 'Failed Tests:' -ForegroundColor Red
$pesterTestResults.Failed | ForEach-Object {
Write-Host " - $($_.Name)" -ForegroundColor Red
}
}
if ($ShowTimingSummary)
{
if (-not (Test-Path -LiteralPath $TestTimingSummaryScriptPath -PathType Leaf))
{
Write-Warning "Could not find test timing summary script at '$TestTimingSummaryScriptPath'."
}
else
{
Write-Host ''
$timingSummaryParams = @{
Path = $NUnitResultsPath
Top = $TimingSummaryTop
Title = $TimingSummaryTitle
OutputPath = $TimingSummaryOutputPath
}
try
{
$timingSummaryOutput = & $TestTimingSummaryScriptPath @timingSummaryParams
if ($PassThru)
{
$timingSummaryOutput | ForEach-Object {
Write-Host $_
}
}
else
{
$timingSummaryOutput
}
}
catch
{
Write-Warning "Could not write test timing summary: $($_.Exception.Message)"
}
}
}
# Return results if requested
if ($PassThru)
{
return $pesterTestResults
}
# Exit with appropriate code
exit $pesterTestResults.FailedCount