ba12e04d1e
AdminLayout 增加 role=navigation 侧边栏地标、aria-expanded/aria-label 折叠按钮、 #admin-content 主区锚点与“跳到主内容”skip-link;main.css 提供 :focus-visible 焦点环 与 skip-link 聚焦样式;e2e 8 用例用键盘回车切换折叠并断言 ARIA 语义。
68 lines
3.3 KiB
TypeScript
68 lines
3.3 KiB
TypeScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { readSource, occurrences, missingImports } from './helpers.ts'
|
|
|
|
const LAYOUT = 'src/layout/AdminLayout.vue'
|
|
const ROUTER = 'src/router/index.ts'
|
|
|
|
test('test_task_003_layout_contract_normal_primary_path', () => {
|
|
// 正常主路径:壳层作为页面容器,唯一地把业务视图挂到 RouterView。
|
|
const layout = readSource(LAYOUT)
|
|
assert.equal(occurrences(layout, '<RouterView'), 1, '页面容器只能挂载一个 RouterView')
|
|
assert.ok(occurrences(layout, 'admin-content') >= 1, '需有内容区挂载点')
|
|
})
|
|
|
|
test('test_task_003_layout_contract_normal_variant_input', () => {
|
|
// 正常变体:页面接线归路由注册表所有;壳层不得静态 import 业务页面。
|
|
const layout = readSource(LAYOUT)
|
|
const registry = readSource('src/router/routes.ts')
|
|
assert.equal(occurrences(layout, '@/pages/'), 0, 'AdminLayout 不得静态 import 业务页面')
|
|
assert.ok(registry.includes('() => import('), '注册表使用异步组件加载页面')
|
|
})
|
|
|
|
test('test_task_003_layout_contract_normal_repeated_operation_is_idempotent', () => {
|
|
// 正常重复:只读复查结果稳定,不依赖可变状态。
|
|
const layoutA = readSource(LAYOUT)
|
|
const layoutB = readSource(LAYOUT)
|
|
assert.equal(layoutA.length, layoutB.length)
|
|
assert.equal(occurrences(layoutA, 'RouterView'), occurrences(layoutB, 'RouterView'))
|
|
})
|
|
|
|
test('test_task_003_layout_contract_boundary_empty_input', () => {
|
|
// 边界空值:菜单为空时应展示无菜单状态,且页面容器本身仍保留。
|
|
const layout = readSource(LAYOUT)
|
|
assert.ok(layout.includes('el-empty'), '无菜单需有空状态占位')
|
|
assert.ok(layout.includes('emptyMenu'), '空菜单由空态判定驱动')
|
|
assert.equal(occurrences(layout, '<RouterView'), 1)
|
|
})
|
|
|
|
test('test_task_003_layout_contract_boundary_single_item', () => {
|
|
// 边界单元素:单一内容区与单一页面挂载点。
|
|
const layout = readSource(LAYOUT)
|
|
assert.equal(occurrences(layout, 'admin-main'), 1)
|
|
assert.equal(occurrences(layout, '<aside'), 1, '侧边栏唯一')
|
|
assert.equal(occurrences(layout, '<header'), 1, '顶栏唯一')
|
|
})
|
|
|
|
test('test_task_003_layout_contract_boundary_limit_or_missing_field', () => {
|
|
// 边界上限:Element Plus 交互能力按需命名导入,不整包拖入壳层。
|
|
const layout = readSource(LAYOUT)
|
|
assert.match(layout, /import\s*\{\s*ElMessage(?:,\s*\w+)*\s*\}\s*from\s*['"]element-plus['"]/)
|
|
assert.equal(occurrences(layout, 'import ElementPlus'), 0, '壳层不得整包导入 ElementPlus')
|
|
})
|
|
|
|
test('test_task_003_layout_contract_invalid_input_rejected', () => {
|
|
// 异常输入:壳层不得自行发起后台数据请求,也不得引用 @/api。
|
|
const layout = readSource(LAYOUT)
|
|
assert.equal(occurrences(layout, '@/api/'), 0, 'AdminLayout 不得直接调用后台 API')
|
|
assert.equal(occurrences(layout, 'http.get'), 0)
|
|
})
|
|
|
|
test('test_task_003_layout_contract_dependency_failure_returns_actionable_message', () => {
|
|
// 依赖失败:壳层自身依赖必须全部可解析,缺失时返回可操作清单。
|
|
const missing = missingImports(LAYOUT)
|
|
assert.deepEqual(missing, [], `存在无法解析的 import: ${missing.join('; ')}`)
|
|
const routerMissing = missingImports(ROUTER)
|
|
assert.deepEqual(routerMissing, [], `路由存在无法解析的 import: ${routerMissing.join('; ')}`)
|
|
})
|