mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-07-16 00:40:39 +00:00
* Clean up analyzer warnings * Align analyzer warning cleanup policy * Run PSScriptAnalyzer directly in unittests workflow * Bind lazy-rendered buttons to click handlers * Remove WinUtil performance tracing * Fix Win11 ISO creator temp directory reuse
126 lines
6.1 KiB
PowerShell
126 lines
6.1 KiB
PowerShell
#===========================================================================
|
|
# Tests - Install tab rendering
|
|
#===========================================================================
|
|
|
|
BeforeAll {
|
|
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|
}
|
|
|
|
Describe "Install app rendering startup contract" {
|
|
It "queues app entries after creating category containers" {
|
|
$categoryScript = Get-Content -Path (Join-Path $script:repoRoot "functions\private\Initialize-InstallCategoryAppList.ps1") -Raw
|
|
|
|
$categoryScript | Should -Match '\$sync\.InstallAppRenderQueue = \[System\.Collections\.Queue\]::new\(\)'
|
|
$categoryScript | Should -Match 'Start-WinUtilInstallAppRendering'
|
|
$categoryScript | Should -Match 'Pre-group apps by category before creating WPF controls'
|
|
}
|
|
|
|
It "renders queued apps through dispatcher callbacks when a form dispatcher exists" {
|
|
$renderScript = Get-Content -Path (Join-Path $script:repoRoot "functions\private\Start-WinUtilInstallAppRendering.ps1") -Raw
|
|
|
|
$renderScript | Should -Match 'Dispatcher\.BeginInvoke'
|
|
$renderScript | Should -Match 'Invoke-WinUtilInstallAppRenderNextBatch'
|
|
$renderScript | Should -Match 'Initialize-InstallAppEntry'
|
|
$renderScript | Should -Match '\$sync\.InstallAppEntriesRendered = \$true'
|
|
}
|
|
|
|
It "does not use dispatcher timers for deferred install rendering" {
|
|
$renderScript = Get-Content -Path (Join-Path $script:repoRoot "functions\private\Start-WinUtilInstallAppRendering.ps1") -Raw
|
|
|
|
$renderScript | Should -Not -Match 'DispatcherTimer'
|
|
$renderScript | Should -Not -Match '\$timer'
|
|
$renderScript | Should -Not -Match '\$dispatcherTimer'
|
|
$renderScript | Should -Not -Match '\$timer\.Stop\(\)'
|
|
$renderScript | Should -Not -Match '& \$renderCategory'
|
|
}
|
|
|
|
It "drains queued app batches on the WPF dispatcher without timer scope errors" {
|
|
Add-Type -AssemblyName WindowsBase
|
|
. (Join-Path $script:repoRoot "functions\private\Start-WinUtilInstallAppRendering.ps1")
|
|
|
|
$previousSync = Get-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
|
$previousInitializeAppEntry = Get-Item -Path Function:\Initialize-InstallAppEntry -ErrorAction SilentlyContinue
|
|
$previousSearch = Get-Item -Path Function:\Find-AppsByNameOrDescription -ErrorAction SilentlyContinue
|
|
$errorCountBefore = $global:Error.Count
|
|
|
|
try {
|
|
$global:sync = [Hashtable]::Synchronized(@{})
|
|
$global:sync.currentTab = "Install"
|
|
$global:sync.SearchBar = [pscustomobject]@{ Text = "" }
|
|
$global:sync.Form = [pscustomobject]@{ Dispatcher = [System.Windows.Threading.Dispatcher]::CurrentDispatcher }
|
|
$global:sync.InstallAppRenderQueue = [System.Collections.Queue]::new()
|
|
|
|
$renderedApps = [System.Collections.Generic.List[string]]::new()
|
|
|
|
function global:Initialize-InstallAppEntry {
|
|
param($TargetElement, $AppKey)
|
|
$renderedApps.Add($AppKey)
|
|
return "entry:$AppKey"
|
|
}
|
|
|
|
function global:Find-AppsByNameOrDescription {
|
|
param($SearchString)
|
|
throw "Search should not run for an empty search box in this test."
|
|
}
|
|
|
|
$global:sync.InstallAppRenderQueue.Enqueue([pscustomobject]@{ TargetElement = [pscustomobject]@{}; AppKeys = @("AppA", "AppB") })
|
|
$global:sync.InstallAppRenderQueue.Enqueue([pscustomobject]@{ TargetElement = [pscustomobject]@{}; AppKeys = @("AppC") })
|
|
|
|
$frame = New-Object System.Windows.Threading.DispatcherFrame
|
|
$timeout = [System.Diagnostics.Stopwatch]::StartNew()
|
|
Start-WinUtilInstallAppRendering
|
|
|
|
$closeTimer = New-Object System.Windows.Threading.DispatcherTimer
|
|
$closeTimer.Interval = [TimeSpan]::FromMilliseconds(25)
|
|
$closeTimer.Add_Tick({
|
|
param($eventSender)
|
|
$timer = [System.Windows.Threading.DispatcherTimer]$eventSender
|
|
|
|
if ($global:sync.InstallAppEntriesRendered -or $timeout.Elapsed.TotalSeconds -gt 5) {
|
|
$timer.Stop()
|
|
$frame.Continue = $false
|
|
}
|
|
})
|
|
$closeTimer.Start()
|
|
|
|
[System.Windows.Threading.Dispatcher]::PushFrame($frame)
|
|
|
|
$global:sync.InstallAppEntriesRendered | Should -BeTrue
|
|
$global:sync.InstallAppRenderQueue.Count | Should -Be 0
|
|
@($renderedApps) | Should -Be @("AppA", "AppB", "AppC")
|
|
$global:Error.Count | Should -Be $errorCountBefore
|
|
} finally {
|
|
if ($previousSync) {
|
|
Set-Variable -Name sync -Value $previousSync.Value -Scope Global
|
|
} else {
|
|
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
foreach ($functionBackup in @(
|
|
@{ Name = "Initialize-InstallAppEntry"; Backup = $previousInitializeAppEntry },
|
|
@{ Name = "Find-AppsByNameOrDescription"; Backup = $previousSearch }
|
|
)) {
|
|
if ($functionBackup.Backup) {
|
|
Set-Item -Path "Function:\$($functionBackup.Name)" -Value $functionBackup.Backup.ScriptBlock
|
|
} else {
|
|
Remove-Item -Path "Function:\$($functionBackup.Name)" -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
It "keeps app-entry metadata lookup independent from the old caller scope" {
|
|
$entryScript = Get-Content -Path (Join-Path $script:repoRoot "functions\private\Initialize-InstallAppEntry.ps1") -Raw
|
|
|
|
$entryScript | Should -Match '\$app = \$sync\.configs\.applicationsHashtable\.\$appKey'
|
|
$entryScript | Should -Not -Match '\$Apps\.\$appKey'
|
|
}
|
|
|
|
It "restores delayed app checkbox state from selected apps" {
|
|
$entryScript = Get-Content -Path (Join-Path $script:repoRoot "functions\private\Initialize-InstallAppEntry.ps1") -Raw
|
|
|
|
$entryScript | Should -Match '\$sync\.selectedApps -contains \$appKey'
|
|
$entryScript | Should -Match '\$checkBox\.IsChecked = \$true'
|
|
}
|
|
}
|