fix: match tweak presets and app-removal scope by stable ID instead of translatable text (#737)

This commit is contained in:
Sashank
2026-08-16 17:52:58 +02:00
committed by GitHub
parent 31feaeb6f5
commit 5072958b10
7 changed files with 262 additions and 30 deletions
+12
View File
@@ -2,50 +2,62 @@
"Version": "1.0", "Version": "1.0",
"Categories": [ "Categories": [
{ {
"CategoryId": "PrivacySuggestedContent",
"Name": "Privacy & Suggested Content", "Name": "Privacy & Suggested Content",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "System",
"Name": "System", "Name": "System",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "StartMenuSearch",
"Name": "Start Menu & Search", "Name": "Start Menu & Search",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "AI",
"Name": "AI", "Name": "AI",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "WindowsUpdate",
"Name": "Windows Update", "Name": "Windows Update",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "Taskbar",
"Name": "Taskbar", "Name": "Taskbar",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "Appearance",
"Name": "Appearance", "Name": "Appearance",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "FileExplorer",
"Name": "File Explorer", "Name": "File Explorer",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "Gaming",
"Name": "Gaming", "Name": "Gaming",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "MultiTasking",
"Name": "Multi-tasking", "Name": "Multi-tasking",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "OptionalWindowsFeatures",
"Name": "Optional Windows Features", "Name": "Optional Windows Features",
"Icon": "" "Icon": ""
}, },
{ {
"CategoryId": "Other",
"Name": "Other", "Name": "Other",
"Icon": "" "Icon": ""
} }
+46 -4
View File
@@ -174,6 +174,10 @@ function Update-AppSelectionStatus {
} }
} }
<#
.SYNOPSIS
Updates the app-removal scope description to match the selected ComboBoxItem.
#>
function Update-AppRemovalScopeDescription { function Update-AppRemovalScopeDescription {
param( param(
[System.Windows.Controls.ComboBox]$AppRemovalScopeCombo, [System.Windows.Controls.ComboBox]$AppRemovalScopeCombo,
@@ -182,20 +186,58 @@ function Update-AppRemovalScopeDescription {
$selectedItem = $AppRemovalScopeCombo.SelectedItem $selectedItem = $AppRemovalScopeCombo.SelectedItem
if ($selectedItem) { if ($selectedItem) {
switch ($selectedItem.Content) { # Content is the display text and will change once translated; Name is stable.
"All users" { switch ($selectedItem.Name) {
"AppRemovalScopeAllUsers" {
$AppRemovalScopeDescription.Text = "Apps will be removed for all users and from the Windows image to prevent reinstallation for new users." $AppRemovalScopeDescription.Text = "Apps will be removed for all users and from the Windows image to prevent reinstallation for new users."
} }
"Current user only" { "AppRemovalScopeCurrentUser" {
$AppRemovalScopeDescription.Text = "Apps will only be removed for the current user." $AppRemovalScopeDescription.Text = "Apps will only be removed for the current user."
} }
"Target user only" { "AppRemovalScopeTargetUser" {
$AppRemovalScopeDescription.Text = "Apps will only be removed for the specified target user." $AppRemovalScopeDescription.Text = "Apps will only be removed for the specified target user."
} }
} }
} }
} }
<#
.SYNOPSIS
Tests whether the app-removal scope combo is currently set to "Target user only".
#>
function Test-AppRemovalScopeTargetsOtherUser {
param(
[System.Windows.Controls.ComboBox]$AppRemovalScopeCombo
)
return ($AppRemovalScopeCombo -and $AppRemovalScopeCombo.SelectedItem -and $AppRemovalScopeCombo.SelectedItem.Name -eq 'AppRemovalScopeTargetUser')
}
<#
.SYNOPSIS
Resolves the -AppRemovalTarget value for the selected app-removal scope.
#>
function Get-AppRemovalScopeTarget {
param(
[System.Windows.Controls.ComboBox]$AppRemovalScopeCombo,
[System.Windows.Controls.TextBox]$OtherUsernameTextBox
)
$selectedItem = $AppRemovalScopeCombo.SelectedItem
if (-not $selectedItem) { return $null }
if (Test-AppRemovalScopeTargetsOtherUser -AppRemovalScopeCombo $AppRemovalScopeCombo) {
return $OtherUsernameTextBox.Text.Trim()
}
switch ($selectedItem.Name) {
"AppRemovalScopeAllUsers" { return 'AllUsers' }
"AppRemovalScopeCurrentUser" { return 'CurrentUser' }
}
return $null
}
function Invoke-AppPreset { function Invoke-AppPreset {
param( param(
[System.Windows.Controls.Panel]$AppsPanel, [System.Windows.Controls.Panel]$AppsPanel,
+12 -9
View File
@@ -219,8 +219,8 @@ function Get-TweakPresetControlMap {
.PARAMETER Window .PARAMETER Window
The window that owns the visible tweak controls. The window that owns the visible tweak controls.
.PARAMETER Category .PARAMETER CategoryId
The category whose mapped controls are included. The stable CategoryId (from Features.json) whose mapped controls are included.
.OUTPUTS .OUTPUTS
System.Collections.Hashtable. Control metadata keyed by control name. System.Collections.Hashtable. Control metadata keyed by control name.
@@ -228,7 +228,7 @@ function Get-TweakPresetControlMap {
function Get-CategoryTweakPresetMap { function Get-CategoryTweakPresetMap {
param( param(
[System.Windows.Window]$Window, [System.Windows.Window]$Window,
[string]$Category [string]$CategoryId
) )
$presetMap = @{} $presetMap = @{}
@@ -236,7 +236,7 @@ function Get-CategoryTweakPresetMap {
foreach ($controlName in $script:UiControlMappings.Keys) { foreach ($controlName in $script:UiControlMappings.Keys) {
$mapping = $script:UiControlMappings[$controlName] $mapping = $script:UiControlMappings[$controlName]
if ($mapping.Category -ne $Category) { continue } if ($mapping.CategoryId -ne $CategoryId) { continue }
$control = $Window.FindName($controlName) $control = $Window.FindName($controlName)
if (-not $control -or $control.Visibility -ne 'Visible') { continue } if (-not $control -or $control.Visibility -ne 'Visible') { continue }
@@ -403,8 +403,8 @@ function Initialize-TweakPresetSources {
$script:DefaultTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $DefaultSettingsJson $script:DefaultTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $DefaultSettingsJson
$script:LastUsedTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $LastUsedSettingsJson $script:LastUsedTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $LastUsedSettingsJson
$script:PrivacyTweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -Category 'Privacy & Suggested Content' $script:PrivacyTweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -CategoryId 'PrivacySuggestedContent'
$script:AITweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -Category 'AI' $script:AITweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -CategoryId 'AI'
$presetLastUsedTweaksBtn = $Window.FindName('PresetLastUsedTweaksBtn') $presetLastUsedTweaksBtn = $Window.FindName('PresetLastUsedTweaksBtn')
if ($presetLastUsedTweaksBtn) { if ($presetLastUsedTweaksBtn) {
@@ -478,11 +478,14 @@ function Test-OtherUsername {
[System.Windows.Window]$Window, [System.Windows.Window]$Window,
[System.Windows.Controls.ComboBox]$UserSelectionCombo, [System.Windows.Controls.ComboBox]$UserSelectionCombo,
[System.Windows.Controls.TextBox]$OtherUsernameTextBox, [System.Windows.Controls.TextBox]$OtherUsernameTextBox,
[System.Windows.Controls.TextBlock]$UsernameValidationMessage [System.Windows.Controls.TextBlock]$UsernameValidationMessage,
[System.Windows.Controls.ComboBox]$AppRemovalScopeCombo
) )
# Only validate if "Other User" is selected # Only validate if "Other User" is the deployment target, or "Target user only" is the app-removal scope
if ($UserSelectionCombo.SelectedIndex -ne 1) { $isOtherUserSelected = ($UserSelectionCombo.SelectedIndex -eq 1)
$isAppRemovalTargetUserSelected = Test-AppRemovalScopeTargetsOtherUser -AppRemovalScopeCombo $AppRemovalScopeCombo
if (-not $isOtherUserSelected -and -not $isAppRemovalTargetUserSelected) {
return $true return $true
} }
+16 -6
View File
@@ -234,8 +234,17 @@ function New-DynamicTweakControls {
foreach ($c in $featuresJson.Categories) { foreach ($c in $featuresJson.Categories) {
$categoryName = if ($c -is [string]) { $c } else { $c.Name } $categoryName = if ($c -is [string]) { $c } else { $c.Name }
if ($categoriesPresent.ContainsKey($categoryName)) { if ($categoriesPresent.ContainsKey($categoryName)) {
# Store the full category object (or create one with default icon for string categories) # Store the full category object (or create one with default icon for string categories).
$categoryObj = if ($c -is [string]) { @{Name = $c; Icon = '&#xE712;' } } else { $c } # A category without its own CategoryId falls back to its Name, same as before CategoryId existed.
$categoryObj = if ($c -is [string]) {
@{Name = $c; CategoryId = $c; Icon = '&#xE712;' }
}
elseif (-not $c.CategoryId) {
@{Name = $c.Name; CategoryId = $c.Name; Icon = $c.Icon }
}
else {
$c
}
$orderedCategories += $categoryObj $orderedCategories += $categoryObj
} }
} }
@@ -243,7 +252,7 @@ function New-DynamicTweakControls {
else { else {
# For backward compatibility, create category objects from keys # For backward compatibility, create category objects from keys
foreach ($catName in $categoriesPresent.Keys) { foreach ($catName in $categoriesPresent.Keys) {
$orderedCategories += @{Name = $catName; Icon = '&#xE712;' } $orderedCategories += @{Name = $catName; CategoryId = $catName; Icon = '&#xE712;' }
} }
} }
@@ -255,6 +264,7 @@ function New-DynamicTweakControls {
foreach ($categoryObj in $orderedCategories) { foreach ($categoryObj in $orderedCategories) {
$categoryName = $categoryObj.Name $categoryName = $categoryObj.Name
$categoryId = $categoryObj.CategoryId
# Card is created lazily on the first rendered item # Card is created lazily on the first rendered item
$panel = $null $panel = $null
@@ -359,7 +369,7 @@ function New-DynamicTweakControls {
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {} try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock } if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
} }
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeature.Label; Category = $categoryName } $script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeature.Label; Category = $categoryName; CategoryId = $categoryId }
} }
continue continue
} }
@@ -379,7 +389,7 @@ function New-DynamicTweakControls {
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {} try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock } if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
} }
$script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $group.Label; Category = $categoryName } $script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $group.Label; Category = $categoryName; CategoryId = $categoryId }
} }
elseif ($item.Type -eq 'feature') { elseif ($item.Type -eq 'feature') {
$feature = $item.Data $feature = $item.Data
@@ -406,7 +416,7 @@ function New-DynamicTweakControls {
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {} try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock } if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
} }
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $feature.FeatureId; Label = $feature.Label; Category = $categoryName } $script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $feature.FeatureId; Label = $feature.Label; Category = $categoryName; CategoryId = $categoryId }
} }
} }
} }
+6 -9
View File
@@ -577,6 +577,7 @@ function Show-MainWindow {
# ---- App removal scope combo ---- # ---- App removal scope combo ----
$appRemovalScopeCombo.Add_SelectionChanged({ $appRemovalScopeCombo.Add_SelectionChanged({
Update-AppRemovalScopeDescription -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeDescription $appRemovalScopeDescription Update-AppRemovalScopeDescription -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeDescription $appRemovalScopeDescription
Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage -AppRemovalScopeCombo $appRemovalScopeCombo | Out-Null
}) })
# ---- Other username text box ---- # ---- Other username text box ----
@@ -588,12 +589,12 @@ function Show-MainWindow {
$usernameTextBoxPlaceholder.Visibility = 'Collapsed' $usernameTextBoxPlaceholder.Visibility = 'Collapsed'
} }
Update-UserSelectionDescription -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UserSelectionDescription $userSelectionDescription Update-UserSelectionDescription -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UserSelectionDescription $userSelectionDescription
Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage | Out-Null Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage -AppRemovalScopeCombo $appRemovalScopeCombo | Out-Null
}) })
# ---- Validate target user helper ---- # ---- Validate target user helper ----
$ensureValidTargetUserOrWarn = { $ensureValidTargetUserOrWarn = {
if (-not (Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage)) { if (-not (Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage -AppRemovalScopeCombo $appRemovalScopeCombo)) {
$validationMessage = if (-not [string]::IsNullOrWhiteSpace($usernameValidationMessage.Text)) { $validationMessage = if (-not [string]::IsNullOrWhiteSpace($usernameValidationMessage.Text)) {
$usernameValidationMessage.Text $usernameValidationMessage.Text
} }
@@ -672,13 +673,9 @@ function Show-MainWindow {
Add-Parameter 'RemoveApps' Add-Parameter 'RemoveApps'
Add-Parameter 'Apps' ($selectedApps -join ',') Add-Parameter 'Apps' ($selectedApps -join ',')
$selectedScopeItem = $appRemovalScopeCombo.SelectedItem $scopeTarget = Get-AppRemovalScopeTarget -AppRemovalScopeCombo $appRemovalScopeCombo -OtherUsernameTextBox $otherUsernameTextBox
if ($selectedScopeItem) { if ($scopeTarget) {
switch ($selectedScopeItem.Content) { Add-Parameter 'AppRemovalTarget' $scopeTarget
"All users" { Add-Parameter 'AppRemovalTarget' 'AllUsers' }
"Current user only" { Add-Parameter 'AppRemovalTarget' 'CurrentUser' }
"Target user only" { Add-Parameter 'AppRemovalTarget' ($otherUsernameTextBox.Text.Trim()) }
}
} }
} }
+95 -2
View File
@@ -117,14 +117,27 @@ Describe 'Get-PendingTweakActions' {
$checkBox = New-Object System.Windows.Controls.CheckBox $checkBox = New-Object System.Windows.Controls.CheckBox
$checkBox.Visibility = 'Visible' $checkBox.Visibility = 'Visible'
$window.RegisterName('DisableTelemetryCheckBox', $checkBox) $window.RegisterName('DisableTelemetryCheckBox', $checkBox)
$script:UiControlMappings = @{ DisableTelemetryCheckBox = [PSCustomObject]@{ Category = 'Privacy'; Type = 'feature'; FeatureId = 'DisableTelemetry' } } $script:UiControlMappings = @{ DisableTelemetryCheckBox = [PSCustomObject]@{ Category = 'Privacy & Suggested Content'; CategoryId = 'PrivacySuggestedContent'; Type = 'feature'; FeatureId = 'DisableTelemetry' } }
$map = Get-CategoryTweakPresetMap -Window $window -Category 'Privacy' $map = Get-CategoryTweakPresetMap -Window $window -CategoryId 'PrivacySuggestedContent'
$map['DisableTelemetryCheckBox'].ControlType | Should -Be 'CheckBox' $map['DisableTelemetryCheckBox'].ControlType | Should -Be 'CheckBox'
$map['DisableTelemetryCheckBox'].DesiredValue | Should -BeTrue $map['DisableTelemetryCheckBox'].DesiredValue | Should -BeTrue
} }
It 'matches a mapping whose CategoryId fell back to its Name, for a category missing CategoryId' {
$window = New-TestWindow
$checkBox = New-Object System.Windows.Controls.CheckBox
$checkBox.Visibility = 'Visible'
$window.RegisterName('LegacyCheckBox', $checkBox)
# New-DynamicTweakControls falls back to Name when a category has no CategoryId of its own.
$script:UiControlMappings = @{ LegacyCheckBox = [PSCustomObject]@{ Category = 'Legacy Category'; CategoryId = 'Legacy Category'; Type = 'feature'; FeatureId = 'SomeFeature' } }
$map = Get-CategoryTweakPresetMap -Window $window -CategoryId 'Legacy Category'
$map['LegacyCheckBox'].ControlType | Should -Be 'CheckBox'
}
It 'updates navigation visibility and progress indicators for an interior tab' { It 'updates navigation visibility and progress indicators for an interior tab' {
$window = New-TestWindow $window = New-TestWindow
$tabControl = New-Object System.Windows.Controls.TabControl $tabControl = New-Object System.Windows.Controls.TabControl
@@ -142,6 +155,86 @@ Describe 'Get-PendingTweakActions' {
} }
} }
Describe 'Update-AppRemovalScopeDescription' {
It 'matches on the ComboBoxItem Name rather than its (translatable) Content for <Name>' -ForEach @(
@{ Name = 'AppRemovalScopeAllUsers'; ExpectedText = 'Apps will be removed for all users and from the Windows image to prevent reinstallation for new users.' }
@{ Name = 'AppRemovalScopeCurrentUser'; ExpectedText = 'Apps will only be removed for the current user.' }
@{ Name = 'AppRemovalScopeTargetUser'; ExpectedText = 'Apps will only be removed for the specified target user.' }
) {
$combo = New-Object System.Windows.Controls.ComboBox
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Name = $Name
$item.Content = 'Texto traducido'
$combo.Items.Add($item) | Out-Null
$combo.SelectedItem = $item
$description = New-Object System.Windows.Controls.TextBlock
Update-AppRemovalScopeDescription -AppRemovalScopeCombo $combo -AppRemovalScopeDescription $description
$description.Text | Should -Be $ExpectedText
}
}
Describe 'Get-AppRemovalScopeTarget' {
It 'matches on the ComboBoxItem Name rather than its (translatable) Content for <Name>' -ForEach @(
@{ Name = 'AppRemovalScopeAllUsers'; ExpectedTarget = 'AllUsers' }
@{ Name = 'AppRemovalScopeCurrentUser'; ExpectedTarget = 'CurrentUser' }
) {
$combo = New-Object System.Windows.Controls.ComboBox
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Name = $Name
$item.Content = 'Texto traducido'
$combo.Items.Add($item) | Out-Null
$combo.SelectedItem = $item
$usernameBox = New-Object System.Windows.Controls.TextBox
Get-AppRemovalScopeTarget -AppRemovalScopeCombo $combo -OtherUsernameTextBox $usernameBox | Should -Be $ExpectedTarget
}
It 'returns the trimmed username for the target-user scope' {
$combo = New-Object System.Windows.Controls.ComboBox
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Name = 'AppRemovalScopeTargetUser'
$item.Content = 'Texto traducido'
$combo.Items.Add($item) | Out-Null
$combo.SelectedItem = $item
$usernameBox = New-Object System.Windows.Controls.TextBox
$usernameBox.Text = ' jdoe '
Get-AppRemovalScopeTarget -AppRemovalScopeCombo $combo -OtherUsernameTextBox $usernameBox | Should -Be 'jdoe'
}
It 'returns null when nothing is selected' {
$combo = New-Object System.Windows.Controls.ComboBox
$usernameBox = New-Object System.Windows.Controls.TextBox
Get-AppRemovalScopeTarget -AppRemovalScopeCombo $combo -OtherUsernameTextBox $usernameBox | Should -BeNullOrEmpty
}
It 'returns null for an unrecognized ComboBoxItem Name' {
$combo = New-Object System.Windows.Controls.ComboBox
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Name = 'SomeUnrelatedControl'
$combo.Items.Add($item) | Out-Null
$combo.SelectedItem = $item
$usernameBox = New-Object System.Windows.Controls.TextBox
Get-AppRemovalScopeTarget -AppRemovalScopeCombo $combo -OtherUsernameTextBox $usernameBox | Should -BeNullOrEmpty
}
It 'returns an empty string for the target-user scope when the username is blank' {
$combo = New-Object System.Windows.Controls.ComboBox
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Name = 'AppRemovalScopeTargetUser'
$combo.Items.Add($item) | Out-Null
$combo.SelectedItem = $item
$usernameBox = New-Object System.Windows.Controls.TextBox
$usernameBox.Text = ' '
Get-AppRemovalScopeTarget -AppRemovalScopeCombo $combo -OtherUsernameTextBox $usernameBox | Should -BeExactly ''
}
}
Describe 'Set-WindowThemeResources' { Describe 'Set-WindowThemeResources' {
It 'populates themed resources and selects the Windows 11 icon font' { It 'populates themed resources and selects the Windows 11 icon font' {
$window = New-TestWindow $window = New-TestWindow
+75
View File
@@ -1,6 +1,40 @@
BeforeAll { BeforeAll {
Add-Type -AssemblyName PresentationFramework
function Test-TargetUserName { param($UserName) }
. (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-AppSelection.ps1')
. (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-Deployment.ps1') . (Join-Path $PSScriptRoot '..\Scripts\GUI\MainWindow-Deployment.ps1')
. (Join-Path $PSScriptRoot '..\Scripts\GUI\Get-SystemUsesDarkMode.ps1') . (Join-Path $PSScriptRoot '..\Scripts\GUI\Get-SystemUsesDarkMode.ps1')
function New-TestWindow {
$window = New-Object System.Windows.Window
$window.Resources['ValidationErrorColor'] = [System.Windows.Media.Brushes]::Red
$window.Resources['ValidationSuccessColor'] = [System.Windows.Media.Brushes]::Green
return $window
}
function New-UserSelectionCombo {
param([int]$SelectedIndex = 0)
$combo = New-Object System.Windows.Controls.ComboBox
'Current User', 'Other User', 'Windows Default User (Sysprep)' | ForEach-Object {
$combo.Items.Add((New-Object System.Windows.Controls.ComboBoxItem -Property @{ Content = $_ })) | Out-Null
}
$combo.SelectedIndex = $SelectedIndex
return $combo
}
function New-AppRemovalScopeCombo {
param([string]$SelectedItemName)
$combo = New-Object System.Windows.Controls.ComboBox
if ($SelectedItemName) {
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Name = $SelectedItemName
$combo.Items.Add($item) | Out-Null
$combo.SelectedItem = $item
}
return $combo
}
} }
Describe 'Get-UndoFeatureLabel' { Describe 'Get-UndoFeatureLabel' {
@@ -31,3 +65,44 @@ Describe 'Get-UndoFeatureLabel' {
Get-SystemUsesDarkMode | Should -BeFalse Get-SystemUsesDarkMode | Should -BeFalse
} }
} }
Describe 'Test-OtherUsername' {
It 'skips validation when neither the deployment target nor the app-removal scope needs a username' {
Mock Test-TargetUserName { [PSCustomObject]@{ IsValid = $false; Message = 'unused' } }
$window = New-TestWindow
$userCombo = New-UserSelectionCombo -SelectedIndex 0
$usernameBox = New-Object System.Windows.Controls.TextBox
$message = New-Object System.Windows.Controls.TextBlock
$scopeCombo = New-AppRemovalScopeCombo -SelectedItemName 'AppRemovalScopeAllUsers'
Test-OtherUsername -Window $window -UserSelectionCombo $userCombo -OtherUsernameTextBox $usernameBox -UsernameValidationMessage $message -AppRemovalScopeCombo $scopeCombo | Should -BeTrue
Should -Invoke Test-TargetUserName -Times 0 -Exactly
}
It 'validates when the deployment target is Other User' {
Mock Test-TargetUserName { [PSCustomObject]@{ IsValid = $false; Message = 'Please enter a username' } }
$window = New-TestWindow
$userCombo = New-UserSelectionCombo -SelectedIndex 1
$usernameBox = New-Object System.Windows.Controls.TextBox
$usernameBox.Text = ''
$message = New-Object System.Windows.Controls.TextBlock
Test-OtherUsername -Window $window -UserSelectionCombo $userCombo -OtherUsernameTextBox $usernameBox -UsernameValidationMessage $message | Should -BeFalse
}
It 'validates when the app-removal scope is Target user only, even if the deployment target is not Other User' {
$window = New-TestWindow
$userCombo = New-UserSelectionCombo -SelectedIndex 0
$usernameBox = New-Object System.Windows.Controls.TextBox
$usernameBox.Text = ' '
$message = New-Object System.Windows.Controls.TextBlock
$scopeCombo = New-AppRemovalScopeCombo -SelectedItemName 'AppRemovalScopeTargetUser'
Mock Test-TargetUserName { [PSCustomObject]@{ IsValid = $false; Message = 'Please enter a username' } }
Test-OtherUsername -Window $window -UserSelectionCombo $userCombo -OtherUsernameTextBox $usernameBox -UsernameValidationMessage $message -AppRemovalScopeCombo $scopeCombo | Should -BeFalse
$usernameBox.Text = 'jdoe'
Mock Test-TargetUserName { [PSCustomObject]@{ IsValid = $true; Message = 'User found: jdoe' } }
Test-OtherUsername -Window $window -UserSelectionCombo $userCombo -OtherUsernameTextBox $usernameBox -UsernameValidationMessage $message -AppRemovalScopeCombo $scopeCombo | Should -BeTrue
}
}