mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-08-10 01:51:18 +10:00
* Harden Pester CI and discard runspace return values * Add prioritized test backlog * Add WinUtil action logging * Add config integrity tests * Add XAML control wiring tests * Add registry and service helper tests * Add package manager tests * Add runspace behavior tests * Add tweak orchestration tests * Add install workflow tests * Add update profile tests * Add AppX removal tests * Log package installer output * Add UI state helper tests * Pin Pester test runner version * Expand Win11 Creator tests * Add preferences and theme tests * Add search filter tests * Add compile contract tests * Use single WinUtil session log * Remove installer output logging helper * Handle locked WinUtil transcript log * Avoid appending to active transcript log * Log install uninstall package identities
348 lines
11 KiB
PowerShell
348 lines
11 KiB
PowerShell
#===========================================================================
|
|
# Tests - Preferences and Themes
|
|
#===========================================================================
|
|
|
|
BeforeAll {
|
|
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|
|
|
if (-not ("PackageManagers" -as [type])) {
|
|
Add-Type @"
|
|
public enum PackageManagers
|
|
{
|
|
Winget,
|
|
Choco
|
|
}
|
|
"@
|
|
}
|
|
|
|
if (-not ("Windows.Media.SolidColorBrush" -as [type])) {
|
|
Add-Type @"
|
|
namespace Windows.Media
|
|
{
|
|
public class SolidColorBrush
|
|
{
|
|
public object Value { get; private set; }
|
|
|
|
public SolidColorBrush(object value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Value == null ? "" : Value.ToString();
|
|
}
|
|
}
|
|
|
|
public struct Color
|
|
{
|
|
public byte R;
|
|
public byte G;
|
|
public byte B;
|
|
|
|
public static Color FromRgb(byte r, byte g, byte b)
|
|
{
|
|
return new Color { R = r, G = g, B = b };
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("#{0:X2}{1:X2}{2:X2}", R, G, B);
|
|
}
|
|
}
|
|
|
|
public class FontFamily
|
|
{
|
|
public string Value { get; private set; }
|
|
|
|
public FontFamily(string value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace System.Windows
|
|
{
|
|
public class CornerRadius
|
|
{
|
|
public double Value { get; private set; }
|
|
|
|
public CornerRadius(double value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Value.ToString();
|
|
}
|
|
}
|
|
|
|
public class GridLength
|
|
{
|
|
public double Value { get; private set; }
|
|
|
|
public GridLength(double value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Value.ToString();
|
|
}
|
|
}
|
|
|
|
public class Thickness
|
|
{
|
|
public double Left { get; private set; }
|
|
public double Top { get; private set; }
|
|
public double Right { get; private set; }
|
|
public double Bottom { get; private set; }
|
|
|
|
public Thickness(double uniformLength)
|
|
{
|
|
Left = uniformLength;
|
|
Top = uniformLength;
|
|
Right = uniformLength;
|
|
Bottom = uniformLength;
|
|
}
|
|
|
|
public Thickness(double horizontal, double vertical)
|
|
{
|
|
Left = horizontal;
|
|
Top = vertical;
|
|
Right = horizontal;
|
|
Bottom = vertical;
|
|
}
|
|
|
|
public Thickness(double left, double top, double right, double bottom)
|
|
{
|
|
Left = left;
|
|
Top = top;
|
|
Right = right;
|
|
Bottom = bottom;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("{0},{1},{2},{3}", Left, Top, Right, Bottom);
|
|
}
|
|
}
|
|
}
|
|
"@
|
|
}
|
|
|
|
. (Join-Path $script:repoRoot "functions\private\Set-Preferences.ps1")
|
|
. (Join-Path $script:repoRoot "functions\private\Invoke-WinutilThemeChange.ps1")
|
|
|
|
function Get-WinUtilToggleStatus {
|
|
param($ToggleName)
|
|
return $false
|
|
}
|
|
|
|
function script:New-WinUtilPreferencesTestRoot {
|
|
$testRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilPreferences_$([guid]::NewGuid())"
|
|
New-Item -Path $testRoot -ItemType Directory -Force | Out-Null
|
|
$testRoot
|
|
}
|
|
|
|
function script:New-WinUtilFakeThemeForm {
|
|
$themeButton = [pscustomobject]@{
|
|
Content = $null
|
|
}
|
|
|
|
$form = [pscustomobject]@{
|
|
Resources = @{}
|
|
ThemeButton = $themeButton
|
|
}
|
|
$form | Add-Member -MemberType ScriptMethod -Name FindName -Value {
|
|
param($name)
|
|
|
|
if ($name -eq "ThemeButton") {
|
|
return $this.ThemeButton
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
$form
|
|
}
|
|
|
|
function script:New-WinUtilPreferenceSync {
|
|
param(
|
|
[hashtable]$Preferences = @{}
|
|
)
|
|
|
|
$script:sync = [Hashtable]::Synchronized(@{
|
|
preferences = $Preferences
|
|
configs = @{
|
|
themes = Get-Content -Path (Join-Path $script:repoRoot "config\themes.json") -Raw | ConvertFrom-Json
|
|
}
|
|
Form = New-WinUtilFakeThemeForm
|
|
})
|
|
$global:sync = $script:sync
|
|
}
|
|
|
|
function script:Remove-WinUtilPreferenceGlobals {
|
|
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
|
Remove-Variable -Name winutildir -Scope Global -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
Describe "Set-Preferences" {
|
|
AfterEach {
|
|
if ($script:testRoot -and (Test-Path $script:testRoot)) {
|
|
Remove-Item -Path $script:testRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
$script:testRoot = $null
|
|
Remove-WinUtilPreferenceGlobals
|
|
}
|
|
|
|
It "loads default preferences when no preferences file exists" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync
|
|
|
|
Set-Preferences
|
|
|
|
$script:sync.preferences.theme | Should -Be "Auto"
|
|
$script:sync.preferences.packagemanager | Should -Be ([PackageManagers]::Winget)
|
|
}
|
|
|
|
It "loads saved preferences and converts the package manager to an enum" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync
|
|
Set-Content -Path (Join-Path $script:testRoot "preferences.ini") -Value @(
|
|
"theme = Dark"
|
|
"packagemanager = Choco"
|
|
)
|
|
|
|
Set-Preferences
|
|
|
|
$script:sync.preferences.theme | Should -Be "Dark"
|
|
$script:sync.preferences.packagemanager | Should -Be ([PackageManagers]::Choco)
|
|
}
|
|
|
|
It "saves current preferences to preferences.ini" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync -Preferences @{
|
|
theme = "Light"
|
|
packagemanager = [PackageManagers]::Winget
|
|
}
|
|
|
|
Set-Preferences -save
|
|
|
|
$preferencesText = Get-Content -Path (Join-Path $script:testRoot "preferences.ini") -Raw
|
|
$preferencesText | Should -Match "theme=Light"
|
|
$preferencesText | Should -Match "packagemanager=Winget"
|
|
}
|
|
|
|
It "absorbs legacy preference files and removes old markers" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync
|
|
Set-Content -Path (Join-Path $script:testRoot "LightTheme.ini") -Value ""
|
|
Set-Content -Path (Join-Path $script:testRoot "preferChocolatey.ini") -Value ""
|
|
|
|
Set-Preferences
|
|
|
|
$script:sync.preferences.theme | Should -Be "Light"
|
|
$script:sync.preferences.packagemanager | Should -Be ([PackageManagers]::Choco)
|
|
Test-Path (Join-Path $script:testRoot "LightTheme.ini") | Should -BeFalse
|
|
Test-Path (Join-Path $script:testRoot "preferChocolatey.ini") | Should -BeFalse
|
|
}
|
|
|
|
It "absorbs old package manager preferences stored without a key" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync
|
|
Set-Content -Path (Join-Path $script:testRoot "preferences.ini") -Value "Choco"
|
|
|
|
Set-Preferences
|
|
|
|
$script:sync.preferences.theme | Should -Be "Auto"
|
|
$script:sync.preferences.packagemanager | Should -Be ([PackageManagers]::Choco)
|
|
}
|
|
}
|
|
|
|
Describe "Invoke-WinutilThemeChange" {
|
|
AfterEach {
|
|
if ($script:testRoot -and (Test-Path $script:testRoot)) {
|
|
Remove-Item -Path $script:testRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
$script:testRoot = $null
|
|
Remove-WinUtilPreferenceGlobals
|
|
}
|
|
|
|
It "applies shared and selected theme resources, saves the preference, and updates the theme button" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync
|
|
|
|
Invoke-WinutilThemeChange -theme "Dark"
|
|
|
|
$script:sync.preferences.theme | Should -Be "Dark"
|
|
$script:sync.Form.Resources.ContainsKey("FontFamily") | Should -BeTrue
|
|
$script:sync.Form.Resources.ContainsKey("ButtonCornerRadius") | Should -BeTrue
|
|
$script:sync.Form.Resources.ContainsKey("MainBackgroundColor") | Should -BeTrue
|
|
$script:sync.Form.Resources.ContainsKey("CBorderColor") | Should -BeTrue
|
|
$script:sync.Form.ThemeButton.Content | Should -Be ([string][char]0xE708)
|
|
|
|
$preferencesText = Get-Content -Path (Join-Path $script:testRoot "preferences.ini") -Raw
|
|
$preferencesText | Should -Match "theme=Dark"
|
|
}
|
|
|
|
It "uses the system dark-mode toggle when Auto theme is selected" {
|
|
$script:testRoot = New-WinUtilPreferencesTestRoot
|
|
$global:winutildir = $script:testRoot
|
|
New-WinUtilPreferenceSync
|
|
Mock Get-WinUtilToggleStatus { return $true }
|
|
|
|
Invoke-WinutilThemeChange -theme "Auto"
|
|
|
|
Should -Invoke -CommandName Get-WinUtilToggleStatus -Times 1 -Exactly -ParameterFilter {
|
|
$ToggleName -eq "WPFToggleDarkMode"
|
|
}
|
|
$script:sync.preferences.theme | Should -Be "Auto"
|
|
$script:sync.Form.Resources.ContainsKey("MainBackgroundColor") | Should -BeTrue
|
|
$script:sync.Form.ThemeButton.Content | Should -Be ([string][char]0xF08C)
|
|
}
|
|
}
|
|
|
|
Describe "Theme configuration" {
|
|
It "contains resources with value shapes consumed by theme application" {
|
|
$themes = Get-Content -Path (Join-Path $script:repoRoot "config\themes.json") -Raw | ConvertFrom-Json
|
|
$invalidEntries = [System.Collections.Generic.List[string]]::new()
|
|
|
|
foreach ($themeName in @("shared", "Light", "Dark")) {
|
|
foreach ($property in $themes.$themeName.PSObject.Properties) {
|
|
if ($property.Name -like "*Color" -and [string]$property.Value -notmatch '^(#[0-9A-Fa-f]{6}|Transparent)$') {
|
|
$invalidEntries.Add("$themeName.$($property.Name) should be a hex color or Transparent")
|
|
}
|
|
elseif ($property.Name -like "*Radius" -and [string]$property.Value -notmatch '^\d+(\.\d+)?$') {
|
|
$invalidEntries.Add("$themeName.$($property.Name) should be numeric")
|
|
}
|
|
elseif (($property.Name -like "*Thickness" -or $property.Name -like "*Margin") -and [string]$property.Value -notmatch '^\d+(\.\d+)?(,\d+(\.\d+)?){0,3}$') {
|
|
$invalidEntries.Add("$themeName.$($property.Name) should be one, two, or four numeric values")
|
|
}
|
|
elseif ($property.Name -like "*RowHeight*" -and [string]$property.Value -notmatch '^\d+(\.\d+)?$') {
|
|
$invalidEntries.Add("$themeName.$($property.Name) should be numeric")
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($invalidEntries.Count -gt 0) {
|
|
throw ($invalidEntries -join "`n")
|
|
}
|
|
}
|
|
}
|