From 3a8ba1112e5cba97e3daf0b1d72453f547f5186a Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Thu, 17 Jun 2021 06:58:36 +1200 Subject: [PATCH] Refactor Wiki Functions (#81) - Added private functions: - Get-TemporaryPath - returns the appropriate temp path for the OS. - New-DscResourceWikiPage - Refactored to split into two private functions New-DscMofResourceWikiPage and New-DscClassResourceWikiPage. - Get-MofSchemaObject - Refactored to reduce code duplication when adding functions for supporting composite resources. --- CHANGELOG.md | 6 + source/Private/Get-MofSchemaObject.ps1 | 37 +- source/Private/Get-TemporaryPath.ps1 | 50 + .../Private/New-DscClassResourceWikiPage.ps1 | 148 ++ source/Private/New-DscMofResourceWikiPage.ps1 | 158 ++ source/Public/New-DscResourceWikiPage.ps1 | 206 +- .../DscResource.DocGenerator.strings.psd1 | 1 + .../tasks/Generate_Conceptual_Help.build.ps1 | 12 +- source/tasks/Generate_Wiki_Content.build.ps1 | 12 +- .../Publish_GitHub_Wiki_Content.build.ps1 | 12 +- .../unit/private/Get-TemporaryPath.Tests.ps1 | 213 ++ .../New-DscClassResourceWikiPage.Tests.ps1 | 866 +++++++++ .../New-DscMofResourceWikiPage.Tests.ps1 | 939 +++++++++ .../public/New-DscResourceWikiPage.Tests.ps1 | 1725 +---------------- 14 files changed, 2442 insertions(+), 1943 deletions(-) create mode 100644 source/Private/Get-TemporaryPath.ps1 create mode 100644 source/Private/New-DscClassResourceWikiPage.ps1 create mode 100644 source/Private/New-DscMofResourceWikiPage.ps1 create mode 100644 tests/unit/private/Get-TemporaryPath.Tests.ps1 create mode 100644 tests/unit/private/New-DscClassResourceWikiPage.Tests.ps1 create mode 100644 tests/unit/private/New-DscMofResourceWikiPage.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index ac561fa..7066cb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Get-ClassResourceProperty` - Returns DSC class resource properties from the provided class or classes. - `Format-Text` - Format a string according to predefined options. + - `Get-TemporaryPath` - returns the appropriate temp path for the OS. ### Changed @@ -21,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - If a class-based resource has a parent class that contains DSC resource properties they will now also be returned as part of the DSC resource parameters ([issue #62](https://github.com/dsccommunity/DscResource.DocGenerator/issues/62)). + - Refactored to split into two private functions `New-DscMofResourceWikiPage` and + `New-DscClassResourceWikiPage`. +- `Get-MofSchemaObject` + - Refactored to reduce code duplication when adding functions for supporting + composite resources. ### Fixed diff --git a/source/Private/Get-MofSchemaObject.ps1 b/source/Private/Get-MofSchemaObject.ps1 index 3518399..d354a01 100644 --- a/source/Private/Get-MofSchemaObject.ps1 +++ b/source/Private/Get-MofSchemaObject.ps1 @@ -15,6 +15,13 @@ $mof = Get-MofSchemaObject -FileName C:\repos\SharePointDsc\DSCRescoures\MSFT_SPSite\MSFT_SPSite.schema.mof This example parses a MOF schema file. + + .NOTES + The function will thrown when run on MacOS because currently there is an + issue using the type + [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache] + on macOS. See issue https://github.com/PowerShell/PowerShell/issues/5970 + and issue https://github.com/PowerShell/MMI/issues/33. #> function Get-MofSchemaObject { @@ -27,35 +34,13 @@ function Get-MofSchemaObject $FileName ) - $temporaryPath = $null - - # Determine the correct $env:TEMP drive - switch ($true) + if ($IsMacOS) { - (-not (Test-Path -Path variable:IsWindows) -or $IsWindows) - { - # Windows PowerShell or PowerShell 6+ - $temporaryPath = $env:TEMP - } - - $IsMacOS - { - $temporaryPath = $env:TMPDIR - - throw 'NotImplemented: Currently there is an issue using the type [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache] on macOS. See issue https://github.com/PowerShell/PowerShell/issues/5970 and issue https://github.com/PowerShell/MMI/issues/33.' - } - - $IsLinux - { - $temporaryPath = '/tmp' - } - - Default - { - throw 'Cannot set the temporary path. Unknown operating system.' - } + throw 'NotImplemented: Currently there is an issue using the type [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache] on macOS. See issue https://github.com/PowerShell/PowerShell/issues/5970 and issue https://github.com/PowerShell/MMI/issues/33.' } + $temporaryPath = Get-TemporaryPath + #region Workaround for OMI_BaseResource inheritance not resolving. $filePath = (Resolve-Path -Path $FileName).Path diff --git a/source/Private/Get-TemporaryPath.ps1 b/source/Private/Get-TemporaryPath.ps1 new file mode 100644 index 0000000..c35c59e --- /dev/null +++ b/source/Private/Get-TemporaryPath.ps1 @@ -0,0 +1,50 @@ +<# + .SYNOPSIS + Get-TemporaryPath returns the temporary path for the OS. + + .DESCRIPTION + The Get-TemporaryPath function will return the temporary + path specific to the OS. It will return $ENV:Temp when run + on Windows OS, '/tmp' when run in Linux and $ENV:TMPDIR when + run on MacOS. + + .NOTES + We use Get-Variable to determine if the OS specific variables are + defined so that we can mock these during testing. We also use Get-Item + to get the environment variables so we can also mock these for testing. +#> + +function Get-TemporaryPath +{ + [CmdletBinding()] + [OutputType([System.String])] + param () + + $temporaryPath = $null + + switch ($true) + { + (-not (Test-Path -Path variable:IsWindows) -or ((Get-Variable -Name 'IsWindows' -ValueOnly -ErrorAction SilentlyContinue) -eq $true)) + { + # Windows PowerShell or PowerShell 6+ + $temporaryPath = (Get-Item -Path env:TEMP).Value + } + + ((Get-Variable -Name 'IsMacOs' -ValueOnly -ErrorAction SilentlyContinue) -eq $true) + { + $temporaryPath = (Get-Item -Path env:TMPDIR).Value + } + + ((Get-Variable -Name 'IsLinux' -ValueOnly -ErrorAction SilentlyContinue) -eq $true) + { + $temporaryPath = '/tmp' + } + + Default + { + throw 'Cannot set the temporary path. Unknown operating system.' + } + } + + return $temporaryPath +} diff --git a/source/Private/New-DscClassResourceWikiPage.ps1 b/source/Private/New-DscClassResourceWikiPage.ps1 new file mode 100644 index 0000000..f2a69c4 --- /dev/null +++ b/source/Private/New-DscClassResourceWikiPage.ps1 @@ -0,0 +1,148 @@ +<# + .SYNOPSIS + New-DscClassResourceWikiPage generates wiki pages for class-based resources + that can be uploaded to GitHub to use as public documentation for a module. + + .DESCRIPTION + The New-DscClassResourceWikiPage cmdlet will review all of the class-based and + in a specified module directory and will output the Markdown files to the + specified directory. These help files include details on the property types + for each resource, as well as a text description and examples where they exist. + + .PARAMETER OutputPath + Where should the files be saved to. + + .PARAMETER SourcePath + The path to the root of the DSC resource module (where the PSD1 file is found, + not the folder for and individual DSC resource). + + .PARAMETER BuiltModulePath + The path to the root of the built DSC resource module, e.g. + 'output/MyResource/1.0.0'. + + .EXAMPLE + New-DscClassResourceWikiPage ` + -SourcePath C:\repos\MyResource\source ` + -BuiltModulePath C:\repos\MyResource\output\MyResource\1.0.0 ` + -OutputPath C:\repos\MyResource\output\WikiContent + + This example shows how to generate wiki documentation for a specific module. +#> +function New-DscClassResourceWikiPage +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $OutputPath, + + [Parameter(Mandatory = $true)] + [System.String] + $SourcePath, + + [Parameter(Mandatory = $true)] + [System.String] + $BuiltModulePath, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + if (Test-Path -Path $BuiltModulePath) + { + <# + This must not use Recurse. Then it could potentially find resources + that are part of common modules in the Modules folder. + #> + $getChildItemParameters = @{ + Path = Join-Path -Path $BuiltModulePath -ChildPath '*' + Include = '*.psm1' + ErrorAction = 'Stop' + File = $true + } + + $builtModuleScriptFiles = Get-ChildItem @getChildItemParameters + + # Looping through each module file (normally just one). + foreach ($builtModuleScriptFile in $builtModuleScriptFiles) + { + $dscResourceAsts = Get-ClassResourceAst -ScriptFile $builtModuleScriptFile.FullName + + Write-Verbose -Message ($script:localizedData.FoundClassBasedMessage -f $dscResourceAsts.Count, $builtModuleScriptFile.FullName) + + # Looping through each class-based resource. + foreach ($dscResourceAst in $dscResourceAsts) + { + Write-Verbose -Message ($script:localizedData.GenerateWikiPageMessage -f $dscResourceAst.Name) + + $output = New-Object -TypeName 'System.Text.StringBuilder' + + $null = $output.AppendLine("# $($dscResourceAst.Name)") + $null = $output.AppendLine() + $null = $output.AppendLine('## Parameters') + $null = $output.AppendLine() + + $sourceFilePath = Join-Path -Path $SourcePath -ChildPath ('Classes/*{0}.ps1' -f $dscResourceAst.Name) + + $className = @() + + if ($dscResourceAst.BaseTypes.Count -gt 0) + { + $className += @($dscResourceAst.BaseTypes.TypeName.Name) + } + + $className += $dscResourceAst.Name + + # Returns the properties for class and any existing parent class(es). + $resourceProperty = Get-ClassResourceProperty -ClassName $className -SourcePath $SourcePath -BuiltModuleScriptFilePath $builtModuleScriptFile.FullName + + $propertyContent = Get-DscResourceSchemaPropertyContent -Property $resourceProperty -UseMarkdown + + foreach ($line in $propertyContent) + { + $null = $output.AppendLine($line) + } + + $null = $output.AppendLine() + + $dscResourceCommentBasedHelp = Get-ClassResourceCommentBasedHelp -Path $sourceFilePath + + $description = $dscResourceCommentBasedHelp.Description + $description = $description -replace '[\r|\n]+$' # Removes all blank rows and whitespace at the end + + $null = $output.AppendLine('## Description') + $null = $output.AppendLine() + $null = $output.AppendLine($description) + $null = $output.AppendLine() + + $examplesPath = Join-Path -Path $SourcePath -ChildPath ('Examples\Resources\{0}' -f $dscResourceAst.Name) + + $examplesOutput = Get-ResourceExampleAsMarkdown -Path $examplesPath + + if ($examplesOutput.Length -gt 0) + { + $null = $output.Append($examplesOutput) + } + + $outputFileName = '{0}.md' -f $dscResourceAst.Name + + $savePath = Join-Path -Path $OutputPath -ChildPath $outputFileName + + Write-Verbose -Message ($script:localizedData.OutputWikiPageMessage -f $savePath) + + $outputToWrite = $output.ToString() + $outputToWrite = $outputToWrite -replace '[\r|\n]+$' # Removes all blank rows and whitespace at the end + $outputToWrite = $outputToWrite -replace '\r?\n', "`r`n" # Normalize to CRLF + $outputToWrite = $outputToWrite -replace '[ ]+\r\n', "`r`n" # Remove indentation from blank rows + + $null = Out-File ` + -InputObject $outputToWrite ` + -FilePath $savePath ` + -Encoding utf8 ` + -Force:$Force + } + } + } +} diff --git a/source/Private/New-DscMofResourceWikiPage.ps1 b/source/Private/New-DscMofResourceWikiPage.ps1 new file mode 100644 index 0000000..39d14ae --- /dev/null +++ b/source/Private/New-DscMofResourceWikiPage.ps1 @@ -0,0 +1,158 @@ +<# + .SYNOPSIS + New-DscMofResourceWikiPage generates wiki pages for MOF-based resources + that can be uploaded to GitHub to use as public documentation for a module. + + .DESCRIPTION + The New-DscMofResourceWikiPage cmdlet will review all of the MOF-based and + in a specified module directory and will output the Markdown files to the + specified directory. These help files include details on the property types + for each resource, as well as a text description and examples where they exist. + + .PARAMETER OutputPath + Where should the files be saved to. + + .PARAMETER SourcePath + The path to the root of the DSC resource module (where the PSD1 file is found, + not the folder for and individual DSC resource). + + .PARAMETER BuiltModulePath + The path to the root of the built DSC resource module, e.g. + 'output/MyResource/1.0.0'. + + .EXAMPLE + New-DscMofResourceWikiPage ` + -SourcePath C:\repos\MyResource\source ` + -BuiltModulePath C:\repos\MyResource\output\MyResource\1.0.0 ` + -OutputPath C:\repos\MyResource\output\WikiContent + + This example shows how to generate wiki documentation for a specific module. +#> +function New-DscMofResourceWikiPage +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $OutputPath, + + [Parameter(Mandatory = $true)] + [System.String] + $SourcePath, + + [Parameter(Mandatory = $true)] + [System.String] + $BuiltModulePath, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + $mofSearchPath = Join-Path -Path $SourcePath -ChildPath '\**\*.schema.mof' + $mofSchemaFiles = @(Get-ChildItem -Path $mofSearchPath -Recurse) + + Write-Verbose -Message ($script:localizedData.FoundMofFilesMessage -f $mofSchemaFiles.Count, $SourcePath) + + # Loop through all the Schema files found in the modules folder + foreach ($mofSchemaFile in $mofSchemaFiles) + { + $mofSchemas = Get-MofSchemaObject -FileName $mofSchemaFile.FullName + + $dscResourceName = $mofSchemaFile.Name.Replace('.schema.mof', '') + + <# + In a resource with one or more embedded instances (CIM classes) this + will get the main resource CIM class. + #> + $resourceSchema = $mofSchemas | + Where-Object -FilterScript { + ($_.ClassName -eq $dscResourceName) -and ($null -ne $_.FriendlyName) + } + + [System.Array] $readmeFile = Get-ChildItem -Path $mofSchemaFile.DirectoryName | + Where-Object -FilterScript { + $_.Name -like 'readme.md' + } + + if ($readmeFile.Count -eq 1) + { + Write-Verbose -Message ($script:localizedData.GenerateWikiPageMessage -f $resourceSchema.FriendlyName) + + $output = New-Object -TypeName System.Text.StringBuilder + + $null = $output.AppendLine("# $($resourceSchema.FriendlyName)") + $null = $output.AppendLine('') + $null = $output.AppendLine('## Parameters') + $null = $output.AppendLine('') + + $propertyContent = Get-DscResourceSchemaPropertyContent -Property $resourceSchema.Attributes -UseMarkdown + + foreach ($line in $propertyContent) + { + $null = $output.AppendLine($line) + } + + <# + In a resource with one or more embedded instances (CIM classes) this + will get the embedded instances (CIM classes). + #> + $embeddedSchemas = $mofSchemas | + Where-Object -FilterScript { + ($_.ClassName -ne $dscResourceName) + } + + foreach ($embeddedSchema in $embeddedSchemas) + { + $null = $output.AppendLine() + $null = $output.AppendLine("### $($embeddedSchema.ClassName)") + $null = $output.AppendLine('') + $null = $output.AppendLine('#### Parameters') + $null = $output.AppendLine('') + + $propertyContent = Get-DscResourceSchemaPropertyContent -Property $embeddedSchema.Attributes -UseMarkdown + + foreach ($line in $propertyContent) + { + $null = $output.AppendLine($line) + } + } + + $descriptionContent = Get-Content -Path $readmeFile.FullName -Raw + + # Change the description H1 header to an H2 + $descriptionContent = $descriptionContent -replace '# Description', '## Description' + $null = $output.AppendLine() + $null = $output.AppendLine($descriptionContent) + + $examplesPath = Join-Path -Path $SourcePath -ChildPath ('Examples\Resources\{0}' -f $resourceSchema.FriendlyName) + + $examplesOutput = Get-ResourceExampleAsMarkdown -Path $examplesPath + + if ($examplesOutput.Length -gt 0) + { + $null = $output.Append($examplesOutput) + } + + $outputFileName = "$($resourceSchema.FriendlyName).md" + $savePath = Join-Path -Path $OutputPath -ChildPath $outputFileName + + Write-Verbose -Message ($script:localizedData.OutputWikiPageMessage -f $savePath) + + $null = Out-File ` + -InputObject ($output.ToString() -replace '\r?\n', "`r`n") ` + -FilePath $savePath ` + -Encoding utf8 ` + -Force:$Force + } + elseif ($readmeFile.Count -gt 1) + { + Write-Warning -Message ($script:localizedData.MultipleDescriptionFileFoundWarning -f $resourceSchema.FriendlyName, $readmeFile.Count) + } + else + { + Write-Warning -Message ($script:localizedData.NoDescriptionFileFoundWarning -f $resourceSchema.FriendlyName) + } + } +} diff --git a/source/Public/New-DscResourceWikiPage.ps1 b/source/Public/New-DscResourceWikiPage.ps1 index 148fa33..fe35726 100644 --- a/source/Public/New-DscResourceWikiPage.ps1 +++ b/source/Public/New-DscResourceWikiPage.ps1 @@ -51,209 +51,7 @@ function New-DscResourceWikiPage $Force ) - #region MOF-based resource - $mofSearchPath = Join-Path -Path $SourcePath -ChildPath '\**\*.schema.mof' - $mofSchemaFiles = @(Get-ChildItem -Path $mofSearchPath -Recurse) + New-DscMofResourceWikiPage @PSBoundParameters - Write-Verbose -Message ($script:localizedData.FoundMofFilesMessage -f $mofSchemaFiles.Count, $SourcePath) - - # Loop through all the Schema files found in the modules folder - foreach ($mofSchemaFile in $mofSchemaFiles) - { - $mofSchemas = Get-MofSchemaObject -FileName $mofSchemaFile.FullName - - $dscResourceName = $mofSchemaFile.Name.Replace('.schema.mof', '') - - <# - In a resource with one or more embedded instances (CIM classes) this - will get the main resource CIM class. - #> - $resourceSchema = $mofSchemas | - Where-Object -FilterScript { - ($_.ClassName -eq $dscResourceName) -and ($null -ne $_.FriendlyName) - } - - [System.Array] $readmeFile = Get-ChildItem -Path $mofSchemaFile.DirectoryName | - Where-Object -FilterScript { - $_.Name -like 'readme.md' - } - - if ($readmeFile.Count -eq 1) - { - Write-Verbose -Message ($script:localizedData.GenerateWikiPageMessage -f $resourceSchema.FriendlyName) - - $output = New-Object -TypeName System.Text.StringBuilder - - $null = $output.AppendLine("# $($resourceSchema.FriendlyName)") - $null = $output.AppendLine('') - $null = $output.AppendLine('## Parameters') - $null = $output.AppendLine('') - - $propertyContent = Get-DscResourceSchemaPropertyContent -Property $resourceSchema.Attributes -UseMarkdown - - foreach ($line in $propertyContent) - { - $null = $output.AppendLine($line) - } - - <# - In a resource with one or more embedded instances (CIM classes) this - will get the embedded instances (CIM classes). - #> - $embeddedSchemas = $mofSchemas | - Where-Object -FilterScript { - ($_.ClassName -ne $dscResourceName) - } - - foreach ($embeddedSchema in $embeddedSchemas) - { - $null = $output.AppendLine() - $null = $output.AppendLine("### $($embeddedSchema.ClassName)") - $null = $output.AppendLine('') - $null = $output.AppendLine('#### Parameters') - $null = $output.AppendLine('') - - $propertyContent = Get-DscResourceSchemaPropertyContent -Property $embeddedSchema.Attributes -UseMarkdown - - foreach ($line in $propertyContent) - { - $null = $output.AppendLine($line) - } - } - - $descriptionContent = Get-Content -Path $readmeFile.FullName -Raw - - # Change the description H1 header to an H2 - $descriptionContent = $descriptionContent -replace '# Description', '## Description' - $null = $output.AppendLine() - $null = $output.AppendLine($descriptionContent) - - $examplesPath = Join-Path -Path $SourcePath -ChildPath ('Examples\Resources\{0}' -f $resourceSchema.FriendlyName) - - $examplesOutput = Get-ResourceExampleAsMarkdown -Path $examplesPath - - if ($examplesOutput.Length -gt 0) - { - $null = $output.Append($examplesOutput) - } - - $outputFileName = "$($resourceSchema.FriendlyName).md" - $savePath = Join-Path -Path $OutputPath -ChildPath $outputFileName - - Write-Verbose -Message ($script:localizedData.OutputWikiPageMessage -f $savePath) - - $null = Out-File ` - -InputObject ($output.ToString() -replace '\r?\n', "`r`n") ` - -FilePath $savePath ` - -Encoding utf8 ` - -Force:$Force - } - elseif ($readmeFile.Count -gt 1) - { - Write-Warning -Message ($script:localizedData.MultipleDescriptionFileFoundWarning -f $resourceSchema.FriendlyName, $readmeFile.Count) - } - else - { - Write-Warning -Message ($script:localizedData.NoDescriptionFileFoundWarning -f $resourceSchema.FriendlyName) - } - } - #endregion MOF-based resource - - #region Class-based resource - if (Test-Path -Path $BuiltModulePath) - { - <# - This must not use Recurse. Then it could potentially find resources - that are part of common modules in the Modules folder. - #> - $getChildItemParameters = @{ - Path = Join-Path -Path $BuiltModulePath -ChildPath '*' - Include = '*.psm1' - ErrorAction = 'Stop' - File = $true - } - - $builtModuleScriptFiles = Get-ChildItem @getChildItemParameters - - # Looping through each module file (normally just one). - foreach ($builtModuleScriptFile in $builtModuleScriptFiles) - { - $dscResourceAsts = Get-ClassResourceAst -ScriptFile $builtModuleScriptFile.FullName - - Write-Verbose -Message ($script:localizedData.FoundClassBasedMessage -f $dscResourceAsts.Count, $builtModuleScriptFile.FullName) - - # Looping through each class-based resource. - foreach ($dscResourceAst in $dscResourceAsts) - { - Write-Verbose -Message ($script:localizedData.GenerateWikiPageMessage -f $dscResourceAst.Name) - - $output = New-Object -TypeName 'System.Text.StringBuilder' - - $null = $output.AppendLine("# $($dscResourceAst.Name)") - $null = $output.AppendLine() - $null = $output.AppendLine('## Parameters') - $null = $output.AppendLine() - - $sourceFilePath = Join-Path -Path $SourcePath -ChildPath ('Classes/*{0}.ps1' -f $dscResourceAst.Name) - - $className = @() - - if ($dscResourceAst.BaseTypes.Count -gt 0) - { - $className += @($dscResourceAst.BaseTypes.TypeName.Name) - } - - $className += $dscResourceAst.Name - - # Returns the properties for class and any existing parent class(es). - $resourceProperty = Get-ClassResourceProperty -ClassName $className -SourcePath $SourcePath -BuiltModuleScriptFilePath $builtModuleScriptFile.FullName - - $propertyContent = Get-DscResourceSchemaPropertyContent -Property $resourceProperty -UseMarkdown - - foreach ($line in $propertyContent) - { - $null = $output.AppendLine($line) - } - - $null = $output.AppendLine() - - $dscResourceCommentBasedHelp = Get-ClassResourceCommentBasedHelp -Path $sourceFilePath - - $description = $dscResourceCommentBasedHelp.Description - $description = $description -replace '[\r|\n]+$' # Removes all blank rows and whitespace at the end - - $null = $output.AppendLine('## Description') - $null = $output.AppendLine() - $null = $output.AppendLine($description) - $null = $output.AppendLine() - - $examplesPath = Join-Path -Path $SourcePath -ChildPath ('Examples\Resources\{0}' -f $dscResourceAst.Name) - - $examplesOutput = Get-ResourceExampleAsMarkdown -Path $examplesPath - - if ($examplesOutput.Length -gt 0) - { - $null = $output.Append($examplesOutput) - } - - $outputFileName = '{0}.md' -f $dscResourceAst.Name - - $savePath = Join-Path -Path $OutputPath -ChildPath $outputFileName - - Write-Verbose -Message ($script:localizedData.OutputWikiPageMessage -f $savePath) - - $outputToWrite = $output.ToString() - $outputToWrite = $outputToWrite -replace '[\r|\n]+$' # Removes all blank rows and whitespace at the end - $outputToWrite = $outputToWrite -replace '\r?\n', "`r`n" # Normalize to CRLF - $outputToWrite = $outputToWrite -replace '[ ]+\r\n', "`r`n" # Remove indentation from blank rows - - $null = Out-File ` - -InputObject $outputToWrite ` - -FilePath $savePath ` - -Encoding utf8 ` - -Force:$Force - } - } - } - #endregion Class-based resource + New-DscClassResourceWikiPage @PSBoundParameters } diff --git a/source/en-US/DscResource.DocGenerator.strings.psd1 b/source/en-US/DscResource.DocGenerator.strings.psd1 index 3903a39..04bb64f 100644 --- a/source/en-US/DscResource.DocGenerator.strings.psd1 +++ b/source/en-US/DscResource.DocGenerator.strings.psd1 @@ -30,4 +30,5 @@ ConvertFrom-StringData @' ClassBasedCommentBasedHelpMessage = Reading comment-based help from source file '{0}'. FoundResourceExamplesMessage = Found {0} examples. IgnoreAstParseErrorMessage = Errors was found during parsing of comment-based help. These errors were ignored: {0} + FoundCompositeFilesMessage = Found {0} composite files in path '{1}'. '@ diff --git a/source/tasks/Generate_Conceptual_Help.build.ps1 b/source/tasks/Generate_Conceptual_Help.build.ps1 index 6d8696c..5bb23bc 100644 --- a/source/tasks/Generate_Conceptual_Help.build.ps1 +++ b/source/tasks/Generate_Conceptual_Help.build.ps1 @@ -89,14 +89,18 @@ task Generate_Conceptual_Help { if ($VersionedOutputDirectory) { - # VersionedOutputDirectory is not [bool]'' nor $false nor [bool]$null - # Assume true, wherever it was set + <# + VersionedOutputDirectory is not [bool]'' nor $false nor [bool]$null + Assume true, wherever it was set + #> $VersionedOutputDirectory = $true } else { - # VersionedOutputDirectory may be [bool]'' but we can't tell where it's - # coming from, so assume the build info (Build.yaml) is right + <# + VersionedOutputDirectory may be [bool]'' but we can't tell where it's + coming from, so assume the build info (Build.yaml) is right + #> $VersionedOutputDirectory = $BuildInfo['VersionedOutputDirectory'] } diff --git a/source/tasks/Generate_Wiki_Content.build.ps1 b/source/tasks/Generate_Wiki_Content.build.ps1 index 1fef7bf..6cb5853 100644 --- a/source/tasks/Generate_Wiki_Content.build.ps1 +++ b/source/tasks/Generate_Wiki_Content.build.ps1 @@ -92,14 +92,18 @@ task Generate_Wiki_Content { if ($VersionedOutputDirectory) { - # VersionedOutputDirectory is not [bool]'' nor $false nor [bool]$null - # Assume true, wherever it was set + <# + VersionedOutputDirectory is not [bool]'' nor $false nor [bool]$null + Assume true, wherever it was set + #> $VersionedOutputDirectory = $true } else { - # VersionedOutputDirectory may be [bool]'' but we can't tell where it's - # coming from, so assume the build info (Build.yaml) is right + <# + VersionedOutputDirectory may be [bool]'' but we can't tell where it's + coming from, so assume the build info (Build.yaml) is right + #> $VersionedOutputDirectory = $BuildInfo['VersionedOutputDirectory'] } diff --git a/source/tasks/Publish_GitHub_Wiki_Content.build.ps1 b/source/tasks/Publish_GitHub_Wiki_Content.build.ps1 index 5c20b48..4f57e1f 100644 --- a/source/tasks/Publish_GitHub_Wiki_Content.build.ps1 +++ b/source/tasks/Publish_GitHub_Wiki_Content.build.ps1 @@ -113,14 +113,18 @@ task Publish_GitHub_Wiki_Content { if ($VersionedOutputDirectory) { - # VersionedOutputDirectory is not [bool]'' nor $false nor [bool]$null - # Assume true, wherever it was set + <# + VersionedOutputDirectory is not [bool]'' nor $false nor [bool]$null + Assume true, wherever it was set + #> $VersionedOutputDirectory = $true } else { - # VersionedOutputDirectory may be [bool]'' but we can't tell where it's - # coming from, so assume the build info (Build.yaml) is right + <# + VersionedOutputDirectory may be [bool]'' but we can't tell where it's + coming from, so assume the build info (Build.yaml) is right + #> $VersionedOutputDirectory = $BuildInfo['VersionedOutputDirectory'] } diff --git a/tests/unit/private/Get-TemporaryPath.Tests.ps1 b/tests/unit/private/Get-TemporaryPath.Tests.ps1 new file mode 100644 index 0000000..56ef8ff --- /dev/null +++ b/tests/unit/private/Get-TemporaryPath.Tests.ps1 @@ -0,0 +1,213 @@ +#region HEADER +$script:projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$script:projectName = (Get-ChildItem -Path "$script:projectPath\*\*.psd1" | Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try + { + Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { + $false + }) + }).BaseName + +$script:moduleName = Get-Module -Name $script:projectName -ListAvailable | Select-Object -First 1 +Remove-Module -Name $script:moduleName -Force -ErrorAction 'SilentlyContinue' + +Import-Module $script:moduleName -Force -ErrorAction 'Stop' +#endregion HEADER + +InModuleScope $script:moduleName { + Describe 'Get-TemporaryPath' { + # Mocks + $script:mockTempPath = 'temp path' + $script:mockTmpDirPath = 'tmpdir path' + + $script:getItemTemp_mock = @{ + Name = 'TEMP' + Value = $script:mockTempPath + } + + $script:getItemTmpDir_mock = @{ + Name = 'TMPDIR' + Value = $script:mockTmpDirPath + } + + # Parameter filters + $script:testPath_parameterFilter = { + $Path -eq 'variable:IsWindows' + } + + $script:getItemTemp_parameterFilter = { + $Path -eq 'env:TEMP' + } + + $script:getItemTmpDir_parameterFilter = { + $Path -eq 'env:TMPDIR' + } + + $script:getVariableIsWindows_parameterFilter = { + $Name -eq 'IsWindows' -and ` + $ValueOnly -eq $true + } + + $script:getVariableIsLinux_parameterFilter = { + $Name -eq 'IsLinux' -and ` + $ValueOnly -eq $true + } + + $script:getVariableIsMacOs_parameterFilter = { + $Name -eq 'IsMacOS' -and ` + $ValueOnly -eq $true + } + + Context 'When run on Windows PowerShell 5.1 or lower' { + BeforeAll { + Mock ` + -CommandName Test-Path ` + -MockWith { $false } ` + -ParameterFilter $script:testPath_parameterFilter + + Mock ` + -CommandName Get-Item ` + -MockWith { $script:getItemTemp_mock } ` + -ParameterFilter $script:getItemTemp_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -ParameterFilter $script:getVariableIsWindows_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -ParameterFilter $script:getVariableIsLinux_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -ParameterFilter $script:getVariableIsMacOs_parameterFilter + } + + It 'Should not throw exception' { + { + $script:getTemporaryPathResult = Get-TemporaryPath + } | Should -Not -Throw + } + + It 'Should return the temp path' { + $script:getTemporaryPathResult | Should -BeExactly $script:mockTempPath + } + } + + Context 'When run on PowerShell 6 or above on Windows' { + BeforeAll { + Mock ` + -CommandName Test-Path ` + -MockWith { $true } ` + -ParameterFilter $script:testPath_parameterFilter + + Mock ` + -CommandName Get-Item ` + -MockWith { $script:getItemTemp_mock } ` + -ParameterFilter $script:getItemTemp_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $true } ` + -ParameterFilter $script:getVariableIsWindows_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $false } ` + -ParameterFilter $script:getVariableIsLinux_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $false } ` + -ParameterFilter $script:getVariableIsMacOs_parameterFilter + } + + It 'Should not throw exception' { + { + $script:getTemporaryPathResult = Get-TemporaryPath + } | Should -Not -Throw + } + + It 'Should return the temp path' { + $script:getTemporaryPathResult | Should -BeExactly $script:mockTempPath + } + } + + Context 'When run on PowerShell 6 or above on MacOS' { + BeforeAll { + Mock ` + -CommandName Test-Path ` + -MockWith { $true } ` + -ParameterFilter $script:testPath_parameterFilter + + Mock ` + -CommandName Get-Item ` + -MockWith { $script:getItemTmpDir_mock } ` + -ParameterFilter $script:getItemTmpDir_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $false } ` + -ParameterFilter $script:getVariableIsWindows_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $false } ` + -ParameterFilter $script:getVariableIsLinux_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $true } ` + -ParameterFilter $script:getVariableIsMacOs_parameterFilter + } + + It 'Should not throw exception' { + { + $script:getTemporaryPathResult = Get-TemporaryPath + } | Should -Not -Throw + } + + It 'Should return the tmpdir path' { + $script:getTemporaryPathResult | Should -BeExactly $script:mockTmpDirPath + } + } + + Context 'When run on PowerShell 6 or above on Linux' { + BeforeAll { + Mock ` + -CommandName Test-Path ` + -MockWith { $true } ` + -ParameterFilter $script:testPath_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $false } ` + -ParameterFilter $script:getVariableIsWindows_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $true } ` + -ParameterFilter $script:getVariableIsLinux_parameterFilter + + Mock ` + -CommandName Get-Variable ` + -MockWith { $false } ` + -ParameterFilter $script:getVariableIsMacOs_parameterFilter + } + + It 'Should not throw exception' { + { + $script:getTemporaryPathResult = Get-TemporaryPath + } | Should -Not -Throw + } + + It 'Should return the /tmp path' { + $script:getTemporaryPathResult | Should -BeExactly '/tmp' + } + } + } +} diff --git a/tests/unit/private/New-DscClassResourceWikiPage.Tests.ps1 b/tests/unit/private/New-DscClassResourceWikiPage.Tests.ps1 new file mode 100644 index 0000000..86b5c04 --- /dev/null +++ b/tests/unit/private/New-DscClassResourceWikiPage.Tests.ps1 @@ -0,0 +1,866 @@ +#region HEADER +$script:projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$script:projectName = (Get-ChildItem -Path "$script:projectPath\*\*.psd1" | Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try + { + Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { + $false + }) + }).BaseName + +$script:moduleName = Get-Module -Name $script:projectName -ListAvailable | Select-Object -First 1 +Remove-Module -Name $script:moduleName -Force -ErrorAction 'SilentlyContinue' + +Import-Module $script:moduleName -Force -ErrorAction 'Stop' +#endregion HEADER + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../../helpers/DscResource.DocGenerator.TestHelper.psm1') -Force + +InModuleScope $script:moduleName { + <# + .NOTES + This stub function is created because when original Out-File is + mocked in PowerShell 6.x it changes the type of the Encoding + parameter to [System.Text.Encoding] which when called with + `OutFile -Encoding 'ascii'` fails with the error message + "Cannot process argument transformation on parameter 'Encoding'. + Cannot convert the "ascii" value of type "System.String" to type + "System.Text.Encoding". + #> + function Out-File + { + [CmdletBinding()] + param + ( + [Parameter(ValueFromPipeline = $true)] + [System.String] + $InputObject, + + [Parameter()] + [System.String] + $FilePath, + + [Parameter()] + [System.String] + $Encoding, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + throw 'StubNotImplemented' + } + + Describe 'New-DscClassResourceWikiPage' { + Context 'When generating documentation for class-based resources' { + BeforeAll { + $mockBuiltModulePath = Join-Path -Path $TestDrive -ChildPath 'output\MyClassModule\1.0.0' + $mockSourcePath = Join-Path -Path $TestDrive -ChildPath 'source' + + New-Item -Path $mockBuiltModulePath -ItemType 'Directory' -Force + New-Item -Path "$mockSourcePath\Classes" -ItemType 'Directory' -Force + New-Item -Path "$mockSourcePath\Examples\Resources\AzDevOpsProject" -ItemType 'Directory' -Force + + $mockExpectedFileOutput = '' + + $script:outFileContent_ParameterFilter = { + if ($InputObject -ne $mockExpectedFileOutput) + { + # Helper to output the diff. + Out-Diff -Expected $mockExpectedFileOutput -Actual $InputObject + } + + $InputObject -eq $mockExpectedFileOutput + } + } + + Context 'When the resource is describe with just one key property with no description for resource or property' { + BeforeAll { + # The class DSC resource in the built module. + $mockBuiltModuleScript = @' +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force + + # The source file of class DSC resource. + $mockSourceScript = @' +<# + .SYNOPSIS + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. +#> +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force + + $mockExpectedFileOutput = @' +# AzDevOpsProject + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **ProjectName** | Key | System.String | | | + +## Description +'@ -replace '\r?\n', "`r`n" + + $mockNewDscResourcePowerShellHelpParameters = @{ + SourcePath = $mockSourcePath + BuiltModulePath = $mockBuiltModulePath + OutputPath = $TestDrive + Verbose = $true + } + + Mock -CommandName Out-File + } + + It 'Should not throw an exception' { + { + New-DscClassResourceWikiPage @mockNewDscResourcePowerShellHelpParameters + } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_ParameterFilter ` + -Exactly -Times 1 -Scope Context + } + } + + Context 'When the resource is describe with just description and one key property that does not have description' { + BeforeAll { + # The class DSC resource in the built module. + $mockBuiltModuleScript = @' +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force + + # The source file of class DSC resource. + $mockSourceScript = @' +<# + .DESCRIPTION + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. +#> +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force + + $mockExpectedFileOutput = @' +# AzDevOpsProject + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **ProjectName** | Key | System.String | | | + +## Description + +A DSC Resource for Azure DevOps that +represents the Project resource. + +This is another row. +'@ -replace '\r?\n', "`r`n" + + $mockNewDscResourcePowerShellHelpParameters = @{ + SourcePath = $mockSourcePath + BuiltModulePath = $mockBuiltModulePath + OutputPath = $TestDrive + Verbose = $true + } + + Mock -CommandName Out-File + } + + It 'Should not throw an exception' { + { + New-DscClassResourceWikiPage @mockNewDscResourcePowerShellHelpParameters + } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_ParameterFilter ` + -Exactly -Times 1 -Scope Context + } + } + + Context 'When the resource have one example' { + BeforeAll { + # The class DSC resource in the built module. + $mockBuiltModuleScript = @' +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force + + # The source file of class DSC resource. + $mockSourceScript = @' +<# + .DESCRIPTION + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. +#> +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force + + $mockExampleScript = @' +<# + .DESCRIPTION + This example shows how to ensure that the Azure DevOps project + called 'Test Project' exists (or is added if it does not exist). +#> +Configuration Example +{ + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDevOpsProject 'AddProject' + { + Ensure = 'Present' + ProjectName = 'Test Project' + } + } +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockExampleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Examples\Resources\AzDevOpsProject\1-AddProject.ps1" -Encoding ascii -Force + + $mockExpectedFileOutput = @' +# AzDevOpsProject + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **ProjectName** | Key | System.String | | | + +## Description + +A DSC Resource for Azure DevOps that +represents the Project resource. + +This is another row. + +## Examples + +### EXAMPLE 1 + +This example shows how to ensure that the Azure DevOps project +called 'Test Project' exists (or is added if it does not exist). + +```powershell +Configuration Example +{ + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDevOpsProject 'AddProject' + { + Ensure = 'Present' + ProjectName = 'Test Project' + } + } +} +``` +'@ -replace '\r?\n', "`r`n" + + $mockNewDscResourcePowerShellHelpParameters = @{ + SourcePath = $mockSourcePath + BuiltModulePath = $mockBuiltModulePath + OutputPath = $TestDrive + Verbose = $true + } + + Mock -CommandName Out-File + } + + It 'Should not throw an exception' { + { + New-DscClassResourceWikiPage @mockNewDscResourcePowerShellHelpParameters + } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_ParameterFilter ` + -Exactly -Times 1 -Scope Context + } + } + + Context 'When the resource is fully described and with several properties of different types' { + BeforeAll { + # The class DSC resource in the built module. + $mockBuiltModuleScript = @' +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName + + [DscProperty()] + [System.String]$ProjectId + + [DscProperty()] + [ValidateSet('Up', 'Down')] + [System.String]$ValidateSetProperty + + [DscProperty(Mandatory)] + [System.String]$MandatoryProperty + + [DscProperty(NotConfigurable)] + [String[]]$Reasons +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force + + # The source file of class DSC resource. + $mockSourceScript = @' +<# + .SYNOPSIS + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. + + .DESCRIPTION + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. + + .PARAMETER ProjectName + ProjectName description. + + .PARAMETER ProjectId + ProjectId description. + + Second row with text. + + .PARAMETER MandatoryProperty + MandatoryProperty description. + + .PARAMETER Reasons + Reasons description. +#> +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName + + [DscProperty()] + [System.String]$ProjectId + + [DscProperty()] + [ValidateSet('Up', 'Down')] + [System.String]$ValidateSetProperty + + [DscProperty(Mandatory)] + [System.String]$MandatoryProperty + + [DscProperty(NotConfigurable)] + [String[]]$Reasons +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force + + $mockExpectedFileOutput = @' +# AzDevOpsProject + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **ProjectName** | Key | System.String | ProjectName description. | | +| **ProjectId** | Write | System.String | ProjectId description. Second row with text. | | +| **ValidateSetProperty** | Write | System.String | | `Up`, `Down` | +| **MandatoryProperty** | Required | System.String | MandatoryProperty description. | | +| **Reasons** | Read | String[] | Reasons description. | | + +## Description + +A DSC Resource for Azure DevOps that +represents the Project resource. + +This is another row. +'@ -replace '\r?\n', "`r`n" + + $mockNewDscResourcePowerShellHelpParameters = @{ + SourcePath = $mockSourcePath + BuiltModulePath = $mockBuiltModulePath + OutputPath = $TestDrive + Verbose = $true + } + + Mock -CommandName Out-File + } + + It 'Should not throw an exception' { + { + New-DscClassResourceWikiPage @mockNewDscResourcePowerShellHelpParameters + } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_ParameterFilter ` + -Exactly -Times 1 -Scope Context + } + } + + Context 'When the resource has a parent class that also has a DSC property' { + BeforeAll { + # The class DSC resource in the built module. + $mockBuiltModuleScript = @' +class ResourceBase +{ + hidden [System.String] $NotADscProperty + + [DscProperty()] + [System.String] + $Ensure +} + +[DscResource()] +class AzDevOpsProject : ResourceBase +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName + + [DscProperty()] + [System.String]$ProjectId + + [DscProperty()] + [ValidateSet('Up', 'Down')] + [System.String]$ValidateSetProperty + + [DscProperty(Mandatory)] + [System.String]$MandatoryProperty + + [DscProperty(NotConfigurable)] + [String[]]$Reasons +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force + + # The source file of class DSC resource. + $mockResourceSourceScript = @' +<# + .SYNOPSIS + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. + + .DESCRIPTION + A DSC Resource for Azure DevOps that + represents the Project resource. + + This is another row. + + .PARAMETER ProjectName + ProjectName description. + + .PARAMETER ProjectId + ProjectId description. + + Second row with text. + + .PARAMETER MandatoryProperty + MandatoryProperty description. + + .PARAMETER Reasons + Reasons description. +#> +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName + + [DscProperty()] + [System.String]$ProjectId + + [DscProperty()] + [ValidateSet('Up', 'Down')] + [System.String]$ValidateSetProperty + + [DscProperty(Mandatory)] + [System.String]$MandatoryProperty + + [DscProperty(NotConfigurable)] + [String[]]$Reasons +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockResourceSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force + + $mockBaseClassSourceScript = @' +<# + .SYNOPSIS + Synopsis for base class. + + .DESCRIPTION + Description for base class + + .PARAMETER Ensure + Ensure description. +#> +class ResourceBase +{ + hidden [System.String] $NotADscProperty + + [DscProperty()] + [System.String] + $Ensure +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBaseClassSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\001.ResourceBase.ps1" -Encoding ascii -Force + + $mockExpectedFileOutput = @' +# AzDevOpsProject + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Ensure** | Write | System.String | Ensure description. | | +| **ProjectName** | Key | System.String | ProjectName description. | | +| **ProjectId** | Write | System.String | ProjectId description. Second row with text. | | +| **ValidateSetProperty** | Write | System.String | | `Up`, `Down` | +| **MandatoryProperty** | Required | System.String | MandatoryProperty description. | | +| **Reasons** | Read | String[] | Reasons description. | | + +## Description + +A DSC Resource for Azure DevOps that +represents the Project resource. + +This is another row. +'@ -replace '\r?\n', "`r`n" + + $mockNewDscResourcePowerShellHelpParameters = @{ + SourcePath = $mockSourcePath + BuiltModulePath = $mockBuiltModulePath + OutputPath = $TestDrive + Verbose = $true + } + + Mock -CommandName Out-File + } + + It 'Should not throw an exception' { + { + New-DscClassResourceWikiPage @mockNewDscResourcePowerShellHelpParameters + } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_ParameterFilter ` + -Exactly -Times 1 -Scope Context + } + } + + Context 'When the resource has a parent class that also have a DSC property, but the property does not have a parameter description' { + BeforeAll { + # The class DSC resource in the built module. + $mockBuiltModuleScript = @' +class ResourceBase +{ + [DscProperty()] + [System.String] + $Ensure +} + +[DscResource()] +class AzDevOpsProject : ResourceBase +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force + + # The source file of class DSC resource. + $mockResourceSourceScript = @' +<# + .SYNOPSIS + Resource synopsis. + + .DESCRIPTION + Resource description. + + .PARAMETER ProjectName + ProjectName description. +#> +[DscResource()] +class AzDevOpsProject +{ + [AzDevOpsProject] Get() + { + return [AzDevOpsProject] $this + } + + [System.Boolean] Test() + { + return $true + } + + [void] Set() {} + + [DscProperty(Key)] + [System.String]$ProjectName +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockResourceSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force + + $mockBaseClassSourceScript = @' +class ResourceBase +{ + hidden [System.String] $NotADscProperty + + [DscProperty()] + [System.String] + $Ensure +} +'@ + # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. + $mockBaseClassSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\001.ResourceBase.ps1" -Encoding ascii -Force + + $mockExpectedFileOutput = @' +# AzDevOpsProject + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Ensure** | Write | System.String | | | +| **ProjectName** | Key | System.String | ProjectName description. | | + +## Description + +Resource description. +'@ -replace '\r?\n', "`r`n" + + $mockNewDscResourcePowerShellHelpParameters = @{ + SourcePath = $mockSourcePath + BuiltModulePath = $mockBuiltModulePath + OutputPath = $TestDrive + Verbose = $true + } + + Mock -CommandName Out-File + } + + It 'Should not throw an exception' { + { + New-DscClassResourceWikiPage @mockNewDscResourcePowerShellHelpParameters + } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_ParameterFilter ` + -Exactly -Times 1 -Scope Context + } + } + } + } +} diff --git a/tests/unit/private/New-DscMofResourceWikiPage.Tests.ps1 b/tests/unit/private/New-DscMofResourceWikiPage.Tests.ps1 new file mode 100644 index 0000000..d6a27b8 --- /dev/null +++ b/tests/unit/private/New-DscMofResourceWikiPage.Tests.ps1 @@ -0,0 +1,939 @@ +#region HEADER +$script:projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$script:projectName = (Get-ChildItem -Path "$script:projectPath\*\*.psd1" | Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try + { + Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { + $false + }) + }).BaseName + +$script:moduleName = Get-Module -Name $script:projectName -ListAvailable | Select-Object -First 1 +Remove-Module -Name $script:moduleName -Force -ErrorAction 'SilentlyContinue' + +Import-Module $script:moduleName -Force -ErrorAction 'Stop' +#endregion HEADER + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../../helpers/DscResource.DocGenerator.TestHelper.psm1') -Force + +InModuleScope $script:moduleName { + <# + .NOTES + This stub function is created because when original Out-File is + mocked in PowerShell 6.x it changes the type of the Encoding + parameter to [System.Text.Encoding] which when called with + `OutFile -Encoding 'ascii'` fails with the error message + "Cannot process argument transformation on parameter 'Encoding'. + Cannot convert the "ascii" value of type "System.String" to type + "System.Text.Encoding". + #> + function Out-File + { + [CmdletBinding()] + param + ( + [Parameter(ValueFromPipeline = $true)] + [System.String] + $InputObject, + + [Parameter()] + [System.String] + $FilePath, + + [Parameter()] + [System.String] + $Encoding, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + throw 'StubNotImplemented' + } + + Describe 'New-DscMofResourceWikiPage' { + Context 'When generating documentation for MOF-based resources' { + $script:mockOutputPath = Join-Path -Path $TestDrive -ChildPath 'docs' + $script:mockSourcePath = Join-Path -Path $TestDrive -ChildPath 'module' + + # Schema file info + $script:mockResourceName = 'MyResource' + $script:expectedSchemaPath = Join-Path -Path $script:mockSourcePath -ChildPath '\**\*.schema.mof' + $script:mockSchemaBaseName = "MSFT_$($script:mockResourceName).schema" + $script:mockSchemaFileName = "$($script:mockSchemaBaseName).mof" + $script:mockSchemaFolder = Join-Path -Path $script:mockSourcePath -ChildPath "DSCResources\$($script:mockResourceName)" + $script:mockSchemaFilePath = Join-Path -Path $script:mockSchemaFolder -ChildPath $script:mockSchemaFileName + $script:mockSchemaFiles = @( + @{ + FullName = $script:mockSchemaFilePath + Name = $script:mockSchemaFileName + DirectoryName = $script:mockSchemaFolder + BaseName = $script:mockSchemaBaseName + } + ) + + $script:mockGetMofSchemaObject = @{ + ClassName = 'MSFT_MyResource' + Attributes = @( + @{ + State = 'Key' + DataType = 'String' + ValueMap = @() + IsArray = $false + Name = 'Id' + Description = 'Id Description' + EmbeddedInstance = '' + }, + @{ + State = 'Write' + DataType = 'String' + ValueMap = @( 'Value1', 'Value2', 'Value3' ) + IsArray = $false + Name = 'Enum' + Description = 'Enum Description.' + EmbeddedInstance = '' + }, + @{ + State = 'Required' + DataType = 'Uint32' + ValueMap = @() + IsArray = $false + Name = 'Int' + Description = 'Int Description.' + EmbeddedInstance = '' + }, + @{ + State = 'Read' + DataType = 'String' + ValueMap = @() + IsArray = $false + Name = 'Read' + Description = 'Read Description.' + EmbeddedInstance = '' + } + ) + ClassVersion = '1.0.0' + FriendlyName = 'MyResource' + } + + # Example file info + $script:mockExampleFilePath = Join-Path -Path $script:mockSourcePath -ChildPath "\Examples\Resources\$($script:mockResourceName)\$($script:mockResourceName)_Example1_Config.ps1" + $script:expectedExamplePath = Join-Path -Path $script:mockSourcePath -ChildPath "\Examples\Resources\$($script:mockResourceName)\*.ps1" + $script:mockExampleFiles = @( + @{ + Name = "$($script:mockResourceName)_Example1_Config.ps1" + FullName = $script:mockExampleFilePath + } + ) + + $script:mockExampleContent = '.EXAMPLE 1 + +Example description. + +Configuration Example +{ + Import-DSCResource -ModuleName MyModule + Node localhost + { + MyResource Something + { + Id = ''MyId'' + Enum = ''Value1'' + Int = 1 + } + } +}' + + # General mock values + $script:mockReadmePath = Join-Path -Path $script:mockSchemaFolder -ChildPath 'readme.md' + $script:mockReadmeFolder = $script:mockSchemaFolder + $script:mockOutputFile = Join-Path -Path $script:mockOutputPath -ChildPath "$($script:mockResourceName).md" + $script:mockSavePath = Join-Path -Path $script:mockOutputPath -ChildPath "$($script:mockResourceName).md" + $script:mockGetContentReadme = '# Description + +The description of the resource. +Second row of description. +' + $script:mockWikiContentOutput = '# MyResource + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Id** | Key | String | Id Description | | +| **Enum** | Write | String | Enum Description. | `Value1`, `Value2`, `Value3` | +| **Int** | Required | Uint32 | Int Description. | | +| **Read** | Read | String | Read Description. | | + +## Description + +The description of the resource. +Second row of description. + +## Examples + +.EXAMPLE 1 + +Example description. + +Configuration Example +{ + Import-DSCResource -ModuleName MyModule + Node localhost + { + MyResource Something + { + Id = ''MyId'' + Enum = ''Value1'' + Int = 1 + } + } +} +' -replace '\r?\n', "`r`n" + + # Parameter filters + $script:getChildItemSchema_parameterFilter = { + $Path -eq $script:expectedSchemaPath + } + + $script:getChildItemDescription_parameterFilter = { + $Path -eq $script:mockReadmeFolder + } + + $script:getChildItemExample_parameterFilter = { + $Path -eq $script:expectedExamplePath + } + + $script:getMofSchemaObjectSchema_parameterFilter = { + $Filename -eq $script:mockSchemaFilePath + } + + $script:getTestPathReadme_parameterFilter = { + $Path -eq $script:mockReadmePath + } + + $script:getContentReadme_parameterFilter = { + $Path -eq $script:mockReadmePath + } + + $script:getDscResourceWikiExampleContent_parameterFilter = { + $ExamplePath -eq $script:mockExampleFilePath -and $ExampleNumber -eq 1 + } + + $script:outFile_parameterFilter = { + $FilePath -eq $script:mockSavePath + } + + $script:outFileContent_parameterFilter = { + if ($InputObject -ne $script:mockWikiContentOutput) + { + # Helper to output the diff. + Out-Diff -Expected $script:mockWikiContentOutput -Actual $InputObject + } + + $InputObject -eq $script:mockWikiContentOutput + } + + $script:writeWarningDescription_parameterFilter = { + $Message -eq ($script:localizedData.NoDescriptionFileFoundWarning -f $script:mockResourceName) + } + + $script:writeWarningMultipleDescription_parameterFilter = { + $Message -eq ($script:localizedData.MultipleDescriptionFileFoundWarning -f $script:mockResourceName, 2) + } + + $script:writeWarningExample_parameterFilter = { + $Message -eq ($script:localizedData.NoExampleFileFoundWarning -f $script:mockResourceName) + } + + # Function call parameters + $script:newDscResourceWikiPage_parameters = @{ + SourcePath = $script:mockSourcePath + Verbose = $true + } + + $script:newDscResourceWikiPageOutput_parameters = @{ + SourcePath = $script:mockSourcePath + OutputPath = $script:mockOutputPath + BuiltModulePath = '.' # Not used for MOF-based resources + Verbose = $true + } + + Context 'When there are no schemas found in the module folder' { + BeforeAll { + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter + + Mock ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Out-File ` + -Exactly -Times 0 + } + } + + Context 'When there is no resource description found' { + BeforeAll { + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -MockWith { $script:mockSchemaFiles } + + Mock ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -MockWith { $script:mockGetMofSchemaObject } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -MockWith { $null } + + Mock ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter ` + -Exactly -Times 0 + } + } + + Context 'When there are multiple resource descriptions found' { + BeforeAll { + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -MockWith { $script:mockSchemaFiles } + + Mock ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -MockWith { $script:mockGetMofSchemaObject } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -MockWith { + return @( + @{ + Name = 'README.MD' + FullName = $script:mockReadmePath + }, + @{ + Name = 'Readme.md' + FullName = $script:mockReadmePath + } + ) + } + + Mock ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningMultipleDescription_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningMultipleDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter ` + -Exactly -Times 0 + } + } + + Context 'When there is no resource example file found' { + BeforeAll { + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -MockWith { $script:mockSchemaFiles } + + Mock ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -MockWith { $script:mockGetMofSchemaObject } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -MockWith { + return @( + @{ + Name = 'README.MD' + FullName = $script:mockReadmePath + } + ) + } + + Mock ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -MockWith { $script:mockGetContentReadme } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter + + Mock ` + -CommandName Get-DscResourceWikiExampleContent + + Mock ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -Exactly -Times 0 + + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter ` + -Exactly -Times 0 + } + } + + Context 'When there is one schema found in the module folder and one example using .EXAMPLE and the OutputPath is specified' { + BeforeAll { + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -MockWith { $script:mockSchemaFiles } + + Mock ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -MockWith { $script:mockGetMofSchemaObject } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -MockWith { + return @( + @{ + Name = 'README.MD' + FullName = $script:mockReadmePath + } + ) + } + + Mock ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -MockWith { $script:mockGetContentReadme } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -MockWith { $script:mockExampleFiles } + + Mock ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -MockWith { $script:mockExampleContent } + + Mock ` + -CommandName Out-File + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter ` + -Exactly -Times 1 + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter ` + -Exactly -Times 0 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter ` + -Exactly -Times 0 + } + } + + Context 'When there is one schema found in the module folder and one example using .EXAMPLE and only the parameter SourcePath is specified' { + BeforeAll { + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -MockWith { $script:mockSchemaFiles } + + Mock ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -MockWith { $script:mockGetMofSchemaObject } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -MockWith { + return @( + @{ + Name = 'README.MD' + FullName = $script:mockReadmePath + } + ) + } + + Mock ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -MockWith { $script:mockGetContentReadme } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -MockWith { $script:mockExampleFiles } + + Mock ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -MockWith { $script:mockExampleContent } + + Mock ` + -CommandName Out-File + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFileContent_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter $script:outFile_parameterFilter ` + -Exactly -Times 1 + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter ` + -Exactly -Times 0 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter ` + -Exactly -Times 0 + } + } + + Context 'When the schema is using an embedded instance' { + BeforeAll { + <# + This is the mocked embedded schema that is to be returned + together with the resource schema (which is mocked above) + for the mocked function Get-MofSchemaObject. + #> + $script:mockEmbeddedSchemaObject = @{ + ClassName = 'DSC_EmbeddedInstance' + ClassVersion = '1.0.0' + FriendlyName = 'EmbeddedInstance' + Attributes = @( + @{ + State = 'Key' + DataType = 'String' + ValueMap = @() + IsArray = $false + Name = 'EmbeddedId' + Description = 'Id Description' + EmbeddedInstance = '' + }, + @{ + State = 'Write' + DataType = 'String' + ValueMap = @( 'Value1', 'Value2', 'Value3' ) + IsArray = $false + Name = 'EmbeddedEnum' + Description = 'Enum Description.' + EmbeddedInstance = '' + }, + @{ + State = 'Required' + DataType = 'Uint32' + ValueMap = @() + IsArray = $false + Name = 'EmbeddedInt' + Description = 'Int Description.' + EmbeddedInstance = '' + }, + @{ + State = 'Read' + DataType = 'String' + ValueMap = @() + IsArray = $false + Name = 'EmbeddedRead' + Description = 'Read Description.' + EmbeddedInstance = '' + } + ) + } + + $mockWikiContentOutput = '# MyResource + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Id** | Key | String | Id Description | | +| **Enum** | Write | String | Enum Description. | `Value1`, `Value2`, `Value3` | +| **Int** | Required | Uint32 | Int Description. | | +| **Read** | Read | String | Read Description. | | + +### DSC_EmbeddedInstance + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **EmbeddedId** | Key | String | Id Description | | +| **EmbeddedEnum** | Write | String | Enum Description. | `Value1`, `Value2`, `Value3` | +| **EmbeddedInt** | Required | Uint32 | Int Description. | | +| **EmbeddedRead** | Read | String | Read Description. | | + +## Description + +The description of the resource. +Second row of description. + +## Examples + +.EXAMPLE 1 + +Example description. + +Configuration Example +{ + Import-DSCResource -ModuleName MyModule + Node localhost + { + MyResource Something + { + Id = ''MyId'' + Enum = ''Value1'' + Int = 1 + } + } +} +' -replace '\r?\n', "`r`n" + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -MockWith { $script:mockSchemaFiles } + + Mock ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -MockWith { + return @( + $script:mockGetMofSchemaObject + $script:mockEmbeddedSchemaObject + ) + } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -MockWith { + return @( + @{ + Name = 'README.MD' + FullName = $script:mockReadmePath + } + ) + } + + Mock ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -MockWith { $script:mockGetContentReadme } + + Mock ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -MockWith { $script:mockExampleFiles } + + Mock ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -MockWith { $script:mockExampleContent } + + Mock ` + -CommandName Out-File + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter + + Mock ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter + } + + It 'Should not throw an exception' { + { New-DscMofResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw + } + + It 'Should produce the correct output' { + Assert-MockCalled ` + -CommandName Out-File ` + -ParameterFilter { + if ($InputObject -ne $mockWikiContentOutput) + { + # Helper to output the diff. + Out-Diff -Expected $mockWikiContentOutput -Actual $InputObject + } + + $InputObject -eq $mockWikiContentOutput + } ` + -Exactly -Times 1 + } + + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-MofSchemaObject ` + -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemDescription_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-Content ` + -ParameterFilter $script:getContentReadme_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-ChildItem ` + -ParameterFilter $script:getChildItemExample_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Get-DscResourceWikiExampleContent ` + -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` + -Exactly -Times 1 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningExample_parameterFilter ` + -Exactly -Times 0 + + Assert-MockCalled ` + -CommandName Write-Warning ` + -ParameterFilter $script:writeWarningDescription_parameterFilter ` + -Exactly -Times 0 + } + } + } + } +} diff --git a/tests/unit/public/New-DscResourceWikiPage.Tests.ps1 b/tests/unit/public/New-DscResourceWikiPage.Tests.ps1 index bc776e1..bda20d5 100644 --- a/tests/unit/public/New-DscResourceWikiPage.Tests.ps1 +++ b/tests/unit/public/New-DscResourceWikiPage.Tests.ps1 @@ -21,1724 +21,47 @@ Import-Module $script:moduleName -Force -ErrorAction 'Stop' Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../../helpers/DscResource.DocGenerator.TestHelper.psm1') -Force InModuleScope $script:moduleName { - <# - .NOTES - This stub function is created because when original Out-File is - mocked in PowerShell 6.x it changes the type of the Encoding - parameter to [System.Text.Encoding] which when called with - `OutFile -Encoding 'ascii'` fails with the error message - "Cannot process argument transformation on parameter 'Encoding'. - Cannot convert the "ascii" value of type "System.String" to type - "System.Text.Encoding". - #> - function Out-File - { - [CmdletBinding()] - param - ( - [Parameter(ValueFromPipeline = $true)] - [System.String] - $InputObject, - - [Parameter()] - [System.String] - $FilePath, - - [Parameter()] - [System.String] - $Encoding, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - throw 'StubNotImplemented' - } - Describe 'New-DscResourceWikiPage' { - Context 'When generating documentation for MOF-based resources' { + Context 'When generating documentation for resources' { $script:mockOutputPath = Join-Path -Path $TestDrive -ChildPath 'docs' $script:mockSourcePath = Join-Path -Path $TestDrive -ChildPath 'module' + $script:mockBuildModulePath = Join-Path -Path $TestDrive -ChildPath '1.0.0' - # Schema file info - $script:mockResourceName = 'MyResource' - $script:expectedSchemaPath = Join-Path -Path $script:mockSourcePath -ChildPath '\**\*.schema.mof' - $script:mockSchemaBaseName = "MSFT_$($script:mockResourceName).schema" - $script:mockSchemaFileName = "$($script:mockSchemaBaseName).mof" - $script:mockSchemaFolder = Join-Path -Path $script:mockSourcePath -ChildPath "DSCResources\$($script:mockResourceName)" - $script:mockSchemaFilePath = Join-Path -Path $script:mockSchemaFolder -ChildPath $script:mockSchemaFileName - $script:mockSchemaFiles = @( - @{ - FullName = $script:mockSchemaFilePath - Name = $script:mockSchemaFileName - DirectoryName = $script:mockSchemaFolder - BaseName = $script:mockSchemaBaseName - } - ) - - $script:mockGetMofSchemaObject = @{ - ClassName = 'MSFT_MyResource' - Attributes = @( - @{ - State = 'Key' - DataType = 'String' - ValueMap = @() - IsArray = $false - Name = 'Id' - Description = 'Id Description' - EmbeddedInstance = '' - }, - @{ - State = 'Write' - DataType = 'String' - ValueMap = @( 'Value1', 'Value2', 'Value3' ) - IsArray = $false - Name = 'Enum' - Description = 'Enum Description.' - EmbeddedInstance = '' - }, - @{ - State = 'Required' - DataType = 'Uint32' - ValueMap = @() - IsArray = $false - Name = 'Int' - Description = 'Int Description.' - EmbeddedInstance = '' - }, - @{ - State = 'Read' - DataType = 'String' - ValueMap = @() - IsArray = $false - Name = 'Read' - Description = 'Read Description.' - EmbeddedInstance = '' - } - ) - ClassVersion = '1.0.0' - FriendlyName = 'MyResource' - } - - # Example file info - $script:mockExampleFilePath = Join-Path -Path $script:mockSourcePath -ChildPath "\Examples\Resources\$($script:mockResourceName)\$($script:mockResourceName)_Example1_Config.ps1" - $script:expectedExamplePath = Join-Path -Path $script:mockSourcePath -ChildPath "\Examples\Resources\$($script:mockResourceName)\*.ps1" - $script:mockExampleFiles = @( - @{ - Name = "$($script:mockResourceName)_Example1_Config.ps1" - FullName = $script:mockExampleFilePath - } - ) - - $script:mockExampleContent = '.EXAMPLE 1 - -Example description. - -Configuration Example -{ - Import-DSCResource -ModuleName MyModule - Node localhost - { - MyResource Something - { - Id = ''MyId'' - Enum = ''Value1'' - Int = 1 - } - } -}' - - # General mock values - $script:mockReadmePath = Join-Path -Path $script:mockSchemaFolder -ChildPath 'readme.md' - $script:mockReadmeFolder = $script:mockSchemaFolder - $script:mockOutputFile = Join-Path -Path $script:mockOutputPath -ChildPath "$($script:mockResourceName).md" - $script:mockSavePath = Join-Path -Path $script:mockOutputPath -ChildPath "$($script:mockResourceName).md" - $script:mockGetContentReadme = '# Description - -The description of the resource. -Second row of description. -' - $script:mockWikiContentOutput = '# MyResource - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **Id** | Key | String | Id Description | | -| **Enum** | Write | String | Enum Description. | `Value1`, `Value2`, `Value3` | -| **Int** | Required | Uint32 | Int Description. | | -| **Read** | Read | String | Read Description. | | - -## Description - -The description of the resource. -Second row of description. - -## Examples - -.EXAMPLE 1 - -Example description. - -Configuration Example -{ - Import-DSCResource -ModuleName MyModule - Node localhost - { - MyResource Something - { - Id = ''MyId'' - Enum = ''Value1'' - Int = 1 - } - } -} -' -replace '\r?\n', "`r`n" - - # Parameter filters - $script:getChildItemSchema_parameterFilter = { - $Path -eq $script:expectedSchemaPath - } - - $script:getChildItemDescription_parameterFilter = { - $Path -eq $script:mockReadmeFolder - } - - $script:getChildItemExample_parameterFilter = { - $Path -eq $script:expectedExamplePath - } - - $script:getMofSchemaObjectSchema_parameterFilter = { - $Filename -eq $script:mockSchemaFilePath - } - - $script:getTestPathReadme_parameterFilter = { - $Path -eq $script:mockReadmePath - } - - $script:getContentReadme_parameterFilter = { - $Path -eq $script:mockReadmePath - } - - $script:getDscResourceWikiExampleContent_parameterFilter = { - $ExamplePath -eq $script:mockExampleFilePath -and $ExampleNumber -eq 1 - } - - $script:outFile_parameterFilter = { - $FilePath -eq $script:mockSavePath - } - - $script:outFileContent_parameterFilter = { - if ($InputObject -ne $script:mockWikiContentOutput) - { - # Helper to output the diff. - Out-Diff -Expected $script:mockWikiContentOutput -Actual $InputObject - } - - $InputObject -eq $script:mockWikiContentOutput - } - - $script:writeWarningDescription_parameterFilter = { - $Message -eq ($script:localizedData.NoDescriptionFileFoundWarning -f $script:mockResourceName) - } - - $script:writeWarningMultipleDescription_parameterFilter = { - $Message -eq ($script:localizedData.MultipleDescriptionFileFoundWarning -f $script:mockResourceName, 2) - } - - $script:writeWarningExample_parameterFilter = { - $Message -eq ($script:localizedData.NoExampleFileFoundWarning -f $script:mockResourceName) - } - - # Function call parameters $script:newDscResourceWikiPage_parameters = @{ - SourcePath = $script:mockSourcePath - Verbose = $true - } - - $script:newDscResourceWikiPageOutput_parameters = @{ SourcePath = $script:mockSourcePath OutputPath = $script:mockOutputPath - BuiltModulePath = '.' # Not used for MOF-based resources + BuiltModulePath = $script:mockBuildModulePath Verbose = $true } - Context 'When there are no schemas found in the module folder' { - BeforeAll { - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter - - Mock ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Out-File ` - -Exactly -Times 0 - } - } - - Context 'When there is no resource description found' { - BeforeAll { - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -MockWith { $script:mockSchemaFiles } - - Mock ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -MockWith { $script:mockGetMofSchemaObject } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -MockWith { $null } - - Mock ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter ` - -Exactly -Times 0 - } - } - - Context 'When there are multiple resource descriptions found' { - BeforeAll { - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -MockWith { $script:mockSchemaFiles } - - Mock ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -MockWith { $script:mockGetMofSchemaObject } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -MockWith { - return @( - @{ - Name = 'README.MD' - FullName = $script:mockReadmePath - }, - @{ - Name = 'Readme.md' - FullName = $script:mockReadmePath - } - ) - } - - Mock ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningMultipleDescription_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningMultipleDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter ` - -Exactly -Times 0 - } - } - - Context 'When there is no resource example file found' { - BeforeAll { - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -MockWith { $script:mockSchemaFiles } - - Mock ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -MockWith { $script:mockGetMofSchemaObject } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -MockWith { - return @( - @{ - Name = 'README.MD' - FullName = $script:mockReadmePath - } - ) - } - - Mock ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -MockWith { $script:mockGetContentReadme } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter - - Mock ` - -CommandName Get-DscResourceWikiExampleContent - - Mock ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -Exactly -Times 0 - - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter ` - -Exactly -Times 0 - } - } - - Context 'When there is one schema found in the module folder and one example using .EXAMPLE and the OutputPath is specified' { - BeforeAll { - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -MockWith { $script:mockSchemaFiles } - - Mock ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -MockWith { $script:mockGetMofSchemaObject } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -MockWith { - return @( - @{ - Name = 'README.MD' - FullName = $script:mockReadmePath - } - ) - } - - Mock ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -MockWith { $script:mockGetContentReadme } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -MockWith { $script:mockExampleFiles } - - Mock ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -MockWith { $script:mockExampleContent } - - Mock ` - -CommandName Out-File - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter ` - -Exactly -Times 1 - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter ` - -Exactly -Times 0 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter ` - -Exactly -Times 0 - } - } - - Context 'When there is one schema found in the module folder and one example using .EXAMPLE and only the parameter SourcePath is specified' { - BeforeAll { - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -MockWith { $script:mockSchemaFiles } - - Mock ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -MockWith { $script:mockGetMofSchemaObject } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -MockWith { - return @( - @{ - Name = 'README.MD' - FullName = $script:mockReadmePath - } - ) - } - - Mock ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -MockWith { $script:mockGetContentReadme } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -MockWith { $script:mockExampleFiles } - - Mock ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -MockWith { $script:mockExampleContent } - - Mock ` - -CommandName Out-File - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFile_parameterFilter ` - -Exactly -Times 1 - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter ` - -Exactly -Times 0 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter ` - -Exactly -Times 0 - } - } - - Context 'When the schema is using an embedded instance' { - BeforeAll { - <# - This is the mocked embedded schema that is to be returned - together with the resource schema (which is mocked above) - for the mocked function Get-MofSchemaObject. - #> - $script:mockEmbeddedSchemaObject = @{ - ClassName = 'DSC_EmbeddedInstance' - ClassVersion = '1.0.0' - FriendlyName = 'EmbeddedInstance' - Attributes = @( - @{ - State = 'Key' - DataType = 'String' - ValueMap = @() - IsArray = $false - Name = 'EmbeddedId' - Description = 'Id Description' - EmbeddedInstance = '' - }, - @{ - State = 'Write' - DataType = 'String' - ValueMap = @( 'Value1', 'Value2', 'Value3' ) - IsArray = $false - Name = 'EmbeddedEnum' - Description = 'Enum Description.' - EmbeddedInstance = '' - }, - @{ - State = 'Required' - DataType = 'Uint32' - ValueMap = @() - IsArray = $false - Name = 'EmbeddedInt' - Description = 'Int Description.' - EmbeddedInstance = '' - }, - @{ - State = 'Read' - DataType = 'String' - ValueMap = @() - IsArray = $false - Name = 'EmbeddedRead' - Description = 'Read Description.' - EmbeddedInstance = '' - } - ) - } - - $mockWikiContentOutput = '# MyResource - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **Id** | Key | String | Id Description | | -| **Enum** | Write | String | Enum Description. | `Value1`, `Value2`, `Value3` | -| **Int** | Required | Uint32 | Int Description. | | -| **Read** | Read | String | Read Description. | | - -### DSC_EmbeddedInstance - -#### Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **EmbeddedId** | Key | String | Id Description | | -| **EmbeddedEnum** | Write | String | Enum Description. | `Value1`, `Value2`, `Value3` | -| **EmbeddedInt** | Required | Uint32 | Int Description. | | -| **EmbeddedRead** | Read | String | Read Description. | | - -## Description - -The description of the resource. -Second row of description. - -## Examples - -.EXAMPLE 1 - -Example description. - -Configuration Example -{ - Import-DSCResource -ModuleName MyModule - Node localhost - { - MyResource Something - { - Id = ''MyId'' - Enum = ''Value1'' - Int = 1 - } - } -} -' -replace '\r?\n', "`r`n" - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -MockWith { $script:mockSchemaFiles } - - Mock ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -MockWith { - return @( - $script:mockGetMofSchemaObject - $script:mockEmbeddedSchemaObject - ) - } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -MockWith { - return @( - @{ - Name = 'README.MD' - FullName = $script:mockReadmePath - } - ) - } - - Mock ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -MockWith { $script:mockGetContentReadme } - - Mock ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -MockWith { $script:mockExampleFiles } - - Mock ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -MockWith { $script:mockExampleContent } - - Mock ` - -CommandName Out-File - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter - - Mock ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter - } - - It 'Should not throw an exception' { - { New-DscResourceWikiPage @script:newDscResourceWikiPageOutput_parameters } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter { - if ($InputObject -ne $mockWikiContentOutput) - { - # Helper to output the diff. - Out-Diff -Expected $mockWikiContentOutput -Actual $InputObject - } - - $InputObject -eq $mockWikiContentOutput - } ` - -Exactly -Times 1 - } - - It 'Should call the expected mocks ' { - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-MofSchemaObject ` - -ParameterFilter $script:getMofSchemaObjectSchema_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemDescription_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-Content ` - -ParameterFilter $script:getContentReadme_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-ChildItem ` - -ParameterFilter $script:getChildItemExample_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Get-DscResourceWikiExampleContent ` - -ParameterFilter $script:getDscResourceWikiExampleContent_parameterFilter ` - -Exactly -Times 1 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningExample_parameterFilter ` - -Exactly -Times 0 - - Assert-MockCalled ` - -CommandName Write-Warning ` - -ParameterFilter $script:writeWarningDescription_parameterFilter ` - -Exactly -Times 0 - } - } - } - - Context 'When generating documentation for class-based resources' { - BeforeAll { - $mockBuiltModulePath = Join-Path -Path $TestDrive -ChildPath 'output\MyClassModule\1.0.0' - $mockSourcePath = Join-Path -Path $TestDrive -ChildPath 'source' - - New-Item -Path $mockBuiltModulePath -ItemType 'Directory' -Force - New-Item -Path "$mockSourcePath\Classes" -ItemType 'Directory' -Force - New-Item -Path "$mockSourcePath\Examples\Resources\AzDevOpsProject" -ItemType 'Directory' -Force - - $mockExpectedFileOutput = '' - - $script:outFileContent_ParameterFilter = { - if ($InputObject -ne $mockExpectedFileOutput) - { - # Helper to output the diff. - Out-Diff -Expected $mockExpectedFileOutput -Actual $InputObject - } - - $InputObject -eq $mockExpectedFileOutput - } - } - - Context 'When the resource is describe with just one key property with no description for resource or property' { - BeforeAll { - # The class DSC resource in the built module. - $mockBuiltModuleScript = @' -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force - - # The source file of class DSC resource. - $mockSourceScript = @' -<# - .SYNOPSIS - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. -#> -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force - - $mockExpectedFileOutput = @' -# AzDevOpsProject - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **ProjectName** | Key | System.String | | | - -## Description -'@ -replace '\r?\n', "`r`n" - - $mockNewDscResourcePowerShellHelpParameters = @{ - SourcePath = $mockSourcePath - BuiltModulePath = $mockBuiltModulePath - OutputPath = $TestDrive - Verbose = $true - } - - Mock -CommandName Out-File - } - - It 'Should not throw an exception' { - { - New-DscResourceWikiPage @mockNewDscResourcePowerShellHelpParameters - } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_ParameterFilter ` - -Exactly -Times 1 -Scope Context - } - } - - Context 'When the resource is describe with just description and one key property that does not have description' { - BeforeAll { - # The class DSC resource in the built module. - $mockBuiltModuleScript = @' -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force - - # The source file of class DSC resource. - $mockSourceScript = @' -<# - .DESCRIPTION - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. -#> -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force - - $mockExpectedFileOutput = @' -# AzDevOpsProject - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **ProjectName** | Key | System.String | | | - -## Description - -A DSC Resource for Azure DevOps that -represents the Project resource. - -This is another row. -'@ -replace '\r?\n', "`r`n" - - $mockNewDscResourcePowerShellHelpParameters = @{ - SourcePath = $mockSourcePath - BuiltModulePath = $mockBuiltModulePath - OutputPath = $TestDrive - Verbose = $true - } - - Mock -CommandName Out-File - } - - It 'Should not throw an exception' { - { - New-DscResourceWikiPage @mockNewDscResourcePowerShellHelpParameters - } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_ParameterFilter ` - -Exactly -Times 1 -Scope Context - } - } - - Context 'When the resource have one example' { - BeforeAll { - # The class DSC resource in the built module. - $mockBuiltModuleScript = @' -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force - - # The source file of class DSC resource. - $mockSourceScript = @' -<# - .DESCRIPTION - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. -#> -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force - - $mockExampleScript = @' -<# - .DESCRIPTION - This example shows how to ensure that the Azure DevOps project - called 'Test Project' exists (or is added if it does not exist). -#> -Configuration Example -{ - Import-DscResource -ModuleName 'AzureDevOpsDsc' - - node localhost - { - AzDevOpsProject 'AddProject' - { - Ensure = 'Present' - ProjectName = 'Test Project' - } - } -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockExampleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Examples\Resources\AzDevOpsProject\1-AddProject.ps1" -Encoding ascii -Force - - $mockExpectedFileOutput = @' -# AzDevOpsProject - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **ProjectName** | Key | System.String | | | - -## Description - -A DSC Resource for Azure DevOps that -represents the Project resource. - -This is another row. - -## Examples - -### EXAMPLE 1 - -This example shows how to ensure that the Azure DevOps project -called 'Test Project' exists (or is added if it does not exist). - -```powershell -Configuration Example -{ - Import-DscResource -ModuleName 'AzureDevOpsDsc' - - node localhost - { - AzDevOpsProject 'AddProject' - { - Ensure = 'Present' - ProjectName = 'Test Project' - } - } -} -``` -'@ -replace '\r?\n', "`r`n" - - $mockNewDscResourcePowerShellHelpParameters = @{ - SourcePath = $mockSourcePath - BuiltModulePath = $mockBuiltModulePath - OutputPath = $TestDrive - Verbose = $true - } - - Mock -CommandName Out-File - } - - It 'Should not throw an exception' { - { - New-DscResourceWikiPage @mockNewDscResourcePowerShellHelpParameters - } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_ParameterFilter ` - -Exactly -Times 1 -Scope Context - } - } - - Context 'When the resource is fully described and with several properties of different types' { - BeforeAll { - # The class DSC resource in the built module. - $mockBuiltModuleScript = @' -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName - - [DscProperty()] - [System.String]$ProjectId - - [DscProperty()] - [ValidateSet('Up', 'Down')] - [System.String]$ValidateSetProperty - - [DscProperty(Mandatory)] - [System.String]$MandatoryProperty - - [DscProperty(NotConfigurable)] - [String[]]$Reasons -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force - - # The source file of class DSC resource. - $mockSourceScript = @' -<# - .SYNOPSIS - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. - - .DESCRIPTION - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. - - .PARAMETER ProjectName - ProjectName description. - - .PARAMETER ProjectId - ProjectId description. - - Second row with text. - - .PARAMETER MandatoryProperty - MandatoryProperty description. - - .PARAMETER Reasons - Reasons description. -#> -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName - - [DscProperty()] - [System.String]$ProjectId - - [DscProperty()] - [ValidateSet('Up', 'Down')] - [System.String]$ValidateSetProperty - - [DscProperty(Mandatory)] - [System.String]$MandatoryProperty - - [DscProperty(NotConfigurable)] - [String[]]$Reasons -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force - - $mockExpectedFileOutput = @' -# AzDevOpsProject - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **ProjectName** | Key | System.String | ProjectName description. | | -| **ProjectId** | Write | System.String | ProjectId description. Second row with text. | | -| **ValidateSetProperty** | Write | System.String | | `Up`, `Down` | -| **MandatoryProperty** | Required | System.String | MandatoryProperty description. | | -| **Reasons** | Read | String[] | Reasons description. | | - -## Description - -A DSC Resource for Azure DevOps that -represents the Project resource. - -This is another row. -'@ -replace '\r?\n', "`r`n" - - $mockNewDscResourcePowerShellHelpParameters = @{ - SourcePath = $mockSourcePath - BuiltModulePath = $mockBuiltModulePath - OutputPath = $TestDrive - Verbose = $true - } - - Mock -CommandName Out-File - } - - It 'Should not throw an exception' { - { - New-DscResourceWikiPage @mockNewDscResourcePowerShellHelpParameters - } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_ParameterFilter ` - -Exactly -Times 1 -Scope Context - } + $script:newDscResourceWikiPage_parameterFilter = { + $OutputPath -eq $script:mockOutputPath -and ` + $SourcePath -eq $script:mockSourcePath -and ` + $BuiltModulePath -eq $script:mockBuildModulePath } - Context 'When the resource has a parent class that also has a DSC property' { - BeforeAll { - # The class DSC resource in the built module. - $mockBuiltModuleScript = @' -class ResourceBase -{ - hidden [System.String] $NotADscProperty - - [DscProperty()] - [System.String] - $Ensure -} - -[DscResource()] -class AzDevOpsProject : ResourceBase -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName - - [DscProperty()] - [System.String]$ProjectId - - [DscProperty()] - [ValidateSet('Up', 'Down')] - [System.String]$ValidateSetProperty - - [DscProperty(Mandatory)] - [System.String]$MandatoryProperty - - [DscProperty(NotConfigurable)] - [String[]]$Reasons -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force - - # The source file of class DSC resource. - $mockResourceSourceScript = @' -<# - .SYNOPSIS - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. - - .DESCRIPTION - A DSC Resource for Azure DevOps that - represents the Project resource. - - This is another row. - - .PARAMETER ProjectName - ProjectName description. - - .PARAMETER ProjectId - ProjectId description. - - Second row with text. - - .PARAMETER MandatoryProperty - MandatoryProperty description. - - .PARAMETER Reasons - Reasons description. -#> -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } + Mock ` + -CommandName New-DscMofResourceWikiPage ` + -ParameterFilter $script:newDscResourceWikiPage_parameterFilter - [void] Set() {} + Mock ` + -CommandName New-DscClassResourceWikiPage ` + -ParameterFilter $script:newDscResourceWikiPage_parameterFilter - [DscProperty(Key)] - [System.String]$ProjectName - - [DscProperty()] - [System.String]$ProjectId - - [DscProperty()] - [ValidateSet('Up', 'Down')] - [System.String]$ValidateSetProperty - - [DscProperty(Mandatory)] - [System.String]$MandatoryProperty - - [DscProperty(NotConfigurable)] - [String[]]$Reasons -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockResourceSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force - - $mockBaseClassSourceScript = @' -<# - .SYNOPSIS - Synopsis for base class. - - .DESCRIPTION - Description for base class - - .PARAMETER Ensure - Ensure description. -#> -class ResourceBase -{ - hidden [System.String] $NotADscProperty - - [DscProperty()] - [System.String] - $Ensure -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBaseClassSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\001.ResourceBase.ps1" -Encoding ascii -Force - - $mockExpectedFileOutput = @' -# AzDevOpsProject - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **Ensure** | Write | System.String | Ensure description. | | -| **ProjectName** | Key | System.String | ProjectName description. | | -| **ProjectId** | Write | System.String | ProjectId description. Second row with text. | | -| **ValidateSetProperty** | Write | System.String | | `Up`, `Down` | -| **MandatoryProperty** | Required | System.String | MandatoryProperty description. | | -| **Reasons** | Read | String[] | Reasons description. | | - -## Description - -A DSC Resource for Azure DevOps that -represents the Project resource. - -This is another row. -'@ -replace '\r?\n', "`r`n" - - $mockNewDscResourcePowerShellHelpParameters = @{ - SourcePath = $mockSourcePath - BuiltModulePath = $mockBuiltModulePath - OutputPath = $TestDrive - Verbose = $true - } - - Mock -CommandName Out-File - } - - It 'Should not throw an exception' { - { - New-DscResourceWikiPage @mockNewDscResourcePowerShellHelpParameters - } | Should -Not -Throw - } - - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_ParameterFilter ` - -Exactly -Times 1 -Scope Context - } + It 'Should not throw an exception' { + { New-DscResourceWikiPage @script:newDscResourceWikiPage_parameters } | Should -Not -Throw } - Context 'When the resource has a parent class that also have a DSC property, but the property does not have a parameter description' { - BeforeAll { - # The class DSC resource in the built module. - $mockBuiltModuleScript = @' -class ResourceBase -{ - [DscProperty()] - [System.String] - $Ensure -} - -[DscResource()] -class AzDevOpsProject : ResourceBase -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBuiltModuleScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockBuiltModulePath\MyClassModule.psm1" -Encoding ascii -Force - - # The source file of class DSC resource. - $mockResourceSourceScript = @' -<# - .SYNOPSIS - Resource synopsis. - - .DESCRIPTION - Resource description. - - .PARAMETER ProjectName - ProjectName description. -#> -[DscResource()] -class AzDevOpsProject -{ - [AzDevOpsProject] Get() - { - return [AzDevOpsProject] $this - } - - [System.Boolean] Test() - { - return $true - } - - [void] Set() {} - - [DscProperty(Key)] - [System.String]$ProjectName -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockResourceSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\010.AzDevOpsProject.ps1" -Encoding ascii -Force - - $mockBaseClassSourceScript = @' -class ResourceBase -{ - hidden [System.String] $NotADscProperty - - [DscProperty()] - [System.String] - $Ensure -} -'@ - # Uses Microsoft.PowerShell.Utility\Out-File to override the stub that is needed for the mocks. - $mockBaseClassSourceScript | Microsoft.PowerShell.Utility\Out-File -FilePath "$mockSourcePath\Classes\001.ResourceBase.ps1" -Encoding ascii -Force - - $mockExpectedFileOutput = @' -# AzDevOpsProject - -## Parameters - -| Parameter | Attribute | DataType | Description | Allowed Values | -| --- | --- | --- | --- | --- | -| **Ensure** | Write | System.String | | | -| **ProjectName** | Key | System.String | ProjectName description. | | - -## Description - -Resource description. -'@ -replace '\r?\n', "`r`n" - - $mockNewDscResourcePowerShellHelpParameters = @{ - SourcePath = $mockSourcePath - BuiltModulePath = $mockBuiltModulePath - OutputPath = $TestDrive - Verbose = $true - } - - Mock -CommandName Out-File - } - - It 'Should not throw an exception' { - { - New-DscResourceWikiPage @mockNewDscResourcePowerShellHelpParameters - } | Should -Not -Throw - } + It 'Should call the expected mocks ' { + Assert-MockCalled ` + -CommandName New-DscMofResourceWikiPage ` + -ParameterFilter $script:newDscResourceWikiPage_parameterFilter ` + -Exactly -Times 1 - It 'Should produce the correct output' { - Assert-MockCalled ` - -CommandName Out-File ` - -ParameterFilter $script:outFileContent_ParameterFilter ` - -Exactly -Times 1 -Scope Context - } + Assert-MockCalled ` + -CommandName New-DscClassResourceWikiPage ` + -ParameterFilter $script:newDscResourceWikiPage_parameterFilter ` + -Exactly -Times 1 } } }