Files
Clipboard/test/echo-wiring.test.js
2026-09-12 13:59:12 +08:00

85 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/**
* 回声抑制的**接线**断言main.js 必须真的在用 lib/echo-suppress.js 的本体。
*
* 为什么需要这份测试echo-suppress.test.js 只测 lib 本体。本体的行为全对,
* 也挡不住 main.js 把接线改坏 —— 比如漏调 seed每次重启把剪贴板里的旧内容
* 重入一次)、漏调 markOwnWrite自己的复制被当新内容重复入库、observe 的
* 返回值不再按四态分发('empty' 当 'new' 入库 = 空剪贴板噪音灌满列表)。
* lib 测试对这类回归永远是绿的 —— 正是这个文件头当年批评旧测试的那句话。
*
* 风格沿用 window-factory.test.js / window-lifecycle.test.js 的静态断言:
* grep main.js 源码里的调用点。静态断言测不了运行时,但能拦住"重构时把
* 调用点删了/改名了"这类最常见的接线回归,成本几乎为零。
*/
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
const MAIN = fs.readFileSync(path.join(__dirname, '..', 'main.js'), 'utf8');
let passed = 0;
function check(name, fn) {
try { fn(); console.log(' ok -', name); passed++; }
catch (e) { console.error(' FAIL -', name, '\n', e.message); process.exitCode = 1; }
}
check('main.js 引用的是 lib/echo-suppress 本体', () => {
assert.match(MAIN, /require\('\.\/lib\/echo-suppress'\)/);
const { createEchoSuppress } = require('../lib/echo-suppress.js');
assert.strictEqual(typeof createEchoSuppress, 'function');
});
check('文本 / 图片各一个独立状态机实例(两份状态不能共用)', () => {
assert.match(MAIN, /textSuppress\s*=\s*createEchoSuppress\(\)/);
assert.match(MAIN, /imageSuppress\s*=\s*createEchoSuppress\(\)/);
const instances = MAIN.match(/createEchoSuppress\(\)/g) || [];
assert.strictEqual(instances.length, 2, `应有且仅有 2 个实例,实际 ${instances.length}`);
});
check('启动采样两条线都调 seed漏掉会导致重启时旧内容重入库', () => {
assert.match(MAIN, /textSuppress\.seed\(/);
assert.match(MAIN, /imageSuppress\.seed\(/);
});
check('轮询两条线都调 observe漏掉 text 的 = 搜索不到新复制,漏掉 image 的 = 图片全丢', () => {
assert.match(MAIN, /textSuppress\.observe\(/);
assert.match(MAIN, /imageSuppress\.observe\(/);
});
check('observe 的四态都被分发empty / suppressed / new 各自有分支', () => {
// unchanged 不需要显式分支(落空即图片分支),但其余三态必须显式处理。
// 漏 'empty':清空剪贴板的 '' 会被当新文本入库;漏 'suppressed':自己的回声重复入库。
for (const verdict of ['empty', 'suppressed', 'new']) {
assert.ok(
MAIN.includes(`verdict === '${verdict}'`),
`pollClipboard 缺少对 '${verdict}' 的分发`
);
}
});
check('写剪贴板两条线都调 markOwnWrite漏掉 = 点"复制"后自己的回声被当新内容入库', () => {
assert.match(MAIN, /textSuppress\.markOwnWrite\(/);
assert.match(MAIN, /imageSuppress\.markOwnWrite\(/);
});
check('1×1 空图走 resetBaseline 而不是直接赋基线', () => {
assert.match(MAIN, /imageSuppress\.resetBaseline\(\)/);
});
check('入库失败两条线都 abortLastObserve漏掉 = 用户那条复制永久吞掉直到重启', () => {
// 用户报告"卡住"的核心修法observe 把 last 设成新内容后入库失败,
// 必须回滚基线,否则下次 tick 读到同一条内容判 'unchanged' 静默吞掉。
assert.match(MAIN, /textSuppress\.abortLastObserve\(\)/);
assert.match(MAIN, /imageSuppress\.abortLastObserve\(\)/);
});
check('复制失败的两条 catch 都 clearPending漏掉会吞掉用户下一次真实复制', () => {
const clears = MAIN.match(/textSuppress\.clearPending\(\)/g) || [];
assert.ok(clears.length >= 2, `textSuppress.clearPending 应至少 2 处clip / fav 复制),实际 ${clears.length}`);
const imgClears = MAIN.match(/imageSuppress\.clearPending\(\)/g) || [];
assert.ok(imgClears.length >= 2, `imageSuppress.clearPending 应至少 2 处,实际 ${imgClears.length}`);
});
console.log(`\n${passed} checks passed`);