diff --git a/PesterExplorer/Private/Get-ListPanel.ps1 b/PesterExplorer/Private/Get-ListPanel.ps1 index 9bc59ea..8b89a8e 100644 --- a/PesterExplorer/Private/Get-ListPanel.ps1 +++ b/PesterExplorer/Private/Get-ListPanel.ps1 @@ -30,7 +30,9 @@ function Get-ListPanel { $List, [string] $SelectedItem, - [string]$SelectedPane = "list" + [string]$SelectedPane = "list", + [int]$ScrollOffset = 0, + [int]$ListHeight = 0 ) $paneColor = if($SelectedPane -ne "list") { # If the selected pane is not preview, return an empty panel @@ -44,7 +46,19 @@ function Get-ListPanel { StemColor = [Spectre.Console.Color]::Grey LeafColor = [Spectre.Console.Color]::White } - $results = $List | ForEach-Object { + # Calculate the visible window. Panel borders and potential ellipsis rows consume 4 rows. + $visibleCount = if ($ListHeight -gt 4) { $ListHeight - 4 } else { $List.Count } + $startIndex = [Math]::Max(0, [Math]::Min($ScrollOffset, $List.Count - 1)) + $endIndex = [Math]::Min($startIndex + $visibleCount - 1, $List.Count - 1) + $showEllipsisTop = $startIndex -gt 0 + $showEllipsisBottom = $endIndex -lt ($List.Count - 1) + $visibleList = if ($List.Count -gt 0) { @($List[$startIndex..$endIndex]) } else { @() } + + $results = @() + if ($showEllipsisTop) { + $results += Write-SpectreHost "[grey]...[/]" -PassThru | Format-SpectrePadded -Padding 0 + } + $results += $visibleList | ForEach-Object { $name = $_ if($name -eq '..') { # This is a parent item, so we show it as a folder @@ -87,6 +101,9 @@ function Get-ListPanel { } } } + if ($showEllipsisBottom) { + $results += Write-SpectreHost "[grey]...[/]" -PassThru | Format-SpectrePadded -Padding 0 + } $results | Format-SpectreRows | Format-SpectrePanel -Header "[white]List[/]" -Expand -Color $paneColor diff --git a/PesterExplorer/Public/Show-PesterResult.ps1 b/PesterExplorer/Public/Show-PesterResult.ps1 index 759e19c..3b41bd4 100644 --- a/PesterExplorer/Public/Show-PesterResult.ps1 +++ b/PesterExplorer/Public/Show-PesterResult.ps1 @@ -79,6 +79,7 @@ function Show-PesterResult { $object = $PesterResult $selectedPane = 'list' $scrollPosition = 0 + $listScrollOffset = 0 #endregion Initial State while ($true) { @@ -86,6 +87,7 @@ function Show-PesterResult { $sizes = $layout | Get-SpectreLayoutSizes $previewHeight = $sizes["preview"].Height $previewWidth = $sizes["preview"].Width + $listHeight = $sizes["list"].Height Write-Debug "Preview size: $previewWidth x $previewHeight" # Handle input @@ -106,7 +108,7 @@ function Show-PesterResult { $scrollPosition = 0 } elseif ($lastKeyPressed.Key -eq "PageUp") { $currentIndex = $list.IndexOf($selectedItem) - $newIndex = [Math]::Max($currentIndex - 10, $list.Count - 1) + $newIndex = [Math]::Max($currentIndex - 10, 0) $selectedItem = $list[$newIndex] $scrollPosition = 0 } elseif ($lastKeyPressed.Key -eq "Home") { @@ -135,6 +137,7 @@ function Show-PesterResult { $list = [array]$items.Keys $selectedItem = $list[0] $scrollPosition = 0 + $listScrollOffset = 0 } elseif ($lastKeyPressed.Key -eq "Escape") { # Move up via Esc key if($stack.Count -eq 0) { @@ -146,7 +149,17 @@ function Show-PesterResult { $list = [array]$items.Keys $selectedItem = $list[0] $scrollPosition = 0 + $listScrollOffset = 0 } + # Ensure the selected item is always within the visible viewport + $selectedIndex = $list.IndexOf($selectedItem) + $visibleCount = if ($listHeight -gt 4) { $listHeight - 4 } else { $list.Count } + if ($selectedIndex -lt $listScrollOffset) { + $listScrollOffset = $selectedIndex + } elseif ($selectedIndex -ge ($listScrollOffset + $visibleCount)) { + $listScrollOffset = $selectedIndex - $visibleCount + 1 + } + if ($listScrollOffset -lt 0) { $listScrollOffset = 0 } } else { #region Preview Navigation @@ -175,6 +188,8 @@ function Show-PesterResult { List = $list SelectedItem = $selectedItem SelectedPane = $selectedPane + ScrollOffset = $listScrollOffset + ListHeight = $listHeight } $listPanel = Get-ListPanel @getListPanelSplat diff --git a/tests/Get-ListPanel.tests.ps1 b/tests/Get-ListPanel.tests.ps1 new file mode 100644 index 0000000..efa0399 --- /dev/null +++ b/tests/Get-ListPanel.tests.ps1 @@ -0,0 +1,113 @@ +Describe 'Get-ListPanel' { + BeforeAll { + . (Join-Path $PSScriptRoot 'Helpers.ps1') + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop + + InModuleScope $env:BHProjectName { + $script:tenItems = @('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j') + + # Viewport scrolling panel: ScrollOffset=2, ListHeight=8 + # visibleCount = 8-4 = 4; items c,d,e,f visible; top and bottom ellipsis shown + $script:scrolledPanel = Get-ListPanel ` + -List $script:tenItems ` + -SelectedItem 'c' ` + -ScrollOffset 2 ` + -ListHeight 8 + $script:scrolledText = global:Get-RenderedText -Panel $script:scrolledPanel + + # Bottom-of-list panel: ScrollOffset=6, ListHeight=20 + # visibleCount = 16; items g,h,i,j visible; top ellipsis only + $script:bottomPanel = Get-ListPanel ` + -List $script:tenItems ` + -SelectedItem 'j' ` + -ScrollOffset 6 ` + -ListHeight 20 + $script:bottomText = global:Get-RenderedText -Panel $script:bottomPanel + } + } + + It 'should return a Spectre.Console.Panel object' { + InModuleScope $env:BHProjectName { + $panel = Get-ListPanel -List @('item1', 'item2') -SelectedItem 'item1' + $panel.GetType().ToString() | Should -BeExactly 'Spectre.Console.Panel' + } + } + + It 'should show all items when list fits within available height' { + InModuleScope $env:BHProjectName { + $panel = Get-ListPanel -List $script:tenItems -SelectedItem 'a' -ListHeight 20 + $rendered = global:Get-RenderedText -Panel $panel + foreach ($item in $script:tenItems) { + $rendered | Should -BeLike "*$item*" + } + } + } + + It 'should show all items when ListHeight is 0 (default, no constraint)' { + InModuleScope $env:BHProjectName { + $panel = Get-ListPanel -List $script:tenItems -SelectedItem 'a' + $rendered = global:Get-RenderedText -Panel $panel + foreach ($item in $script:tenItems) { + $rendered | Should -BeLike "*$item*" + } + } + } + + It 'should show only items within the visible window when scrolled' { + InModuleScope $env:BHProjectName { + $script:scrolledText | Should -BeLike '*c*' + $script:scrolledText | Should -BeLike '*d*' + $script:scrolledText | Should -BeLike '*e*' + $script:scrolledText | Should -BeLike '*f*' + } + } + + It 'should show top ellipsis when items are scrolled above the window' { + InModuleScope $env:BHProjectName { + $script:scrolledText | Should -BeLike '*...*' + } + } + + It 'should show bottom ellipsis when items are below the window' { + InModuleScope $env:BHProjectName { + # Both top and bottom ellipsis are present since items a-b are above and g-j below + $script:scrolledText | Should -BeLike '*...*' + } + } + + It 'should show visible items when scrolled to the bottom' { + InModuleScope $env:BHProjectName { + $script:bottomText | Should -BeLike '*g*' + $script:bottomText | Should -BeLike '*j*' + } + } + + It 'should show top ellipsis when scrolled to the bottom' { + InModuleScope $env:BHProjectName { + $script:bottomText | Should -BeLike '*...*' + } + } + + It 'should highlight the selected item in the rendered output' { + InModuleScope $env:BHProjectName { + $panel = Get-ListPanel -List @('alpha', 'beta', 'gamma') -SelectedItem 'beta' + $rendered = global:Get-RenderedText -Panel $panel + $rendered | Should -BeLike '*beta*' + } + } + + It 'should return a Panel when list pane is not selected' { + InModuleScope $env:BHProjectName { + $panel = Get-ListPanel -List @('item1') -SelectedItem 'item1' -SelectedPane 'preview' + $panel.GetType().ToString() | Should -BeExactly 'Spectre.Console.Panel' + } + } +} diff --git a/tests/Help.tests.ps1 b/tests/Help.tests.ps1 index 4fe12b3..f76f66c 100644 --- a/tests/Help.tests.ps1 +++ b/tests/Help.tests.ps1 @@ -108,7 +108,7 @@ Describe "Test help for <_.Name>" -ForEach $commands { # Shouldn't find extra parameters in help. It "finds help parameter in code: <_>" { - $_ -in $parameterNames | Should -Be $true + $_ -in $commandParameterNames | Should -Be $true } } }