Tests/show-syntax.Tests.ps1

BeforeAll {
    $ModulePath = Join-Path $PSScriptRoot '..'
    $ModulePath = Join-Path $ModulePath 'show-syntax.psd1'
    Import-Module $ModulePath -Force

    $ESC = [char]27

    # Create a temp directory for test files
    $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) "show-syntax-tests-$(New-Guid)"
    New-Item -ItemType Directory -Path $script:TempDir | Out-Null
}

AfterAll {
    Remove-Module show-syntax -ErrorAction SilentlyContinue
    if (Test-Path $script:TempDir) {
        Remove-Item -Recurse -Force $script:TempDir
    }
}

Describe 'Show-Syntax' {

    Context 'File resolution and error handling' {

        It 'Writes an error when the file does not exist' {
            $fakePath = Join-Path $script:TempDir 'nonexistent.ps1'
            { Show-Syntax -Path $fakePath -ErrorAction Stop } | Should -Throw
        }

        It 'Writes an error when the path is a directory' {
            { Show-Syntax -Path $script:TempDir -ErrorAction Stop } | Should -Throw
        }

    }

    Context 'Line numbers' {

        BeforeAll {
            $script:LineNumberFile = Join-Path $script:TempDir 'sample.txt'
            Set-Content -Path $script:LineNumberFile -Value @('first line', 'second line', 'third line') -Encoding UTF8
        }

        AfterAll {
            Remove-Item -Path $script:LineNumberFile -ErrorAction SilentlyContinue
        }

        It 'Prepends line numbers when -ShowLineNumbers is used' {
            $output = Show-Syntax -Path $script:LineNumberFile -ShowLineNumbers 6>&1 |
                ForEach-Object { $_.ToString() }

            # At least one output line should contain a number followed by the content
            $contentLines = $output | Where-Object { $_ -match '\d' -and $_ -notmatch '^[─]' }
            $contentLines | Should -Not -BeNullOrEmpty

            # Line 1 content should appear
            $lineWithFirst = $contentLines | Where-Object { $_ -match 'first line' }
            $lineWithFirst | Should -Not -BeNullOrEmpty

            # The line with 'first line' should also contain the line number '1'
            $lineWithFirst | ForEach-Object {
                $_ | Should -Match '1'
            }
        }

        It 'Does NOT prepend line numbers when -ShowLineNumbers is omitted' {
            $output = Show-Syntax -Path $script:LineNumberFile 6>&1 |
                ForEach-Object { $_.ToString() }

            $contentLines = $output | Where-Object { $_ -match 'first line' }
            $contentLines | Should -Not -BeNullOrEmpty

            # Should not start with padded digits followed by a space before the content
            $contentLines | ForEach-Object {
                # After stripping ANSI codes, there should be no leading digits/spaces before 'first line'
                $stripped = $_ -replace "$ESC\[[0-9;]*m", ''
                $stripped | Should -Not -Match '^\s*\d+\s+first line'
            }
        }

    }

    Context 'Language detection' {

        BeforeAll {
            $script:JsonFile = Join-Path $script:TempDir 'data.json'
            $jsonContent = '{"name": "show-syntax", "version": "1.0.0", "active": true}'
            Set-Content -Path $script:JsonFile -Value $jsonContent -Encoding UTF8

            $script:Ps1File = Join-Path $script:TempDir 'script.ps1'
            Set-Content -Path $script:Ps1File -Value 'function Get-Foo { return $true }' -Encoding UTF8

            $script:XmlFile = Join-Path $script:TempDir 'config.xml'
            Set-Content -Path $script:XmlFile -Value '<root><item key="value">text</item></root>' -Encoding UTF8

            $script:MdFile = Join-Path $script:TempDir 'notes.md'
            Set-Content -Path $script:MdFile -Value '# Heading' -Encoding UTF8
        }

        AfterAll {
            Remove-Item -Path $script:JsonFile, $script:Ps1File, $script:XmlFile, $script:MdFile -ErrorAction SilentlyContinue
        }

        It 'Recognizes a .json file and applies ANSI colour codes' {
            $output = (Show-Syntax -Path $script:JsonFile 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            # Output should contain at least one ANSI escape sequence
            $output | Should -Match "$ESC\["
        }

        It 'Recognizes a .ps1 file and applies ANSI colour codes' {
            $output = (Show-Syntax -Path $script:Ps1File 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            $output | Should -Match "$ESC\["
        }

        It 'Recognizes a .xml file and applies ANSI colour codes' {
            $output = (Show-Syntax -Path $script:XmlFile 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            $output | Should -Match "$ESC\["
        }

        It 'Recognizes a .md file and applies ANSI colour codes' {
            $output = (Show-Syntax -Path $script:MdFile 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            $output | Should -Match "$ESC\["
        }

        It 'Applies highlighting when -Language is explicitly specified' {
            # Use a .txt file but force JSON highlighting
            $txtFile = Join-Path $script:TempDir 'data.txt'
            Set-Content -Path $txtFile -Value '{"key": "value"}' -Encoding UTF8

            $output = (Show-Syntax -Path $txtFile -Language json 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            $output | Should -Match "$ESC\["

            Remove-Item -Path $txtFile -ErrorAction SilentlyContinue
        }

    }

    Context 'Pipeline input' {

        BeforeAll {
            $script:PipeFile1 = Join-Path $script:TempDir 'pipe1.ps1'
            $script:PipeFile2 = Join-Path $script:TempDir 'pipe2.ps1'
            Set-Content -Path $script:PipeFile1 -Value '$x = 1' -Encoding UTF8
            Set-Content -Path $script:PipeFile2 -Value '$y = 2' -Encoding UTF8
        }

        AfterAll {
            Remove-Item -Path $script:PipeFile1, $script:PipeFile2 -ErrorAction SilentlyContinue
        }

        It 'Accepts a path via pipeline by value' {
            $output = ($script:PipeFile1 | Show-Syntax 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            $output | Should -Match '\$x'
        }

        It 'Accepts multiple files via pipeline from Get-ChildItem' {
            $output = (Get-ChildItem -Path $script:TempDir -Filter 'pipe*.ps1' |
                Show-Syntax 6>&1 |
                ForEach-Object { $_.ToString() }) -join ''

            $output | Should -Match '\$x'
            $output | Should -Match '\$y'
        }

    }

    Context 'Output structure' {

        BeforeAll {
            $script:StructFile = Join-Path $script:TempDir 'structure.txt'
            Set-Content -Path $script:StructFile -Value @('line one', 'line two') -Encoding UTF8
        }

        AfterAll {
            Remove-Item -Path $script:StructFile -ErrorAction SilentlyContinue
        }

        It 'Outputs a header containing the file path' {
            $output = Show-Syntax -Path $script:StructFile 6>&1 |
                ForEach-Object { $_.ToString() }

            $headerLine = $output | Where-Object { $_ -match [regex]::Escape('structure.txt') }
            $headerLine | Should -Not -BeNullOrEmpty
        }

        It 'Outputs the file content lines' {
            $output = Show-Syntax -Path $script:StructFile 6>&1 |
                ForEach-Object { $_.ToString() }

            ($output -join '') | Should -Match 'line one'
            ($output -join '') | Should -Match 'line two'
        }

    }

}