From 1bc492da4fed939fa3013047548bf0f84ce0cc09 Mon Sep 17 00:00:00 2001 From: Brian Bunke Date: Wed, 3 Oct 2018 21:11:06 -0700 Subject: [PATCH 01/14] Add basic "characterization" unit test --- Tests/ConvertTo-Table.Tests.ps1 | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Tests/ConvertTo-Table.Tests.ps1 diff --git a/Tests/ConvertTo-Table.Tests.ps1 b/Tests/ConvertTo-Table.Tests.ps1 new file mode 100644 index 0000000..99abb3d --- /dev/null +++ b/Tests/ConvertTo-Table.Tests.ps1 @@ -0,0 +1,42 @@ +Describe 'ConvertTo-Table' -Tag 'unit' { + Remove-Module ConfluencePS -Force + Import-Module $PSScriptRoot\..\ConfluencePS\ConfluencePS.psd1 + + Context 'Example 1' { + $return = Get-Service | Select Name,DisplayName,Status -First 10 | ConvertTo-ConfluenceTable + $array = $return -split [Environment]::NewLine + + It 'Out-String is called at the end of the function' { + $return.Count | Should -Be 1 + } + + It 'Returns the correct number of rows' { + $array.Count | Should -Be 12 + } + + It 'Returns a header row first' { + $array[0] | Should -BeExactly '||Name||DisplayName||Status||' + } + + It 'Wraps other rows with table formatting' { + $array[1..10] | ForEach-Object { + $_ | Should -Match '^\|.*?\|.*?\|Running|Stopped\|' + } + } + + It 'Returns an empty row at the end' { + $array[11] | Should -BeNullOrEmpty + } + } + + # Not much for Example 2 to check + + Context 'Example 3' { + $return = Get-Alias | Where {$_.Name.Length -eq 1} | Select CommandType,DisplayName | ConvertTo-ConfluenceTable -NoHeader + $array = $return -split [Environment]::NewLine + + It 'Does not return a header row' { + $array[0] | Should -Not -Match '\|\|' + } + } +} From da75f2c1be7686335ebff1f2d659f0a36d85b3c5 Mon Sep 17 00:00:00 2001 From: Brian Bunke Date: Wed, 3 Oct 2018 22:03:34 -0700 Subject: [PATCH 02/14] Clean up existing table code --- ConfluencePS/Public/ConvertTo-Table.ps1 | 32 +++++++++++++++---------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/ConfluencePS/Public/ConvertTo-Table.ps1 b/ConfluencePS/Public/ConvertTo-Table.ps1 index d9d3bff..d2957d4 100644 --- a/ConfluencePS/Public/ConvertTo-Table.ps1 +++ b/ConfluencePS/Public/ConvertTo-Table.ps1 @@ -14,7 +14,7 @@ function ConvertTo-Table { BEGIN { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" - $RowArray = New-Object System.Collections.ArrayList + $RowArray = New-Object System.Collections.Generic.List[string] } PROCESS { @@ -27,26 +27,32 @@ function ConvertTo-Table { # This ForEach needed if the content wasn't piped in $Content | ForEach-Object { - # First row enclosed by ||, all other rows by | - If (!$HeaderGenerated) { - $_.PSObject.Properties | - ForEach-Object -Begin {$Header = ""} ` + # Header row enclosed by || + If ($null -eq $HeaderGenerated) { + $_.PSObject.Properties | ForEach-Object ` + -Begin {$Header = ""} ` -Process {$Header += "||$($_.Name)"} ` - -End {$Header += "||"} - $RowArray.Add($Header) | Out-Null + -End {$Header += "||"} + [void]$RowArray.Add($Header) $HeaderGenerated = $true } - $_.PSObject.Properties | - ForEach-Object -Begin {$Row = ""} ` - -Process {if ($($_.value)) {$Row += "|$($_.Value)"} else {$Row += "| "}} ` - -End {$Row += "|"} - $RowArray.Add($Row) | Out-Null + + # All other rows enclosed by | + $_.PSObject.Properties | ForEach-Object ` + -Begin {$Row = ""} ` + -Process { + if ($_.value) {$Row += "|$($_.Value)"} + else {$Row += "| "} + } ` + -End {$Row += "|"} + [void]$RowArray.Add($Row) | Out-Null } } END { + # Return the array as one large, multi-line string $RowArray | Out-String - Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ened" + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended" } } From 146a3834c7d3c67f09c1cbdbb1b76130a8063463 Mon Sep 17 00:00:00 2001 From: Brian Bunke Date: Wed, 3 Oct 2018 23:10:18 -0700 Subject: [PATCH 03/14] Add -Vertical to ConvertTo-Table --- ConfluencePS/Public/ConvertTo-Table.ps1 | 53 +++++++++++++++++-------- Tests/ConvertTo-Table.Tests.ps1 | 49 ++++++++++++++++++++++- docs/commands/ConvertTo-Table.md | 44 ++++++++++++++++++-- 3 files changed, 124 insertions(+), 22 deletions(-) diff --git a/ConfluencePS/Public/ConvertTo-Table.ps1 b/ConfluencePS/Public/ConvertTo-Table.ps1 index d2957d4..66b52c9 100644 --- a/ConfluencePS/Public/ConvertTo-Table.ps1 +++ b/ConfluencePS/Public/ConvertTo-Table.ps1 @@ -8,6 +8,8 @@ function ConvertTo-Table { )] $Content, + [switch]$Vertical, + [switch]$NoHeader ) @@ -27,25 +29,42 @@ function ConvertTo-Table { # This ForEach needed if the content wasn't piped in $Content | ForEach-Object { - # Header row enclosed by || - If ($null -eq $HeaderGenerated) { + If ($Vertical) { + If ($HeaderGenerated) {$pipe = '|'} + Else {$pipe = '||'} + + # Put an empty row between multiple tables (objects) + If ($Spacer) { + [void]$RowArray.Add('') + } + + $_.PSObject.Properties | ForEach-Object { + $Row = "$pipe{0}$pipe{1}|" -f $_.Name, $_.Value + [void]$RowArray.Add($Row) + } + + $Spacer = $true + } Else { + # Header row enclosed by || + If ($null -eq $HeaderGenerated) { + $_.PSObject.Properties | ForEach-Object ` + -Begin {$Header = ""} ` + -Process {$Header += "||$($_.Name)"} ` + -End {$Header += "||"} + [void]$RowArray.Add($Header) + $HeaderGenerated = $true + } + + # All other rows enclosed by | $_.PSObject.Properties | ForEach-Object ` - -Begin {$Header = ""} ` - -Process {$Header += "||$($_.Name)"} ` - -End {$Header += "||"} - [void]$RowArray.Add($Header) - $HeaderGenerated = $true + -Begin {$Row = ""} ` + -Process { + if ($_.value) {$Row += "|$($_.Value)"} + else {$Row += "| "} + } ` + -End {$Row += "|"} + [void]$RowArray.Add($Row) } - - # All other rows enclosed by | - $_.PSObject.Properties | ForEach-Object ` - -Begin {$Row = ""} ` - -Process { - if ($_.value) {$Row += "|$($_.Value)"} - else {$Row += "| "} - } ` - -End {$Row += "|"} - [void]$RowArray.Add($Row) | Out-Null } } diff --git a/Tests/ConvertTo-Table.Tests.ps1 b/Tests/ConvertTo-Table.Tests.ps1 index 99abb3d..d7937d7 100644 --- a/Tests/ConvertTo-Table.Tests.ps1 +++ b/Tests/ConvertTo-Table.Tests.ps1 @@ -3,7 +3,7 @@ Import-Module $PSScriptRoot\..\ConfluencePS\ConfluencePS.psd1 Context 'Example 1' { - $return = Get-Service | Select Name,DisplayName,Status -First 10 | ConvertTo-ConfluenceTable + $return = Get-Service | Select-Object Name,DisplayName,Status -First 10 | ConvertTo-ConfluenceTable $array = $return -split [Environment]::NewLine It 'Out-String is called at the end of the function' { @@ -21,6 +21,7 @@ It 'Wraps other rows with table formatting' { $array[1..10] | ForEach-Object { $_ | Should -Match '^\|.*?\|.*?\|Running|Stopped\|' + $_ | Should -Not -Match '\|\|' } } @@ -32,11 +33,55 @@ # Not much for Example 2 to check Context 'Example 3' { - $return = Get-Alias | Where {$_.Name.Length -eq 1} | Select CommandType,DisplayName | ConvertTo-ConfluenceTable -NoHeader + $return = Get-Alias | Where-Object {$_.Name.Length -eq 1} | Select-Object CommandType,DisplayName | ConvertTo-ConfluenceTable -NoHeader $array = $return -split [Environment]::NewLine It 'Does not return a header row' { $array[0] | Should -Not -Match '\|\|' } } + + Context 'Example 4' { + $return = [PSCustomObject]@{Name = 'Max'; Age = 123} | ConvertTo-ConfluenceTable -Vertical + $array = $return -split [Environment]::NewLine + + It 'Out-String is called at the end of the function' { + $return.Count | Should -Be 1 + } + + It 'Returns the correct number of rows' { + $array.Count | Should -Be 3 + } + + It 'Returns a vertical table with a header column' { + $array[0] | Should -BeExactly '||Name||Max|' + $array[1] | Should -BeExactly '||Age||123|' + } + + It 'Returns an empty row at the end' { + $array[2] | Should -BeNullOrEmpty + } + } + + Context 'Example 5' { + $return = Get-Alias | Where-Object {$_.Name.Length -eq 1} | Select-Object Name,Definition | + ConvertTo-ConfluenceTable -Vertical -NoHeader + $array = $return -split [Environment]::NewLine + + It 'Returns the correct number of rows' { + # This assumes Get-Alias finds 4 one-character aliases + $array.Count | Should -Be 12 + } + + It 'Returns a vertical table with no header column' { + $array[0] | Should -BeExactly '|Name|%|' + $array[1] | Should -BeExactly '|Definition|ForEach-Object|' + } + + It 'Spaces objects into multiple vertical tables' { + # We expect four two-line tables + # So rows 3, 6, 9, and 12 should be empty + $array[2,5,8,11] | ForEach-Object {$_ | Should -BeNullOrEmpty} + } + } } diff --git a/docs/commands/ConvertTo-Table.md b/docs/commands/ConvertTo-Table.md index 5a396c3..b1b1a03 100644 --- a/docs/commands/ConvertTo-Table.md +++ b/docs/commands/ConvertTo-Table.md @@ -32,7 +32,7 @@ This work is performed locally, and does not perform a REST call. ### -------------------------- EXAMPLE 1 -------------------------- ```powershell -Get-Service | Select Name,DisplayName,Status -First 10 | ConvertTo-ConfluenceTable +Get-Service | Select-Object Name,DisplayName,Status -First 10 | ConvertTo-ConfluenceTable ``` List the first ten services on your computer, and convert to a table in Confluence markup format. @@ -40,7 +40,7 @@ List the first ten services on your computer, and convert to a table in Confluen ### -------------------------- EXAMPLE 2 -------------------------- ```powershell -$SvcTable = Get-Service | Select Name,Status -First 10 | +$SvcTable = Get-Service | Select-Object Name,Status -First 10 | ConvertTo-ConfluenceTable | ConvertTo-ConfluenceStorageFormat ``` @@ -50,12 +50,32 @@ Store the results in $SvcTable for a later New-ConfluencePage/etc. command. ### -------------------------- EXAMPLE 3 -------------------------- ```powershell -Get-Alias | Where {$_.Name.Length -eq 1} | Select CommandType,DisplayName | +Get-Alias | Where-Object {$_.Name.Length -eq 1} | Select-Object CommandType,DisplayName | ConvertTo-ConfluenceTable -NoHeader ``` Make a table of all one-character PowerShell aliases, and don't include the header row. +### -------------------------- EXAMPLE 4 -------------------------- + +```powershell +[PSCustomObject]@{Name = 'Max'; Age = 123} | ConvertTo-ConfluenceTable -Vertical +``` + +Output a vertical table instead. Property names will be a left header column +with bold highlighting. Property values will be in a normal right column. +Multiple objects will output as multiple tables, one on top of the next. + +### -------------------------- EXAMPLE 5 -------------------------- + +```powershell +Get-Alias | Where-Object {$_.Name.Length -eq 1} | Select-Object Name,Definition | + ConvertTo-ConfluenceTable -Vertical -NoHeader +``` + +Output one string containing four vertical tables (one for each object returned). +Property names are still displayed, but -NoHeader suppresses the bold highlighting. + ## PARAMETERS ### -Content @@ -74,10 +94,28 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -Vertical + +Create a vertical, two-column table. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -NoHeader Ignore the property names, keeping a table of values with no header row highlighting. +In a vertical table, the property names remain, but the bold highlighting is removed. + ```yaml Type: SwitchParameter Parameter Sets: (All) From e323f950619161d309f2cc39a554215c9db38def Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Mon, 8 Oct 2018 11:49:36 +0200 Subject: [PATCH 04/14] Improved generation of markdown table --- ConfluencePS/Public/ConvertTo-Table.ps1 | 36 +++++++++---------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/ConfluencePS/Public/ConvertTo-Table.ps1 b/ConfluencePS/Public/ConvertTo-Table.ps1 index 66b52c9..ac150e4 100644 --- a/ConfluencePS/Public/ConvertTo-Table.ps1 +++ b/ConfluencePS/Public/ConvertTo-Table.ps1 @@ -8,24 +8,22 @@ function ConvertTo-Table { )] $Content, - [switch]$Vertical, + [Switch]$Vertical, - [switch]$NoHeader + [Switch]$NoHeader ) BEGIN { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" - $RowArray = New-Object System.Collections.Generic.List[string] + $sb = [System.Text.StringBuilder]::new() } PROCESS { Write-Debug "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" Write-Debug "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" - If ($NoHeader) { - $HeaderGenerated = $true - } + $HeaderGenerated = $NoHeader # This ForEach needed if the content wasn't piped in $Content | ForEach-Object { @@ -35,42 +33,32 @@ function ConvertTo-Table { # Put an empty row between multiple tables (objects) If ($Spacer) { - [void]$RowArray.Add('') + $null = $sb.AppendLine('') } $_.PSObject.Properties | ForEach-Object { - $Row = "$pipe{0}$pipe{1}|" -f $_.Name, $_.Value - [void]$RowArray.Add($Row) + $row = ("$pipe {0} $pipe {1} |" -f $_.Name, $_.Value) -replace "\|\s\s", "| " + $null = $sb.AppendLine($row) } $Spacer = $true } Else { # Header row enclosed by || - If ($null -eq $HeaderGenerated) { - $_.PSObject.Properties | ForEach-Object ` - -Begin {$Header = ""} ` - -Process {$Header += "||$($_.Name)"} ` - -End {$Header += "||"} - [void]$RowArray.Add($Header) + If (-not $HeaderGenerated) { + $null = $sb.AppendLine("|| {0} ||" -f ($_.PSObject.Properties.Name -join " || ")) $HeaderGenerated = $true } # All other rows enclosed by | - $_.PSObject.Properties | ForEach-Object ` - -Begin {$Row = ""} ` - -Process { - if ($_.value) {$Row += "|$($_.Value)"} - else {$Row += "| "} - } ` - -End {$Row += "|"} - [void]$RowArray.Add($Row) + $row = ("| " + ($_.PSObject.Properties.Value -join " | ") + " |") -replace "\|\s\s", "| " + $null = $sb.AppendLine($row) } } } END { # Return the array as one large, multi-line string - $RowArray | Out-String + $sb.ToString() Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended" } From e544ff5cdd091c37368d24eac1ec22887100aedc Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Mon, 8 Oct 2018 11:49:51 +0200 Subject: [PATCH 05/14] Improved unit tests for markdown table --- Tests/ConvertTo-Table.Tests.ps1 | 87 --------- Tests/Functions/ConvertTo-Table.Tests.ps1 | 207 ++++++++++++++++++++++ 2 files changed, 207 insertions(+), 87 deletions(-) delete mode 100644 Tests/ConvertTo-Table.Tests.ps1 create mode 100644 Tests/Functions/ConvertTo-Table.Tests.ps1 diff --git a/Tests/ConvertTo-Table.Tests.ps1 b/Tests/ConvertTo-Table.Tests.ps1 deleted file mode 100644 index d7937d7..0000000 --- a/Tests/ConvertTo-Table.Tests.ps1 +++ /dev/null @@ -1,87 +0,0 @@ -Describe 'ConvertTo-Table' -Tag 'unit' { - Remove-Module ConfluencePS -Force - Import-Module $PSScriptRoot\..\ConfluencePS\ConfluencePS.psd1 - - Context 'Example 1' { - $return = Get-Service | Select-Object Name,DisplayName,Status -First 10 | ConvertTo-ConfluenceTable - $array = $return -split [Environment]::NewLine - - It 'Out-String is called at the end of the function' { - $return.Count | Should -Be 1 - } - - It 'Returns the correct number of rows' { - $array.Count | Should -Be 12 - } - - It 'Returns a header row first' { - $array[0] | Should -BeExactly '||Name||DisplayName||Status||' - } - - It 'Wraps other rows with table formatting' { - $array[1..10] | ForEach-Object { - $_ | Should -Match '^\|.*?\|.*?\|Running|Stopped\|' - $_ | Should -Not -Match '\|\|' - } - } - - It 'Returns an empty row at the end' { - $array[11] | Should -BeNullOrEmpty - } - } - - # Not much for Example 2 to check - - Context 'Example 3' { - $return = Get-Alias | Where-Object {$_.Name.Length -eq 1} | Select-Object CommandType,DisplayName | ConvertTo-ConfluenceTable -NoHeader - $array = $return -split [Environment]::NewLine - - It 'Does not return a header row' { - $array[0] | Should -Not -Match '\|\|' - } - } - - Context 'Example 4' { - $return = [PSCustomObject]@{Name = 'Max'; Age = 123} | ConvertTo-ConfluenceTable -Vertical - $array = $return -split [Environment]::NewLine - - It 'Out-String is called at the end of the function' { - $return.Count | Should -Be 1 - } - - It 'Returns the correct number of rows' { - $array.Count | Should -Be 3 - } - - It 'Returns a vertical table with a header column' { - $array[0] | Should -BeExactly '||Name||Max|' - $array[1] | Should -BeExactly '||Age||123|' - } - - It 'Returns an empty row at the end' { - $array[2] | Should -BeNullOrEmpty - } - } - - Context 'Example 5' { - $return = Get-Alias | Where-Object {$_.Name.Length -eq 1} | Select-Object Name,Definition | - ConvertTo-ConfluenceTable -Vertical -NoHeader - $array = $return -split [Environment]::NewLine - - It 'Returns the correct number of rows' { - # This assumes Get-Alias finds 4 one-character aliases - $array.Count | Should -Be 12 - } - - It 'Returns a vertical table with no header column' { - $array[0] | Should -BeExactly '|Name|%|' - $array[1] | Should -BeExactly '|Definition|ForEach-Object|' - } - - It 'Spaces objects into multiple vertical tables' { - # We expect four two-line tables - # So rows 3, 6, 9, and 12 should be empty - $array[2,5,8,11] | ForEach-Object {$_ | Should -BeNullOrEmpty} - } - } -} diff --git a/Tests/Functions/ConvertTo-Table.Tests.ps1 b/Tests/Functions/ConvertTo-Table.Tests.ps1 new file mode 100644 index 0000000..ab77c6d --- /dev/null +++ b/Tests/Functions/ConvertTo-Table.Tests.ps1 @@ -0,0 +1,207 @@ +#requires -modules BuildHelpers +#requires -modules @{ ModuleName = "Pester"; ModuleVersion = "4.3.1" } + +Describe 'ConvertTo-Table' -Tag Unit { + + BeforeAll { + Remove-Item -Path Env:\BH* + $projectRoot = (Resolve-Path "$PSScriptRoot/../..").Path + if ($projectRoot -like "*Release") { + $projectRoot = (Resolve-Path "$projectRoot/..").Path + } + + Import-Module BuildHelpers + Set-BuildEnvironment -BuildOutput '$ProjectPath/Release' -Path $projectRoot -ErrorAction SilentlyContinue + + $env:BHManifestToTest = $env:BHPSModuleManifest + $script:isBuild = $PSScriptRoot -like "$env:BHBuildOutput*" + if ($script:isBuild) { + $Pattern = [regex]::Escape($env:BHProjectPath) + + $env:BHBuildModuleManifest = $env:BHPSModuleManifest -replace $Pattern, $env:BHBuildOutput + $env:BHManifestToTest = $env:BHBuildModuleManifest + } + + Import-Module "$env:BHProjectPath/Tools/BuildTools.psm1" + + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Import-Module $env:BHManifestToTest + } + AfterAll { + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Remove-Module BuildHelpers -ErrorAction SilentlyContinue + Remove-Item -Path Env:\BH* + } + + Context "Sanity checking" { + $command = Get-Command -Name ConvertTo-ConfluenceTable + + It "has a [System.Object] -Content parameter" { + $command.Parameters.ContainsKey("Content") + $command.Parameters["Content"].ParameterType | Should -Be "System.Object" + } + It "has a [Switch] -Vertical parameter" { + $command.Parameters.ContainsKey("Vertical") + $command.Parameters["Vertical"].ParameterType | Should -Be "Switch" + } + It "has a [Switch] -NoHeader parameter" { + $command.Parameters.ContainsKey("NoHeader") + $command.Parameters["NoHeader"].ParameterType | Should -Be "Switch" + } + } + + Context "Behavior checking" { + + #region Mocking + Mock Get-Service { + [PSCustomObject]@{ + Name = "AppMgmt" + DisplayName = "Application Management" + Status = "Running" + } + [PSCustomObject]@{ + Name = "BITS" + DisplayName = "Background Intelligent Transfer Service" + Status = "Running" + } + [PSCustomObject]@{ + Name = "Dhcp" + DisplayName = "DHCP Client" + Status = "Running" + } + [PSCustomObject]@{ + Name = "DsmSvc" + DisplayName = "Device Setup Manager" + Status = "Running" + } + [PSCustomObject]@{ + Name = "EFS" + DisplayName = "Encrypting File System (EFS)" + Status = "Running" + } + [PSCustomObject]@{ + Name = "lmhosts" + DisplayName = "TCP/IP NetBIOS Helper" + Status = "Running" + } + [PSCustomObject]@{ + Name = "MSDTC" + DisplayName = "Distributed Transaction Coordinator" + Status = "Stopped" + } + [PSCustomObject]@{ + Name = "NlaSvc" + DisplayName = "Network Location Awareness" + Status = "Stopped" + } + [PSCustomObject]@{ + Name = "PolicyAgent" + DisplayName = "IPsec Policy Agent" + Status = "Stopped" + } + [PSCustomObject]@{ + Name = "SessionEnv" + DisplayName = "Remote Desktop Configuration" + Status = "Stopped" + } + } + #endregion Mocking + + It "creates a table with a header row" { + $table = ConvertTo-ConfluenceTable -Content (Get-Service) + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 12 + $row[0] | Should -BeExactly '|| Name || DisplayName || Status ||' + $row[1..10] | ForEach-Object { + $_ | Should -Match '^| [\w\s]+? | [\w\s]+? | [\w\s]+? |$' + $_ | Should -Not -Match '\|\|' + $_ | Should -Not -Match '\|\s\s+\|' + } + $row[11] | Should -BeNullOrEmpty + } + + It "creates an empty table with header row" { + $table = ConvertTo-ConfluenceTable ([PSCustomObject]@{ Name = $null; DisplayName = $null; Status = $null }) + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 3 + $row[0] | Should -BeExactly '|| Name || DisplayName || Status ||' + $row[1] | Should -BeExactly '| | | |' + $row[2] | Should -BeNullOrEmpty + } + + It "creates a table without a header row" { + $table = ConvertTo-ConfluenceTable (Get-Service) -NoHeader + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 11 + $row[0..9] | ForEach-Object { + $_ | Should -Match '^| [\w\s]+? | [\w\s]+? | [\w\s]+? |$' + $_ | Should -Not -Match '\|\|' + $_ | Should -Not -Match '\|\s\s+\|' + } + $row[10] | Should -BeNullOrEmpty + } + + It "creates a vertical table with a header column" { + $table = ConvertTo-ConfluenceTable ([PSCustomObject]@{ Name = "winlogon"; DisplayName = "Windows logon"; Status = "Running" }) -Vertical + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 4 + $row[0..3] | ForEach-Object { + $_ | Should -Match '^|| [\w\s]+ || [\w\s]+ |$' + $_ | Should -Not -Match '\|\|$' + $_ | Should -Not -Match '\|\s\s+\|' + } + $row[4] | Should -BeNullOrEmpty + } + + It "creates an empty vertical table with a header column" { + $table = ConvertTo-ConfluenceTable ([PSCustomObject]@{ Name = $null; DisplayName = $null; Status = $null }) -Vertical + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 4 + $row[0..3] | ForEach-Object { + $_ | Should -Match '^|| [\w\s]+? || |$' + $_ | Should -Not -Match '\|\|$' + $_ | Should -Not -Match '\|\s\s+\|' + } + $row[4] | Should -BeNullOrEmpty + } + + It "creates a vertical table without a header column" { + $table = ConvertTo-ConfluenceTable ([PSCustomObject]@{ Name = "winlogon"; DisplayName = "Windows logon"; Status = "Running" }) -Vertical -NoHeader + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 4 + $row[0..3] | ForEach-Object { + $_ | Should -Match '^| [\w\s]+? | [\w\s]+? |$' + $_ | Should -Not -Match '\|\|$' + $_ | Should -Not -Match '\|\s\s+\|' + } + $row[4] | Should -BeNullOrEmpty + } + + It "creates an empty vertical table without a header column" { + $table = ConvertTo-ConfluenceTable ([PSCustomObject]@{ Name = $null; DisplayName = $null; Status = $null }) -Vertical -NoHeader + $row = $table -split [Environment]::NewLine + + $row.Count | Should -Be 4 + $row[0..3] | ForEach-Object { + $_ | Should -Match '^| [\w\s]+? | |$' + $_ | Should -Not -Match '\|\|$' + $_ | Should -Not -Match '\|\s\s+\|' + } + $row[4] | Should -BeNullOrEmpty + } + + It "returns a single string object" { + $table = ConvertTo-ConfluenceTable (Get-Service) + $row = $table -split [Environment]::NewLine + + $table.Count | Should -Be 1 + $row.Count | Should -BeGreaterThan 1 + } + } +} From 7b38ffcd7c3497118fb0abe83f8155c8e2a3e191 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Tue, 4 Dec 2018 09:09:01 +0100 Subject: [PATCH 06/14] Updated syntax of ConvertTo-Table --- docs/en-US/commands/ConvertTo-Table.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/commands/ConvertTo-Table.md b/docs/en-US/commands/ConvertTo-Table.md index f57bae1..acffb24 100644 --- a/docs/en-US/commands/ConvertTo-Table.md +++ b/docs/en-US/commands/ConvertTo-Table.md @@ -16,7 +16,7 @@ Convert your content to Confluence's wiki markup table format. ## SYNTAX ```powershell -ConvertTo-ConfluenceTable [-Content] [-NoHeader] +ConvertTo-ConfluenceTable [-Content] [-Vertical] [-NoHeader] ``` ## DESCRIPTION From 4cfffe593f8be8c5e077eabf0038e97ed1b16766 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Tue, 4 Dec 2018 09:29:25 +0100 Subject: [PATCH 07/14] Updated required pester version --- Tests/Functions/ConvertTo-Table.Tests.ps1 | 33 ++++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Tests/Functions/ConvertTo-Table.Tests.ps1 b/Tests/Functions/ConvertTo-Table.Tests.ps1 index ab77c6d..a6f391f 100644 --- a/Tests/Functions/ConvertTo-Table.Tests.ps1 +++ b/Tests/Functions/ConvertTo-Table.Tests.ps1 @@ -1,5 +1,5 @@ -#requires -modules BuildHelpers -#requires -modules @{ ModuleName = "Pester"; ModuleVersion = "4.3.1" } +#requires -modules BuildHelpers +#requires -modules @{ ModuleName = "Pester"; ModuleVersion = "4.4.2" } Describe 'ConvertTo-Table' -Tag Unit { @@ -53,7 +53,8 @@ Describe 'ConvertTo-Table' -Tag Unit { Context "Behavior checking" { #region Mocking - Mock Get-Service { + # linux and macOS don't have Fake + function Get-FakeService { [PSCustomObject]@{ Name = "AppMgmt" DisplayName = "Application Management" @@ -108,7 +109,7 @@ Describe 'ConvertTo-Table' -Tag Unit { #endregion Mocking It "creates a table with a header row" { - $table = ConvertTo-ConfluenceTable -Content (Get-Service) + $table = ConvertTo-ConfluenceTable -Content (Get-FakeService) $row = $table -split [Environment]::NewLine $row.Count | Should -Be 12 @@ -132,7 +133,7 @@ Describe 'ConvertTo-Table' -Tag Unit { } It "creates a table without a header row" { - $table = ConvertTo-ConfluenceTable (Get-Service) -NoHeader + $table = ConvertTo-ConfluenceTable (Get-FakeService) -NoHeader $row = $table -split [Environment]::NewLine $row.Count | Should -Be 11 @@ -149,12 +150,12 @@ Describe 'ConvertTo-Table' -Tag Unit { $row = $table -split [Environment]::NewLine $row.Count | Should -Be 4 - $row[0..3] | ForEach-Object { + $row[0..2] | ForEach-Object { $_ | Should -Match '^|| [\w\s]+ || [\w\s]+ |$' $_ | Should -Not -Match '\|\|$' $_ | Should -Not -Match '\|\s\s+\|' } - $row[4] | Should -BeNullOrEmpty + $row[3] | Should -BeNullOrEmpty } It "creates an empty vertical table with a header column" { @@ -162,12 +163,12 @@ Describe 'ConvertTo-Table' -Tag Unit { $row = $table -split [Environment]::NewLine $row.Count | Should -Be 4 - $row[0..3] | ForEach-Object { + $row[0..2] | ForEach-Object { $_ | Should -Match '^|| [\w\s]+? || |$' $_ | Should -Not -Match '\|\|$' $_ | Should -Not -Match '\|\s\s+\|' } - $row[4] | Should -BeNullOrEmpty + $row[3] | Should -BeNullOrEmpty } It "creates a vertical table without a header column" { @@ -175,12 +176,12 @@ Describe 'ConvertTo-Table' -Tag Unit { $row = $table -split [Environment]::NewLine $row.Count | Should -Be 4 - $row[0..3] | ForEach-Object { + $row[0..2] | ForEach-Object { $_ | Should -Match '^| [\w\s]+? | [\w\s]+? |$' $_ | Should -Not -Match '\|\|$' $_ | Should -Not -Match '\|\s\s+\|' } - $row[4] | Should -BeNullOrEmpty + $row[3] | Should -BeNullOrEmpty } It "creates an empty vertical table without a header column" { @@ -188,20 +189,20 @@ Describe 'ConvertTo-Table' -Tag Unit { $row = $table -split [Environment]::NewLine $row.Count | Should -Be 4 - $row[0..3] | ForEach-Object { + $row[0..2] | ForEach-Object { $_ | Should -Match '^| [\w\s]+? | |$' $_ | Should -Not -Match '\|\|$' $_ | Should -Not -Match '\|\s\s+\|' } - $row[4] | Should -BeNullOrEmpty + $row[3] | Should -BeNullOrEmpty } It "returns a single string object" { - $table = ConvertTo-ConfluenceTable (Get-Service) + $table = ConvertTo-ConfluenceTable (Get-FakeService) $row = $table -split [Environment]::NewLine - $table.Count | Should -Be 1 - $row.Count | Should -BeGreaterThan 1 + @($table).Count | Should -Be 1 + @($row).Count | Should -BeGreaterThan 1 } } } From 8495335cecafe2e48e367eda038cba5994f5e8e0 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Tue, 4 Dec 2018 17:22:06 +0100 Subject: [PATCH 08/14] Clean up --- ConfluencePS.build.ps1 | 3 +-- ConfluencePS/Public/Get-Label.ps1 | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ConfluencePS.build.ps1 b/ConfluencePS.build.ps1 index aac7a42..85e2c31 100644 --- a/ConfluencePS.build.ps1 +++ b/ConfluencePS.build.ps1 @@ -59,9 +59,9 @@ task InstallDependencies { # Synopsis: Get the next version for the build task GetNextVersion { + $manifestVersion = [Version](Get-Metadata -Path $env:BHPSModuleManifest) try { $env:CurrentOnlineVersion = [Version](Find-Module -Name $env:BHProjectName).Version - $manifestVersion = [Version](Get-Metadata -Path $env:BHPSModuleManifest) $nextOnlineVersion = Get-NextNugetPackageVersion -Name $env:BHProjectName if ( ($manifestVersion.Major -gt $nextOnlineVersion.Major) -or @@ -250,7 +250,6 @@ task Test Init, { } $testResults = Invoke-Pester @parameter - Write-Host (Get-Childitem $env:BHProjectPath) Assert-True ($testResults.FailedCount -eq 0) "$($testResults.FailedCount) Pester test(s) failed." } catch { diff --git a/ConfluencePS/Public/Get-Label.ps1 b/ConfluencePS/Public/Get-Label.ps1 index 84bf286..388786a 100644 --- a/ConfluencePS/Public/Get-Label.ps1 +++ b/ConfluencePS/Public/Get-Label.ps1 @@ -63,7 +63,6 @@ function Get-Label { $InputObject = Get-Page -PageID $_page -ApiURi $apiURi -Credential $Credential } $iwParameters["Uri"] = $resourceApi -f $_page - Write-debug "Hey" $output = New-Object -TypeName ConfluencePS.ContentLabelSet $output.Page = $InputObject $output.Labels += (Invoke-Method @iwParameters) From 49d9496d754ad5e3ea32412441aaadb8a90e3c6d Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Tue, 4 Dec 2018 17:22:16 +0100 Subject: [PATCH 09/14] Improved Tests --- Tests/ConfluencePS.Tests.ps1 | 18 +++ Tests/Integration.Tests.ps1 | 213 ++++++++++++++++++----------------- Tests/Project.Tests.ps1 | 21 ++-- azure-pipelines.yml | 6 +- 4 files changed, 147 insertions(+), 111 deletions(-) diff --git a/Tests/ConfluencePS.Tests.ps1 b/Tests/ConfluencePS.Tests.ps1 index abf933a..163e4cd 100644 --- a/Tests/ConfluencePS.Tests.ps1 +++ b/Tests/ConfluencePS.Tests.ps1 @@ -59,4 +59,22 @@ Describe "General project validation" -Tag Unit { [Version](Get-Metadata -Path $env:BHManifestToTest -PropertyName ModuleVersion) | Should -Not -BeNullOrEmpty [Version](Get-Metadata -Path $env:BHManifestToTest -PropertyName ModuleVersion) | Should -BeOfType [Version] } + + It "module is imported with default prefix" { + $prefix = Get-Metadata -Path $env:BHManifestToTest -PropertyName DefaultCommandPrefix + + Import-Module $env:BHManifestToTest -Force -ErrorAction Stop + (Get-Command -Module $env:BHProjectName).Name | ForEach-Object { + $_ | Should -Match "\-$prefix" + } + } + + It "module is imported with custom prefix" { + $prefix = "Wiki" + + Import-Module $env:BHManifestToTest -Prefix $prefix -Force -ErrorAction Stop + (Get-Command -Module $env:BHProjectName).Name | ForEach-Object { + $_ | Should -Match "\-$prefix" + } + } } diff --git a/Tests/Integration.Tests.ps1 b/Tests/Integration.Tests.ps1 index 9b3916a..ecbf615 100644 --- a/Tests/Integration.Tests.ps1 +++ b/Tests/Integration.Tests.ps1 @@ -1,3 +1,6 @@ +#requires -modules BuildHelpers +#requires -modules Pester + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( "PSUseDeclaredVarsMoreThanAssigments", "", @@ -12,35 +15,41 @@ param() # Pester integration/acceptance tests to use during module development. Dave Wyatt's five-part series: # http://blogs.technet.com/b/heyscriptingguy/archive/2015/12/14/what-is-pester-and-why-should-i-care.aspx -Describe 'Load Module' { - # ARRANGE +$SpaceID = Get-Random + +Describe 'Integration Tests' -Tag Integration { + BeforeAll { - Remove-Module ConfluencePS -ErrorAction SilentlyContinue - } - AfterEach { - Remove-Module ConfluencePS -ErrorAction SilentlyContinue - } + Remove-Item -Path Env:\BH* + $projectRoot = (Resolve-Path "$PSScriptRoot/..").Path + if ($projectRoot -like "*Release") { + $projectRoot = (Resolve-Path "$projectRoot/..").Path + } - # ACT - Import-Module "$PSScriptRoot\..\ConfluencePS" -Force -ErrorAction Stop + Import-Module BuildHelpers + Set-BuildEnvironment -BuildOutput '$ProjectPath/Release' -Path $projectRoot -ErrorAction SilentlyContinue - #ASSERT - It "imports the module" { - Get-Module ConfluencePS | Should BeOfType [PSModuleInfo] - } + $env:BHManifestToTest = $env:BHPSModuleManifest + $script:isBuild = $PSScriptRoot -like "$env:BHBuildOutput*" + if ($script:isBuild) { + $Pattern = [regex]::Escape($env:BHProjectPath) - It "imports the module with custom prefix" { - Import-Module "$PSScriptRoot\..\ConfluencePS" -Prefix "Wiki" -Force -ErrorAction Stop - (Get-Command -Module ConfluencePS).Name | ForEach-Object { - $_ -match "\-Wiki" | Should Be $true + $env:BHBuildModuleManifest = $env:BHPSModuleManifest -replace $Pattern, $env:BHBuildOutput + $env:BHManifestToTest = $env:BHBuildModuleManifest } - } -} -Import-Module "$PSScriptRoot\..\ConfluencePS" -Force -ErrorAction Stop -InModuleScope ConfluencePS { + Import-Module "$env:BHProjectPath/Tools/BuildTools.psm1" + + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Import-Module $env:BHManifestToTest + } + AfterAll { + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Remove-Module BuildHelpers -ErrorAction SilentlyContinue + Remove-Item -Path Env:\BH* + } - Describe 'Set-ConfluenceInfo' -Tag 'Integration' { + Context 'Set-ConfluenceInfo' { # ARRANGE # Could be a long one-liner, but breaking down for readability $Pass = ConvertTo-SecureString -AsPlainText -Force -String $env:WikiPass @@ -60,14 +69,14 @@ InModuleScope ConfluencePS { } } - Describe 'New-ConfluenceSpace' -Tag 'Integration' { + Context 'New-ConfluenceSpace' { # ARRANGE # We don't want warnings on the screen $WarningPreference = 'SilentlyContinue' # Set up test values: - $Key1 = "PESTER" - $Key2 = "PESTER1" + $Key1 = "PESTER$SpaceID" + $Key2 = "PESTER1$SpaceID" $Name1 = "Pester Test Space" $Name2 = "Second Pester Space" $Description = "

A nice description

" @@ -123,11 +132,11 @@ InModuleScope ConfluencePS { } } - Describe 'Get-ConfluenceSpace' -Tag 'Integration' { + Context 'Get-ConfluenceSpace' { # ARRANGE # Set up test values: - $Key1 = "PESTER" - $Key2 = "PESTER1" + $Key1 = "PESTER$SpaceID" + $Key2 = "PESTER1$SpaceID" $Name1 = "Pester Test Space" $Name2 = "Second Pester Space" $Description = "

A nice description

" @@ -149,10 +158,10 @@ InModuleScope ConfluencePS { ($GetSpace3 | Get-Member -MemberType Property).Count | Should Be 7 } It 'has the correct number of results' { - $AllSpaces.Count | Should BeGreaterThan 2 - $GetSpace1.Count | Should Be 1 - $GetSpace2.Count | Should Be 1 - $GetSpace3.Count | Should Be 2 + @($AllSpaces).Count | Should BeGreaterThan 2 + @($GetSpace1).Count | Should Be 1 + @($GetSpace2).Count | Should Be 1 + @($GetSpace3).Count | Should Be 2 } It 'id is integer' { $GetSpace1.ID | Should BeOfType [Int] @@ -214,7 +223,7 @@ InModuleScope ConfluencePS { } } - Describe 'ConvertTo-ConfluenceStorageFormat' -Tag 'Integration' { + Context 'ConvertTo-ConfluenceStorageFormat' { # ARRANGE $InputString = "Hi Pester!" $OutputString = "

Hi Pester!

" @@ -237,15 +246,15 @@ InModuleScope ConfluencePS { } } - Describe 'New-ConfluencePage' -Tag 'Integration' { + Context 'New-ConfluencePage' { <# TODO: * Title may not be empty * Space may not be empty when no parent is provided #> # ARRANGE - $SpaceKey = "PESTER" - $parentPage = Get-ConfluencePage -Title "Pester Test Space Home" -SpaceKey "PESTER" -ErrorAction Stop + $SpaceKey = "PESTER$SpaceID" + $parentPage = Get-ConfluencePage -Title "Pester Test Space Home" -SpaceKey $SpaceKey -ErrorAction Stop $Title1 = "Pester New Page Piped" $Title2 = "Pester New Page Orphan" $Title3 = "Pester New Page from Object" @@ -329,15 +338,15 @@ InModuleScope ConfluencePS { } } - Describe 'Get-ConfluencePage' -Tag 'Integration' { + Context 'Get-ConfluencePage' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Title1 = "Pester New Page from Object" $Title2 = "Pester New Page Orphan" $Title3 = "Pester Test Space Home" $Title4 = "orphan" $Title5 = "*orphan" - $Query = "space=PESTER and title~`"*Object`"" + $Query = "space=PESTER$SpaceID and title~`"*Object`"" $ContentRaw = "

Hi Pester!👋

" $ContentFormatted = "

Hi Pester!

👋

" (Get-ConfluenceSpace -SpaceKey $SpaceKey).Homepage | Add-ConfluenceLabel -Label "important" -ErrorAction Stop @@ -358,17 +367,17 @@ InModuleScope ConfluencePS { # ASSERT It 'returns the correct amount of results' { - $GetTitle1.Count | Should Be 1 - $GetTitle2.Count | Should Be 1 - $GetPartial.Count | Should Be 0 - $GetWildcard.Count | Should Be 1 - $GetID1.Count | Should Be 1 - $GetID2.Count | Should Be 1 - $GetKeys.Count | Should Be 5 - $GetByLabel.Count | Should Be 1 - $GetSpacePage.Count | Should Be 5 - $GetByQuery.Count | Should Be 2 - $GetSpacePiped.Count | Should Be 5 + @($GetTitle1).Count | Should Be 1 + @($GetTitle2).Count | Should Be 1 + @($GetPartial).Count | Should Be 0 + @($GetWildcard).Count | Should Be 1 + @($GetID1).Count | Should Be 1 + @($GetID2).Count | Should Be 1 + @($GetKeys).Count | Should Be 5 + @($GetByLabel).Count | Should Be 1 + @($GetSpacePage).Count | Should Be 5 + @($GetByQuery).Count | Should Be 2 + @($GetSpacePiped).Count | Should Be 5 } It 'returns an object with specific properties' { $GetTitle1 | Should BeOfType [ConfluencePS.Page] @@ -408,7 +417,7 @@ InModuleScope ConfluencePS { $GetID2.Title | Should BeExactly $Title2 $GetKeys.Title -contains $Title3 | Should Be $true $GetKeys.Title -contains $GetID1.Title | Should Be $true - $GetByLabel.Title -like "PESTER * Home" | Should Be $true + $GetByLabel.Title -like "PESTER Test Space Home" | Should Be $true } It 'space matches the specified value' { $GetTitle1.Space.Key | Should BeExactly $SpaceKey @@ -425,9 +434,11 @@ InModuleScope ConfluencePS { $GetByLabel.Version.Number | Should Be 1 } It 'body matches the specified value' { - (ConvertFrom-HTMLEncoded $GetTitle1.Body) | Should BeExactly $ContentFormatted - (ConvertFrom-HTMLEncoded $GetTitle2.Body) | Should BeExactly $ContentRaw - (ConvertFrom-HTMLEncoded $GetID1.Body) | Should BeExactly $ContentFormatted + . "$env:BHProjectPath/$env:BHProjectName/Private/ConvertFrom-HTMLEncoded.ps1" + + ConvertFrom-HTMLEncoded $GetTitle1.Body | Should BeExactly $ContentFormatted + ConvertFrom-HTMLEncoded $GetTitle2.Body | Should BeExactly $ContentRaw + ConvertFrom-HTMLEncoded $GetID1.Body | Should BeExactly $ContentFormatted } It 'url is string' { $GetTitle1.URL | Should BeOfType [String] @@ -468,9 +479,9 @@ InModuleScope ConfluencePS { } } - Describe 'Add-ConfluenceLabel' -Tag 'Integration' { + Context 'Add-ConfluenceLabel' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -Title "Pester New Page Piped" -SpaceKey $SpaceKey -ErrorAction Stop $Label1 = "pestera", "pesterb", "pesterc" $Label2 = "pesterall" @@ -513,9 +524,9 @@ InModuleScope ConfluencePS { } } - Describe 'Set-ConfluenceLabel' -Tag 'Integration' { + Context 'Set-ConfluenceLabel' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Title1 = "Pester New Page from Object" $Label1 = @("overwrite", "remove") $Label2 = "final" @@ -544,8 +555,8 @@ InModuleScope ConfluencePS { It 'label matches the specified value' { $After1.Labels.Name | Should BeExactly $Label1 $After2.Labels.Name | Should BeExactly $Label2 - $After1.Labels.Name -notcontains $Before.Labels.Name | Should Be $true - $After2.Labels.Name -notcontains $Before.Labels.Name | Should Be $true + $After1.Labels.Name -notcontains $Before1.Labels.Name | Should Be $true + $After2.Labels.Name -notcontains $Before1.Labels.Name | Should Be $true } It 'labelid is not null or empty' { $After1.Labels.ID | Should Not BeNullOrEmpty @@ -553,9 +564,9 @@ InModuleScope ConfluencePS { } } - Describe 'Get-ConfluenceLabel' -Tag 'Integration' { + Context 'Get-ConfluenceLabel' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $patternLabel1 = "pester[abc]$" $patternLabel2 = "(pest|import|fin)" $Page = Get-ConfluencePage -Title "Pester New Page Piped" -SpaceKey $SpaceKey @@ -594,7 +605,7 @@ InModuleScope ConfluencePS { } } - Describe 'Set-ConfluencePage' -Tag 'Integration' { + Context 'Set-ConfluencePage' { <# TODO: * Title may not be empty * fails when version is 1 larger than current version @@ -630,7 +641,7 @@ InModuleScope ConfluencePS { } } - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Piped" $Page2 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Orphan" $Page3 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page from Object" @@ -670,15 +681,15 @@ InModuleScope ConfluencePS { # ASSERT It 'returns the correct amount of results' { - $SetPage1.Count | Should Be 1 - $SetPage2.Count | Should Be 1 - $SetPage3.Count | Should Be 1 - $SetPage4.Count | Should Be 1 - $SetPage5.Count | Should Be 1 - $SetPage6.Count | Should Be 1 - $SetPage7.Count | Should Be 1 - $SetPage8.Count | Should Be 1 - $AllChangedPages.Count | Should Be 9 + @($SetPage1).Count | Should Be 1 + @($SetPage2).Count | Should Be 1 + @($SetPage3).Count | Should Be 1 + @($SetPage4).Count | Should Be 1 + @($SetPage5).Count | Should Be 1 + @($SetPage6).Count | Should Be 1 + @($SetPage7).Count | Should Be 1 + @($SetPage8).Count | Should Be 1 + @($AllChangedPages).Count | Should Be 9 } It 'returns an object with specific properties' { $SetPage1 | Should BeOfType [ConfluencePS.Page] @@ -766,12 +777,12 @@ InModuleScope ConfluencePS { } } - Describe 'Get-ConfluenceChildPage' -Tag 'Integration' { + Context 'Get-ConfluenceChildPage' { # ARRANGE # ACT - $ChildPages = (Get-ConfluenceSpace -SpaceKey PESTER).Homepage | Get-ConfluenceChildPage - $DesendantPages = (Get-ConfluenceSpace -SpaceKey PESTER).Homepage | Get-ConfluenceChildPage -Recurse + $ChildPages = (Get-ConfluenceSpace -SpaceKey "PESTER$SpaceID").Homepage | Get-ConfluenceChildPage + $DesendantPages = (Get-ConfluenceSpace -SpaceKey "PESTER$SpaceID").Homepage | Get-ConfluenceChildPage -Recurse # ASSERT It 'returns the correct amount of results' { @@ -784,7 +795,7 @@ InModuleScope ConfluencePS { } } - Describe 'Add-ConfluenceAttachment' -Tag 'Integration' { + Context 'Add-ConfluenceAttachment' { # ARRANGE BeforeAll { $originalWarningPreference = $WarningPreference @@ -793,7 +804,7 @@ InModuleScope ConfluencePS { AfterAll { $WarningPreference = $originalWarningPreference } - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Piped" -ErrorAction Stop $Page2 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Orphan" -ErrorAction Stop $Page3 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page from Object" -ErrorAction Stop @@ -865,9 +876,9 @@ InModuleScope ConfluencePS { } } - Describe 'Get-ConfluenceAttachment' -Tag 'Integration' { + Context 'Get-ConfluenceAttachment' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Piped" -ErrorAction Stop $Page2 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Orphan" -ErrorAction Stop $Page3 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page from Object" -ErrorAction Stop @@ -910,7 +921,7 @@ InModuleScope ConfluencePS { } } - Describe 'Get-ConfluenceAttachmentFile' -Tag 'Integration' { + Context 'Get-ConfluenceAttachmentFile' { # ARRANGE BeforeAll { Push-Location -Path "TestDrive:\" @@ -921,7 +932,7 @@ InModuleScope ConfluencePS { $null = New-Item -Path "TestDrive:\Folder1" -ItemType Directory $null = New-Item -Path "TestDrive:\Folder2" -ItemType Directory $null = New-Item -Path "TestDrive:\Folder3" -ItemType Directory - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Piped" -ErrorAction Stop $Page2 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Orphan" -ErrorAction Stop $Attachments = $Page1, $Page2 | Get-ConfluenceAttachment -ErrorAction Stop @@ -964,9 +975,9 @@ InModuleScope ConfluencePS { } } - Describe 'Set-ConfluenceAttachment' -Tag 'Integration' { + Context 'Set-ConfluenceAttachment' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Piped" -ErrorAction Stop $Attachment = $Page1 | Get-ConfluenceAttachment -FileNameFilter "Test.txt" -ErrorAction Stop $TextFile = Get-Item -Path "$PSScriptRoot/resources/Test.txt" @@ -1012,7 +1023,7 @@ InModuleScope ConfluencePS { } } - Describe 'Remove-ConfluenceAttachment' -Tag 'Integration' { + Context 'Remove-ConfluenceAttachment' { # ARRANGE BeforeAll { $originalWarningPreference = $WarningPreference @@ -1021,7 +1032,7 @@ InModuleScope ConfluencePS { AfterAll { $WarningPreference = $originalWarningPreference } - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Page1 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Piped" -ErrorAction Stop $Page2 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page Orphan" -ErrorAction Stop $Page3 = Get-ConfluencePage -SpaceKey $SpaceKey -Title "Pester New Page from Object" -ErrorAction Stop @@ -1054,12 +1065,12 @@ InModuleScope ConfluencePS { } } - Describe 'Remove-ConfluenceLabel' -Tag 'Integration' { + Context 'Remove-ConfluenceLabel' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Label1 = "pesterc" $Page1 = Get-ConfluencePage -Title 'Pester New Page Piped' -SpaceKey $SpaceKey -ErrorAction Stop - $Page2 = (Get-ConfluenceSpace -SpaceKey PESTER).Homepage + $Page2 = (Get-ConfluenceSpace -SpaceKey $SpaceKey).Homepage # ACT $Before1 = $Page1 | Get-ConfluenceLabel -ErrorAction SilentlyContinue @@ -1071,18 +1082,18 @@ InModuleScope ConfluencePS { # ASSERT It 'page has one label less' { - ($Before1.Labels).Count - ($After1.Labels).Count| Should Be 1 - ($Before2.Labels).Count - ($After2.Labels).Count| Should Be 2 + @($Before1.Labels).Count - @($After1.Labels).Count | Should Be 1 + ($After1.Labels).Name -notcontains $Label1 | Should Be $true } It 'page does not have labels' { - $After1.Labels.Name -notcontains $Label1 | Should Be $true - $After2.Labels.Name -notcontains $Label1 | Should Be $true + @($Before2.Labels).Count | Should Be 2 + $After2.Labels | Should BeNullOrEmpty } } - Describe 'Remove-ConfluencePage' -Tag 'Integration' { + Context 'Remove-ConfluencePage' { # ARRANGE - $SpaceKey = "PESTER" + $SpaceKey = "PESTER$SpaceID" $Title = "Pester New Page Orphan" $PageID = Get-ConfluencePage -Title $Title -SpaceKey $SpaceKey -ErrorAction Stop $Before = Get-ConfluencePage -SpaceKey $SpaceKey -ErrorAction Stop @@ -1097,24 +1108,24 @@ InModuleScope ConfluencePS { $Before | Should Not BeNullOrEmpty } It 'space does not have pages after' { - $After.ID | Should BeNullOrEmpty + $After | Should BeNullOrEmpty } } - Describe 'Remove-ConfluenceSpace' -Tag 'Integration' { + Context 'Remove-ConfluenceSpace' { # ARRANGE # We don't want warnings on the screen $WarningPreference = 'SilentlyContinue' # ACT - Remove-ConfluenceSpace -Key PESTER -Force -ErrorAction Stop - "PESTER1" | Remove-ConfluenceSpace -Force -ErrorAction Stop + Remove-ConfluenceSpace -Key "PESTER$SpaceID" -Force -ErrorAction Stop + "PESTER1$SpaceID" | Remove-ConfluenceSpace -Force -ErrorAction Stop # ASSERT Start-Sleep -Seconds 20 It 'space is no longer available' { - { Get-ConfluenceSpace -Key PESTER -ErrorAction Stop } | Should Throw - { Get-ConfluenceSpace -Key PESTER1 -ErrorAction Stop } | Should Throw + { Get-ConfluenceSpace -Key "PESTER$SpaceID" -ErrorAction Stop } | Should Throw + { Get-ConfluenceSpace -Key "PESTER1$SpaceID" -ErrorAction Stop } | Should Throw } } } diff --git a/Tests/Project.Tests.ps1 b/Tests/Project.Tests.ps1 index 0eea233..79b2f88 100644 --- a/Tests/Project.Tests.ps1 +++ b/Tests/Project.Tests.ps1 @@ -36,17 +36,17 @@ Describe "General project validation" -Tag Unit { $module = Get-Module $env:BHProjectName $testFiles = Get-ChildItem $PSScriptRoot -Include "*.Tests.ps1" -Recurse - <# Context "Public functions" { $publicFunctions = (Get-ChildItem "$env:BHModulePath/Public/*.ps1").BaseName foreach ($function in $publicFunctions) { - It "has a test file for $function" { - $expectedTestFile = "$function.Unit.Tests.ps1" + # TODO + # It "has a test file for $function" { + # $expectedTestFile = "$function.Unit.Tests.ps1" - $testFiles.Name | Should -Contain $expectedTestFile - } + # $testFiles.Name | Should -Contain $expectedTestFile + # } It "exports $function" { $expectedFunctionName = $function -replace "\-", "-$($module.Prefix)" @@ -60,11 +60,13 @@ Describe "General project validation" -Tag Unit { $privateFunctions = (Get-ChildItem "$env:BHModulePath/Private/*.ps1").BaseName foreach ($function in $privateFunctions) { - It "has a test file for $function" { - $expectedTestFile = "$function.Unit.Tests.ps1" - $testFiles.Name | Should -Contain $expectedTestFile - } + # TODO + # It "has a test file for $function" { + # $expectedTestFile = "$function.Unit.Tests.ps1" + + # $testFiles.Name | Should -Contain $expectedTestFile + # } It "does not export $function" { $expectedFunctionName = $function -replace "\-", "-$($module.Prefix)" @@ -74,6 +76,7 @@ Describe "General project validation" -Tag Unit { } } + <# Context "Classes" { foreach ($class in ([AtlassianPS.ServerData].Assembly.GetTypes() | Where-Object IsClass)) { diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a59d9c3..b849fb0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -183,7 +183,11 @@ phases: Invoke-Build -Task ShowInfo displayName: Setup - - powershell: 'Invoke-Build -Task Test -Tag "Integration"' + - powershell: 'Invoke-Build -Task Test -Tag "Integration" -ExcludeTag ""' + env: + WikiURI: WikiURI + WikiUser: WikiUser + WikiPass: WikiPass displayName: Test - task: PublishTestResults@2 From 7cd833b47e922d12f385657253599cb95ef940f7 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Tue, 4 Dec 2018 22:46:59 +0100 Subject: [PATCH 10/14] Added support for TLS1.2 --- ConfluencePS/Private/Set-TlsLevel.ps1 | 26 ++++++++++++++++++++++++++ ConfluencePS/Public/Invoke-Method.ps1 | 4 ++++ 2 files changed, 30 insertions(+) create mode 100644 ConfluencePS/Private/Set-TlsLevel.ps1 diff --git a/ConfluencePS/Private/Set-TlsLevel.ps1 b/ConfluencePS/Private/Set-TlsLevel.ps1 new file mode 100644 index 0000000..6629053 --- /dev/null +++ b/ConfluencePS/Private/Set-TlsLevel.ps1 @@ -0,0 +1,26 @@ +function Set-TlsLevel { + [CmdletBinding( SupportsShouldProcess = $false )] + [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '')] + param ( + [Parameter(Mandatory, ParameterSetName = 'Set')] + [Switch]$Tls12, + + [Parameter(Mandatory, ParameterSetName = 'Revert')] + [Switch]$Revert + ) + + begin { + switch ($PSCmdlet.ParameterSetName) { + "Set" { + $Script:OriginalTlsSettings = [Net.ServicePointManager]::SecurityProtocol + + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + } + "Revert" { + if ($Script:OriginalTlsSettings) { + [Net.ServicePointManager]::SecurityProtocol = $Script:OriginalTlsSettings + } + } + } + } +} diff --git a/ConfluencePS/Public/Invoke-Method.ps1 b/ConfluencePS/Public/Invoke-Method.ps1 index dac96d3..41ec060 100644 --- a/ConfluencePS/Public/Invoke-Method.ps1 +++ b/ConfluencePS/Public/Invoke-Method.ps1 @@ -50,6 +50,8 @@ function Invoke-Method { BEGIN { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + Set-TlsLevel -Tls12 + # pass input to local variable # this allows to use the PSBoundParameters for recursion $_headers = @{ # Set any default headers @@ -253,6 +255,8 @@ function Invoke-Method { } END { + Set-TlsLevel -Revert + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended" } } From 76f22cf081d50f259597d4290fc7a47bb5543b68 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Tue, 4 Dec 2018 23:09:47 +0100 Subject: [PATCH 11/14] Fix trailing / when URL does not include a path --- ConfluencePS/Public/Invoke-Method.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ConfluencePS/Public/Invoke-Method.ps1 b/ConfluencePS/Public/Invoke-Method.ps1 index 41ec060..fb2c031 100644 --- a/ConfluencePS/Public/Invoke-Method.ps1 +++ b/ConfluencePS/Public/Invoke-Method.ps1 @@ -52,6 +52,11 @@ function Invoke-Method { Set-TlsLevel -Tls12 + # Sanitize double slash `//` + # Happens when the BaseUri is the domain name + # [Uri]"http://google.com" vs [Uri]"http://google.com/foo" + $URi = $URi -replace '(? Date: Wed, 12 Dec 2018 23:04:50 +0100 Subject: [PATCH 12/14] Quick and dirty fixes --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- ConfluencePS/Public/Get-Page.ps1 | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 393a39c..6dcf3ca 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -32,7 +32,7 @@ about: Create a report to help us improve > ```powershell -> Get-Module AtlassianPS.Configuration -ListAvailable | Select Name, Version +> Get-Module ConfluencePS -ListAvailable | Select Name, Version > $PSVersionTable > ``` diff --git a/ConfluencePS/Public/Get-Page.ps1 b/ConfluencePS/Public/Get-Page.ps1 index 0ee184c..c4a5c8a 100644 --- a/ConfluencePS/Public/Get-Page.ps1 +++ b/ConfluencePS/Public/Get-Page.ps1 @@ -110,6 +110,7 @@ function Get-Page { break } "bySpace" { # This includes 'bySpaceObject' + $iwParameters["Paging"] = $true $iwParameters["Uri"] = $resourceApi -f '' $iwParameters["GetParameters"]["type"] = "page" if ($SpaceKey) { $iwParameters["GetParameters"]["spaceKey"] = $SpaceKey } @@ -123,6 +124,7 @@ function Get-Page { break } "byLabel" { + $iwParameters["Paging"] = $true $iwParameters["Uri"] = $resourceApi -f "/search" $CQLparameters = @("type=page", "label=$Label") @@ -135,6 +137,7 @@ function Get-Page { break } "byQuery" { + $iwParameters["Paging"] = $true $iwParameters["Uri"] = $resourceApi -f "/search" $cqlQuery = ConvertTo-URLEncoded $Query From fc52b96574401266687170c13fbefda7be8f3ef1 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Wed, 12 Dec 2018 23:54:56 +0100 Subject: [PATCH 13/14] Bumped version to 2.4 --- CHANGELOG.md | 15 +++++++++++++++ ConfluencePS/ConfluencePS.psd1 | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3f2ba0..f554504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/). . +## [2.4] 2018-12-12 + +### Added + +- Added `-Vertical` to `ConvertTo-Table` (#148, [@brianbunke]) +- Added support for TLS1.2 (#155, [@lipkau]) + +### Changed + +- Changed productive module files to be compiled into single `.psm1` file (#133, [@lipkau]) +- Fixed `ConvertTo-Table` for empty cells (#144, [@FelixMelchert]) +- Changed CI/CD pipeline from AppVeyor to Azure DevOps (#150, [@lipkau]) +- Fixed trailing slash in ApiURi parameter (#153, [@lipkau]) + ## [2.3] 2018-03-22 ### Added @@ -167,6 +181,7 @@ No changelog available for version `1.0` of ConfluencePS. `1.0` was created in l [@colhal]: https://github.com/colhal [@Dejulia489]: https://github.com/Dejulia489 [@ebekker]: https://github.com/ebekker +[@FelixMelchert]: https://github.com/FelixMelchert [@jkknorr]: https://github.com/jkknorr [@JohnAdders]: https://github.com/JohnAdders [@kittholland]: https://github.com/kittholland diff --git a/ConfluencePS/ConfluencePS.psd1 b/ConfluencePS/ConfluencePS.psd1 index 1df1197..338efc7 100644 --- a/ConfluencePS/ConfluencePS.psd1 +++ b/ConfluencePS/ConfluencePS.psd1 @@ -12,7 +12,7 @@ RootModule = 'ConfluencePS.psm1' # Version number of this module. - ModuleVersion = '2.3' + ModuleVersion = '2.4' # ID used to uniquely identify this module GUID = '20d32089-48ef-464d-ba73-6ada240e26b3' From 401912bd8f894750bba0876bfa3a9bc4951b1ba6 Mon Sep 17 00:00:00 2001 From: Oliver Lipkau Date: Thu, 13 Dec 2018 00:06:19 +0100 Subject: [PATCH 14/14] Fixed wrong parameter --- ConfluencePS/Public/Get-Page.ps1 | 3 --- 1 file changed, 3 deletions(-) diff --git a/ConfluencePS/Public/Get-Page.ps1 b/ConfluencePS/Public/Get-Page.ps1 index c4a5c8a..0ee184c 100644 --- a/ConfluencePS/Public/Get-Page.ps1 +++ b/ConfluencePS/Public/Get-Page.ps1 @@ -110,7 +110,6 @@ function Get-Page { break } "bySpace" { # This includes 'bySpaceObject' - $iwParameters["Paging"] = $true $iwParameters["Uri"] = $resourceApi -f '' $iwParameters["GetParameters"]["type"] = "page" if ($SpaceKey) { $iwParameters["GetParameters"]["spaceKey"] = $SpaceKey } @@ -124,7 +123,6 @@ function Get-Page { break } "byLabel" { - $iwParameters["Paging"] = $true $iwParameters["Uri"] = $resourceApi -f "/search" $CQLparameters = @("type=page", "label=$Label") @@ -137,7 +135,6 @@ function Get-Page { break } "byQuery" { - $iwParameters["Paging"] = $true $iwParameters["Uri"] = $resourceApi -f "/search" $cqlQuery = ConvertTo-URLEncoded $Query