Skip to content

Commit 99bd1f5

Browse files
🚀 [Feature]: ReportAsJson to publish TestResults and CodeCoverage as JSON files (#31)
w## Description This pull request updates configuration handling, output management, and test structure, including the addition of JSON report output, modifications to the `ConvertFrom-PesterConfiguration` filter, and updates to the test result and code coverage paths. - Fixes #29 - Fixes #30 ### Improvements to configuration and output management * `README.md`: * Added `ReportAsJson` input to allow output of generated reports in JSON format. * `action.yml`: * Introduced `ReportAsJson` input and updated the `runs` section to use this input for environment variables. * `scripts/main.ps1`: * Added logic to export test results and code coverage results to JSON format if `ReportAsJson` is enabled. ### Enhancements to configuration handling * `scripts/Helpers.psm1`: * Renamed `Convert-PesterConfigurationToHashtable` to `ConvertFrom-PesterConfiguration` and added the `AsHashtable` parameter to improve configuration conversion. ### Updates to test structure and paths * `scripts/init.ps1`: * Updated paths for test result and code coverage output to new directories. * `tests/1-Simple/Emoji.Tests.ps1`: * Refactored test script to source the emoji data from an external script file. ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [ ] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [x] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas
1 parent 4d5720c commit 99bd1f5

12 files changed

Lines changed: 312 additions & 105 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ jobs:
255255
| **Input** | **Description** | **Default** |
256256
|--------------------------------------|--------------------------------------------------------------------------------------------------------|---------------------------------|
257257
| `Path` | Path to where tests are located or a configuration file. | *(none)* |
258+
| `ReportAsJson` | Output generated reports in JSON format in addition to the configured format through Pester. | `true` |
258259
| `Run_Path` | Directories/files to be searched for tests. | *(none)* |
259260
| `Run_ExcludePath` | Directories/files to exclude from the run. | *(none)* |
260261
| `Run_ScriptBlock` | ScriptBlocks containing tests to be executed. | *(none)* |

action.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ inputs:
1010
description: |
1111
Path to where tests are located or a configuration file.
1212
required: false
13+
ReportAsJson:
14+
description: |
15+
Output generated reports in JSON format in addition to the configured format through Pester.
16+
required: false
17+
default: 'true'
1318
Run_Path:
1419
description: |
1520
Directories to be searched for tests, paths directly to test files, or combination of both.
@@ -267,6 +272,8 @@ runs:
267272
shell: pwsh
268273
continue-on-error: true
269274
working-directory: ${{ inputs.WorkingDirectory }}
275+
env:
276+
PSMODULE_INVOKE_PESTER_INPUT_ReportAsJson: ${{ inputs.ReportAsJson }}
270277
id: test
271278
run: ${{ github.action_path }}/scripts/main.ps1
272279

@@ -276,13 +283,17 @@ runs:
276283
with:
277284
name: ${{ steps.test.outputs.TestSuiteName }}-TestResults
278285
path: ${{ steps.test.outputs.TestResultOutputPath }}
286+
retention-days: 1
287+
if-no-files-found: error
279288

280289
- name: Upload code coverage report - [${{ steps.test.outputs.TestSuiteName }}-CodeCoverage]
281290
uses: actions/upload-artifact@v4
282291
if: ${{ steps.test.outputs.CodeCoverageEnabled == 'true' && (success() || failure()) }}
283292
with:
284293
name: ${{ steps.test.outputs.TestSuiteName }}-CodeCoverage
285294
path: ${{ steps.test.outputs.CodeCoverageOutputPath }}
295+
retention-days: 1
296+
if-no-files-found: error
286297

287298
- name: Status
288299
shell: pwsh

scripts/Helpers.psm1

Lines changed: 163 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ function New-PesterConfigurationHashtable {
384384
$result
385385
}
386386

387-
filter Convert-PesterConfigurationToHashtable {
387+
filter ConvertFrom-PesterConfiguration {
388388
<#
389389
.SYNOPSIS
390390
Converts a PesterConfiguration object into a hashtable containing only modified settings.
@@ -396,7 +396,7 @@ filter Convert-PesterConfigurationToHashtable {
396396
When the -IncludeDefaults switch is provided, it includes settings that have not been modified, outputting their default values.
397397
398398
.EXAMPLE
399-
New-PesterConfiguration | Convert-PesterConfigurationToHashtable | Format-Hashtable
399+
New-PesterConfiguration | ConvertFrom-PesterConfiguration -AsHashtable | Format-Hashtable
400400
401401
Output:
402402
```powershell
@@ -475,7 +475,7 @@ filter Convert-PesterConfigurationToHashtable {
475475
.EXAMPLE
476476
$config = New-PesterConfiguration
477477
$config.Run.PassThru = $true
478-
Convert-PesterConfigurationToHashtable -PesterConfiguration $config -OnlyModified | Format-Hashtable
478+
ConvertFrom-PesterConfiguration -PesterConfiguration $config -OnlyModified -AsHashtable | Format-Hashtable
479479
480480
Output:
481481
```powershell
@@ -504,33 +504,57 @@ filter Convert-PesterConfigurationToHashtable {
504504

505505
# Include default values in the output hashtable.
506506
[Parameter()]
507-
[switch] $OnlyModified
507+
[switch] $OnlyModified,
508+
509+
# Output as a hashtable
510+
[Parameter()]
511+
[switch] $AsHashtable
508512
)
509513

510514
# Prepare the output hashtable
511515
$result = @{}
512516

513517
# Iterate over each top-level category (Run, Filter, etc.)
514-
foreach ($category in $PesterConfiguration.PSObject.Properties.Name) {
515-
$categoryObj = $PesterConfiguration.$category
518+
foreach ($category in $PesterConfiguration.PSObject.Properties) {
519+
$categoryName = $category.Name
520+
$categoryValue = $category.Value
516521
$subHash = @{}
517522

518523
# Iterate over each setting within the category
519-
foreach ($settingName in $categoryObj.PSObject.Properties.Name) {
520-
if ($OnlyModified) {
521-
if ($setting.IsModified) {
522-
$subHash[$settingName] = $setting.Value
524+
foreach ($setting in $categoryValue.PSObject.Properties) {
525+
$settingName = $setting.Name
526+
$settingValue = $setting.Value
527+
$settingValue = $OnlyModified ? $settingValue.Value : ($settingValue.IsModified ? $settingValue.Value : $settingValue.Default)
528+
Write-Verbose "[$categoryName] [$settingName] = $settingValue" -Verbose
529+
if ($categoryName -eq 'Run' -and $settingName -eq 'Container') {
530+
Write-Verbose ($settingValue | ConvertTo-Json -Depth 1 | Out-String) -Verbose
531+
$containers = [System.Collections.Generic.List[object]]::new()
532+
foreach ($container in $settingValue) {
533+
$containers.Add(
534+
[pscustomobject]@{
535+
Path = $container.Item.FullName
536+
Data = $container.Data
537+
}
538+
)
523539
}
540+
$subHash[$settingName] = $containers
524541
} else {
525-
$subHash[$settingName] = if ($setting.IsModified) { $setting.Value } else { $setting.Default }
542+
$subHash[$settingName] = $settingValue
526543
}
527544
}
528545

529546
# Add the category sub-hashtable to the result even if empty, to preserve structure.
530-
$result[$category] = $subHash
547+
if ($AsHashtable) {
548+
$result[$categoryName] = $subHash
549+
} else {
550+
$result[$categoryName] = [pscustomobject]$subHash
551+
}
531552
}
532553

533-
return $result
554+
if ($AsHashtable) {
555+
return $result
556+
}
557+
return [PSCustomObject]$result
534558
}
535559

536560
filter Clear-PesterConfigurationEmptyValue {
@@ -639,38 +663,142 @@ filter Get-PesterTestTree {
639663
Mandatory,
640664
ValueFromPipeline
641665
)]
642-
[object] $InputObject
666+
[object] $InputObject,
667+
668+
# Path to this node.
669+
[Parameter()]
670+
[string[]] $Path
643671
)
644672

673+
$children = [System.Collections.Generic.List[object]]::new()
645674
Write-Verbose "Processing object of type: $($InputObject.GetType().Name)"
646675
switch ($InputObject.GetType().Name) {
647676
'Run' {
648-
$inputObject | Add-Member -MemberType NoteProperty -Name Depth -Value 0
649-
$inputObject | Add-Member -MemberType NoteProperty -Name ItemType -Value 'TestSuite'
650-
$inputObject | Add-Member -MemberType NoteProperty -Name Name -Value $($testResults.Configuration.TestResult.TestSuiteName.Value) -Force
651-
$inputObject | Add-Member -MemberType NoteProperty -Name Children -Value $inputObject.Containers
652-
$inputObject
653-
$inputObject.Containers | Get-PesterTestTree
677+
$Name = $InputObject.Configuration.TestResult.TestSuiteName.Value
678+
$childPath = @($Path, $Name)
679+
$children.Add(($InputObject.Containers | Get-PesterTestTree -Path $childPath))
680+
$configuration = $InputObject.Configuration | ConvertFrom-PesterConfiguration
681+
[pscustomobject]@{
682+
Depth = 0
683+
ItemType = 'TestSuite'
684+
Name = $Name
685+
Path = @()
686+
Children = $children
687+
Result = $InputObject.Result
688+
FailedCount = $InputObject.FailedCount
689+
FailedBlocksCount = $InputObject.FailedBlocksCount
690+
FailedContainersCount = $InputObject.FailedContainersCount
691+
PassedCount = $InputObject.PassedCount
692+
SkippedCount = $InputObject.SkippedCount
693+
InconclusiveCount = $InputObject.InconclusiveCount
694+
NotRunCount = $InputObject.NotRunCount
695+
TotalCount = $InputObject.TotalCount
696+
Executed = $InputObject.Executed
697+
ExecutedAt = $InputObject.ExecutedAt
698+
Version = $InputObject.Version
699+
PSVersion = $InputObject.PSVersion
700+
Plugins = $InputObject.Plugins
701+
PluginConfiguration = $InputObject.PluginConfiguration
702+
PluginData = $InputObject.PluginData
703+
Configuration = $configuration
704+
Duration = [int64]$InputObject.Duration.Ticks
705+
DiscoveryDuration = [int64]$InputObject.DiscoveryDuration.Ticks
706+
UserDuration = [int64]$InputObject.UserDuration.Ticks
707+
FrameworkDuration = [int64]$InputObject.FrameworkDuration.Ticks
708+
}
654709
}
655710
'Container' {
656-
$inputObject | Add-Member -MemberType NoteProperty -Name Depth -Value 1
657-
$inputObject | Add-Member -MemberType NoteProperty -Name ItemType -Value 'Container'
658-
$inputObject | Add-Member -MemberType NoteProperty -Name Name -Value ((Split-Path $InputObject.Name -Leaf) -replace '.Tests.ps1') -Force
659-
$inputObject | Add-Member -MemberType NoteProperty -Name Children -Value $InputObject.Blocks
660-
$inputObject
661-
$InputObject.Blocks | Get-PesterTestTree
711+
$Name = (Split-Path $InputObject.Name -Leaf) -replace '.Tests.ps1'
712+
$childPath = @($Path, $Name)
713+
$children.Add(($InputObject.Blocks | Get-PesterTestTree -Path $childPath))
714+
[pscustomobject]@{
715+
Depth = 1
716+
ItemType = 'Container'
717+
Name = $Name
718+
Path = $Path
719+
Children = $children
720+
Result = $InputObject.Result
721+
FailedCount = $InputObject.FailedCount
722+
FailedBlocksCount = $InputObject.FailedBlocksCount
723+
FailedContainersCount = $InputObject.FailedContainersCount
724+
PassedCount = $InputObject.PassedCount
725+
SkippedCount = $InputObject.SkippedCount
726+
InconclusiveCount = $InputObject.InconclusiveCount
727+
NotRunCount = $InputObject.NotRunCount
728+
TotalCount = $InputObject.TotalCount
729+
Executed = $InputObject.Executed
730+
ExecutedAt = $InputObject.ExecutedAt
731+
Version = $InputObject.Version
732+
PSVersion = $InputObject.PSVersion
733+
Plugins = $InputObject.Plugins
734+
PluginConfiguration = $InputObject.PluginConfiguration
735+
PluginData = $InputObject.PluginData
736+
Duration = [int64]$InputObject.Duration.Ticks
737+
DiscoveryDuration = [int64]$InputObject.DiscoveryDuration.Ticks
738+
UserDuration = [int64]$InputObject.UserDuration.Ticks
739+
FrameworkDuration = [int64]$InputObject.FrameworkDuration.Ticks
740+
}
662741
}
663742
'Block' {
664-
$inputObject | Add-Member -MemberType NoteProperty -Name Depth -Value ($InputObject.Path.Count + 1)
665-
$inputObject | Add-Member -MemberType NoteProperty -Name Name -Value ($InputObject.ExpandedName) -Force
666-
$inputObject | Add-Member -MemberType NoteProperty -Name Children -Value $InputObject.Order
667-
$inputObject
668-
$InputObject.Order | Get-PesterTestTree
743+
$Name = ($InputObject.ExpandedName)
744+
$childPath = @($Path, $Name)
745+
$children.Add(($InputObject.Order | Get-PesterTestTree -Path $childPath))
746+
[pscustomobject]@{
747+
Depth = ($InputObject.Path.Count + 1)
748+
ItemType = $InputObject.ItemType
749+
Name = $Name
750+
Path = $Path
751+
Children = $children
752+
Result = $InputObject.Result
753+
FailedCount = $InputObject.FailedCount
754+
FailedBlocksCount = $InputObject.FailedBlocksCount
755+
FailedContainersCount = $InputObject.FailedContainersCount
756+
PassedCount = $InputObject.PassedCount
757+
SkippedCount = $InputObject.SkippedCount
758+
InconclusiveCount = $InputObject.InconclusiveCount
759+
NotRunCount = $InputObject.NotRunCount
760+
TotalCount = $InputObject.TotalCount
761+
Executed = $InputObject.Executed
762+
ExecutedAt = $InputObject.ExecutedAt
763+
Version = $InputObject.Version
764+
PSVersion = $InputObject.PSVersion
765+
Plugins = $InputObject.Plugins
766+
PluginConfiguration = $InputObject.PluginConfiguration
767+
PluginData = $InputObject.PluginData
768+
Duration = [int64]$InputObject.Duration.Ticks
769+
DiscoveryDuration = [int64]$InputObject.DiscoveryDuration.Ticks
770+
UserDuration = [int64]$InputObject.UserDuration.Ticks
771+
FrameworkDuration = [int64]$InputObject.FrameworkDuration.Ticks
772+
}
669773
}
670774
'Test' {
671-
$inputObject | Add-Member -MemberType NoteProperty -Name Depth -Value ($InputObject.Path.Count + 1)
672-
$inputObject | Add-Member -MemberType NoteProperty -Name Name -Value ($InputObject.ExpandedName) -Force
673-
$inputObject
775+
$Name = ($InputObject.ExpandedName)
776+
[pscustomobject]@{
777+
Depth = ($InputObject.Path.Count + 1)
778+
ItemType = $InputObject.ItemType
779+
Name = $Name
780+
Path = $Path
781+
Result = $InputObject.Result
782+
FailedCount = $InputObject.FailedCount
783+
FailedBlocksCount = $InputObject.FailedBlocksCount
784+
FailedContainersCount = $InputObject.FailedContainersCount
785+
PassedCount = $InputObject.PassedCount
786+
SkippedCount = $InputObject.SkippedCount
787+
InconclusiveCount = $InputObject.InconclusiveCount
788+
NotRunCount = $InputObject.NotRunCount
789+
TotalCount = $InputObject.TotalCount
790+
Executed = $InputObject.Executed
791+
ExecutedAt = $InputObject.ExecutedAt
792+
Version = $InputObject.Version
793+
PSVersion = $InputObject.PSVersion
794+
Plugins = $InputObject.Plugins
795+
PluginConfiguration = $InputObject.PluginConfiguration
796+
PluginData = $InputObject.PluginData
797+
Duration = [int64]$InputObject.Duration.Ticks
798+
DiscoveryDuration = [int64]$InputObject.DiscoveryDuration.Ticks
799+
UserDuration = [int64]$InputObject.UserDuration.Ticks
800+
FrameworkDuration = [int64]$InputObject.FrameworkDuration.Ticks
801+
}
674802
}
675803
default {
676804
Write-Error "Unknown object type: [$($InputObject.GetType().Name)]"
@@ -853,7 +981,7 @@ filter Set-PesterReportConfigurationSummary {
853981
[Pester.Run] $TestResults
854982
)
855983

856-
$configurationHashtable = $TestResults.Configuration | Convert-PesterConfigurationToHashtable | Format-Hashtable
984+
$configurationHashtable = $TestResults.Configuration | ConvertFrom-PesterConfiguration -AsHashtable | Format-Hashtable
857985

858986
Details 'Configuration' {
859987
CodeBlock 'pwsh' {

scripts/init.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ LogGroup 'Init - Export containers' {
176176
Write-Output "Containers from configuration: [$($existingContainers.Count)]"
177177
foreach ($existingContainer in $existingContainers) {
178178
Write-Output "Processing container [$existingContainer]"
179-
$containers += $existingContainer | Convert-PesterConfigurationToHashtable
179+
$containers += $existingContainer | ConvertTo-Hashtable
180180
}
181181
}
182182
Write-Output "Containers from configuration: [$($containers.Count)]"
@@ -223,8 +223,8 @@ LogGroup 'Init - Export containers' {
223223

224224
LogGroup 'Init - Export configuration' {
225225
$artifactName = $configuration.TestResult.TestSuiteName ?? 'Pester'
226-
$configuration.TestResult.OutputPath = "$pwd/test_reports/$artifactName-TestResult-Report.xml"
227-
$configuration.CodeCoverage.OutputPath = "$pwd/test_reports/$artifactName-CodeCoverage-Report.xml"
226+
$configuration.TestResult.OutputPath = "$pwd/TestResult/$artifactName-TestResult-Report.xml"
227+
$configuration.CodeCoverage.OutputPath = "$pwd/CodeCoverage/$artifactName-CodeCoverage-Report.xml"
228228
$configuration.Run.PassThru = $true
229229

230230
Format-Hashtable -Hashtable $configuration

0 commit comments

Comments
 (0)