task-16(壳层/路由): 实现退出登录入口的壳层交互(确认式)

logout.ts 定义 runLogout 纯流程(确认通过才 signOut,异常上抛由壳层反馈);
AdminLayout 用 ElMessageBox.confirm 确认后再执行 store.signOut($reset + /login)。
This commit is contained in:
2026-09-05 12:28:27 +08:00
parent 83b935c11b
commit d397c88c3c
4 changed files with 121 additions and 3 deletions
+80
View File
@@ -0,0 +1,80 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
import {
LOGOUT_CONFIRM_CANCEL,
LOGOUT_CONFIRM_MESSAGE,
LOGOUT_CONFIRM_OK,
LOGOUT_CONFIRM_TITLE,
runLogout,
} from '../src/layout/logout.ts'
test('test_task_016_shell_logout_interaction_normal_primary_path', async () => {
// 正常主路径:确认后执行退出。
let signedOut = 0
await runLogout({ confirm: async () => true, signOut: async () => { signedOut += 1 } })
assert.equal(signedOut, 1)
})
test('test_task_016_shell_logout_interaction_normal_variant_input', async () => {
// 正常变体:取消确认则不退出。
let signedOut = 0
await runLogout({ confirm: async () => false, signOut: async () => { signedOut += 1 } })
assert.equal(signedOut, 0)
})
test('test_task_016_shell_logout_interaction_normal_repeated_operation_is_idempotent', async () => {
// 正常重复:多次确认分别各执行一次,互不污染状态。
let signedOut = 0
const deps = { confirm: async () => true, signOut: async () => { signedOut += 1 } }
await runLogout(deps)
await runLogout(deps)
assert.equal(signedOut, 2)
})
test('test_task_016_shell_logout_interaction_boundary_empty_input', async () => {
// 边界空值:确认被拒后即便 signOut 有副作用也不会被调用。
let signedOut = 0
await runLogout({ confirm: async () => false, signOut: async () => { signedOut += 1 } })
assert.equal(signedOut, 0)
})
test('test_task_016_shell_logout_interaction_boundary_single_item', async () => {
// 边界单元素:signOut 异常向上抛出,供调用方反馈(不静默)。
await assert.rejects(
runLogout({ confirm: async () => true, signOut: async () => { throw new Error('退出接口失败') } }),
/退出接口失败/,
)
})
test('test_task_016_shell_logout_interaction_boundary_limit_or_missing_field', () => {
// 边界上限/缺字段:确认文案齐全、按钮语义完整。
assert.ok(LOGOUT_CONFIRM_MESSAGE.length > 0)
assert.ok(LOGOUT_CONFIRM_TITLE.length > 0)
assert.ok(LOGOUT_CONFIRM_OK.length > 0)
assert.ok(LOGOUT_CONFIRM_CANCEL.length > 0)
})
test('test_task_016_shell_logout_interaction_invalid_input_rejected', async () => {
// 异常输入:确认对话框异常(fail-closed),不得退出。
let signedOut = 0
await assert.rejects(
runLogout({
confirm: async () => { throw new Error('弹窗异常') },
signOut: async () => { signedOut += 1 },
}),
/弹窗异常/,
)
assert.equal(signedOut, 0)
})
test('test_task_016_shell_logout_interaction_dependency_failure_returns_actionable_message', () => {
// 依赖失败:壳层使用 ElMessageBox 确认 + runLogoutstore 退出清态并跳登录。
const layout = readSource('src/layout/AdminLayout.vue')
assert.match(layout, /ElMessageBox\.confirm/)
assert.match(layout, /runLogout/)
const store = readSource('src/stores/admin-session.ts')
assert.match(store, /\$reset\(\)/)
assert.match(store, /location\.assign\('\/login'\)/)
assert.match(layout, /LOGOUT_CONFIRM_MESSAGE/)
})
+1 -1
View File
@@ -47,7 +47,7 @@ test('test_task_003_layout_contract_boundary_single_item', () => {
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*\}\s*from\s*['"]element-plus['"]/)
assert.match(layout, /import\s*\{\s*ElMessage(?:,\s*\w+)*\s*\}\s*from\s*['"]element-plus['"]/)
assert.equal(occurrences(layout, 'import ElementPlus'), 0, '壳层不得整包导入 ElementPlus')
})