Files
jiapuapp/tests/data-driven-layout-contract.ps1
T
2026-07-21 07:53:08 +08:00

85 lines
3.8 KiB
PowerShell

$ErrorActionPreference = 'Stop'
$root = Split-Path $PSScriptRoot -Parent
$allowlistPath = Join-Path $PSScriptRoot 'data-driven-layout-risk-allowlist.json'
$allowlist = Get-Content -LiteralPath $allowlistPath -Raw -Encoding UTF8 | ConvertFrom-Json
$allowed = @{}
$seenAllowed = @{}
foreach ($entry in $allowlist) {
foreach ($required in @('file', 'selector', 'risk', 'reason')) {
if (-not $entry.$required) { throw "DATA-DRIVEN-LAYOUT-CONTRACT allowlist entry missing $required" }
}
$allowed["$($entry.file)::$($entry.selector)::$($entry.risk)"] = $entry.reason
}
$violations = New-Object System.Collections.Generic.List[string]
$files = Get-ChildItem -LiteralPath (Join-Path $root 'pages'), (Join-Path $root 'components') -Recurse -File -Filter '*.vue'
function Add-Risk {
param(
[string]$File,
[string]$Selector,
[string]$Risk,
[string]$Evidence
)
$key = "$File::$Selector::$Risk"
if ($allowed.ContainsKey($key)) {
$seenAllowed[$key] = $true
} else {
$violations.Add("$File :: $Selector :: $Risk :: $Evidence")
}
}
foreach ($file in $files) {
$relative = $file.FullName.Substring($root.Length + 1).Replace('\', '/')
$source = [System.IO.File]::ReadAllText($file.FullName, [System.Text.Encoding]::UTF8)
foreach ($styleMatch in [regex]::Matches($source, '(?s)<style\b[^>]*>(?<css>.*?)</style>')) {
$css = [regex]::Replace($styleMatch.Groups['css'].Value, '(?s)/\*.*?\*/', '')
foreach ($rule in [regex]::Matches($css, '(?s)(?<selector>[^{}]+)\{(?<body>[^{}]*)\}')) {
$selector = (($rule.Groups['selector'].Value -replace '(?s)^.*@media[^\{]*', '') -replace '\s+', ' ').Trim()
$body = $rule.Groups['body'].Value
if (-not $selector) { continue }
$height = [regex]::Match($body, '(?im)(?<![-\w])height\s*:\s*(?<value>\d+(?:\.\d+)?(?:rpx|px))\s*;')
if ($height.Success) {
Add-Risk -File $relative -Selector $selector -Risk 'fixed-content-height' -Evidence "height:$($height.Groups['value'].Value)"
}
$overflow = [regex]::Match($body, '(?im)(?<![-\w])overflow(?:-[xy])?\s*:\s*hidden\s*;')
if ($overflow.Success) {
Add-Risk -File $relative -Selector $selector -Risk 'clipping-overflow' -Evidence (($overflow.Value -replace '\s+', ' ').Trim())
}
$singleLine = [regex]::Match($body, '(?im)(white-space\s*:\s*nowrap|text-overflow\s*:\s*ellipsis|-webkit-line-clamp\s*:\s*\d+)\s*;')
if ($singleLine.Success) {
Add-Risk -File $relative -Selector $selector -Risk 'single-line-truncation' -Evidence (($singleLine.Value -replace '\s+', ' ').Trim())
}
$gridRows = [regex]::Match($body, '(?im)grid-template-rows\s*:\s*(?<value>[^;{}]+)\s*;')
if ($gridRows.Success -and $gridRows.Groups['value'].Value -match '(\d+(?:\.\d+)?%|\d+(?:\.\d+)?(?:rpx|px))') {
Add-Risk -File $relative -Selector $selector -Risk 'fixed-grid-track' -Evidence "grid-template-rows:$((($gridRows.Groups['value'].Value -replace '\s+', ' ').Trim()))"
}
$repeat = [regex]::Match($body, '(?im)grid-template-(?:columns|rows)\s*:\s*repeat\(\s*(?<count>\d+)\s*,')
if ($repeat.Success -and [int]$repeat.Groups['count'].Value -ge 12) {
Add-Risk -File $relative -Selector $selector -Risk 'fixed-capacity-canvas' -Evidence (($repeat.Value -replace '\s+', ' ').Trim())
}
}
}
}
$stale = @($allowed.Keys | Where-Object { -not $seenAllowed.ContainsKey($_) } | Sort-Object)
if ($stale.Count -gt 0) {
$stale | ForEach-Object { Write-Output "STALE ALLOWLIST :: $_" }
throw "DATA-DRIVEN-LAYOUT-CONTRACT found $($stale.Count) stale allowlist entries."
}
if ($violations.Count -gt 0) {
$violations | Sort-Object | ForEach-Object { Write-Output $_ }
throw "DATA-DRIVEN-LAYOUT-CONTRACT found $($violations.Count) non-allowlisted layout capacity risks."
}
Write-Output 'DATA-DRIVEN-LAYOUT-CONTRACT PASS'