@@ -0,0 +1,127 @@
|
||||
# 用无头 Chrome 截图,做视觉验收
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\shots.ps1 -Base http://127.0.0.1:18080
|
||||
param(
|
||||
[string]$Base = 'http://127.0.0.1:18080',
|
||||
[string]$User = 'admin',
|
||||
[string]$Pass = 'daili123',
|
||||
[string]$OutDir = ''
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
||||
if (-not $OutDir) { $OutDir = Join-Path $root 'shots' }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
$chrome = 'C:\Program Files\Google\Chrome\Application\chrome.exe'
|
||||
if (-not (Test-Path $chrome)) { throw '找不到 Chrome' }
|
||||
|
||||
# 1) 登录拿会话 cookie
|
||||
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
|
||||
Invoke-WebRequest -Uri "$Base/login" -WebSession $session -UseBasicParsing | Out-Null
|
||||
Invoke-WebRequest -Uri "$Base/login" -Method POST -WebSession $session -UseBasicParsing -Body @{
|
||||
username = $User; password = $Pass; next = '/calendar'
|
||||
} | Out-Null
|
||||
$cookie = ($session.Cookies.GetCookies($Base) | Where-Object { $_.Name -eq 'daili_sid' } | Select-Object -First 1)
|
||||
if (-not $cookie) { throw '登录失败,拿不到会话' }
|
||||
$token = $cookie.Value
|
||||
|
||||
# 2) 拿一个订阅 ID 用于「追加面板」截图
|
||||
$custID = ''
|
||||
$subID = ''
|
||||
$r = Invoke-WebRequest -Uri "$Base/customers?q=7001" -WebSession $session -UseBasicParsing
|
||||
if ($r.Content -match '/customers/(\d+)') {
|
||||
$custID = $matches[1]
|
||||
$r2 = Invoke-WebRequest -Uri "$Base/customers/$custID" -WebSession $session -UseBasicParsing
|
||||
if ($r2.Content -match 'id="sub-(\d+)"') { $subID = $matches[1] }
|
||||
}
|
||||
|
||||
# 3) 启动 Chrome
|
||||
$profile = Join-Path $root ('.chrome-profile-' + (Get-Date).ToString('HHmmss'))
|
||||
$chromeArgs = @(
|
||||
'--headless=new', '--disable-gpu', '--hide-scrollbars', '--no-sandbox',
|
||||
'--no-first-run', '--no-default-browser-check', '--disable-extensions',
|
||||
'--disable-crash-reporter', '--disable-breakpad',
|
||||
'--force-device-scale-factor=1', '--remote-debugging-port=9222',
|
||||
"--user-data-dir=$profile", '--window-size=1440,1000', 'about:blank'
|
||||
)
|
||||
$chromeProc = Start-Process -FilePath $chrome -ArgumentList $chromeArgs -PassThru
|
||||
$targets = $null
|
||||
for ($i = 0; $i -lt 20; $i++) {
|
||||
Start-Sleep -Milliseconds 500
|
||||
try {
|
||||
$targets = Invoke-RestMethod 'http://127.0.0.1:9222/json'
|
||||
if ($targets) { break }
|
||||
} catch { }
|
||||
}
|
||||
if (-not $targets) {
|
||||
Stop-Process -Id $chromeProc.Id -Force -ErrorAction SilentlyContinue
|
||||
throw 'Chrome 调试端口未就绪'
|
||||
}
|
||||
$page = $targets | Where-Object { $_.type -eq 'page' } | Select-Object -First 1
|
||||
if (-not $page) { throw '拿不到调试目标' }
|
||||
|
||||
$script:ws = New-Object System.Net.WebSockets.ClientWebSocket
|
||||
$script:ws.ConnectAsync([Uri]$page.webSocketDebuggerUrl, [Threading.CancellationToken]::None).Wait()
|
||||
$script:msgId = 0
|
||||
|
||||
function Send-CDP {
|
||||
param([string]$Method, $Params)
|
||||
$script:msgId++
|
||||
$payload = @{ id = $script:msgId; method = $Method; params = $Params } | ConvertTo-Json -Depth 12 -Compress
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($payload)
|
||||
$seg = New-Object 'System.ArraySegment[byte]' -ArgumentList @(, $bytes)
|
||||
$script:ws.SendAsync($seg, [Net.WebSockets.WebSocketMessageType]::Text, $true, [Threading.CancellationToken]::None).Wait()
|
||||
while ($true) {
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
do {
|
||||
$buf = New-Object byte[] 131072
|
||||
$segR = New-Object 'System.ArraySegment[byte]' -ArgumentList @(, $buf)
|
||||
$res = $script:ws.ReceiveAsync($segR, [Threading.CancellationToken]::None).Result
|
||||
$ms.Write($buf, 0, $res.Count)
|
||||
} while (-not $res.EndOfMessage)
|
||||
$text = [Text.Encoding]::UTF8.GetString($ms.ToArray())
|
||||
$msg = $text | ConvertFrom-Json
|
||||
if ($msg.id -eq $script:msgId) { return $msg }
|
||||
}
|
||||
}
|
||||
|
||||
function Shoot {
|
||||
param([string]$Name, [string]$Path, [int]$Width = 1440, [int]$Height = 1000, [bool]$SetCookie = $true)
|
||||
Send-CDP 'Emulation.setDeviceMetricsOverride' @{
|
||||
width = $Width; height = $Height; deviceScaleFactor = 1; mobile = ($Width -lt 600)
|
||||
} | Out-Null
|
||||
if ($SetCookie) {
|
||||
Send-CDP 'Network.setCookie' @{
|
||||
name = 'daili_sid'; value = $token; domain = '127.0.0.1'; path = '/'; httpOnly = $true
|
||||
} | Out-Null
|
||||
}
|
||||
Send-CDP 'Page.navigate' @{ url = "$Base$Path" } | Out-Null
|
||||
Start-Sleep -Milliseconds 900
|
||||
$shot = Send-CDP 'Page.captureScreenshot' @{ format = 'png'; captureBeyondViewport = $false }
|
||||
$file = Join-Path $OutDir "$Name.png"
|
||||
[System.IO.File]::WriteAllBytes($file, [Convert]::FromBase64String($shot.result.data))
|
||||
Write-Host (" shot {0,-16} <- {1}{2}" -f $Name, $Base, $Path) -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
Send-CDP 'Network.enable' @{} | Out-Null
|
||||
Send-CDP 'Page.enable' @{} | Out-Null
|
||||
|
||||
Shoot 'login' '/login' 1440 900 $false
|
||||
Shoot 'calendar' '/calendar' 1440 1000
|
||||
Shoot 'customers' '/customers' 1440 900
|
||||
if ($custID) { Shoot 'customer-detail' "/customers/$custID" 1440 1100 }
|
||||
if ($custID -and $subID) {
|
||||
$today = (Get-Date).ToString('yyyy-MM-dd')
|
||||
Shoot 'extend-panel' ("/customers/{0}?extend={1}#extend" -f $custID, $subID) 1440 1200
|
||||
}
|
||||
Shoot 'subscriptions' '/subscriptions?state=all' 1440 900
|
||||
Shoot 'packages' '/packages' 1440 800
|
||||
Shoot 'package-form' '/packages/new' 1200 940
|
||||
Shoot 'customer-form' '/customers/new' 1000 900
|
||||
Shoot 'settings' '/settings' 1440 1000
|
||||
Shoot 'screen' '/screen' 1600 900
|
||||
Shoot 'calendar-mobile' '/calendar' 390 844
|
||||
|
||||
$script:ws.Dispose()
|
||||
Stop-Process -Id $chromeProc.Id -Force -ErrorAction SilentlyContinue
|
||||
Write-Host '截图完成' -ForegroundColor Green
|
||||
Reference in New Issue
Block a user