$ErrorActionPreference = 'Stop' function ConvertFrom-Utf8Base64 { param([string]$Value) return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Value)) } function Assert-Contains { param( [string]$Content, [string]$Expected, [string]$Message ) if ($Content -notmatch [regex]::Escape($Expected)) { throw $Message } } function Assert-NotContains { param( [string]$Content, [string]$Unexpected, [string]$Message ) if ($Content -match [regex]::Escape($Unexpected)) { throw $Message } } $root = Split-Path -Parent $PSScriptRoot $entryPath = Join-Path $root 'pages/auth/a01-entry.vue' $legacyLoginPath = Join-Path $root 'pages/auth/a02-login.vue' $pagesPath = Join-Path $root 'pages.json' if (-not (Test-Path -LiteralPath $entryPath)) { throw 'Missing the single A01 login page.' } if (Test-Path -LiteralPath $legacyLoginPath) { throw 'A02 was merged into A01, so the legacy page file must be removed.' } $pages = Get-Content -LiteralPath $pagesPath -Raw -Encoding utf8 | ConvertFrom-Json if ($pages.pages[0].path -ne 'pages/auth/a01-entry') { throw 'A01 must remain the first application route.' } if ($pages.pages.path -contains 'pages/auth/a02-login') { throw 'pages.json must not retain the legacy A02 route.' } if ($pages.pages.Count -ne 53) { throw "Expected 53 final routes after the A02 merge, found $($pages.pages.Count)." } $entry = Get-Content -LiteralPath $entryPath -Raw -Encoding utf8 # Final decorative surfaces must come from real bitmap assets. foreach ($asset in @( 'a01-login-header-v1.png', 'a01-login-header-exact-v2.png', 'auth-ink-scenery.png', 'a01-login-scroll-frame-v1.png', 'a01-title-divider-v2.png', 'a01-primary-button.png', 'a01-secondary-button.png', 'auth-wechat.png', 'a01-icon-phone-v1.png', 'a01-icon-lock-v1.png', 'a01-icon-eye-open-v1.png', 'a01-icon-eye-closed-pupil-v2.png', 'a01-icon-sms-code-v2.png', 'a02-agreement-unchecked.png', 'a02-agreement-checked.png' )) { Assert-Contains -Content $entry -Expected $asset -Message "A01 is missing asset reference: $asset" } foreach ($obsoleteHeaderAsset in @('auth-header.png', 'brand-seal.png', 'a01-paper-transition-v1.png')) { Assert-NotContains -Content $entry -Unexpected $obsoleteHeaderAsset -Message "A01 must use the selected design's single exact header bitmap instead of composing $obsoleteHeaderAsset." } $requiredCopy = @( '55m75b2V5a626LCx', '5a+G56CB55m75b2V', '6aqM6K+B56CB55m75b2V', '5omL5py65Y+3', '5a+G56CB', '6aqM6K+B56CB', '5b+Y6K6w5a+G56CB', '6I635Y+W6aqM6K+B56CB', '5b6u5L+h55m75b2V', '6L+Y5rKh5pyJ6LSm5Y+377yf', '5rOo5YaM6LSm5Y+3', '44CK55So5oi35Y2P6K6u44CL', '44CK6ZqQ56eB5pS/562W44CL' ) | ForEach-Object { ConvertFrom-Utf8Base64 $_ } foreach ($copy in $requiredCopy) { Assert-Contains -Content $entry -Expected $copy -Message "A01 is missing required visible copy: $copy" } foreach ($contract in @( "const activeLoginMethod = ref('password')", 'const passwordVisible = ref(false)', "const LOGIN_METHOD_STORAGE_KEY = 'a01:last-login-method'", 'uni.getStorageSync(LOGIN_METHOD_STORAGE_KEY)', 'uni.setStorageSync(LOGIN_METHOD_STORAGE_KEY, method)', 'const switchLoginMethod = (method) =>', 'const togglePasswordVisibility = () =>', "url: '/pages/auth/a04-register'", "url: '/pages/auth/a05-reset-password'", 'class="feedback-toast"', 'class="verification-layer"', 'class="login-submit"' )) { Assert-Contains -Content $entry -Expected $contract -Message "A01 is missing state or interaction contract: $contract" } foreach ($nativeUi in @('uni.showToast', 'uni.showModal', 'uni.showLoading', 'uni.showActionSheet')) { Assert-NotContains -Content $entry -Unexpected $nativeUi -Message "A01 must not use native UniApp feedback: $nativeUi" } Assert-NotContains -Content $entry -Unexpected "url: '/pages/auth/a02-login" -Message 'A01 must not navigate to A02.' Assert-NotContains -Content $entry -Unexpected 'brand-intro' -Message 'A01 must remove the legacy oversized brand introduction.' Assert-NotContains -Content $entry -Unexpected 'min-height: 100vh' -Message 'A01 must not depend on 100vh for its page shell.' Assert-NotContains -Content $entry -Unexpected 'exact-chrome-overlay' -Message 'A01 must not stack a second full-screen scroll perimeter over the complete scroll frame.' Assert-NotContains -Content $entry -Unexpected 'auth-design-scenery-v1.png' -Message 'A01 must not use the scenery bitmap that contains stray scroll handles.' Assert-NotContains -Content $entry -Unexpected 'a01-icon-eye-closed-v1.png' -Message 'A01 hidden-password icon must retain the center pupil instead of using the hollow crossed eye.' Assert-NotContains -Content $entry -Unexpected 'a01-icon-verification-v1.png' -Message 'A01 SMS field must use a message-code icon instead of a success-state checkmark.' Assert-NotContains -Content $entry -Unexpected 'a01-icon-sms-code-v1.svg' -Message 'A01 SMS field must use the custom generated icon instead of the temporary third-party SVG.' Assert-NotContains -Content $entry -Unexpected '@media (max-height:' -Message 'A01 must preserve the design ratio on short screens and scroll instead of compressing with pixel breakpoints.' Write-Output 'A01-LOGIN-MERGE-CONTRACT PASS'