Files
jiapuapp/tests/root-pages-visual-contract.ps1
T
2026-07-23 08:24:07 +08:00

22 lines
3.8 KiB
PowerShell

$ErrorActionPreference = 'Stop'
function Assert-Contains { param([string]$Content,[string]$Expected,[string]$Message); if($Content -notmatch [regex]::Escape($Expected)){ throw $Message } }
$root=Split-Path -Parent $PSScriptRoot
$files=@('pages/family/f01-family-feed.vue','pages/family/f02-publish-feed.vue','pages/notification/n01-message-center.vue','pages/profile/m01-profile-home.vue')
$pages=@{}
foreach($file in $files){$content=Get-Content -LiteralPath(Join-Path $root $file)-Raw -Encoding utf8;if($content-match"@/utils/api\.js|\bappApi\b"){ throw "$file must not connect the API layer" };Assert-Contains $content 'ModulePageBackground' "$file must use its module background";$pages[$file]=$content}
$g01=Get-Content -LiteralPath(Join-Path $root 'pages/genealogy/g01-my-genealogies.vue')-Raw -Encoding utf8
if($g01-match"@/utils/api\.js|\bappApi\b"){ throw 'G01 must not connect the API layer' }
Assert-Contains $g01 'GenealogyPageBackground' 'G01 must use its genealogy background'
if($g01 -notmatch '(?s)<PageHeader\s+root\s+notice\s+title="我的家谱"[^>]+@notice="toNotifications"') { throw 'G01 root header must expose the notice action without a back control' }
if($pages['pages/family/f01-family-feed.vue'] -notmatch '(?s)<PageHeader\s+root\s+title="家族动态"\s+:action="hasValidContext \? ''发布'' : ''''"\s+@action="toPublish"') { throw 'F01 root header must expose publish only for a valid family context and without a back control' }
if($pages['pages/profile/m01-profile-home.vue'] -notmatch '(?s)<PageHeader\s+root\s+title="我的"[^>]+@action="toProfile"') { throw 'M01 root header must expose profile editing without a back control' }
foreach($required in @('feed-state--list','feed-state--empty','feed-state--error','@include adaptive.adaptive-family-letter;','openPage("F02"')){Assert-Contains $pages['pages/family/f01-family-feed.vue'] $required "Missing F01 contract: $required"}
foreach($required in @('publish-state--form','publish-state--preview','publish-state--error','publish-state--invalid','@include adaptive.adaptive-family-content;','@include adaptive.adaptive-family-field;','AppButton','AppDialog','AppToast')){Assert-Contains $pages['pages/family/f02-publish-feed.vue'] $required "Missing F02 contract: $required"}
foreach($required in @('notice-state--loading','notice-state--list','notice-state--empty','notice-state--error','@include adaptive.adaptive-notification-content;','AppButton','AppLoading','AppToast')){Assert-Contains $pages['pages/notification/n01-message-center.vue'] $required "Missing N01 contract: $required"}
if($pages['pages/notification/n01-message-center.vue'] -notmatch '(?s)openPage\(\s*"G10",\s*\{ genealogyId: genealogyId\.value \},\s*"N01",?\s*\)'){throw 'N01 review entry must use the registered G10 route with N01 as source'}
if($pages['pages/notification/n01-message-center.vue']-match'application-status-card\.png'){throw 'N01 must not reuse the genealogy application card'}
foreach($required in @('profile-state--ready','profile-state--error','@include adaptive.adaptive-profile-summary;','brand-seal.png','auth-divider-knot.png','chevron-right.png','AppButton','openPage("M02", {}, "M01")')){Assert-Contains $pages['pages/profile/m01-profile-home.vue'] $required "Missing M01 contract: $required"}
foreach($entry in @($pages['pages/notification/n01-message-center.vue'],$pages['pages/profile/m01-profile-home.vue'])){if($entry-match'/pages/'){throw 'N01 and M01 must not embed raw business page paths'}}
foreach($forbidden in @('m01-profile-notice-card.png','m01-profile-menu-frame.png','g03-create-flow-panel.png','g06-search-input-wide.png','application-status-card.png')){if($pages['pages/profile/m01-profile-home.vue']-match[regex]::Escape($forbidden)){throw "M01 must not reuse repeated or opaque business asset: $forbidden"}}
Write-Output 'ROOT-PAGES-VISUAL-CONTRACT PASS'