tests/Menu.Tests.ps1

$moduleRoot = Split-Path -Parent $PSScriptRoot
Import-Module (Join-Path $moduleRoot 'Octa.psd1') -Force
. (Join-Path $moduleRoot 'private\StatusPanel.ps1')
. (Join-Path $moduleRoot 'private\Banner.ps1')
. (Join-Path $moduleRoot 'private\Menu.ps1')

Describe 'Get-OctaTruncated' {
    It 'returns text unchanged when it already fits' {
        Get-OctaTruncated -Text 'short' -MaxWidth 20 | Should Be 'short'
    }

    It 'truncates with an ASCII ellipsis when text is longer than MaxWidth' {
        $result = Get-OctaTruncated -Text 'this is a much longer string than allowed' -MaxWidth 20
        $result.Length | Should Be 20
        $result | Should Match '\.\.\.$'
    }

    It 'handles an empty string without throwing' {
        Get-OctaTruncated -Text '' -MaxWidth 20 | Should Be ''
    }

    It 'does not truncate a string exactly at MaxWidth' {
        $exact = 'exact20characters!!!'
        Get-OctaTruncated -Text $exact -MaxWidth $exact.Length | Should Be $exact
    }
}

Describe 'Menu label width (real data, 009/010 UI regression)' {
    # Found via real user testing: 'Actualizador de Software' (24 chars) and 'Historial de
    # Limpiezas' (22 chars) overflowed the fixed 22-char label column in Spanish, and long
    # category descriptions (some 150+ chars) wrapped unpredictably on a narrow terminal -
    # both fixed by shortening those two labels and dynamically truncating descriptions to the
    # real terminal width (Get-OctaTruncated) instead of printing them unbounded.
    $enStrings = Import-LocalizedData -BaseDirectory $moduleRoot -FileName 'Octa.psd1' -UICulture 'en-US'
    $esStrings = Import-LocalizedData -BaseDirectory $moduleRoot -FileName 'Octa.psd1' -UICulture 'es-ES'
    # Only the fixed-width label column (22 chars) matters here - "Desc"-suffixed keys and
    # menuNavHint all render across the full terminal width via Get-OctaTruncated instead, so
    # they're excluded from this check. Group labels (010) are real fixed-width labels too, so
    # they're included alongside the "menu*" ones.
    $labelKeys = ($enStrings.Keys + $esStrings.Keys) | Sort-Object -Unique |
        Where-Object { ($_ -like 'menu*' -or $_ -like 'group*') -and $_ -ne 'menuNavHint' -and $_ -notlike '*Desc' }

    foreach ($key in $labelKeys) {
        It "'$key' fits the 22-char label column in both locales" {
            $enStrings[$key].Length | Should BeLessThan 23
            $esStrings[$key].Length | Should BeLessThan 23
        }
    }
}

Describe 'Write-OctaMenuRow' {
    It 'pads the rendered line to exactly the requested width' {
        $item = [pscustomobject]@{ Key = '1'; Label = 'Telemetry'; Description = 'Short desc' }
        $info = Write-OctaMenuRow -Item $item -IsSelected $true -LabelWidth 22 -Width 60 6>&1
        $info.MessageData.Message.Length | Should Be 60
    }

    It 'shows the pointer only for the selected item' {
        $item = [pscustomobject]@{ Key = '1'; Label = 'Telemetry'; Description = '' }
        $selected = (Write-OctaMenuRow -Item $item -IsSelected $true -LabelWidth 22 -Width 60 6>&1).MessageData.Message
        $unselected = (Write-OctaMenuRow -Item $item -IsSelected $false -LabelWidth 22 -Width 60 6>&1).MessageData.Message
        $selected.Substring(0, 1) | Should Be '>'
        $unselected.Substring(0, 1) | Should Be ' '
    }

    It 'never throws for a selected or unselected item at a small width' {
        $item = [pscustomobject]@{ Key = 'x'; Label = 'Exit'; Description = '' }
        { Write-OctaMenuRow -Item $item -IsSelected $false -LabelWidth 22 -Width 30 } | Should Not Throw
        { Write-OctaMenuRow -Item $item -IsSelected $true -LabelWidth 22 -Width 30 } | Should Not Throw
    }
}

Describe 'Write-OctaPaddedLine (010 regression: stale header text)' {
    # Real bug: a shorter Header ("Categorias", 10 chars) shown right after a longer one ("Menu
    # principal", 14 chars) at the same screen row left the old header's trailing characters
    # stuck on screen ("Categoriasipal") - Header/NavHint were never padded, only menu item rows
    # were. This is the same bug class as Write-OctaMenuRow already solves, applied here too.
    It 'pads a short line to exactly the requested width' {
        $info = Write-OctaPaddedLine -Text 'Categorias' -Width 20 6>&1
        $info.MessageData.Message.Length | Should Be 20
    }

    It 'fully overwrites a longer previous line''s trailing characters when reused at the same width' {
        $longLine = (Write-OctaPaddedLine -Text 'Menu principal' -Width 20 6>&1).MessageData.Message
        $shortLine = (Write-OctaPaddedLine -Text 'Categorias' -Width 20 6>&1).MessageData.Message
        # If Categorias were written without padding over "Menu principal", the tail characters
        # ("ipal") would still show through - simulate that exact real-world failure and confirm
        # the padded version does not exhibit it.
        $shortLine.Substring(10, 4) | Should Not Be $longLine.Substring(10, 4)
        $shortLine.TrimEnd() | Should Be 'Categorias'
    }

    It 'truncates instead of overflowing when text is longer than Width' {
        $info = Write-OctaPaddedLine -Text 'this text is way too long for the given width' -Width 15 6>&1
        $info.MessageData.Message.Length | Should Be 15
    }
}

Describe 'Get-OctaCenteredLine (010 regression: stale banner tagline)' {
    # Real bug: Show-OctaLanguageMenu's tagline is longer (bilingual) than the normal one - a
    # normal banner drawn afterward at the same row left the bilingual tagline's trailing
    # characters stuck on screen, since centered lines were never padded to the full width.
    It 'pads the centered result to exactly Width' {
        (Get-OctaCenteredLine -Text 'short' -Width 40).Length | Should Be 40
    }

    It 'a shorter centered line fully overwrites a longer one at the same width' {
        $long = Get-OctaCenteredLine -Text 'A much longer bilingual tagline here' -Width 50
        $short = Get-OctaCenteredLine -Text 'Short tagline' -Width 50
        $long.Length | Should Be 50
        $short.Length | Should Be 50
    }
}