2026-07-19 22:06:07 +02:00
BeforeAll {
function Wait-ForKeyPress {}
function Test-AppInWingetList { param ( $appId , $InstalledList ) $false }
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-JsonFile.ps1' )
2026-10-02 00:55:34 +05:30
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-LanguageFile.ps1' )
2026-07-19 22:06:07 +02:00
. ( Join-Path $PSScriptRoot '..\Scripts\Helpers\Add-Parameter.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Save-ToFile.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-Settings.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Save-Settings.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-AppsFromFile.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-AppDetailsFromJson.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Import-AppPresetsFromJson.ps1' )
. ( Join-Path $PSScriptRoot '..\Scripts\FileIO\Get-ValidatedAppList.ps1' )
$script:JsonFixturePath = Join-Path $PSScriptRoot 'TestData\JsonFileLoading'
2026-10-02 00:55:34 +05:30
$script:LanguagesPath = Join-Path $PSScriptRoot '..\Config\Languages'
$script:Lang = Import-LanguageFile -LanguageCode 'en-US'
2026-07-19 22:06:07 +02:00
}
2026-08-12 17:31:50 +02:00
Describe 'Save-ToFile' {
It 'reports the write error and returns false when persistence fails' {
Mock Set-Content { throw [ System.IO.IOException ]:: new ( 'Disk is full' ) }
Mock Write-Error {}
Save-ToFile -Config @ { Setting = $true } -FilePath ( Join-Path $TestDrive 'settings.json' ) | Should -BeFalse
Should -Invoke Write-Error -Times 1 -Exactly -ParameterFilter {
$Message -eq "Failed to write ' $TestDrive \settings.json': Disk is full"
}
}
}
2026-07-19 22:06:07 +02:00
Describe 'Import-Settings' {
BeforeEach {
$script:Params = @ {}
$script:ModernStandbySupported = $false
$script:Features = @ {
Supported = [ PSCustomObject ] @ { FeatureId = 'Supported' ; MinVersion = 22000 ; MaxVersion = 30000 }
TooNew = [ PSCustomObject ] @ { FeatureId = 'TooNew' ; MinVersion = 99999 ; MaxVersion = $null }
DisableModernStandbyNetworking = [ PSCustomObject ] @ { FeatureId = 'DisableModernStandbyNetworking' ; MinVersion = $null ; MaxVersion = $null }
}
Mock Get-ItemPropertyValue { 22631 }
Mock Add-Parameter {}
Mock Write-Error {}
}
It 'loads enabled, known, compatible settings and skips all other entries' {
Import-Settings -filePath ( Join-Path $script:JsonFixturePath 'DefaultSettings.Valid.json' )
Should -Invoke Add-Parameter -Times 1 -Exactly -ParameterFilter { $parameterName -eq 'Supported' -and $value -eq 'configured' }
}
It 'throws when <Case>' -ForEach @ (
@ { Case = 'the last-used settings JSON is invalid' ; FileName = 'LastUsedSettings.Invalid.json' ; WriteErrorCalls = 1 }
@ { Case = 'the default settings file has no Settings property' ; FileName = 'DefaultSettings.MissingSettings.json' ; WriteErrorCalls = 0 }
) {
$path = Join-Path $script:JsonFixturePath $FileName
{
& {
$ErrorActionPreference = 'Continue'
Import-Settings -filePath $path
}
} | Should -Throw "Failed to load settings from $FileName "
Should -Invoke Add-Parameter -Times 0 -Exactly
Should -Invoke Write-Error -Times $WriteErrorCalls -Exactly
}
}
Describe 'Save-Settings' {
BeforeEach {
$script:SavedSettingsFilePath = Join-Path $TestDrive 'LastUsedSettings.json'
$script:ControlParams = @ ( 'Silent' , 'WhatIf' )
$script:Features = @ { Feature = [ PSCustomObject ] @ { FeatureId = 'Feature' } }
Mock Save-ToFile { $true }
Mock Write-Host {}
Mock Write-Output {}
}
It 'saves only feature parameters and excludes control or unknown parameters' {
$script:Params = @ { Feature = 'configured' ; Silent = $true ; Unknown = 42 }
Save-Settings
Should -Invoke Save-ToFile -Times 1 -Exactly -ParameterFilter {
$FilePath -eq $script:SavedSettingsFilePath -and
@ ( $Config . Settings ). Count -eq 1 -and
$Config . Settings [ 0 ]. Name -eq 'Feature' -and
$Config . Settings [ 0 ]. Value -eq 'configured'
}
}
It 'does not persist in WhatIf mode' {
$script:Params = @ { Feature = $true ; WhatIf = $true }
Save-Settings
Should -Invoke Save-ToFile -Times 0 -Exactly
}
It 'reports a persistence failure without throwing' {
$script:Params = @ { Feature = $true }
Mock Save-ToFile { $false }
{ Save-Settings } | Should -Not -Throw
Should -Invoke Write-Host -Times 1 -Exactly -ParameterFilter { $Object -like 'Error:*' }
}
}
Describe 'Import-AppsFromFile' {
It 'returns selected IDs, trims whitespace, and supports scalar and array AppId values' {
$path = Join-Path $TestDrive 'apps.json'
'{"Apps":[{"AppId":" One.App ","SelectedByDefault":true},{"AppId":["Two.App"," "],"SelectedByDefault":true},{"AppId":"Ignored.App","SelectedByDefault":false}]}' |
Set-Content -LiteralPath $path -Encoding UTF8
@ ( Import-AppsFromFile -appsFilePath $path ) | Should -Be @ ( 'One.App' , 'Two.App' )
}
It 'returns an empty collection for a missing file' {
@ ( Import-AppsFromFile -appsFilePath ( Join-Path $TestDrive 'missing.json' )) | Should -HaveCount 0
}
It 'reports invalid JSON and invokes the CLI acknowledgement hook' {
$path = Join-Path $TestDrive 'invalid-apps.json'
'not json' | Set-Content -LiteralPath $path
Mock Write-Error {}
Mock Wait-ForKeyPress {}
@ ( Import-AppsFromFile -appsFilePath $path ) | Should -HaveCount 0
Should -Invoke Write-Error -Times 1 -Exactly
Should -Invoke Wait-ForKeyPress -Times 1 -Exactly
}
}
Describe 'Get-ValidatedAppList' {
It 'normalizes wildcard selections and skips unsupported applications' {
Mock Import-AppDetailsFromJson { @ ([ PSCustomObject ] @ { AppId = @ ( 'One.App' , 'Two.App' ) }) }
Mock Write-Host {}
@ ( Get-ValidatedAppList -appsList @ ( '*One.App*' , ' Missing.App ' )) | Should -Be @ ( 'One.App' )
Should -Invoke Write-Host -Times 1 -Exactly -ParameterFilter { $Object -like "*Missing.App*" }
}
}
Describe 'Import-AppDetailsFromJson' {
BeforeEach {
$script:AppsListFilePath = Join-Path $TestDrive 'Apps.json'
'{"Apps":[{"AppId":["One.App","Alias.App"],"FriendlyName":"One","SelectedByDefault":true,"RemovalMethod":"WinGet"},{"AppId":"Two.App","SelectedByDefault":false},{"AppId":" ","SelectedByDefault":true}],"Presets":[{"Name":"Minimal","AppIds":["One.App"]}]}' |
Set-Content -LiteralPath $script:AppsListFilePath -Encoding UTF8
}
It 'projects app metadata, defaults removal method, and honors initial checked state' {
$apps = @ ( Import-AppDetailsFromJson -InitialCheckedFromJson )
$apps | Should -HaveCount 2
2026-10-02 00:55:34 +05:30
# 'One.App' has no en-US Apps.json entry, so Get-Translation falls back to the raw key.
$apps [ 0 ]. DisplayName | Should -Be 'One.App (One.App, Alias.App)'
2026-07-19 22:06:07 +02:00
$apps [ 0 ]. IsChecked | Should -BeTrue
$apps [ 0 ]. RemovalMethod | Should -Be 'WinGet'
$apps [ 1 ]. FriendlyName | Should -Be 'Two.App'
$apps [ 1 ]. RemovalMethod | Should -Be 'Appx'
$apps [ 1 ]. AppId -is [ array ] | Should -BeTrue
@ ( $apps [ 1 ]. AppId ) | Should -HaveCount 1
}
2026-10-02 00:55:34 +05:30
It 'resolves FriendlyName and Description from Config/Languages/en-US/Apps.json for a real AppId' {
'{"Apps":[{"AppId":"Clipchamp.Clipchamp","FriendlyName":"stale JSON name, should not appear","Description":"stale JSON description, should not appear"}]}' |
Set-Content -LiteralPath $script:AppsListFilePath -Encoding UTF8
$apps = @ ( Import-AppDetailsFromJson )
$apps | Should -HaveCount 1
$apps [ 0 ]. FriendlyName | Should -Be 'Clipchamp'
$apps [ 0 ]. Description | Should -Be 'Video editor from Microsoft'
$apps [ 0 ]. DisplayName | Should -Be 'Clipchamp (Clipchamp.Clipchamp)'
}
2026-07-19 22:06:07 +02:00
It 'skips missing, blank, and non-string app IDs without failing the complete catalog' {
'{"Apps":[{"AppId":null},{"FriendlyName":"Missing"},{"AppId":42},{"AppId":[" Valid.App ",null,{},""]}]}' |
Set-Content -LiteralPath $script:AppsListFilePath -Encoding UTF8
$apps = @ ( Import-AppDetailsFromJson )
$apps | Should -HaveCount 1
$apps [ 0 ]. AppId -is [ array ] | Should -BeTrue
$apps [ 0 ]. AppId | Should -Be @ ( 'Valid.App' )
}
It 'filters to installed apps using Appx and winget detection' {
Mock Get-AppxPackage { param ( $Name ) if ( $Name -eq 'Two.App' ) { [ PSCustomObject ] @ { Name = $Name } } }
Mock Test-AppInWingetList { $false }
$apps = @ ( Import-AppDetailsFromJson -OnlyInstalled -InstalledList @ ())
$apps | Should -HaveCount 1
$apps [ 0 ]. AppId | Should -Be 'Two.App'
}
It 'loads presets and preserves their ID arrays' {
$presets = @ ( Import-AppPresetsFromJson )
$presets | Should -HaveCount 1
$presets [ 0 ]. Name | Should -Be 'Minimal'
$presets [ 0 ]. AppIds -is [ array ] | Should -BeTrue
$presets [ 0 ]. AppIds | Should -Be 'One.App'
}
It 'returns empty collections and reports malformed JSON' {
'not json' | Set-Content -LiteralPath $script:AppsListFilePath
Mock Write-Error {}
Mock Write-Warning {}
@ ( Import-AppDetailsFromJson ) | Should -HaveCount 0
@ ( Import-AppPresetsFromJson ) | Should -HaveCount 0
Should -Invoke Write-Error -Times 1 -Exactly
Should -Invoke Write-Warning -Times 1 -Exactly
}
}