This commit is contained in:
2026-09-12 13:59:12 +08:00
commit 941ce4baab
71 changed files with 13037 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
'use strict';
/**
* 回归测试:设置窗口必须真正渲染出配置内容(尤其是"数据保存位置"路径)。
*
* 守护两类曾经发生过的静默失败:
* 1. settings.js 顶层 `const themeApi` 与 contextBridge 暴露的不可配置全局属性
* window.themeApi 同名 → 脚本实例化阶段抛 SyntaxError整个文件一行都不执行。
* 2. 重构时误删了末尾的 refresh() 调用 → 函数定义了却从未调用,界面全空白。
*
* 两种情况下界面都"看起来正常"但内容为空,只有断言 DOM 实际文本才能发现。
* 运行npx electron test/settings-render.test.js
*/
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');
const assert = require('node:assert');
const DATA_DIR = 'C:\\Users\\probe\\.clipboard-app';
const cfg = {
// 只用 config.js ALLOWED_* 里的合法值,且必须是 settings.html 里存在的 <option>
autoLaunch: false, maxItems: 0, displayLimit: 300, pollMs: 1000,
hotkey: 'Ctrl+Alt+V',
dataDir: DATA_DIR, theme: 'linear', accent: 'purple', followSystem: false,
};
ipcMain.handle('settings:get', () => ({ ...cfg }));
ipcMain.handle('settings:set', () => ({ ...cfg }));
ipcMain.handle('settings:clear-history', () => 0);
ipcMain.handle('settings:open-dir', () => {});
ipcMain.handle('settings:choose-dir', () => ({ ok: false }));
ipcMain.handle('theme:get', () => ({ ...cfg }));
ipcMain.handle('theme:set', (_e, { theme }) => ({ theme, accent: cfg.accent }));
ipcMain.handle('theme:set-accent', (_e, { accent }) => ({ theme: cfg.theme, accent }));
ipcMain.handle('theme:set-follow-system', () => ({ theme: cfg.theme, accent: cfg.accent }));
// settings.js 的 initTheme() 会订阅主题变更stub 里缺这个 handler 时
// invoke 会抛错,渲染层的 catch 打 console.error被下面的"无脚本错误"
// 断言当成 SyntaxError 一类的致命问题误杀。
ipcMain.handle('theme:subscribe', () => true);
let failed = 0;
function check(name, fn) {
try { fn(); console.log(' ok -', name); }
catch (e) { console.error(' FAIL -', name, '\n ', e.message); failed++; }
}
app.whenReady().then(async () => {
// 与 main.js openSettings() 一致的窗口尺寸
const win = new BrowserWindow({
width: 420, height: 690, show: false, frame: false, resizable: false,
webPreferences: {
preload: path.join(__dirname, '..', 'preload.js'),
contextIsolation: true, nodeIntegration: false, sandbox: false,
},
});
const errors = [];
win.webContents.on('console-message', (_e, level, message) => {
// level 3 = error
if (level === 3) errors.push(message);
});
await win.loadFile(path.join(__dirname, '..', 'renderer', 'settings.html'));
await new Promise((r) => setTimeout(r, 900));
const state = await win.webContents.executeJavaScript(`(() => {
const dir = document.getElementById('opt-datadir');
const grid = document.getElementById('theme-grid');
const accents = document.getElementById('accent-grid');
return {
dirText: dir ? dir.textContent : null,
themeCards: grid ? grid.children.length : -1,
accentSwatches: accents ? accents.children.length : -1,
maxItems: (document.getElementById('opt-maxitems')||{}).value,
displayLimit: (document.getElementById('opt-displaylimit')||{}).value,
pollMs: (document.getElementById('opt-pollms')||{}).value,
hotkey: (document.getElementById('opt-hotkey')||{}).value,
};
})()`);
console.log('设置窗口渲染回归测试:');
check('settings.js 无脚本错误SyntaxError 会让整个文件不执行)', () => {
assert.deepStrictEqual(errors, []);
});
check('"数据保存位置" 渲染出真实路径', () => {
assert.strictEqual(state.dirText, DATA_DIR);
});
check('主题网格已渲染出 6 张卡片', () => {
assert.strictEqual(state.themeCards, 6);
});
check('强调色已渲染出 5 个色块', () => {
assert.strictEqual(state.accentSwatches, 5);
});
check('最大保留条数已回填配置值', () => {
assert.strictEqual(state.maxItems, '0');
});
check('界面显示条数已回填配置值', () => {
assert.strictEqual(state.displayLimit, '300');
});
check('轮询间隔已回填配置值', () => {
assert.strictEqual(state.pollMs, '1000');
});
check('唤起快捷键已回填配置值', () => {
assert.strictEqual(state.hotkey, 'Ctrl+Alt+V');
});
console.log(failed === 0 ? '\n全部通过' : `\n${failed} 项失败`);
app.exit(failed === 0 ? 0 : 1);
});