import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' import { clearGlobalErrors, dismissGlobalError, GLOBAL_ERROR_FALLBACK, GLOBAL_ERROR_LIMIT, normalizeErrorMessage, notifyGlobalError, snapshotGlobalErrors, subscribeGlobalError, } from '../src/layout/error-bus.ts' test('test_task_013_global_error_notice_normal_primary_path', () => { // 正常主路径:通知一条错误进入提示流。 clearGlobalErrors() notifyGlobalError('登录已过期') const list = snapshotGlobalErrors() assert.equal(list.length, 1) assert.equal(list[0].message, '登录已过期') }) test('test_task_013_global_error_notice_normal_variant_input', () => { // 正常变体:不同错误逐条追加、顺序保留。 clearGlobalErrors() notifyGlobalError('错误A') notifyGlobalError('错误B') assert.deepEqual(snapshotGlobalErrors().map((i) => i.message), ['错误A', '错误B']) }) test('test_task_013_global_error_notice_normal_repeated_operation_is_idempotent', () => { // 正常重复:相同消息重复通知产生稳定独立的条目(id 单调递增)。 clearGlobalErrors() notifyGlobalError('重试') const first = snapshotGlobalErrors()[0] notifyGlobalError('重试') const second = snapshotGlobalErrors()[1] assert.notEqual(first.id, second.id) assert.equal(first.message, second.message) }) test('test_task_013_global_error_notice_boundary_empty_input', () => { // 边界空值:空/缺省消息回落到通用提示,不出现空白条目。 clearGlobalErrors() assert.equal(normalizeErrorMessage(' '), GLOBAL_ERROR_FALLBACK) notifyGlobalError('') assert.equal(snapshotGlobalErrors()[0].message, GLOBAL_ERROR_FALLBACK) }) test('test_task_013_global_error_notice_boundary_single_item', () => { // 边界单元素:单条可按 id 关闭。 clearGlobalErrors() notifyGlobalError('单条') const [item] = snapshotGlobalErrors() dismissGlobalError(item.id) assert.deepEqual(snapshotGlobalErrors(), []) }) test('test_task_013_global_error_notice_boundary_limit_or_missing_field', () => { // 边界上限:超过上限只保留最近 N 条,不堆积。 clearGlobalErrors() for (let i = 1; i <= GLOBAL_ERROR_LIMIT + 2; i += 1) notifyGlobalError(`err${i}`) const list = snapshotGlobalErrors() assert.equal(list.length, GLOBAL_ERROR_LIMIT) assert.equal(list[list.length - 1].message, `err${GLOBAL_ERROR_LIMIT + 2}`) }) test('test_task_013_global_error_notice_invalid_input_rejected', () => { // 异常输入:Error/数字/对象负载均归一化到可展示文案。 clearGlobalErrors() assert.equal(normalizeErrorMessage(new Error('连接被拒绝')), '连接被拒绝') assert.equal(normalizeErrorMessage(500), GLOBAL_ERROR_FALLBACK) assert.equal(normalizeErrorMessage({ code: 1 }), GLOBAL_ERROR_FALLBACK) assert.equal(normalizeErrorMessage(new Error(' ')), GLOBAL_ERROR_FALLBACK) }) test('test_task_013_global_error_notice_dependency_failure_returns_actionable_message', () => { // 依赖失败:订阅提供取消函数,取消后不再回调(容器卸载无泄漏)。 clearGlobalErrors() const seen: number[] = [] const unsubscribe = subscribeGlobalError((list) => seen.push(list.length)) notifyGlobalError('a') unsubscribe() notifyGlobalError('b') assert.deepEqual(seen, [0, 1], '取消订阅后不应再收到更新') clearGlobalErrors() // 容器组件本身在卸载时执行清理并保留可关闭错误项。 const container = readSource('src/layout/GlobalErrorContainer.vue') assert.match(container, /unsubscribe/) assert.match(container, /dismissGlobalError/) const layout = readSource('src/layout/AdminLayout.vue') assert.match(layout, /GlobalErrorContainer/) })