From 5072958b10f7f6b7cd03b960b096008ae2df6c70 Mon Sep 17 00:00:00 2001 From: Sashank Date: Sun, 16 Aug 2026 21:22:58 +0530 Subject: [PATCH] fix: match tweak presets and app-removal scope by stable ID instead of translatable text (#737) --- Config/Features.json | 12 +++ Scripts/GUI/MainWindow-AppSelection.ps1 | 50 +++++++++++- Scripts/GUI/MainWindow-Deployment.ps1 | 21 ++--- Scripts/GUI/MainWindow-TweaksBuilder.ps1 | 22 ++++-- Scripts/GUI/Show-MainWindow.ps1 | 15 ++-- Tests/MainWindow-AppSelection.Tests.ps1 | 97 +++++++++++++++++++++++- Tests/MainWindow-Deployment.Tests.ps1 | 75 ++++++++++++++++++ 7 files changed, 262 insertions(+), 30 deletions(-) diff --git a/Config/Features.json b/Config/Features.json index 80f2386..17ed6de 100644 --- a/Config/Features.json +++ b/Config/Features.json @@ -2,50 +2,62 @@ "Version": "1.0", "Categories": [ { + "CategoryId": "PrivacySuggestedContent", "Name": "Privacy & Suggested Content", "Icon": "" }, { + "CategoryId": "System", "Name": "System", "Icon": "" }, { + "CategoryId": "StartMenuSearch", "Name": "Start Menu & Search", "Icon": "" }, { + "CategoryId": "AI", "Name": "AI", "Icon": "" }, { + "CategoryId": "WindowsUpdate", "Name": "Windows Update", "Icon": "" }, { + "CategoryId": "Taskbar", "Name": "Taskbar", "Icon": "" }, { + "CategoryId": "Appearance", "Name": "Appearance", "Icon": "" }, { + "CategoryId": "FileExplorer", "Name": "File Explorer", "Icon": "" }, { + "CategoryId": "Gaming", "Name": "Gaming", "Icon": "" }, { + "CategoryId": "MultiTasking", "Name": "Multi-tasking", "Icon": "" }, { + "CategoryId": "OptionalWindowsFeatures", "Name": "Optional Windows Features", "Icon": "" }, { + "CategoryId": "Other", "Name": "Other", "Icon": "" } diff --git a/Scripts/GUI/MainWindow-AppSelection.ps1 b/Scripts/GUI/MainWindow-AppSelection.ps1 index 4df7baa..37490f8 100644 --- a/Scripts/GUI/MainWindow-AppSelection.ps1 +++ b/Scripts/GUI/MainWindow-AppSelection.ps1 @@ -174,6 +174,10 @@ function Update-AppSelectionStatus { } } +<# + .SYNOPSIS + Updates the app-removal scope description to match the selected ComboBoxItem. +#> function Update-AppRemovalScopeDescription { param( [System.Windows.Controls.ComboBox]$AppRemovalScopeCombo, @@ -182,20 +186,58 @@ function Update-AppRemovalScopeDescription { $selectedItem = $AppRemovalScopeCombo.SelectedItem if ($selectedItem) { - switch ($selectedItem.Content) { - "All users" { + # Content is the display text and will change once translated; Name is stable. + switch ($selectedItem.Name) { + "AppRemovalScopeAllUsers" { $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." } - "Target user only" { + "AppRemovalScopeTargetUser" { $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 { param( [System.Windows.Controls.Panel]$AppsPanel, diff --git a/Scripts/GUI/MainWindow-Deployment.ps1 b/Scripts/GUI/MainWindow-Deployment.ps1 index 604855d..9aa921e 100644 --- a/Scripts/GUI/MainWindow-Deployment.ps1 +++ b/Scripts/GUI/MainWindow-Deployment.ps1 @@ -219,8 +219,8 @@ function Get-TweakPresetControlMap { .PARAMETER Window The window that owns the visible tweak controls. - .PARAMETER Category - The category whose mapped controls are included. + .PARAMETER CategoryId + The stable CategoryId (from Features.json) whose mapped controls are included. .OUTPUTS System.Collections.Hashtable. Control metadata keyed by control name. @@ -228,7 +228,7 @@ function Get-TweakPresetControlMap { function Get-CategoryTweakPresetMap { param( [System.Windows.Window]$Window, - [string]$Category + [string]$CategoryId ) $presetMap = @{} @@ -236,7 +236,7 @@ function Get-CategoryTweakPresetMap { foreach ($controlName in $script:UiControlMappings.Keys) { $mapping = $script:UiControlMappings[$controlName] - if ($mapping.Category -ne $Category) { continue } + if ($mapping.CategoryId -ne $CategoryId) { continue } $control = $Window.FindName($controlName) 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:LastUsedTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $LastUsedSettingsJson - $script:PrivacyTweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -Category 'Privacy & Suggested Content' - $script:AITweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -Category 'AI' + $script:PrivacyTweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -CategoryId 'PrivacySuggestedContent' + $script:AITweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -CategoryId 'AI' $presetLastUsedTweaksBtn = $Window.FindName('PresetLastUsedTweaksBtn') if ($presetLastUsedTweaksBtn) { @@ -478,11 +478,14 @@ function Test-OtherUsername { [System.Windows.Window]$Window, [System.Windows.Controls.ComboBox]$UserSelectionCombo, [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 - if ($UserSelectionCombo.SelectedIndex -ne 1) { + # Only validate if "Other User" is the deployment target, or "Target user only" is the app-removal scope + $isOtherUserSelected = ($UserSelectionCombo.SelectedIndex -eq 1) + $isAppRemovalTargetUserSelected = Test-AppRemovalScopeTargetsOtherUser -AppRemovalScopeCombo $AppRemovalScopeCombo + if (-not $isOtherUserSelected -and -not $isAppRemovalTargetUserSelected) { return $true } diff --git a/Scripts/GUI/MainWindow-TweaksBuilder.ps1 b/Scripts/GUI/MainWindow-TweaksBuilder.ps1 index d1e940f..410667a 100644 --- a/Scripts/GUI/MainWindow-TweaksBuilder.ps1 +++ b/Scripts/GUI/MainWindow-TweaksBuilder.ps1 @@ -234,8 +234,17 @@ function New-DynamicTweakControls { foreach ($c in $featuresJson.Categories) { $categoryName = if ($c -is [string]) { $c } else { $c.Name } if ($categoriesPresent.ContainsKey($categoryName)) { - # Store the full category object (or create one with default icon for string categories) - $categoryObj = if ($c -is [string]) { @{Name = $c; Icon = '' } } else { $c } + # Store the full category object (or create one with default icon for string categories). + # 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 = '' } + } + elseif (-not $c.CategoryId) { + @{Name = $c.Name; CategoryId = $c.Name; Icon = $c.Icon } + } + else { + $c + } $orderedCategories += $categoryObj } } @@ -243,7 +252,7 @@ function New-DynamicTweakControls { else { # For backward compatibility, create category objects from keys foreach ($catName in $categoriesPresent.Keys) { - $orderedCategories += @{Name = $catName; Icon = '' } + $orderedCategories += @{Name = $catName; CategoryId = $catName; Icon = '' } } } @@ -255,6 +264,7 @@ function New-DynamicTweakControls { foreach ($categoryObj in $orderedCategories) { $categoryName = $categoryObj.Name + $categoryId = $categoryObj.CategoryId # Card is created lazily on the first rendered item $panel = $null @@ -359,7 +369,7 @@ function New-DynamicTweakControls { try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {} 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 } @@ -379,7 +389,7 @@ function New-DynamicTweakControls { try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {} 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') { $feature = $item.Data @@ -406,7 +416,7 @@ function New-DynamicTweakControls { try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {} 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 } } } } diff --git a/Scripts/GUI/Show-MainWindow.ps1 b/Scripts/GUI/Show-MainWindow.ps1 index 7fc1319..3dcdaa0 100644 --- a/Scripts/GUI/Show-MainWindow.ps1 +++ b/Scripts/GUI/Show-MainWindow.ps1 @@ -577,6 +577,7 @@ function Show-MainWindow { # ---- App removal scope combo ---- $appRemovalScopeCombo.Add_SelectionChanged({ Update-AppRemovalScopeDescription -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeDescription $appRemovalScopeDescription + Test-OtherUsername -Window $window -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -UsernameValidationMessage $usernameValidationMessage -AppRemovalScopeCombo $appRemovalScopeCombo | Out-Null }) # ---- Other username text box ---- @@ -588,12 +589,12 @@ function Show-MainWindow { $usernameTextBoxPlaceholder.Visibility = 'Collapsed' } 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 ---- $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)) { $usernameValidationMessage.Text } @@ -672,13 +673,9 @@ function Show-MainWindow { Add-Parameter 'RemoveApps' Add-Parameter 'Apps' ($selectedApps -join ',') - $selectedScopeItem = $appRemovalScopeCombo.SelectedItem - if ($selectedScopeItem) { - switch ($selectedScopeItem.Content) { - "All users" { Add-Parameter 'AppRemovalTarget' 'AllUsers' } - "Current user only" { Add-Parameter 'AppRemovalTarget' 'CurrentUser' } - "Target user only" { Add-Parameter 'AppRemovalTarget' ($otherUsernameTextBox.Text.Trim()) } - } + $scopeTarget = Get-AppRemovalScopeTarget -AppRemovalScopeCombo $appRemovalScopeCombo -OtherUsernameTextBox $otherUsernameTextBox + if ($scopeTarget) { + Add-Parameter 'AppRemovalTarget' $scopeTarget } } diff --git a/Tests/MainWindow-AppSelection.Tests.ps1 b/Tests/MainWindow-AppSelection.Tests.ps1 index 1e43196..9ed933b 100644 --- a/Tests/MainWindow-AppSelection.Tests.ps1 +++ b/Tests/MainWindow-AppSelection.Tests.ps1 @@ -117,14 +117,27 @@ Describe 'Get-PendingTweakActions' { $checkBox = New-Object System.Windows.Controls.CheckBox $checkBox.Visibility = 'Visible' $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'].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' { $window = New-TestWindow $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 ' -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 ' -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' { It 'populates themed resources and selects the Windows 11 icon font' { $window = New-TestWindow diff --git a/Tests/MainWindow-Deployment.Tests.ps1 b/Tests/MainWindow-Deployment.Tests.ps1 index 2ec767f..4217237 100644 --- a/Tests/MainWindow-Deployment.Tests.ps1 +++ b/Tests/MainWindow-Deployment.Tests.ps1 @@ -1,6 +1,40 @@ 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\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' { @@ -31,3 +65,44 @@ Describe 'Get-UndoFeatureLabel' { 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 + } +}