Tests/Tests-PSColorStyle.ps1

#Requires -Modules Pester

BeforeAll {
    # Importing the module makes [PSColorStyle] available
    $ModuleRoot = Split-Path -Parent $PSScriptRoot
    Import-Module "$ModuleRoot\PSWriteColorEX.psd1" -Force
}

Describe 'PSColorStyle Class' -Tag 'Unit', 'Class' {

    Context 'Constructors' {
        It 'Creates instance with default constructor' {
            $style = [PSColorStyle]::new()

            $style | Should -Not -BeNullOrEmpty
            $style.Name | Should -Be 'Custom'
            $style.ForegroundColor | Should -Be 'Gray'
            $style.BackgroundColor | Should -BeNullOrEmpty
        }

        It 'Creates instance with name only' {
            $style = [PSColorStyle]::new('TestStyle')

            $style.Name | Should -Be 'TestStyle'
            $style.ForegroundColor | Should -Be 'Gray'
            $style.BackgroundColor | Should -BeNullOrEmpty
        }

        It 'Creates instance with name, foreground, and background' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', 'Blue')

            $style.Name | Should -Be 'TestStyle'
            $style.ForegroundColor | Should -Be 'Red'
            $style.BackgroundColor | Should -Be 'Blue'
        }

        It 'Accepts null background color' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)

            $style.Name | Should -Be 'TestStyle'
            $style.ForegroundColor | Should -Be 'Red'
            $style.BackgroundColor | Should -BeNullOrEmpty
        }
    }

    Context 'Property Initialization' {
        BeforeAll {
            $script:style = [PSColorStyle]::new('TestStyle', 'Red', 'Blue')
        }

        It 'Initializes boolean properties to false' {
            $style.Bold | Should -Be $false
            $style.Italic | Should -Be $false
            $style.Underline | Should -Be $false
            $style.Blink | Should -Be $false
            $style.Faint | Should -Be $false
            $style.CrossedOut | Should -Be $false
            $style.DoubleUnderline | Should -Be $false
            $style.Overline | Should -Be $false
            $style.ShowTime | Should -Be $false
            $style.NoNewLine | Should -Be $false
            $style.HorizontalCenter | Should -Be $false
        }

        It 'Initializes numeric properties to zero' {
            $style.StartTab | Should -Be 0
            $style.StartSpaces | Should -Be 0
            $style.LinesBefore | Should -Be 0
            $style.LinesAfter | Should -Be 0
        }

        It 'Initializes Style as empty or null' {
            # Style can be null or empty array
            if ($style.Style) {
                $style.Style.Count | Should -Be 0
            } else {
                $style.Style | Should -BeNullOrEmpty
            }
        }

        It 'Initializes Gradient as null' {
            $style.Gradient | Should -BeNullOrEmpty
        }
    }

    Context 'Property Modification' {
        BeforeEach {
            $script:style = [PSColorStyle]::new('TestStyle', 'Red', $null)
        }

        It 'Allows setting Bold to true' {
            $style.Bold = $true
            $style.Bold | Should -Be $true
        }

        It 'Allows setting multiple style properties' {
            $style.Bold = $true
            $style.Italic = $true
            $style.Underline = $true

            $style.Bold | Should -Be $true
            $style.Italic | Should -Be $true
            $style.Underline | Should -Be $true
        }

        It 'Allows setting numeric properties' {
            $style.StartTab = 2
            $style.StartSpaces = 4
            $style.LinesBefore = 1
            $style.LinesAfter = 1

            $style.StartTab | Should -Be 2
            $style.StartSpaces | Should -Be 4
            $style.LinesBefore | Should -Be 1
            $style.LinesAfter | Should -Be 1
        }

        It 'Allows setting Gradient array' {
            $style.Gradient = @('Red', 'Blue', 'Green')

            $style.Gradient | Should -Not -BeNullOrEmpty
            $style.Gradient.Count | Should -Be 3
            $style.Gradient[0] | Should -Be 'Red'
        }

        It 'Allows setting Style array' {
            $style.Style = @('Bold', 'Italic')

            $style.Style.Count | Should -Be 2
            $style.Style[0] | Should -Be 'Bold'
            $style.Style[1] | Should -Be 'Italic'
        }
    }

    Context 'Static Default Property' {
        It 'Has a Default static property' {
            [PSColorStyle]::Default | Should -Not -BeNullOrEmpty
        }

        It 'Default property is of type PSColorStyle' {
            [PSColorStyle]::Default.GetType().Name | Should -Be 'PSColorStyle'
        }
    }

    Context 'Static Profiles Property' {
        It 'Has a Profiles static property' {
            [PSColorStyle]::Profiles | Should -Not -BeNullOrEmpty
        }

        It 'Profiles is a hashtable' {
            [PSColorStyle]::Profiles | Should -BeOfType [hashtable]
        }

        It 'Profiles contains default profiles' {
            [PSColorStyle]::Profiles.ContainsKey('Default') | Should -Be $true
            [PSColorStyle]::Profiles.ContainsKey('Error') | Should -Be $true
            [PSColorStyle]::Profiles.ContainsKey('Warning') | Should -Be $true
            [PSColorStyle]::Profiles.ContainsKey('Info') | Should -Be $true
            [PSColorStyle]::Profiles.ContainsKey('Success') | Should -Be $true
            [PSColorStyle]::Profiles.ContainsKey('Critical') | Should -Be $true
            [PSColorStyle]::Profiles.ContainsKey('Debug') | Should -Be $true
        }
    }

    Context 'SetAsDefault Method' {
        It 'Sets the style as default' {
            $originalDefault = [PSColorStyle]::Default
            $newStyle = [PSColorStyle]::new('NewDefault', 'Magenta', $null)

            $newStyle.SetAsDefault()

            [PSColorStyle]::Default.Name | Should -Be 'NewDefault'
            [PSColorStyle]::Default.ForegroundColor | Should -Be 'Magenta'

            # Restore original default
            $originalDefault.SetAsDefault()
        }
    }

    Context 'AddToProfiles Method' {
        It 'Adds style to profiles collection' {
            $style = [PSColorStyle]::new('CustomProfile', 'Orange', $null)
            $style.AddToProfiles()

            [PSColorStyle]::Profiles.ContainsKey('CustomProfile') | Should -Be $true
            [PSColorStyle]::Profiles['CustomProfile'].Name | Should -Be 'CustomProfile'
        }

        It 'Overwrites existing profile with same name' {
            $style1 = [PSColorStyle]::new('TestProfile', 'Red', $null)
            $style1.AddToProfiles()

            $style2 = [PSColorStyle]::new('TestProfile', 'Blue', $null)
            $style2.AddToProfiles()

            [PSColorStyle]::Profiles['TestProfile'].ForegroundColor | Should -Be 'Blue'
        }
    }

    Context 'GetProfile Static Method' {
        BeforeAll {
            $testStyle = [PSColorStyle]::new('GetProfileTest', 'Cyan', $null)
            $testStyle.AddToProfiles()
        }

        It 'Retrieves existing profile by name' {
            $retrieved = [PSColorStyle]::GetProfile('GetProfileTest')

            $retrieved | Should -Not -BeNullOrEmpty
            $retrieved.Name | Should -Be 'GetProfileTest'
            $retrieved.ForegroundColor | Should -Be 'Cyan'
        }

        It 'Returns null for non-existent profile' {
            $retrieved = [PSColorStyle]::GetProfile('NonExistentProfile')

            $retrieved | Should -BeNullOrEmpty
        }

        It 'Retrieves built-in Error profile' {
            $errorProfile = [PSColorStyle]::GetProfile('Error')

            $errorProfile | Should -Not -BeNullOrEmpty
            $errorProfile.Name | Should -Be 'Error'
            $errorProfile.ForegroundColor | Should -Be 'Red'
            $errorProfile.Bold | Should -Be $true
        }
    }

    Context 'ToWriteColorParams Method' {
        It 'Returns hashtable with color properties' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', 'Blue')
            $params = $style.ToWriteColorParams()

            $params | Should -BeOfType [hashtable]
            $params['Color'] | Should -Be 'Red'
            $params['BackGroundColor'] | Should -Be 'Blue'
        }

        It 'Includes Bold when set to true' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.Bold = $true
            $params = $style.ToWriteColorParams()

            $params.ContainsKey('Bold') | Should -Be $true
            $params['Bold'] | Should -Be $true
        }

        It 'Excludes Bold when set to false' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.Bold = $false
            $params = $style.ToWriteColorParams()

            $params.ContainsKey('Bold') | Should -Be $false
        }

        It 'Includes multiple style properties' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.Bold = $true
            $style.Italic = $true
            $style.Underline = $true
            $params = $style.ToWriteColorParams()

            $params['Bold'] | Should -Be $true
            $params['Italic'] | Should -Be $true
            $params['Underline'] | Should -Be $true
        }

        It 'Includes formatting properties when set' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.StartTab = 2
            $style.LinesBefore = 1
            $style.LinesAfter = 1
            $params = $style.ToWriteColorParams()

            $params['StartTab'] | Should -Be 2
            $params['LinesBefore'] | Should -Be 1
            $params['LinesAfter'] | Should -Be 1
        }

        It 'Excludes formatting properties when zero' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $params = $style.ToWriteColorParams()

            $params.ContainsKey('StartTab') | Should -Be $false
            $params.ContainsKey('LinesBefore') | Should -Be $false
            $params.ContainsKey('LinesAfter') | Should -Be $false
        }

        It 'Includes Gradient when set' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.Gradient = @('Red', 'Blue')
            $params = $style.ToWriteColorParams()

            $params.ContainsKey('Gradient') | Should -Be $true
            $params['Gradient'].Count | Should -Be 2
        }

        It 'Excludes Gradient when less than 2 colors' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.Gradient = @('Red')
            $params = $style.ToWriteColorParams()

            $params.ContainsKey('Gradient') | Should -Be $false
        }

        It 'Caches parameters for performance' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $style.Bold = $true

            $params1 = $style.ToWriteColorParams()
            $params2 = $style.ToWriteColorParams()

            # Both should have the same values but be different instances (cloned)
            $params1['Bold'] | Should -Be $params2['Bold']
            $params1 -eq $params2 | Should -Be $false
        }

        It 'Returns clone to prevent external modification' {
            $style = [PSColorStyle]::new('TestStyle', 'Red', $null)
            $params = $style.ToWriteColorParams()

            # Modify returned params
            $params['Color'] = 'Blue'

            # Original should still be Red
            $params2 = $style.ToWriteColorParams()
            $params2['Color'] | Should -Be 'Red'
        }

        It 'Lets a subclass''s own ToWriteColorParams decide what -StyleProfile applies' {
            $esc = [string][char]27
            # The class is defined at run time, once the module has made PSColorStyle
            $style = . ([scriptblock]::Create(@'
class ColorStyleSubclassProbe : PSColorStyle {
    ColorStyleSubclassProbe() : base('Probe', 'Red', $null) { }
    [hashtable] ToWriteColorParams() { return @{ Color = 'Blue' } }
}
[ColorStyleSubclassProbe]::new()
'@
))

            Format-ColorEX -Text 'ab' -StyleProfile $style -ColorSupport ANSI4 | Should -Be "$esc[94mab$esc[0m"
        }
    }

    Context 'Clone Method' {
        It 'Creates a copy of the style' {
            $original = [PSColorStyle]::new('Original', 'Red', 'Blue')
            $original.Bold = $true
            $original.StartTab = 2

            $clone = $original.Clone()

            $clone.Name | Should -Be 'Original_Copy'
            $clone.ForegroundColor | Should -Be 'Red'
            $clone.BackgroundColor | Should -Be 'Blue'
            $clone.Bold | Should -Be $true
            $clone.StartTab | Should -Be 2
        }

        It 'Creates independent copy that can be modified' {
            $original = [PSColorStyle]::new('Original', 'Red', $null)
            $clone = $original.Clone()

            $clone.ForegroundColor = 'Blue'

            $original.ForegroundColor | Should -Be 'Red'
            $clone.ForegroundColor | Should -Be 'Blue'
        }

        It 'Clone does not share cached parameters' {
            $original = [PSColorStyle]::new('Original', 'Red', $null)
            $original.Bold = $true

            # Warm cache
            $null = $original.ToWriteColorParams()

            $clone = $original.Clone()

            # Clone should have null cache initially
            $clone.Bold = $false
            $params = $clone.ToWriteColorParams()
            $params.ContainsKey('Bold') | Should -Be $false
        }

        It 'Copies all style properties' {
            $original = [PSColorStyle]::new('Original', 'Red', 'Blue')
            $original.Bold = $true
            $original.Italic = $true
            $original.Underline = $true
            $original.Blink = $true
            $original.Faint = $true
            $original.CrossedOut = $true
            $original.DoubleUnderline = $true
            $original.Overline = $true

            $clone = $original.Clone()

            $clone.Bold | Should -Be $true
            $clone.Italic | Should -Be $true
            $clone.Underline | Should -Be $true
            $clone.Blink | Should -Be $true
            $clone.Faint | Should -Be $true
            $clone.CrossedOut | Should -Be $true
            $clone.DoubleUnderline | Should -Be $true
            $clone.Overline | Should -Be $true
        }

        It 'Copies Gradient array' {
            $original = [PSColorStyle]::new('Original', 'Red', $null)
            $original.Gradient = @('Red', 'Blue', 'Green')

            $clone = $original.Clone()

            $clone.Gradient.Count | Should -Be 3
            $clone.Gradient[0] | Should -Be 'Red'
            $clone.Gradient[1] | Should -Be 'Blue'
            $clone.Gradient[2] | Should -Be 'Green'
        }
    }

    Context 'InitializeDefaultProfiles Static Method' {
        It 'Initializes all default profiles' {
            # This is called during module load, so profiles should exist
            [PSColorStyle]::Profiles.Count | Should -BeGreaterOrEqual 7
        }

        It 'Error profile has correct properties' {
            $errorProfile = [PSColorStyle]::GetProfile('Error')

            $errorProfile.Name | Should -Be 'Error'
            $errorProfile.ForegroundColor | Should -Be 'Red'
            $errorProfile.Bold | Should -Be $true
        }

        It 'Warning profile has correct properties' {
            $warningProfile = [PSColorStyle]::GetProfile('Warning')

            $warningProfile.Name | Should -Be 'Warning'
            $warningProfile.ForegroundColor | Should -Be 'Yellow'
        }

        It 'Info profile has correct properties' {
            $infoProfile = [PSColorStyle]::GetProfile('Info')

            $infoProfile.Name | Should -Be 'Info'
            $infoProfile.ForegroundColor | Should -Be 'Cyan'
        }

        It 'Success profile has correct properties' {
            $successProfile = [PSColorStyle]::GetProfile('Success')

            $successProfile.Name | Should -Be 'Success'
            $successProfile.ForegroundColor | Should -Be 'Green'
        }

        It 'Critical profile has correct properties' {
            $criticalProfile = [PSColorStyle]::GetProfile('Critical')

            $criticalProfile.Name | Should -Be 'Critical'
            $criticalProfile.ForegroundColor | Should -Be 'White'
            $criticalProfile.BackgroundColor | Should -Be 'DarkRed'
            $criticalProfile.Bold | Should -Be $true
            $criticalProfile.Blink | Should -Be $true
        }

        It 'Debug profile has correct properties' {
            $debugProfile = [PSColorStyle]::GetProfile('Debug')

            $debugProfile.Name | Should -Be 'Debug'
            $debugProfile.ForegroundColor | Should -Be 'DarkGray'
            $debugProfile.Italic | Should -Be $true
        }

        It 'Default profile returns its parameters' {
            $defaultProfile = [PSColorStyle]::GetProfile('Default')
            $params = $defaultProfile.ToWriteColorParams()

            $params | Should -Not -BeNullOrEmpty
        }
    }

    Context 'Parameters follow property changes' {
        It 'ToWriteColorParams reflects a change to Bold' {
            $style = [PSColorStyle]::new('Test', 'Red', $null)
            $style.ToWriteColorParams().ContainsKey('Bold') | Should -Be $false

            $style.Bold = $true

            $style.ToWriteColorParams().Bold | Should -Be $true
        }

        It 'ToWriteColorParams reflects a change to ForegroundColor' {
            $style = [PSColorStyle]::new('Test', 'Red', $null)
            $style.ToWriteColorParams().Color | Should -Be 'Red'

            $style.ForegroundColor = 'Blue'

            $style.ToWriteColorParams().Color | Should -Be 'Blue'
        }

        It 'ToWriteColorParams reflects a change to BackgroundColor' {
            $style = [PSColorStyle]::new('Test', 'Red', 'Yellow')
            $style.ToWriteColorParams().BackGroundColor | Should -Be 'Yellow'

            $style.BackgroundColor = 'Green'

            $style.ToWriteColorParams().BackGroundColor | Should -Be 'Green'
        }

        It 'ToWriteColorParams reflects changes to several properties' {
            $style = [PSColorStyle]::new('Test', 'Red', $null)
            $null = $style.ToWriteColorParams()

            $style.Bold = $true
            $style.Italic = $true
            $style.Underline = $true
            $style.StartTab = 2

            $params = $style.ToWriteColorParams()
            $params.Bold | Should -Be $true
            $params.Italic | Should -Be $true
            $params.Underline | Should -Be $true
            $params.StartTab | Should -Be 2
        }

        It 'Each call returns a new hashtable' {
            $style = [PSColorStyle]::new('Test', 'Red', $null)
            $first = $style.ToWriteColorParams()
            $first['Color'] = 'Green'

            $style.ToWriteColorParams().Color | Should -Be 'Red'
        }

        It 'InvalidateCache can still be called' {
            $style = [PSColorStyle]::new('Test', 'Red', $null)

            { $style.InvalidateCache() } | Should -Not -Throw
            $style.ToWriteColorParams().Color | Should -Be 'Red'
        }

        It 'A change to a built-in profile reaches its helper' {
            $errorProfile = [PSColorStyle]::GetProfile('Error')
            $saved = $errorProfile.ForegroundColor
            try {
                $errorProfile.ForegroundColor = 'Magenta'

                [PSColorStyle]::GetProfile('Error').ToWriteColorParams().Color | Should -Be 'Magenta'
            } finally {
                $errorProfile.ForegroundColor = $saved
            }
        }

        It 'SetAsDefault keeps the parameters' {
            $originalDefault = [PSColorStyle]::Default
            $style = [PSColorStyle]::new('Test', 'Cyan', $null)
            $style.Bold = $true

            try {
                $style.SetAsDefault()

                $params = $style.ToWriteColorParams()
                $params | Should -Not -BeNullOrEmpty
                $params.Bold | Should -Be $true
            } finally {
                $originalDefault.SetAsDefault()
            }
        }

        It 'AddToProfiles keeps the parameters' {
            $style = [PSColorStyle]::new('ParamsTest', 'Magenta', $null)
            $style.Italic = $true

            $style.AddToProfiles()

            $params = $style.ToWriteColorParams()
            $params | Should -Not -BeNullOrEmpty
            $params.Italic | Should -Be $true

            [PSColorStyle]::Profiles.Remove('ParamsTest')
        }

        It 'A clone changes apart from the original' {
            $original = [PSColorStyle]::new('Original', 'Red', $null)
            $original.Bold = $true

            $clone = $original.Clone()
            $clone.Bold = $false

            $original.ToWriteColorParams().Bold | Should -Be $true
            # Bold = $false leaves the key out
            $clone.ToWriteColorParams().ContainsKey('Bold') | Should -Be $false
        }

        It 'A clone has its own arrays' {
            $original = [PSColorStyle]::new('Original', @(255, 0, 0), $null)
            $original.Gradient = @('Red', 'Blue')
            $original.Style = @('Bold', 'Italic')

            $clone = $original.Clone()
            $clone.Gradient[0] = 'Green'
            $clone.Style[0] = 'Underline'
            $clone.ForegroundColor[0] = 0

            $original.Gradient[0] | Should -Be 'Red'
            $original.Style[0] | Should -Be 'Bold'
            $original.ForegroundColor[0] | Should -Be 255
        }
    }
}

