UI 改进: - 设置页面内嵌显示(不再弹窗) - 临时邮箱卡片添加验证码提取按钮 - 导入账号弹窗 textarea 样式优化 - 账号头像 8 色渐变轮转 - 临时邮箱页顶栏邮箱点击复制 - 邮箱管理页自动选中默认分组 - 切换分组时重置邮件列 UI - 修复收件箱/垃圾邮件 Tab 切换(.folder-tab → .email-tab) - 修复从临时邮箱返回后账号列数据残留 - 仪表盘分组列表可点击导航 恢复: - 恢复 .github/ CI 工作流 - 恢复 tests/ 测试目录 - 恢复 docs/ 文档目录 - 恢复 CONTRIBUTING.md, SECURITY.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
114 lines
2.2 KiB
JavaScript
114 lines
2.2 KiB
JavaScript
/**
|
|
* Jest 测试环境设置
|
|
* 模拟浏览器 API 和 DOM 环境
|
|
*/
|
|
|
|
// 模拟 localStorage
|
|
class LocalStorageMock {
|
|
constructor() {
|
|
this.store = {};
|
|
}
|
|
|
|
clear() {
|
|
this.store = {};
|
|
}
|
|
|
|
getItem(key) {
|
|
return this.store[key] || null;
|
|
}
|
|
|
|
setItem(key, value) {
|
|
this.store[key] = String(value);
|
|
}
|
|
|
|
removeItem(key) {
|
|
delete this.store[key];
|
|
}
|
|
|
|
get length() {
|
|
return Object.keys(this.store).length;
|
|
}
|
|
|
|
key(index) {
|
|
const keys = Object.keys(this.store);
|
|
return keys[index] || null;
|
|
}
|
|
}
|
|
|
|
global.localStorage = new LocalStorageMock();
|
|
// jsdom 环境下 window.localStorage 可能是独立实例;这里统一指向 mock,避免跨用例污染
|
|
try {
|
|
if (typeof window !== 'undefined') {
|
|
Object.defineProperty(window, 'localStorage', {
|
|
configurable: true,
|
|
value: global.localStorage
|
|
});
|
|
}
|
|
} catch (_) {
|
|
// ignore
|
|
}
|
|
|
|
// 模拟主应用的 toast(避免 LayoutManager.notify() 退化到 window.alert,触发 jsdom 的 not implemented 报错)
|
|
global.showToast = () => {};
|
|
try {
|
|
if (typeof window !== 'undefined') {
|
|
window.showToast = global.showToast;
|
|
}
|
|
} catch (_) {
|
|
// ignore
|
|
}
|
|
|
|
// 模拟 requestAnimationFrame
|
|
global.requestAnimationFrame = (callback) => {
|
|
return setTimeout(callback, 16);
|
|
};
|
|
|
|
global.cancelAnimationFrame = (id) => {
|
|
clearTimeout(id);
|
|
};
|
|
|
|
// 模拟 performance.now
|
|
if (!global.performance) {
|
|
global.performance = {};
|
|
}
|
|
global.performance.now = () => Date.now();
|
|
|
|
// 模拟 CSS.supports
|
|
if (!global.CSS) {
|
|
global.CSS = {};
|
|
}
|
|
global.CSS.supports = (property, value) => {
|
|
// 简单模拟,假设支持所有现代 CSS 特性
|
|
return true;
|
|
};
|
|
|
|
// 模拟 getComputedStyle
|
|
global.getComputedStyle = (element) => {
|
|
return {
|
|
getPropertyValue: (prop) => {
|
|
if (
|
|
element &&
|
|
element.style &&
|
|
typeof element.style.getPropertyValue === 'function'
|
|
) {
|
|
return element.style.getPropertyValue(prop) || '';
|
|
}
|
|
return '';
|
|
}
|
|
};
|
|
};
|
|
|
|
// 清理函数
|
|
afterEach(() => {
|
|
// 清空 localStorage
|
|
localStorage.clear();
|
|
|
|
// 清空 document.body
|
|
document.body.innerHTML = '';
|
|
|
|
// 重置 CSS 变量
|
|
if (document.documentElement.style) {
|
|
document.documentElement.style.cssText = '';
|
|
}
|
|
});
|