mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
- 通知服务新增页面可见性监听:标签页隐藏时暂停轮询,回到前台后恢复定时,离开期间超过检查周期则立即补查,节省 Netlify Function 配额 - 性能监控改为批量缓冲写入 sessionStorage,达到 1 秒间隔或 20 条上限自动落盘,并在页面隐藏/卸载时冲刷剩余缓冲,避免逐条读写开销 - 通知管理器改用原生 DOM API 构建节点并以 textContent 渲染消息,移除 innerHTML 与 escapeHtml 方法,从结构上杜绝 XSS 注入 - 新增 notification-service、notification-manager、performance-monitor 三组单元测试,覆盖后台暂停恢复、缓冲落盘、XSS 防护与空消息容错等场景 - 合并共享 Toast 样式至 design-system.css 并支持主题色定制,入场动画改为渐进增强,清理废弃请求日志中间件、SW 预缓存路径等冗余代码
91 lines
3.2 KiB
JavaScript
91 lines
3.2 KiB
JavaScript
/**
|
||
* notification-manager 单元测试
|
||
* 覆盖:通知渲染、消息文本化(XSS 防护)、空消息容错、手动关闭、自动消失、类型图标
|
||
*/
|
||
|
||
import notificationManager from '../../src/js/modules/notification-manager.js';
|
||
|
||
describe('NotificationManager', () => {
|
||
beforeEach(() => {
|
||
jest.useFakeTimers();
|
||
document.body.innerHTML = '';
|
||
// 重新初始化容器(单例只建一次,这里清空 body 后重建)
|
||
notificationManager.container = null;
|
||
notificationManager.notifications.clear();
|
||
notificationManager.init();
|
||
});
|
||
|
||
afterEach(() => {
|
||
jest.useRealTimers();
|
||
notificationManager.clearAll();
|
||
document.body.innerHTML = '';
|
||
});
|
||
|
||
it('显示成功通知并渲染消息文本', () => {
|
||
notificationManager.success('操作成功');
|
||
|
||
const notification = document.querySelector('.notification');
|
||
expect(notification).toBeTruthy();
|
||
expect(notification.classList.contains('notification-success')).toBe(true);
|
||
expect(notification.textContent).toContain('操作成功');
|
||
});
|
||
|
||
it('消息作为纯文本渲染,不解析 HTML', () => {
|
||
notificationManager.show({
|
||
message: '<img src=x onerror=alert(1)>',
|
||
type: 'info'
|
||
});
|
||
|
||
const notification = document.querySelector('.notification');
|
||
expect(notification.querySelector('img')).toBeNull();
|
||
expect(notification.textContent).toContain('<img src=x onerror=alert(1)>');
|
||
});
|
||
|
||
it('null/undefined 消息不应抛错', () => {
|
||
expect(() => notificationManager.show({ message: null, type: 'info' })).not.toThrow();
|
||
expect(() => notificationManager.show({ message: undefined, type: 'info' })).not.toThrow();
|
||
});
|
||
|
||
it('手动关闭按钮可隐藏通知', () => {
|
||
const id = notificationManager.show({ message: '可关闭', type: 'info', closable: true });
|
||
|
||
const closeBtn = document.querySelector('.notification-close');
|
||
expect(closeBtn).toBeTruthy();
|
||
closeBtn.click();
|
||
|
||
// hide 后 300ms 移除元素
|
||
jest.advanceTimersByTime(400);
|
||
expect(document.querySelector('.notification')).toBeNull();
|
||
expect(notificationManager.notifications.has(id)).toBe(false);
|
||
});
|
||
|
||
it('closable=false 时不渲染关闭按钮', () => {
|
||
notificationManager.show({ message: '不可关闭', type: 'info', closable: false });
|
||
expect(document.querySelector('.notification-close')).toBeNull();
|
||
});
|
||
|
||
it('达到持续时间后自动消失', () => {
|
||
notificationManager.success('自动消失', 1000);
|
||
expect(document.querySelector('.notification')).toBeTruthy();
|
||
|
||
jest.advanceTimersByTime(1400);
|
||
expect(document.querySelector('.notification')).toBeNull();
|
||
});
|
||
|
||
it('各类型映射正确的图标类', () => {
|
||
const cases = [
|
||
['success', 'fa-check-circle'],
|
||
['warning', 'fa-exclamation-triangle'],
|
||
['error', 'fa-times-circle'],
|
||
['info', 'fa-info-circle']
|
||
];
|
||
|
||
cases.forEach(([type, iconClass]) => {
|
||
notificationManager.show({ message: type, type });
|
||
const icon = document.querySelectorAll('.notification-icon')[document.querySelectorAll('.notification').length - 1];
|
||
expect(icon.classList.contains(iconClass)).toBe(true);
|
||
notificationManager.clearAll();
|
||
});
|
||
});
|
||
});
|