This commit is contained in:
2026-08-03 10:51:53 +08:00
parent beb86d53e7
commit f098610a6c
15 changed files with 125 additions and 115 deletions
+21 -1
View File
@@ -3,13 +3,15 @@ import { readFileSync } from 'node:fs'
import test from 'node:test'
const layoutSource = readFileSync(new URL('./AppLayout.vue', import.meta.url), 'utf8')
const shellStyles = readFileSync(new URL('../styles/shell.css', import.meta.url), 'utf8')
const globalStyles = readFileSync(new URL('../styles/index.css', import.meta.url), 'utf8')
const routerSource = readFileSync(new URL('../router/index.ts', import.meta.url), 'utf8')
const sidebarMenuSource = readFileSync(new URL('../components/ClientSidebarMenu.vue', import.meta.url), 'utf8')
const sidebarMenuNodeSource = readFileSync(new URL('../components/ClientSidebarMenuNode.vue', import.meta.url), 'utf8')
test('source contract: renders the grouped sidebar menu and mobile drawer controls', () => {
assert.match(layoutSource, /import \{ computed, nextTick, onBeforeUnmount, onMounted, ref, watch \} from 'vue'/)
assert.match(layoutSource, /import \{[\s\S]*?\bX,[\s\S]*?\} from '@lucide\/vue'/)
assert.match(layoutSource, /import \{[\s\S]*?\bX\s*\} from '@lucide\/vue'/)
assert.match(layoutSource, /import ClientSidebarMenu from '@\/components\/ClientSidebarMenu\.vue'/)
assert.match(layoutSource, /:class="\{ 'mobile-menu-open': mobileMenuOpen \}"/)
assert.match(layoutSource, /<button\s+v-if="mobileMenuOpen"[^>]*class="client-sidebar-scrim"[^>]*aria-label="关闭导航"[^>]*@click="closeMobileMenu"/s)
@@ -42,6 +44,24 @@ test('source contract: removes the header and main content from the accessibilit
assert.match(layoutSource, /<main\s+class="client-main"\s+:inert="mobileMenuOpen"\s+:aria-hidden="mobileMenuOpen \? 'true' : undefined"\s*>/)
})
test('source contract: keeps route context and user actions in the compact header without tenant identity', () => {
assert.match(layoutSource, /const pageTitle = computed\(\(\) => route\.meta\.title \|\| '业务总览'\)/)
assert.match(layoutSource, /const pageDescription = computed\(\(\) => route\.meta\.description \|\| ''\)/)
assert.match(layoutSource, /<div class="client-header-heading">\s*<h1>\{\{ pageTitle \}\}<\/h1>\s*<p v-if="pageDescription">\{\{ pageDescription \}\}<\/p>/s)
assert.match(layoutSource, /<div class="client-user">[\s\S]*\{\{ auth\.user\?\.name \}\}[\s\S]*aria-label="退出登录"/)
assert.doesNotMatch(layoutSource, /Building2|tenant-name|tenantName/)
assert.doesNotMatch(routerSource, /showPageHeading|showTenantName/)
})
test('source contract: uses compact desktop padding and a bounded mobile header', () => {
assert.match(shellStyles, /\.top-header \{[^}]*padding: 0 20px;/)
assert.match(shellStyles, /\.client-main \{[^}]*padding: 88px 20px 40px;/)
assert.match(globalStyles, /@media \(max-width: 720px\) \{[\s\S]*\.top-header \{ padding: 0 14px; \}/)
assert.match(globalStyles, /\.client-brand \{ display: none; \}/)
assert.match(globalStyles, /\.client-header-menu-button \{ display: grid; \}/)
assert.match(globalStyles, /\.client-header-heading p, \.client-user-copy \{ display: none; \}/)
})
test('source contract: closes the mobile menu on route changes and Escape', () => {
assert.match(layoutSource, /watch\(\(\) => route\.fullPath, closeMobileMenu\)/)
assert.match(layoutSource, /event\.key === 'Escape'/)