Files

330 lines
14 KiB
PowerShell

# 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