Describe 'PSColorStyle across runspaces' -Tag 'Unit', 'Class' {
    BeforeAll {
        $script:manifest = Join-Path $ModuleRoot 'PSWriteColorEX.psd1'
        $script:esc = [string][char]27

        # Imports the module in a runspace of its own, runs the script given there and disposes the
        # runspace, returning what the script wrote
        function Invoke-InOtherRunspace {
            param([string]$Script)
            $shell = [powershell]::Create()
            try {
                $null = $shell.AddScript("Import-Module '$script:manifest'; $Script")
                $shell.Invoke()
            } finally {
                $shell.Dispose()
            }
        }
    }

    It 'Keeps the profiles added since and puts back the built-in ones when the module is imported again' {
        $null = New-ColorStyle -Name 'Kept' -ForegroundColor Magenta -AddToProfiles
        [PSColorStyle]::Profiles['Info'].ForegroundColor = 'DarkRed'
        try {
            Import-Module $script:manifest -Force
            [PSColorStyle]::Profiles.ContainsKey('Kept') | Should -BeTrue
            [PSColorStyle]::Profiles['Info'].ForegroundColor | Should -Be 'Cyan'
        } finally {
            [PSColorStyle]::Profiles.Remove('Kept')
        }
    }

    It 'Keeps working after another runspace that imported the module is disposed' {
        $null = Invoke-InOtherRunspace "(Get-GradientColor -Gradient Progress -Place 0.5) -join ','"

        (Get-GradientColor -Gradient Progress -Place 0.5) -join ',' | Should -Be '255,165,0'
        Format-ColorEX -Text 'ab' -Color Red -ColorSupport ANSI8 | Should -Be "$esc[91mab$esc[0m"
        [PSColorStyle]::GetProfile('Info').Name | Should -Be 'Info'
        [PSColorStyle]::Profiles['Error'].ToWriteColorParams().Color | Should -Be 'Red'
        [PSColorStyle]::Profiles['Error'].Clone().Name | Should -Be 'Error_Copy'
        [PSColorStyle]::IsGradient(@('Sunset')) | Should -BeTrue
    }

    It 'Keeps the methods of a style made in a runspace that is then disposed' {
        $style = Invoke-InOtherRunspace "`$made = [PSColorStyle]::new('Made', 'Red', `$null); `$made.Bold = `$true; `$made"

        $style.ToWriteColorParams().Bold | Should -BeTrue
        $style.Clone().Name | Should -Be 'Made_Copy'
        Format-ColorEX -Text 'ab' -StyleProfile $style -ColorSupport ANSI4 | Should -Be "$esc[1m$esc[91mab$esc[0m"
    }

    It 'Lets eight runspaces of a new process import the module at the same moment, all with one PSColorStyle' {
        # A new process, as the module's first import in a process is the one that compiles it
        $harness = Join-Path $TestDrive 'concurrent-import.ps1'
        $output = Join-Path $TestDrive 'concurrent-import.txt'
        Set-Content -LiteralPath $harness -Value @'
param($Manifest)
# All eight runspaces open first, and the imports start once every one is waiting to begin
$pool = [runspacefactory]::CreateRunspacePool(8, 8)
$pool.Open()
$ready = [System.Threading.CountdownEvent]::new(8)
$gate = [System.Threading.ManualResetEventSlim]::new($false)
$jobs = foreach ($n in 1..8) {
    $shell = [powershell]::Create()
    $shell.RunspacePool = $pool
    $null = $shell.AddScript({
            param($Manifest, $Ready, $Gate)
            $Ready.Signal()
            $null = $Gate.Wait(30000)
            Import-Module $Manifest
            [System.Runtime.CompilerServices.RuntimeHelpers]::GetHashCode([type]'PSColorStyle')
        }).AddArgument($Manifest).AddArgument($ready).AddArgument($gate)
    [pscustomobject]@{ Shell = $shell; Handle = $shell.BeginInvoke() }
}
$null = $ready.Wait(30000)
$gate.Set()
foreach ($job in $jobs) { $job.Shell.EndInvoke($job.Handle) }
'@

        $process = Start-Process -FilePath (Get-Process -Id $PID).Path -NoNewWindow -PassThru -RedirectStandardOutput $output `
            -ArgumentList '-NoProfile', '-NonInteractive', '-File', "`"$harness`"", "`"$script:manifest`""
        $finished = $process.WaitForExit(90000)
        if (-not $finished) {
            $process.Kill()
        }

        $finished | Should -BeTrue
        $types = @(Get-Content -LiteralPath $output | Where-Object { $_ -match '^\d+$' })
        $types | Should -HaveCount 8
        @($types | Sort-Object -Unique) | Should -HaveCount 1
    }

    It 'Says the module is not imported when a runspace without it calls a method' {
        $shell = [powershell]::Create()
        try {
            $null = $shell.AddScript("try { [PSColorStyle]::GetProfile('Info') } catch { `$_.Exception.Message }")
            $message = $shell.Invoke()
        } finally {
            $shell.Dispose()
        }

        "$message" | Should -Match 'PSWriteColorEX is not imported in this runspace'
    }

    It 'Has the members the class of version 1.2.0 had' {
        $object = [PSColorStyle]::new()
        ($object | Get-Member -MemberType Property) | Should -HaveCount 36
        ($object | Get-Member -MemberType Method | Where-Object Name -NotIn 'Equals', 'GetHashCode', 'GetType', 'ToString').Definition |
            Should -Be @('void AddToProfiles()', 'PSColorStyle Clone()', 'void SetAsDefault()', 'hashtable ToWriteColorParams()')
        ([PSColorStyle] | Get-Member -Static -MemberType Property).Name | Should -Be @('Default', 'Profiles')
        ([PSColorStyle] | Get-Member -Static -MemberType Method | Where-Object Name -NotIn 'Equals', 'ReferenceEquals', 'new').Definition |
            Should -Be @('static PSColorStyle GetProfile(string name)', 'static void InitializeDefaultProfiles()')
        ([PSColorStyle] | Get-Member -Static -Name new).Definition |
            Should -Be 'PSColorStyle new(), PSColorStyle new(string name), PSColorStyle new(string name, System.Object foreground, System.Object background)'
    }
}