Files
jiapuapp/tests/t01-tree-state-contract.ps1
T
2026-07-29 18:16:08 +08:00

104 lines
4.0 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
$pagePath = Join-Path $root 'pages/tree/t01-tree-overview.vue'
$t02Path = Join-Path $root 'pages/tree/t02-pedigree-overview.vue'
$page = Get-Content -LiteralPath $pagePath -Raw -Encoding utf8
$pedigree = Get-Content -LiteralPath $t02Path -Raw -Encoding utf8
$pages = Get-Content -LiteralPath (Join-Path $root 'pages.json') -Raw -Encoding utf8
if (-not (Test-Path -LiteralPath $t02Path)) { throw 'T02 pedigree route file must exist' }
if ($pages -notmatch 'pages/tree/t02-pedigree-overview') { throw 'T02 pedigree route must be registered in pages.json' }
foreach ($required in @(
'appApi.getTree',
'createRequestController',
'isRequestCancelled',
'treeRequestController.abort()',
'onShow(() =>',
'requestController: treeRequestController',
'genealogyContext.isCurrentGenealogyInvalidated()',
'genealogyContext.setCurrentGenealogyId(genealogyId.value)'
)) { Assert-Contains $page $required "Missing T01 remote data contract: $required" }
foreach ($required in @(
'@action="toPedigree"',
'const treeState = ref("loading")',
'tree-state--tree',
'tree-state--landscape',
'tree-state--empty',
'tree-state--error',
'tree-canvas--state',
'v-for="member in layoutMembers"',
'class="member-node__portrait"',
'class="lineage-pan-cue"',
'scroll-x',
':scroll-left="treeState === ''tree'' ? treeScrollLeft : 0"',
'@scroll="handleTreeScroll"',
':style="nodeGridStyle(member)"',
'const nodeGridStyle = (member)',
'class="generation-rail"',
'class="generation-band"',
'v-for="row in generationRows"',
':style="generationBandStyle(row)"',
'const treeMetrics = computed(',
':style="treeState === ''tree'' ? treeMetricsStyle : undefined"',
'const recenterSelectedMember = async () =>',
'class="tree-recenter"',
'class="member-action-profile"',
'const openMemberPanel = (member) =>',
'"T03",',
'routeKey: "T04"',
'"T06",',
'openPage("T07"'
)) { Assert-Contains $page $required "Missing T01 tree contract: $required" }
foreach ($required in @(
'@action="toTree"',
'const pedigreePages = computed(() =>',
'class="pedigree-sheet"',
'class="pedigree-column pedigree-column--legend"',
'v-for="(page, pageIndex) in pedigreePages"',
'v-for="(member, memberIndex) in page.members"',
'class="pedigree-swiper"',
':disable-touch="false"',
'scroll-y',
'const handlePedigreePageChange = (event)',
'const toTree = () =>'
)) { Assert-Contains $pedigree $required "Missing T02 pedigree contract: $required" }
foreach ($fixtureToken in @('getGenealogyFixtureAccess', 'treeMembers', 'mockTree')) {
if ($page -match [regex]::Escape($fixtureToken)) {
throw "T01 must not use a fixture as its remote tree source: $fixtureToken"
}
}
$invalidatedGuardIndex = $page.IndexOf('genealogyContext.isCurrentGenealogyInvalidated()')
$contextWriteIndex = $page.IndexOf('genealogyContext.setCurrentGenealogyId(genealogyId.value)')
if ($invalidatedGuardIndex -lt 0 -or $contextWriteIndex -le $invalidatedGuardIndex) {
throw 'T01 must reject an invalidated genealogy before any route can rewrite the current context'
}
if ($page -match '<scroll-view\s+class="tree-stage"') {
throw 'T01 outer vertical tree owner must preserve the stage grid instead of wrapping its grid children'
}
if ($page -notmatch '(?s)\.tree-stage--lineage\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*144rpx minmax\(0, 1fr\);') {
throw 'T01 generation rail must stay beside the tree canvas'
}
if ($page -notmatch '(?s)\.member-node\s*\{[^}]*position:\s*relative;') {
throw 'T01 graph node must retain its positioned tree layout'
}
if ($pedigree -match 'scroll-x|treeScrollLeft|changeGeneration =') {
throw 'T02 must leave horizontal gestures to the native swiper, not an outer scroll or click pager'
}
if ($page -notmatch 'openPage\(\s*"T02"') {
throw 'T01 must expose the pedigree page from the tree header'
}
Write-Output 'T01-TREE-STATE-CONTRACT PASS'