diff --git a/Source/Convert-HelpToHtmlTree.ps1 b/Source/Convert-HelpToHtmlTree.ps1 index 4cce92d..fd95b0e 100644 --- a/Source/Convert-HelpToHtmlTree.ps1 +++ b/Source/Convert-HelpToHtmlTree.ps1 @@ -164,6 +164,7 @@ This output is generated from the above input: The MSDN references are retrieved automatically from two fixed MSDN reference pages (one for cmdlets and one for "about" topics). If those fixed references ever change URLs, that will break the generator; update those URLs in the Get-CmdletDocLinks function to mend it. +Note that if you get just plain text with no hyperlink for any of the first four classes of input, chances are you have a typo, because the function or topic could not be found. ==== Output Enhancements: Formatting ==== @@ -302,7 +303,6 @@ function Convert-HelpToHtmlTree Init-Variables - if ($SourceDir) { $moduleRoot = $SourceDir } @@ -313,19 +313,20 @@ function Convert-HelpToHtmlTree $Namespaces | # Convert wildcards (if any) in Namespaces parameter to instances % { - Get-ChildItem $moduleRoot $_ } | + Get-ChildItem -Path $moduleRoot -Filter $_ } | % { $namespace = $_.Name # Grab name out of DirectoryInfo object + $namespaceDir = Join-Path $moduleRoot $namespace Write-Host "Namespace: $namespace" $script:namespaceCount++; $script:moduleSummary = @{} if ($DocTitle) { $title = "{0} {1}" -f $namespace, $DocTitle } else { $title = "$namespace Namespace"} - $namespaceDir = Join-Path $moduleRoot $namespace $saveModuleCount = $moduleCount - Get-ChildItem $namespaceDir | - ? { $_.PsIsContainer } | - % { Process-Module $namespace $_.Name $title} + # load the modules first in case there are any cross-referenced links + $modules = Import-AllModules $namespace + $modules | % { Process-Module $namespace $_ $title} + Remove-AllModules $modules if ($saveModuleCount -eq $moduleCount) { [void](Handle-MissingValue "No modules found"); $noModulesFlagged = $true @@ -356,6 +357,25 @@ function Convert-HelpToHtmlTree ########################### Support ############################# +function Import-AllModules($namespace) +{ + $modules = Get-ChildItem $namespaceDir | + where { $_.PsIsContainer } | + select -ExpandProperty Name + $modules | + % { + if (! (Import-ModuleUnlessDocGeneratorItself $namespace $_)) + { [void](Handle-MissingValue "Cannot load $_ module") } + } + return $modules +} + +function Remove-AllModules($modules) +{ + $modules | + % { Remove-ModuleUnlessDocGeneratorItself $_ } +} + function Get-CmdletDocLinks($referenceWebPage, $topicRegex) { # Adapted from http://powershell.com/cs/blogs/tips/archive/2010/10/06/scraping-information-from-web-pages.aspx @@ -398,29 +418,24 @@ function Process-Module($namespace, $moduleName, $parentTitle) Write-Host " Module: $moduleName" $script:moduleCount++; - if (! (Import-MostModules $namespace $moduleName)) - { [void](Handle-MissingValue "Cannot load $moduleName module") } - else { - if (!$parentTitle) { $parentTitle = "{0} Namespace" -f $namespace } - $moduleDocPath = Join-Path $TargetDir (Join-Path $namespace $moduleName) - if (! (Test-Path $moduleDocPath)) { - if ($PSCmdlet.ShouldProcess($moduleDocPath, "mkdir")) { - mkdir $moduleDocPath | Out-Null } - } + if (!$parentTitle) { $parentTitle = "{0} Namespace" -f $namespace } + $moduleDocPath = Join-Path $TargetDir (Join-Path $namespace $moduleName) + if (! (Test-Path $moduleDocPath)) { + if ($PSCmdlet.ShouldProcess($moduleDocPath, "mkdir")) { + mkdir $moduleDocPath | Out-Null } + } - $moduleDetails = @{} - if ($modulePropertiesInTemplate) { - $modulePropertiesInTemplate | % { - $newval = invoke-expression "(Get-Module $moduleName).$_" - $moduleDetails[$_] = $newval - } + $moduleDetails = @{} + if ($modulePropertiesInTemplate) { + $modulePropertiesInTemplate | % { + $newval = invoke-expression "(Get-Module $moduleName).$_" + $moduleDetails[$_] = $newval } - - $help = @{} - Generate-FunctionPages $moduleName $moduleDocPath $parentTitle $help - Generate-ModulePage $moduleName $moduleDocPath $parentTitle $help - Remove-MostModules $moduleName } + + $help = @{} + Generate-FunctionPages $moduleName $moduleDocPath $parentTitle $help + Generate-ModulePage $moduleName $moduleDocPath $parentTitle $help } function Generate-FunctionPages($moduleName, $moduleDocPath, $parentTitle, $helpHash) @@ -686,7 +701,7 @@ function Handle-MissingValue($message) } # Convert Import-Module into a functional object -function Import-MostModules($namespace, $moduleName) +function Import-ModuleUnlessDocGeneratorItself($namespace, $moduleName) { # Skip reloading this module--causes all functions to be lost! if ($moduleName -eq $thisModule) { return $true } @@ -700,7 +715,7 @@ function Import-MostModules($namespace, $moduleName) return ! (!$?) } -function Remove-MostModules($moduleName) +function Remove-ModuleUnlessDocGeneratorItself($moduleName) { if ($moduleName -ne $thisModule) { # Hmmm... With -WhatIf, Remove-Module complains about no modules removed diff --git a/Tests/DocTreeGenerator.Tests.ps1 b/Tests/DocTreeGenerator.Tests.ps1 index 28e20ef..ac021ff 100644 --- a/Tests/DocTreeGenerator.Tests.ps1 +++ b/Tests/DocTreeGenerator.Tests.ps1 @@ -920,4 +920,96 @@ $content } + Describe 'Main' { + Mock Write-Host + Mock Write-Warning + Mock Get-CmdletDocLinks + Mock Handle-MissingValue { 'missing value' } + Mock Get-ChildItem { @{ 'name' = $Filter } } + Mock Remove-AllModules { $script:sequence += 'remove' } + Mock Process-Module { $script:sequence += 'process' } + Mock Generate-HomePage + Mock Generate-ContentsPage + + It 'Imports each module in a namespace before building' { + Mock Import-AllModules { $script:sequence += 'import'; return 'm1' } + $script:sequence = @() + Convert-HelpToHtmlTree -Namespaces 'ns1' + Assert-MockCalled Import-AllModules 1 -Scope It + $sequence[0] | Should Be 'import' + $sequence[1] | Should Be 'process' + } + + It 'Removes each module in a namespace after building' { + Mock Import-AllModules { $script:sequence += 'import'; return 'm1' } + $script:sequence = @() + Convert-HelpToHtmlTree -Namespaces 'ns1' + Assert-MockCalled Remove-AllModules 1 -Scope It + $sequence[1] | Should Be 'process' + $sequence[2] | Should Be 'remove' + } + + It 'Processes each module in a single namespace' { + $moduleNames = 'm1','m2' + Mock Import-AllModules { return $moduleNames } + $script:sequence = @() + Convert-HelpToHtmlTree -Namespaces 'ns1' + foreach ($name in $moduleNames) { + Assert-MockCalled Process-Module 1 { $moduleName -eq $name } -Scope It + } + } + + It 'Processes each module in multiple namespaces' { + $ns1Modules = @('ns1-m1', 'ns1-m2') + $ns2Modules = @('ns2-m1', 'ns2-m2', 'ns2-m3') + $namespaces = @{ + 'nspace1' = $ns1Modules + 'nspace2' = $ns2Modules + } + # problem getting this to work! + # Mock Import-AllModules -MockWith { return $namespaces[$namespace] } + Mock Import-AllModules -MockWith { $ns1Modules } -ParameterFilter { $namespace -eq 'nspace1' } + Mock Import-AllModules -MockWith { $ns2Modules } -ParameterFilter { $namespace -eq 'nspace2' } + $script:sequence = @() + Convert-HelpToHtmlTree -Namespaces 'nspace1','nspace2' + foreach ($ns in $namespaces.keys) { + foreach ($name in $namespaces[$ns]) { + #Assert-MockCalled Process-Module 1 { $moduleName -eq $name } -Scope It + } + } + } + + It 'WARNS about no namespaces when supplied argument does not resolve to path' { + Mock Import-AllModules + Mock Get-ChildItem { @() } + Convert-HelpToHtmlTree -Namespaces 'unknownNamespace' + Assert-MockCalled Handle-MissingValue 1 { $message -eq 'No namespaces found' } -scope It + } + + It 'Does NOT warn about no namespaces when supplied argument resolves to path' { + Mock Import-AllModules { return 'm1' } + Mock Get-ChildItem { @{ 'name' = $Filter } } + # emulate real Process-Module with respect to generating warning + Mock Process-Module { $script:moduleCount++ } + Convert-HelpToHtmlTree -Namespaces 'ns1' + Assert-MockCalled Handle-MissingValue 0 -Scope It + } + + It 'WARNS about no modules when namespace dir has none' { + Mock Import-AllModules { @() } + Mock Get-ChildItem { @{ 'name' = $Filter } } + Mock Process-Module { $script:moduleCount++ } + Convert-HelpToHtmlTree -Namespaces 'ns1' + Assert-MockCalled Handle-MissingValue 1 { $message -eq 'No modules found' } -scope It + } + It 'Does NOT warn about no modules when modules present' { + Mock Import-AllModules { 'm1' } + Mock Get-ChildItem { @{ 'name' = $Filter } } + Mock Process-Module { $script:moduleCount++ } + Convert-HelpToHtmlTree -Namespaces 'ns1' + Assert-MockCalled Handle-MissingValue 0 -scope It + } + } + + }