# End-to-end test: summon modes (click / long-press / hold), execute-by-click, swipe select, mouse position, global switch. param( [string]$Exe = "src/OneClickRun.App/bin/x64/Debug/net8.0-windows10.0.19041.0/win-x64/OneClickRun.exe" ) $ErrorActionPreference = "Stop" $root = Split-Path -Parent $PSScriptRoot $exePath = Join-Path $root $Exe if (-not (Test-Path $exePath)) { throw "App not found: $exePath" } $settingsDir = Join-Path $env:APPDATA "OneClickRun" $settingsFile = Join-Path $settingsDir "settings.json" $settingsBackup = $null if (Test-Path $settingsFile) { $settingsBackup = $settingsFile + ".e2e-backup" Copy-Item $settingsFile $settingsBackup -Force } Add-Type @" using System; using System.Text; using System.Runtime.InteropServices; public class E2EWin { public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lParam); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid); [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder sb, int max); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect); [DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); [DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } } "@ $script:Proc = $null $script:TargetPid = 0 $script:WheelHwnd = [IntPtr]::Zero $script:WheelRect = New-Object E2EWin+RECT $script:WheelVisible = $false function Get-WheelInfo { $script:WheelVisible = $false $script:WheelHwnd = [IntPtr]::Zero $cb = [E2EWin+EnumWindowsProc]{ param($h, $l) $wpid = 0 [void][E2EWin]::GetWindowThreadProcessId($h, [ref]$wpid) if ($wpid -eq $script:TargetPid) { $sb = New-Object System.Text.StringBuilder 256 [void][E2EWin]::GetWindowText($h, $sb, 256) if ($sb.ToString() -like '*OneClickRun Wheel*') { $script:WheelHwnd = $h $rect = New-Object E2EWin+RECT [void][E2EWin]::GetWindowRect($h, [ref]$rect) $script:WheelRect = $rect $script:WheelVisible = [E2EWin]::IsWindowVisible($h) } } return $true } [void][E2EWin]::EnumWindows($cb, [IntPtr]::Zero) return $script:WheelVisible } function Write-Settings([hashtable]$s) { $s | ConvertTo-Json -Depth 10 | Set-Content -Path $settingsFile -Encoding UTF8 } function Start-App { $script:Proc = Start-Process -FilePath $exePath -PassThru $script:TargetPid = $script:Proc.Id Start-Sleep -Seconds 4 if ($script:Proc.HasExited) { throw "App exited early with code $($script:Proc.ExitCode)" } } function Stop-App { if ($script:Proc -and -not $script:Proc.HasExited) { Stop-Process -Id $script:TargetPid -Force } Start-Sleep -Milliseconds 400 } function Key-Down([byte]$vk) { [E2EWin]::keybd_event($vk, 0, 0, [UIntPtr]::Zero) } function Key-Up([byte]$vk) { [E2EWin]::keybd_event($vk, 0, 2, [UIntPtr]::Zero) } function Press-Hotkey { Key-Down 0x11; Key-Down 0x12; Key-Down 0x51; Start-Sleep -Milliseconds 120; Key-Up 0x51; Key-Up 0x12; Key-Up 0x11 } function Hold-Hotkey-Down { Key-Down 0x11; Key-Down 0x12; Key-Down 0x51 } function Hold-Hotkey-Up { Key-Up 0x51; Key-Up 0x12; Key-Up 0x11 } function Press-Esc { Key-Down 0x1B; Start-Sleep -Milliseconds 60; Key-Up 0x1B } function Assert([bool]$cond, [string]$msg) { if (-not $cond) { throw "Assertion failed: $msg" } } function Get-SectorPoint([int]$index, [int]$count) { $w = $script:WheelRect.Right - $script:WheelRect.Left $scale = $w / 560.0 $cx = ($script:WheelRect.Left + $script:WheelRect.Right) / 2.0 $cy = ($script:WheelRect.Top + $script:WheelRect.Bottom) / 2.0 $span = 360.0 / $count $mid = ($index + 0.5) * $span $rad = $mid * [Math]::PI / 180.0 $r = 177.5 * $scale return @{ X = [int][Math]::Round($cx + $r * [Math]::Sin($rad)); Y = [int][Math]::Round($cy - $r * [Math]::Cos($rad)) } } function Click-At([int]$x, [int]$y) { [void][E2EWin]::SetCursorPos($x, $y) Start-Sleep -Milliseconds 120 [E2EWin]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero) Start-Sleep -Milliseconds 80 [E2EWin]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero) Start-Sleep -Milliseconds 500 } function Click-Once([int]$x, [int]$y) { [void][E2EWin]::SetCursorPos($x, $y) Start-Sleep -Milliseconds 60 [E2EWin]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero) Start-Sleep -Milliseconds 40 [E2EWin]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero) Start-Sleep -Milliseconds 30 } function New-NotepadCommand { @{ Name = "Notepad"; ActionType = 0; Target = (Join-Path $env:WINDIR 'System32\notepad.exe'); IconGlyph = "A"; ColorIndex = 0 } } function New-CalcCommand { @{ Name = "Calc"; ActionType = 0; Target = (Join-Path $env:WINDIR 'System32\calc.exe'); IconGlyph = "B"; ColorIndex = 1 } } function New-BingCommand { @{ Name = "Bing"; ActionType = 2; Target = "https://www.bing.com"; IconGlyph = "C"; ColorIndex = 2 } } function New-BaseSettings([int]$summon, [int]$selection, [int]$position, [bool]$enabled, [bool]$startWithWindows = $false) { return @{ SchemaVersion = 1; StartWithWindows = $startWithWindows; WheelEnabled = $enabled WheelPosition = $position; Hotkey = @{ Modifiers = 3; Key = 81 } SummonMode = $summon; LongPressDelayMs = 400; SelectionMode = $selection; ThemeMode = 2 Commands = @((New-NotepadCommand), (New-CalcCommand), (New-BingCommand)) } } $failed = 0 $results = New-Object System.Collections.Generic.List[string] function Run-Test([string]$name, [scriptblock]$body) { try { & $body $results.Add("PASS $name") Write-Host "PASS $name" } catch { $script:failed++ $results.Add("FAIL $name :: $($_.Exception.Message)") Write-Host "FAIL $name :: $($_.Exception.Message)" -ForegroundColor Red } finally { Stop-App } } try { Run-Test "click summon: show then toggle-hide" { Remove-Item $settingsFile -ErrorAction SilentlyContinue Start-App Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not show wheel" Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (-not (Get-WheelInfo)) "second hotkey press did not hide wheel" } Run-Test "click summon: Esc dismisses wheel" { Remove-Item $settingsFile -ErrorAction SilentlyContinue Start-App Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not show wheel" Press-Esc; Start-Sleep -Milliseconds 500 Assert (-not (Get-WheelInfo)) "wheel still visible after Esc" } Run-Test "click execute: sector 0 starts Notepad" { Remove-Item $settingsFile -ErrorAction SilentlyContinue Start-App Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not show wheel" $p = Get-SectorPoint 0 5 Click-At $p.X $p.Y Start-Sleep -Milliseconds 800 Assert (-not (Get-WheelInfo)) "wheel not hidden after execute" $np = Get-Process notepad -ErrorAction SilentlyContinue Assert ($null -ne $np) "Notepad process was not started" $np | Stop-Process -Force } Run-Test "long-press summon: shows after 400ms and stays after release" { Write-Settings (New-BaseSettings 1 0 0 $true) Start-App Hold-Hotkey-Down Start-Sleep -Milliseconds 300 Assert (-not (Get-WheelInfo)) "wheel shown too early (300ms)" Start-Sleep -Milliseconds 250 Assert (Get-WheelInfo) "wheel not shown after 400ms long press" Hold-Hotkey-Up Start-Sleep -Milliseconds 400 Assert (Get-WheelInfo) "wheel disappeared after release (should stay)" Press-Esc; Start-Sleep -Milliseconds 400 Assert (-not (Get-WheelInfo)) "Esc did not close wheel" } Run-Test "hold summon: visible while held, hidden on release" { Write-Settings (New-BaseSettings 2 0 0 $true) Start-App Hold-Hotkey-Down Start-Sleep -Milliseconds 400 Assert (Get-WheelInfo) "wheel not visible while hotkey held" Hold-Hotkey-Up Start-Sleep -Milliseconds 400 Assert (-not (Get-WheelInfo)) "wheel still visible after release" } Run-Test "swipe select: hold + mouse up, release executes Notepad" { Write-Settings (New-BaseSettings 2 2 0 $true) Start-App Hold-Hotkey-Down Start-Sleep -Milliseconds 400 Assert (Get-WheelInfo) "wheel not visible while hotkey held" $p = Get-SectorPoint 0 3 [void][E2EWin]::SetCursorPos($p.X, $p.Y) Start-Sleep -Milliseconds 150 Hold-Hotkey-Up Start-Sleep -Milliseconds 800 Assert (-not (Get-WheelInfo)) "wheel still visible after release" $np = Get-Process notepad -ErrorAction SilentlyContinue Assert ($null -ne $np) "swipe did not execute Notepad" $np | Stop-Process -Force } Run-Test "wheel position: mouse cursor" { Write-Settings (New-BaseSettings 0 0 1 $true) Start-App [void][E2EWin]::SetCursorPos(400, 300) Start-Sleep -Milliseconds 150 Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not show wheel" $cx = ($script:WheelRect.Left + $script:WheelRect.Right) / 2.0 $cy = ($script:WheelRect.Top + $script:WheelRect.Bottom) / 2.0 $d = [Math]::Sqrt(($cx - 400) * ($cx - 400) + ($cy - 300) * ($cy - 300)) Assert ($d -lt 12) "wheel center offset from cursor by $([int]$d)px" Press-Esc } Run-Test "double-click selection: first click pending, second click executes" { Write-Settings (New-BaseSettings 0 1 0 $true) Start-App Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not show wheel" $p = Get-SectorPoint 0 3 Click-Once $p.X $p.Y Start-Sleep -Milliseconds 150 Assert (Get-WheelInfo) "wheel hidden after first click in double-click mode" $np = Get-Process notepad -ErrorAction SilentlyContinue Assert ($null -eq $np) "Notepad started after single click in double-click mode" Click-Once $p.X $p.Y Start-Sleep -Milliseconds 600 Assert (-not (Get-WheelInfo)) "wheel not hidden after double click" $np = Get-Process notepad -ErrorAction SilentlyContinue Assert ($null -ne $np) "Notepad not started after double click" $np | Stop-Process -Force } Run-Test "autostart: registry Run value written at launch" { Write-Settings (New-BaseSettings 0 0 0 $true $true) Start-App $runKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' $prev = (Get-ItemProperty -Path $runKey -Name OneClickRun -ErrorAction SilentlyContinue).OneClickRun try { $cur = (Get-ItemProperty -Path $runKey -Name OneClickRun -ErrorAction SilentlyContinue).OneClickRun Assert ($null -ne $cur) "autostart registry value missing" Assert ($cur -like '*OneClickRun*') "unexpected autostart value: $cur" } finally { if ($null -eq $prev) { Remove-ItemProperty -Path $runKey -Name OneClickRun -ErrorAction SilentlyContinue } else { Set-ItemProperty -Path $runKey -Name OneClickRun -Value $prev } } } Run-Test "script execute: cmd and ps1 sectors run" { $cmdPath = Join-Path $env:TEMP "ocr-e2e-run.cmd" $ps1Path = Join-Path $env:TEMP "ocr-e2e-run.ps1" $cmdMarker = Join-Path $env:TEMP "ocr-e2e-cmd-marker.txt" $ps1Marker = Join-Path $env:TEMP "ocr-e2e-ps1-marker.txt" Remove-Item $cmdMarker, $ps1Marker -ErrorAction SilentlyContinue $cmdLine = 'echo ok > "' + $cmdMarker + '"' $ps1Line = "Set-Content -Path '" + $ps1Marker + "' -Value ok" Set-Content -Path $cmdPath -Value @('@echo off', $cmdLine) -Encoding ASCII Set-Content -Path $ps1Path -Value $ps1Line -Encoding ASCII $settings = New-BaseSettings 0 0 0 $true $settings.Commands = @( @{ Name = "RunCmd"; ActionType = 3; Target = $cmdPath; IconGlyph = "D"; ColorIndex = 0 }, @{ Name = "RunPs1"; ActionType = 3; Target = $ps1Path; IconGlyph = "E"; ColorIndex = 1 } ) Write-Settings $settings Start-App Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not show wheel" $p0 = Get-SectorPoint 0 2 Click-At $p0.X $p0.Y Start-Sleep -Milliseconds 1200 Assert (Test-Path $cmdMarker) "cmd script did not run" Assert (-not (Get-WheelInfo)) "wheel not hidden after cmd execute" Press-Hotkey; Start-Sleep -Milliseconds 500 Assert (Get-WheelInfo) "hotkey did not re-show wheel" $p1 = Get-SectorPoint 1 2 Click-At $p1.X $p1.Y Start-Sleep -Milliseconds 1200 Assert (Test-Path $ps1Marker) "ps1 script did not run" Remove-Item $cmdMarker, $ps1Marker, $cmdPath, $ps1Path -ErrorAction SilentlyContinue } Run-Test "global switch off: wheel cannot be summoned" { Write-Settings (New-BaseSettings 0 0 0 $false) Start-App Press-Hotkey; Start-Sleep -Milliseconds 600 Assert (-not (Get-WheelInfo)) "wheel summoned while global switch off" } } finally { Stop-App if ($settingsBackup) { Move-Item $settingsBackup $settingsFile -Force } else { Remove-Item $settingsFile -ErrorAction SilentlyContinue } } Write-Host "" Write-Host ("=== E2E RESULT: {0}/{1} passed ===" -f ($results.Count - $failed), $results.Count) exit $failed