Tests/Tests-WriteColorEX.ps1
|
#Requires -Modules Pester BeforeAll { # Import the module $ModuleRoot = Split-Path -Parent $PSScriptRoot Import-Module "$ModuleRoot\PSWriteColorEX.psd1" -Force # Dot-source the class file for tests that reference [PSColorStyle] . "$ModuleRoot\Classes\PSColorStyle.ps1" } Describe 'Write-ColorEX' -Tag 'Unit', 'Function', 'Main' { Context 'Basic Functionality' { It 'Accepts Text parameter' { { Write-ColorEX -Text 'Test' -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of text segments' { { Write-ColorEX -Text @('Test1', 'Test2', 'Test3') -NoConsoleOutput } | Should -Not -Throw } It 'Works without parameters' { { Write-ColorEX -NoConsoleOutput } | Should -Not -Throw } It 'Has WC alias' { $command = Get-Command WC -ErrorAction SilentlyContinue $command | Should -Not -BeNullOrEmpty $command | Should -Not -BeNullOrEmpty $command.ResolvedCommandName | Should -Be 'Write-ColorEX' } It 'Has Write-Color alias' { $command = Get-Command Write-Color -ErrorAction SilentlyContinue $command | Should -Not -BeNullOrEmpty $command | Should -Not -BeNullOrEmpty $command.ResolvedCommandName | Should -Be 'Write-ColorEX' } It 'Has WCEX alias' { $command = Get-Command WCEX -ErrorAction SilentlyContinue $command | Should -Not -BeNullOrEmpty } } Context 'Color Parameter - Color Names' { It 'Accepts single color name' { { Write-ColorEX -Text 'Test' -Color Red -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of color names' { { Write-ColorEX -Text @('A', 'B', 'C') -Color @('Red', 'Green', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Cycles colors when more text than colors' { { Write-ColorEX -Text @('A', 'B', 'C', 'D') -Color @('Red', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Accepts extended color names' { { Write-ColorEX -Text 'Test' -Color Orange -NoConsoleOutput } | Should -Not -Throw } It 'Accepts Dark/Light color variants' { { Write-ColorEX -Text @('Dark', 'Normal', 'Light') -Color @('DarkRed', 'Red', 'LightRed') -NoConsoleOutput } | Should -Not -Throw } } Context 'Color Parameter - Hex Colors' { It 'Accepts hex color with # prefix' { { Write-ColorEX -Text 'Test' -Color '#FF0000' -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts hex color with 0x prefix' { { Write-ColorEX -Text 'Test' -Color '0xFF0000' -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts hex color without prefix' { { Write-ColorEX -Text 'Test' -Color 'FF0000' -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of hex colors' { { Write-ColorEX -Text @('R', 'G', 'B') -Color @('#FF0000', '#00FF00', '#0000FF') -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts lowercase hex' { { Write-ColorEX -Text 'Test' -Color '#ff0000' -TrueColor -NoConsoleOutput } | Should -Not -Throw } } Context 'Color Parameter - RGB Arrays' { It 'Accepts RGB array for single text' { { Write-ColorEX -Text 'Test' -Color @(255, 0, 0) -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of RGB arrays' { { Write-ColorEX -Text @('R', 'G', 'B') -Color @(@(255, 0, 0), @(0, 255, 0), @(0, 0, 255)) -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Clamps RGB values to 0-255 range' { { Write-ColorEX -Text 'Test' -Color @(300, -10, 128) -TrueColor -NoConsoleOutput -Silent } | Should -Not -Throw } } Context 'Color Parameter - ANSI Integers' { It 'Accepts ANSI8 integer (0-255)' { { Write-ColorEX -Text 'Test' -Color 196 -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of ANSI8 integers' { { Write-ColorEX -Text @('A', 'B', 'C') -Color @(196, 46, 226) -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Validates ANSI8 range (0-255)' { { Write-ColorEX -Text 'Test' -Color 300 -ANSI8 -NoConsoleOutput -Silent } | Should -Not -Throw } } Context 'BackGroundColor Parameter' { It 'Accepts background color name' { { Write-ColorEX -Text 'Test' -BackGroundColor Blue -NoConsoleOutput } | Should -Not -Throw } It 'Accepts background hex color' { { Write-ColorEX -Text 'Test' -BackGroundColor '#0000FF' -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts background RGB array' { { Write-ColorEX -Text 'Test' -BackGroundColor @(0, 0, 255) -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts null background color' { # Null background is not supported by parameter validation } It 'Accepts array of background colors' { { Write-ColorEX -Text @('A', 'B') -BackGroundColor @('Red', 'Blue') -NoConsoleOutput } | Should -Not -Throw } } Context 'Color Mode Switches' { It 'Accepts -ANSI4 switch' { { Write-ColorEX -Text 'Test' -Color Red -ANSI4 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -ANSI8 switch' { { Write-ColorEX -Text 'Test' -Color 196 -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -TrueColor switch' { { Write-ColorEX -Text 'Test' -Color '#FF0000' -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -ANSI24 alias for TrueColor' { { Write-ColorEX -Text 'Test' -Color '#FF0000' -ANSI24 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -TC alias for TrueColor' { { Write-ColorEX -Text 'Test' -Color '#FF0000' -TC -NoConsoleOutput } | Should -Not -Throw } It 'Auto-selects color mode when not specified' { { Write-ColorEX -Text 'Test' -Color Red -NoConsoleOutput } | Should -Not -Throw } It 'Handles multiple color mode switches with warning' { { Write-ColorEX -Text 'Test' -Color Red -ANSI4 -ANSI8 -TrueColor -NoConsoleOutput -Silent } | Should -Not -Throw } } Context 'Gradient Parameter' { It 'Accepts 2-color gradient' { { Write-ColorEX -Text 'GRADIENT' -Gradient @('Red', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Accepts multi-stop gradient' { { Write-ColorEX -Text 'RAINBOW' -Gradient @('Red', 'Orange', 'Yellow', 'Green', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Accepts gradient with hex colors' { { Write-ColorEX -Text 'TEST' -Gradient @('#FF0000', '#0000FF') -NoConsoleOutput } | Should -Not -Throw } It 'Accepts gradient with RGB arrays' { { Write-ColorEX -Text 'TEST' -Gradient @(@(255, 0, 0), @(0, 0, 255)) -NoConsoleOutput } | Should -Not -Throw } It 'Rejects gradient with less than 2 colors' { { Write-ColorEX -Text 'TEST' -Gradient @('Red') -NoConsoleOutput -Silent } | Should -Not -Throw # Should disable gradient and continue } It 'Auto-enables TrueColor mode for gradient when supported' { { Write-ColorEX -Text 'TEST' -Gradient @('Red', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Allows color override with gradient' { # Null in color array is not supported - removing this test } } Context 'Style Switches' { It 'Accepts -Bold switch' { { Write-ColorEX -Text 'Test' -Bold -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Italic switch' { { Write-ColorEX -Text 'Test' -Italic -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Underline switch' { { Write-ColorEX -Text 'Test' -Underline -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Blink switch' { { Write-ColorEX -Text 'Test' -Blink -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Faint switch' { { Write-ColorEX -Text 'Test' -Faint -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -CrossedOut switch' { { Write-ColorEX -Text 'Test' -CrossedOut -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Strikethrough alias' { { Write-ColorEX -Text 'Test' -Strikethrough -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -DoubleUnderline switch' { { Write-ColorEX -Text 'Test' -DoubleUnderline -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Overline switch' { { Write-ColorEX -Text 'Test' -Overline -NoConsoleOutput } | Should -Not -Throw } It 'Accepts multiple style switches' { { Write-ColorEX -Text 'Test' -Bold -Italic -Underline -NoConsoleOutput } | Should -Not -Throw } } Context 'Style Parameter (Per-Segment)' { It 'Accepts single style string' { { Write-ColorEX -Text 'Test' -Style 'Bold' -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of styles for single segment' { { Write-ColorEX -Text 'Test' -Style @('Bold', 'Italic') -NoConsoleOutput } | Should -Not -Throw } It 'Accepts array of styles for multiple segments' { { Write-ColorEX -Text @('A', 'B', 'C') -Style @('Bold', @('Italic', 'Underline'), 'Faint') -NoConsoleOutput } | Should -Not -Throw } It 'Accepts integer style' { { Write-ColorEX -Text 'Test' -Style 1 -NoConsoleOutput } | Should -Not -Throw } } Context 'StyleProfile Parameter' { It 'Accepts PSColorStyle object' { $style = [PSColorStyle]::new('TestStyle', 'Red', $null) { Write-ColorEX -Text 'Test' -StyleProfile $style -NoConsoleOutput } | Should -Not -Throw } It 'Applies profile colors' { $style = [PSColorStyle]::new('TestStyle', 'Magenta', 'DarkBlue') { Write-ColorEX -Text 'Test' -StyleProfile $style -NoConsoleOutput } | Should -Not -Throw } It 'Applies profile styles' { $style = [PSColorStyle]::new('TestStyle', 'Red', $null) $style.Bold = $true $style.Italic = $true { Write-ColorEX -Text 'Test' -StyleProfile $style -NoConsoleOutput } | Should -Not -Throw } It 'Uses built-in Error profile' { $errorProfile = [PSColorStyle]::GetProfile('Error') { Write-ColorEX -Text 'Test' -StyleProfile $errorProfile -NoConsoleOutput } | Should -Not -Throw } } Context 'Default Switch' { It 'Accepts -Default switch' { { Write-ColorEX -Text 'Test' -Default -NoConsoleOutput } | Should -Not -Throw } It 'Uses default style when specified' { { Write-ColorEX -Text 'Test' -Default -NoConsoleOutput } | Should -Not -Throw } } Context 'Formatting Parameters' { It 'Accepts -StartTab parameter' { { Write-ColorEX -Text 'Test' -StartTab 2 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Indent alias for StartTab' { { Write-ColorEX -Text 'Test' -Indent 2 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -StartSpaces parameter' { { Write-ColorEX -Text 'Test' -StartSpaces 4 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -LinesBefore parameter' { { Write-ColorEX -Text 'Test' -LinesBefore 2 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -LinesAfter parameter' { { Write-ColorEX -Text 'Test' -LinesAfter 2 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -HorizontalCenter switch' { { Write-ColorEX -Text 'Test' -HorizontalCenter -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Center alias' { { Write-ColorEX -Text 'Test' -Center -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -ShowTime switch' { { Write-ColorEX -Text 'Test' -ShowTime -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -DateTimeFormat parameter' { { Write-ColorEX -Text 'Test' -ShowTime -DateTimeFormat 'HH:mm:ss' -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -NoNewLine switch' { { Write-ColorEX -Text 'Test' -NoNewLine -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -BlankLine switch' { { Write-ColorEX -BlankLine -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -BL alias for BlankLine' { { Write-ColorEX -BL -NoConsoleOutput } | Should -Not -Throw } } Context 'Logging Parameters' { BeforeEach { $script:TestLogFile = Join-Path $TestDrive "test.log" } It 'Accepts -LogFile parameter' { Write-ColorEX -Text 'Test' -LogFile $script:TestLogFile -NoConsoleOutput Test-Path $script:TestLogFile | Should -Be $true } It 'Creates log file with content' { Write-ColorEX -Text 'Test message' -LogFile $script:TestLogFile -NoConsoleOutput $content = Get-Content $script:TestLogFile -Raw $content | Should -Match 'Test message' } It 'Accepts -LogPath parameter' { $logPath = $TestDrive Write-ColorEX -Text 'Test' -LogFile 'custom.log' -LogPath $logPath -NoConsoleOutput Test-Path (Join-Path $logPath 'custom.log') | Should -Be $true } It 'Accepts -LogLevel parameter' { Write-ColorEX -Text 'Test' -LogFile $script:TestLogFile -LogLevel 'INFO' -NoConsoleOutput $content = Get-Content $script:TestLogFile -Raw $content | Should -Match '\[INFO\]' } It 'Accepts -LogTime switch' { Write-ColorEX -Text 'Test' -LogFile $script:TestLogFile -LogTime -NoConsoleOutput $content = Get-Content $script:TestLogFile -Raw $content | Should -Match '\d{4}-\d{2}-\d{2}' } It 'Accepts -LogRetry parameter' { { Write-ColorEX -Text 'Test' -LogFile $script:TestLogFile -LogRetry 5 -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Encoding parameter' { { Write-ColorEX -Text 'Test' -LogFile $script:TestLogFile -Encoding 'UTF8' -NoConsoleOutput } | Should -Not -Throw } It 'Adds .log extension if not specified' { Write-ColorEX -Text 'Test' -LogFile 'testfile' -LogPath $TestDrive -NoConsoleOutput Test-Path (Join-Path $TestDrive 'testfile.log') | Should -Be $true } It 'Combines LogTime and LogLevel' { Write-ColorEX -Text 'Test' -LogFile $script:TestLogFile -LogTime -LogLevel 'WARNING' -NoConsoleOutput $content = Get-Content $script:TestLogFile -Raw $content | Should -Match '\[WARNING\]' $content | Should -Match '\d{4}-\d{2}-\d{2}' } } Context 'NoConsoleOutput Switch' { BeforeEach { $script:TestLogFile = Join-Path $TestDrive "noconsole.log" } It 'Accepts -NoConsoleOutput switch' { { Write-ColorEX -Text 'Test' -NoConsoleOutput } | Should -Not -Throw } It 'Writes to log only with NoConsoleOutput' { Write-ColorEX -Text 'Log only' -LogFile $script:TestLogFile -NoConsoleOutput Test-Path $script:TestLogFile | Should -Be $true $content = Get-Content $script:TestLogFile -Raw $content | Should -Match 'Log only' } It 'Has HideConsole alias' { { Write-ColorEX -Text 'Test' -HideConsole } | Should -Not -Throw } It 'Has LogOnly alias' { { Write-ColorEX -Text 'Test' -LogOnly } | Should -Not -Throw } } Context 'Debugging and Silent Parameters' { It 'Accepts -Debugging switch' { { Write-ColorEX -Text 'Test' -Debugging -NoConsoleOutput } | Should -Not -Throw } It 'Accepts -Silent switch' { { Write-ColorEX -Text 'Test' -Silent -NoConsoleOutput } | Should -Not -Throw } It 'Silent suppresses warnings' { { Write-ColorEX -Text 'Test' -Color 300 -ANSI8 -Silent -NoConsoleOutput } | Should -Not -Throw } } Context 'Color Fallback Behavior' { It 'Falls back from TrueColor to ANSI8 when not supported' { $env:FORCE_COLOR = '2' # Force ANSI8 { Write-ColorEX -Text 'Test' -Color '#FF0000' -TrueColor -Silent -NoConsoleOutput } | Should -Not -Throw $env:FORCE_COLOR = $null } It 'Falls back from TrueColor to ANSI4 when not supported' { $env:FORCE_COLOR = '1' # Force ANSI4 { Write-ColorEX -Text 'Test' -Color '#FF0000' -TrueColor -Silent -NoConsoleOutput } | Should -Not -Throw $env:FORCE_COLOR = $null } It 'Falls back from ANSI8 to ANSI4 when not supported' { $env:FORCE_COLOR = '1' # Force ANSI4 { Write-ColorEX -Text 'Test' -Color 196 -ANSI8 -Silent -NoConsoleOutput } | Should -Not -Throw $env:FORCE_COLOR = $null } It 'Disables ANSI when not supported' { $env:FORCE_COLOR = '0' # Force None { Write-ColorEX -Text 'Test' -Bold -Silent -NoConsoleOutput } | Should -Not -Throw $env:FORCE_COLOR = $null } } Context 'Edge Cases and Error Handling' { It 'Handles empty text array' { { Write-ColorEX -Text @() -NoConsoleOutput } | Should -Not -Throw } It 'Handles null color' { # Null color is not supported by parameter validation } It 'Handles invalid color name gracefully' { { Write-ColorEX -Text 'Test' -Color 'InvalidColorName' -NoConsoleOutput -Silent } | Should -Not -Throw } It 'Handles invalid hex format' { { Write-ColorEX -Text 'Test' -Color '#GGGGGG' -TrueColor -NoConsoleOutput -Silent } | Should -Not -Throw } It 'Handles RGB array with out-of-range values' { { Write-ColorEX -Text 'Test' -Color @(300, -50, 128) -TrueColor -NoConsoleOutput -Silent } | Should -Not -Throw } It 'Handles very long text' { $longText = 'A' * 1000 { Write-ColorEX -Text $longText -NoConsoleOutput } | Should -Not -Throw } It 'Handles many text segments' { $manySegments = 1..100 | ForEach-Object { "Segment $_" } { Write-ColorEX -Text $manySegments -NoConsoleOutput } | Should -Not -Throw } It 'Handles special characters in text' { { Write-ColorEX -Text "Test `n`t special" -NoConsoleOutput } | Should -Not -Throw } It 'Handles Unicode characters' { { Write-ColorEX -Text '✓ ✗ ★ ♥' -NoConsoleOutput } | Should -Not -Throw } } Context 'Performance' { It 'Processes many segments quickly' { $segments = 1..1000 | ForEach-Object { "S$_" } $start = Get-Date Write-ColorEX -Text $segments -NoConsoleOutput $duration = (Get-Date) - $start $duration.TotalMilliseconds | Should -BeLessThan 500 } It 'Uses cached ANSI support' { # Multiple calls should use cached value Write-ColorEX -Text 'Test1' -NoConsoleOutput Write-ColorEX -Text 'Test2' -NoConsoleOutput Write-ColorEX -Text 'Test3' -NoConsoleOutput # Should complete without errors } It 'Uses cached color table' { # Color table should be cached Write-ColorEX -Text 'Test' -Color Orange -NoConsoleOutput Write-ColorEX -Text 'Test' -Color Purple -NoConsoleOutput # Should complete quickly } } Context 'ANSI Escape Sequences' { It 'Skips ANSI when NoConsoleOutput is used without logging' { # Fast path - no console output, no logging { Write-ColorEX -Text 'Test' -NoConsoleOutput } | Should -Not -Throw } It 'Generates ANSI codes for TrueColor' { # Should generate proper ANSI codes { Write-ColorEX -Text 'Test' -Color @(255, 0, 0) -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Generates ANSI codes for ANSI8' { { Write-ColorEX -Text 'Test' -Color 196 -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Generates ANSI codes for ANSI4' { { Write-ColorEX -Text 'Test' -Color Red -ANSI4 -NoConsoleOutput } | Should -Not -Throw } } Context 'Color Mode Validation' { It 'Validates TrueColor expects RGB or hex' { { Write-ColorEX -Text 'Test' -Color 50 -TrueColor -NoConsoleOutput -Silent } | Should -Not -Throw } It 'Validates ANSI8 expects 0-255 range' { { Write-ColorEX -Text 'Test' -Color 300 -ANSI8 -NoConsoleOutput -Silent } | Should -Not -Throw } It 'Validates ANSI8 rejects RGB arrays' { { Write-ColorEX -Text 'Test' -Color @(255, 0, 0) -ANSI8 -NoConsoleOutput -Silent } | Should -Not -Throw } } Context 'Gradient Validation' { It 'Requires ANSI8 or TrueColor for gradient' { $env:FORCE_COLOR = '0' # Force no ANSI { Write-ColorEX -Text 'TEST' -Gradient @('Red', 'Blue') -NoConsoleOutput -Silent } | Should -Not -Throw $env:FORCE_COLOR = $null } It 'Rejects gradient in ANSI4 mode' { $env:FORCE_COLOR = '1' # Force ANSI4 { Write-ColorEX -Text 'TEST' -Gradient @('Red', 'Blue') -NoConsoleOutput -Silent } | Should -Not -Throw $env:FORCE_COLOR = $null } It 'Warns when gradient has more colors than characters' { { Write-ColorEX -Text 'AB' -Gradient @('Red', 'Orange', 'Yellow', 'Green', 'Blue') -NoConsoleOutput -Silent } | Should -Not -Throw } } Context 'Integration with Style Profiles' { It 'Combines StyleProfile with explicit parameters' { $profile = [PSColorStyle]::GetProfile('Error') { Write-ColorEX -Text 'Test' -StyleProfile $profile -Underline -NoConsoleOutput } | Should -Not -Throw } It 'StyleProfile does not override explicit parameters' { $profile = [PSColorStyle]::new('Test', 'Red', $null) { Write-ColorEX -Text 'Test' -StyleProfile $profile -Color Blue -NoConsoleOutput } | Should -Not -Throw } It 'Default profile does not override StyleProfile' { $profile = [PSColorStyle]::GetProfile('Warning') { Write-ColorEX -Text 'Test' -StyleProfile $profile -Default -NoConsoleOutput } | Should -Not -Throw } } Context 'Complex Scenarios' { It 'Combines multiple features' { { Write-ColorEX -Text @('Line1', 'Line2', 'Line3') ` -Color @('Red', 'Green', 'Blue') ` -BackGroundColor @('Yellow', 'Black', 'Cyan') ` -Bold -Italic ` -StartTab 2 -LinesBefore 1 -LinesAfter 1 ` -NoConsoleOutput } | Should -Not -Throw } It 'Complex gradient with color override' { { Write-ColorEX -Text @('START', 'MID', 'END') ` -Gradient @('Red', 'Yellow', 'Green') ` -Color @('Red', 'Cyan', 'Blue') ` -Bold -Underline ` -NoConsoleOutput } | Should -Not -Throw } It 'Full-featured call with all parameters' { $logFile = Join-Path $TestDrive "full_test.log" $style = [PSColorStyle]::new('FullTest', 'Magenta', 'DarkBlue') { Write-ColorEX -Text @('Test1', 'Test2') ` -Color @('Red', 'Blue') ` -BackGroundColor @('Yellow', 'Green') ` -Bold -Italic -Underline ` -StartTab 1 -StartSpaces 2 ` -LinesBefore 1 -LinesAfter 1 ` -ShowTime -DateTimeFormat 'HH:mm:ss' ` -LogFile $logFile -LogLevel 'INFO' -LogTime ` -NoConsoleOutput } | Should -Not -Throw Test-Path $logFile | Should -Be $true } } Context 'Regression Tests' { It 'Handles flattened RGB array correctly' { # Regression: RGB array should be wrapped, not flattened { Write-ColorEX -Text 'Test' -Color @(255, 0, 0) -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Color cycling works correctly' { # Regression: Colors should cycle through all segments { Write-ColorEX -Text @('A', 'B', 'C', 'D', 'E') -Color @('Red', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Gradient with single text segment' { { Write-ColorEX -Text 'RAINBOW' -Gradient @('Red', 'Blue') -NoConsoleOutput } | Should -Not -Throw } It 'Multiple style switches applied correctly' { { Write-ColorEX -Text 'Test' -Bold -Italic -Underline -Blink -Faint -NoConsoleOutput } | Should -Not -Throw } } Context 'Environment Variable Interactions' { BeforeEach { $script:OriginalForceColor = $env:FORCE_COLOR $script:OriginalNoColor = $env:NO_COLOR } AfterEach { $env:FORCE_COLOR = $script:OriginalForceColor $env:NO_COLOR = $script:OriginalNoColor } It 'Respects FORCE_COLOR environment variable' { $env:FORCE_COLOR = '3' { Write-ColorEX -Text 'Test' -NoConsoleOutput } | Should -Not -Throw } It 'Respects NO_COLOR environment variable' { $env:NO_COLOR = '1' { Write-ColorEX -Text 'Test' -Bold -NoConsoleOutput } | Should -Not -Throw } } Context 'Alias Coverage' { It 'C alias works for Color parameter' { { Write-ColorEX -Text 'Test' -C Red -NoConsoleOutput } | Should -Not -Throw } It 'B alias works for BackGroundColor parameter' { { Write-ColorEX -Text 'Test' -B Blue -NoConsoleOutput } | Should -Not -Throw } It 'T alias works for Text parameter' { { Write-ColorEX -T 'Test' -NoConsoleOutput } | Should -Not -Throw } It 'L alias works for LogFile parameter' { $logFile = Join-Path $TestDrive "alias_test.log" { Write-ColorEX -Text 'Test' -L $logFile -NoConsoleOutput } | Should -Not -Throw } } # PHASE 1 TESTS: Background Colors, Formatting, Styles Context 'Background Color Support' { BeforeAll { # Mock Write-Host to prevent console spam and test actual ANSI generation Mock -ModuleName PSWriteColorEX Write-Host {} } It 'Accepts BackGroundColor parameter' { { Write-ColorEX -Text 'Test' -BackGroundColor 'Red' } | Should -Not -Throw } It 'Accepts array of background colors' { { Write-ColorEX -Text 'A','B','C' -BackGroundColor 'Red','Blue' } | Should -Not -Throw } It 'Cycles background colors across segments' { { Write-ColorEX -Text 'A','B','C','D' -BackGroundColor 'Red','Blue' } | Should -Not -Throw } It 'Accepts RGB array for background in TrueColor mode' { { Write-ColorEX -Text 'Test' -BackGroundColor @(255,128,0) -TrueColor } | Should -Not -Throw } It 'Accepts hex color for background' { { Write-ColorEX -Text 'Test' -BackGroundColor '#FF8000' -TrueColor } | Should -Not -Throw } It 'Handles invalid RGB values in background gracefully' { { Write-ColorEX -Text 'Test' -BackGroundColor @(300,-10,500) -TrueColor } | Should -Not -Throw } It 'Accepts ANSI integer code for background' { { Write-ColorEX -Text 'Test' -BackGroundColor 196 -ANSI8 } | Should -Not -Throw } It 'Combines foreground and background colors' { { Write-ColorEX -Text 'Test' -Color 'Yellow' -BackGroundColor 'Blue' } | Should -Not -Throw } } Context 'Formatting Features' { It 'Accepts StartTab parameter' { { Write-ColorEX -Text 'Test' -StartTab 3 } | Should -Not -Throw } It 'Accepts StartSpaces parameter' { { Write-ColorEX -Text 'Test' -StartSpaces 5 } | Should -Not -Throw } It 'Accepts LinesBefore parameter' { { Write-ColorEX -Text 'Test' -LinesBefore 2 } | Should -Not -Throw } It 'Accepts LinesAfter parameter' { { Write-ColorEX -Text 'Test' -LinesAfter 1 } | Should -Not -Throw } It 'Accepts HorizontalCenter parameter' { { Write-ColorEX -Text 'Test' -HorizontalCenter } | Should -Not -Throw } It 'Accepts ShowTime parameter' { { Write-ColorEX -Text 'Test' -ShowTime } | Should -Not -Throw } It 'Combines multiple formatting options' { { Write-ColorEX -Text 'Test' -LinesBefore 1 -LinesAfter 1 -StartTab 2 } | Should -Not -Throw } It 'BlankLine parameter works' { { Write-ColorEX -BlankLine } | Should -Not -Throw } } Context 'Per-Segment Styling' { It 'Applies different styles to each segment' { { Write-ColorEX -Text 'Bold','Italic','Under' -Style 'Bold','Italic','Underline' } | Should -Not -Throw } It 'Handles array of styles for single segment' { { Write-ColorEX -Text 'Test' -Style @('Bold','Italic') } | Should -Not -Throw } It 'Handles mixed single and array styles' { { Write-ColorEX -Text 'A','B','C' -Style 'Bold',@('Italic','Underline'),'CrossedOut' } | Should -Not -Throw } It 'Cycles styles when fewer styles than segments' { { Write-ColorEX -Text 'A','B','C','D' -Style 'Bold','Italic' } | Should -Not -Throw } It 'Combines per-segment and line-wide styles' { { Write-ColorEX -Text 'A','B' -Style 'Italic','Underline' -Bold } | Should -Not -Throw } } # PHASE 2 TESTS: ANSI Sequences, Color Modes, Gradients Context 'ANSI Escape Sequence Generation' { It 'Works with TrueColor mode and RGB array' { { Write-ColorEX -Text 'Test' -Color @(255,128,0) -TrueColor } | Should -Not -Throw } It 'Works with ANSI8 mode and color code' { { Write-ColorEX -Text 'Test' -Color 208 -ANSI8 } | Should -Not -Throw } It 'Works with ANSI4 mode and color name' { { Write-ColorEX -Text 'Test' -Color 'Red' -ANSI4 } | Should -Not -Throw } It 'Works with Bold style' { { Write-ColorEX -Text 'Test' -Bold } | Should -Not -Throw } It 'Works with Italic style' { { Write-ColorEX -Text 'Test' -Italic } | Should -Not -Throw } It 'Works with Underline style' { { Write-ColorEX -Text 'Test' -Underline } | Should -Not -Throw } It 'Combines multiple ANSI styles' { { Write-ColorEX -Text 'Test' -Bold -Italic -Underline } | Should -Not -Throw } It 'Works with background color in TrueColor mode' { { Write-ColorEX -Text 'Test' -BackGroundColor @(0,128,255) -TrueColor } | Should -Not -Throw } It 'Combines foreground, background, and styles' { { Write-ColorEX -Text 'Test' -Color @(255,0,0) -BackGroundColor @(0,0,255) -Bold -Italic -TrueColor } | Should -Not -Throw } } Context 'ANSI Color Mode Testing' { BeforeEach { $script:OriginalForceColor = $env:FORCE_COLOR } AfterEach { if ($null -eq $script:OriginalForceColor) { Remove-Item env:FORCE_COLOR -ErrorAction SilentlyContinue } else { $env:FORCE_COLOR = $script:OriginalForceColor } } It 'Works in ANSI4 mode with color name' { $env:FORCE_COLOR = '1' { Write-ColorEX -Text 'Test' -Color 'Red' -ANSI4 } | Should -Not -Throw } It 'Works in ANSI4 mode with ANSI code' { $env:FORCE_COLOR = '1' { Write-ColorEX -Text 'Test' -Color 91 -ANSI4 } | Should -Not -Throw } It 'Works in ANSI8 mode with color name' { $env:FORCE_COLOR = '2' { Write-ColorEX -Text 'Test' -Color 'Orange' -ANSI8 } | Should -Not -Throw } It 'Works in ANSI8 mode with ANSI8 code' { $env:FORCE_COLOR = '2' { Write-ColorEX -Text 'Test' -Color 208 -ANSI8 } | Should -Not -Throw } It 'Converts RGB to ANSI8 when TrueColor unavailable' { $env:FORCE_COLOR = '2' { Write-ColorEX -Text 'Test' -Color @(255,128,0) -ANSI8 } | Should -Not -Throw } It 'Uses helper function ConvertANSI4ToNativeColor' { $env:FORCE_COLOR = '1' $logFile = Join-Path $TestDrive "ansi4_convert_test.log" Write-ColorEX -Text 'Test' -Color 31 -ANSI4 -LogFile $logFile -NoNewLine # Test completes without error means conversion worked Remove-Item $logFile -Force } } Context 'Enhanced Gradient Features' { It 'Applies 2-color gradient' { { Write-ColorEX -Text 'RAINBOW' -Gradient @('Red','Blue') } | Should -Not -Throw } It 'Applies multi-stop gradient (4 colors)' { { Write-ColorEX -Text 'SPECTRUM' -Gradient @('Red','Yellow','Green','Blue') } | Should -Not -Throw } It 'Applies rainbow gradient (7 colors)' { { Write-ColorEX -Text 'RAINBOW' -Gradient @('Red','Orange','Yellow','Green','Cyan','Blue','Magenta') } | Should -Not -Throw } It 'Allows selective color overrides in gradient' { { Write-ColorEX -Text 'A','B','C' -Gradient @('Red','Blue') -Color 'Yellow' } | Should -Not -Throw } It 'Handles gradient with hex colors' { { Write-ColorEX -Text 'TEST' -Gradient @('#FF0000','#0000FF') } | Should -Not -Throw } It 'Handles gradient with RGB arrays' { { Write-ColorEX -Text 'TEST' -Gradient @(@(255,0,0),@(0,0,255)) } | Should -Not -Throw } It 'Gradient works with multiple text segments' { { Write-ColorEX -Text 'RAIN','BOW' -Gradient @('Red','Orange','Yellow','Green','Blue') } | Should -Not -Throw } It 'Gradient works in ANSI8 mode' { $env:FORCE_COLOR = '2' { Write-ColorEX -Text 'RAINBOW' -Gradient @('Red','Blue') -ANSI8 } | Should -Not -Throw $env:FORCE_COLOR = $null } } Context 'Color Cycling and Arrays' { It 'Cycles colors across many segments' { { Write-ColorEX -Text 'A','B','C','D','E' -Color 'Red','Blue' } | Should -Not -Throw } It 'Handles single color for multiple segments' { { Write-ColorEX -Text 'A','B','C' -Color 'Green' } | Should -Not -Throw } It 'Handles more colors than segments' { { Write-ColorEX -Text 'A','B' -Color 'Red','Blue','Green','Yellow' } | Should -Not -Throw } It 'Cycles background colors independently' { { Write-ColorEX -Text 'A','B','C' -Color 'Red','Blue' -BackGroundColor 'Yellow','Cyan' } | Should -Not -Throw } It 'Handles mixed color types in array' { { Write-ColorEX -Text 'A','B','C' -Color 'Red','Green','Blue' } | Should -Not -Throw } } # NEW TESTS: Color Lightening with Bold in Terminals without True Bold Fonts Context 'Color Lightening for Bold (TrueColor Mode)' { BeforeAll { # Mock Test-AnsiSupport to simulate terminal without bold font support Mock -ModuleName PSWriteColorEX -CommandName Test-AnsiSupport -MockWith { param($Silent) return [PSCustomObject]@{ ColorSupport = 'TrueColor' SupportsBoldFonts = $false # Triggers RGB lightening Details = @{ PowerShellVersion = $PSVersionTable.PSVersion.ToString() IsConsoleHost = $true HasVirtualTerminalProcessing = $true IsPSCore = $PSVersionTable.PSVersion.Major -ge 6 OperatingSystem = 'Win32NT' TerminalType = 'Test Terminal' StyleSupport = @{ Bold = $true Italic = $false Underline = $true } Warnings = @() } } } } AfterAll { # Remove mock to restore normal behavior Remove-Module PSWriteColorEX -Force -ErrorAction SilentlyContinue Import-Module "$ModuleRoot\PSWriteColorEX.psd1" -Force } It 'Lightens RGB colors when Bold is used without bold font support' { { Write-ColorEX -Text 'Test' -Color @(100, 100, 100) -Bold -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Lightens hex colors when Bold is used' { { Write-ColorEX -Text 'Test' -Color '#646464' -Bold -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Handles black color with Bold (edge case)' { { Write-ColorEX -Text 'Test' -Color @(0, 0, 0) -Bold -TrueColor -NoConsoleOutput } | Should -Not -Throw } It 'Handles white color with Bold (clamping test)' { { Write-ColorEX -Text 'Test' -Color @(255, 255, 255) -Bold -TrueColor -NoConsoleOutput } | Should -Not -Throw } } Context 'Color Lightening for Bold (ANSI8 Mode)' { BeforeAll { Mock -ModuleName PSWriteColorEX -CommandName Test-AnsiSupport -MockWith { param($Silent) return [PSCustomObject]@{ ColorSupport = 'ANSI8' SupportsBoldFonts = $false # Triggers color name lightening Details = @{ PowerShellVersion = $PSVersionTable.PSVersion.ToString() IsConsoleHost = $true HasVirtualTerminalProcessing = $true IsPSCore = $false OperatingSystem = 'Win32NT' TerminalType = 'Test Terminal' StyleSupport = @{ Bold = $true Italic = $false Underline = $true } Warnings = @() } } } } AfterAll { Remove-Module PSWriteColorEX -Force -ErrorAction SilentlyContinue Import-Module "$ModuleRoot\PSWriteColorEX.psd1" -Force } It 'Lightens DarkRed to Red when Bold is used' { { Write-ColorEX -Text 'Test' -Color 'DarkRed' -Bold -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Lightens Red to LightRed when Bold is used' { { Write-ColorEX -Text 'Test' -Color 'Red' -Bold -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Keeps LightRed unchanged when Bold is used' { { Write-ColorEX -Text 'Test' -Color 'LightRed' -Bold -ANSI8 -NoConsoleOutput } | Should -Not -Throw } It 'Lightens multiple color names with Bold' { { Write-ColorEX -Text 'A','B','C' -Color 'DarkBlue','Blue','LightBlue' -Bold -ANSI8 -NoConsoleOutput } | Should -Not -Throw } } # NEW TESTS: ConvertANSI4ToNativeColor Function Context 'ANSI4 to Native Color Conversion' { BeforeAll { # Mock Test-AnsiSupport to force Native color mode (no ANSI support) Mock -ModuleName PSWriteColorEX -CommandName Test-AnsiSupport -MockWith { param($Silent) return [PSCustomObject]@{ ColorSupport = 'None' # Forces native PowerShell colors SupportsBoldFonts = $false Details = @{ PowerShellVersion = $PSVersionTable.PSVersion.ToString() IsConsoleHost = $false HasVirtualTerminalProcessing = $false IsPSCore = $false OperatingSystem = 'Win32NT' TerminalType = 'PowerShell ISE' StyleSupport = @{ Bold = $false Italic = $false Underline = $false } Warnings = @('PowerShell ISE does NOT support ANSI escape sequences.') } } } } AfterAll { Remove-Module PSWriteColorEX -Force -ErrorAction SilentlyContinue Import-Module "$ModuleRoot\PSWriteColorEX.psd1" -Force } It 'Converts ANSI4 red (31) to native DarkRed' { { Write-ColorEX -Text 'Test' -Color 'Red' -NoConsoleOutput } | Should -Not -Throw } It 'Converts ANSI4 bright red (91) to native Red' { { Write-ColorEX -Text 'Test' -Color 'Red' -NoConsoleOutput } | Should -Not -Throw } It 'Handles multiple colors in Native mode' { { Write-ColorEX -Text 'A','B','C' -Color 'Red','Green','Blue' -NoConsoleOutput } | Should -Not -Throw } It 'Falls back gracefully when ANSI not supported' { { Write-ColorEX -Text 'Test' -Color 'Cyan' -Bold -NoConsoleOutput } | Should -Not -Throw } } # NEW TESTS: AutoPad Edge Cases with Wide and Zero-Width Characters Context 'AutoPad with Wide Characters' { It 'Handles wide padding character (2 cells)' { # Test with box-drawing character or emoji (simulate 2-cell width) { Write-ColorEX -Text 'Test' -AutoPad 20 -PadChar '●' -NoConsoleOutput } | Should -Not -Throw } It 'Handles text already at target width' { { Write-ColorEX -Text 'ExactlyTwentyChars!!' -AutoPad 20 -NoConsoleOutput } | Should -Not -Throw } It 'Handles text wider than target width (no padding)' { { Write-ColorEX -Text 'ThisIsAVeryLongStringThatExceedsTheTargetWidth' -AutoPad 20 -NoConsoleOutput } | Should -Not -Throw } It 'Handles AutoPad with PadLeft for right-alignment' { { Write-ColorEX -Text 'Right' -AutoPad 20 -PadLeft -NoConsoleOutput } | Should -Not -Throw } It 'Handles AutoPad with custom padding character' { { Write-ColorEX -Text 'Dots' -AutoPad 20 -PadChar '.' -NoConsoleOutput } | Should -Not -Throw } It 'Handles AutoPad with hyphen padding' { { Write-ColorEX -Text 'Header' -AutoPad 40 -PadChar '-' -NoConsoleOutput } | Should -Not -Throw } It 'Handles empty text with AutoPad' { { Write-ColorEX -Text '' -AutoPad 20 -NoConsoleOutput } | Should -Not -Throw } It 'Handles AutoPad with multiple text segments' { { Write-ColorEX -Text 'Part1','Part2' -AutoPad 30 -NoConsoleOutput } | Should -Not -Throw } } Context 'AutoPad with Zero-Width Characters' { It 'Rejects zero-width character as PadChar with warning' { # Zero-width space U+200B { Write-ColorEX -Text 'Test' -AutoPad 20 -PadChar "`u{200B}" -NoConsoleOutput } | Should -Not -Throw } It 'Falls back to space when PadChar is zero-width' { # Should complete without error, using space instead { Write-ColorEX -Text 'Fallback' -AutoPad 25 -PadChar "`u{200B}" -NoConsoleOutput } | Should -Not -Throw } } Context 'AutoPad with Unicode Text' { It 'Handles CJK characters (wide characters in text)' { { Write-ColorEX -Text '日本語' -AutoPad 20 -NoConsoleOutput } | Should -Not -Throw } It 'Handles emoji in text (wide characters)' { { Write-ColorEX -Text 'Server ●' -AutoPad 21 -NoConsoleOutput } | Should -Not -Throw } It 'Handles mixed ASCII and wide characters' { { Write-ColorEX -Text 'Status: ✓' -AutoPad 25 -NoConsoleOutput } | Should -Not -Throw } It 'Handles box-drawing characters' { { Write-ColorEX -Text '║ Content ║' -AutoPad 30 -NoConsoleOutput } | Should -Not -Throw } } } |