Files
jiapuapp/tests/g01-empty-state-contract.ps1
T
2026-07-17 08:08:33 +08:00

82 lines
3.9 KiB
PowerShell

$ErrorActionPreference = 'Stop'
function Assert-Match {
param([string]$Content, [string]$Pattern, [string]$Message)
if ($Content -notmatch $Pattern) { throw $Message }
}
function Assert-NoCssSurface {
param([string]$Content, [string]$ClassName)
foreach ($property in @('border', 'background', 'border-radius')) {
$pattern = "(?s)\\.$ClassName\\s*\\{[^}]*\\b$property\\s*:"
if ($Content -match $pattern) {
throw "G01 empty state must not construct .$ClassName with CSS $property"
}
}
}
$root = Split-Path -Parent $PSScriptRoot
$pages = Get-Content -LiteralPath (Join-Path $root 'pages.json') -Raw -Encoding utf8 | ConvertFrom-Json
$paths = @($pages.pages.path)
$g01 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g01-my-genealogies.vue') -Raw -Encoding utf8
$runtimeSmoke = Get-Content -LiteralPath (Join-Path $root 'tests/g01-empty-state-runtime-smoke.js') -Raw -Encoding utf8
$panelPath = Join-Path $root 'static/assets/modules/genealogy/opaque/g01-empty-panel.png'
if (-not ($paths -contains 'pages/genealogy/g01-my-genealogies')) { throw 'G01 route missing' }
if ($paths -contains 'pages/genealogy/g02-empty-genealogies') { throw 'G02 route must be removed; empty state belongs to G01' }
foreach ($required in @(
'forceEmptyState',
'query.get("state") || "default"',
'g01-empty-panel.png',
'empty-create-action',
'empty-search-action',
'empty-invite-action',
'empty-create-note',
'const createGenealogy = () =>',
'/pages/genealogy/g03-create-genealogy',
'/pages/genealogy/g06-search-genealogies',
'const hasGenealogies = computed(() => !forceEmptyState.value && list.value.length > 0);'
)) {
if ($g01 -notmatch [regex]::Escape($required)) { throw "Missing G01 empty-state contract: $required" }
}
foreach ($required in @(
'class="genealogy-fixed-zone"',
'class="genealogy-list-scroll"',
'scroll-y',
':scroll-top="listScrollCommand"',
'@scroll="handleListScroll"',
'const isListLayout = computed(',
'const resetListScroll = async () =>'
)) {
if ($g01 -notmatch [regex]::Escape($required)) { throw "Missing G01 split-scroll contract: $required" }
}
Assert-Match -Content $g01 -Pattern '(?s)<view class="genealogy-fixed-zone">.*class="current-slip".*class="shortcut-grid".*class="section-divider".*</view>\s*<scroll-view[^>]*class="genealogy-list-scroll"[^>]*>.*class="genealogy-lower".*</scroll-view>' -Message 'G01 fixed and scrolling regions are not separated correctly'
Assert-Match -Content $g01 -Pattern '(?s)\.genealogy-index--split\s*\{[^}]*height:\s*100vh;[^}]*padding-bottom:\s*calc\(112rpx\s*\+\s*env\(safe-area-inset-bottom\)\)' -Message 'G01 split layout must reserve the fixed tabbar and safe area'
Assert-Match -Content $g01 -Pattern '(?s)\.genealogy-list-scroll\s*\{[^}]*height:\s*0;[^}]*flex:\s*1;' -Message 'G01 list scroll region must consume the remaining computed height'
foreach ($className in @('empty-panel', 'empty-create-action', 'empty-search-action')) {
Assert-NoCssSurface -Content $g01 -ClassName $className
}
if (-not (Test-Path -LiteralPath $panelPath)) { throw 'G01 requires its final raster empty-panel asset' }
Assert-Match -Content $runtimeSmoke -Pattern '\{\s*width:\s*412,\s*height:\s*1000\s*\}' -Message 'G01 runtime smoke must include the approved 412x1000 tall-screen stress viewport'
Add-Type -AssemblyName System.Drawing
$panel = [System.Drawing.Bitmap]::FromFile($panelPath)
try {
if ($panel.Width -ne 1122 -or $panel.Height -ne 1402) { throw 'G01 empty-panel asset must remain 1122 x 1402px' }
$corners = @(
$panel.GetPixel(0, 0).A,
$panel.GetPixel(($panel.Width - 1), 0).A,
$panel.GetPixel(0, ($panel.Height - 1)).A,
$panel.GetPixel(($panel.Width - 1), ($panel.Height - 1)).A
)
if (($corners | Where-Object { $_ -ne 255 }).Count -ne 0) { throw 'G01 empty-panel asset must have opaque outer corners' }
} finally {
$panel.Dispose()
}
Write-Output 'G01-EMPTY-STATE-CONTRACT PASS'