'use strict'; const assert = require('node:assert'); const events = require('node:events'); let spawnedCount = 0; class MockChild extends events.EventEmitter { constructor() { super(); this.stdin = { write: (s) => { this._lastWrite = s; }, end: () => {} }; this.stdout = new events.EventEmitter(); this.stderr = new events.EventEmitter(); spawnedCount++; } } const cp = require('node:child_process'); const origSpawn = cp.spawn; cp.spawn = () => new MockChild(); const KeyHelper = require('../keyhelper.js'); let passed = 0; function check(name, fn) { return Promise.resolve() .then(fn) .then(() => { console.log(' ok -', name); passed++; }) .catch((e) => { console.error(' FAIL -', name, e.message); process.exitCode = 1; }); } (async () => { await check('spawn is invoked when KeyHelper is constructed', () => { const kh = new KeyHelper(); assert.strictEqual(spawnedCount >= 1, true); kh.dispose(); }); await check('sendCtrlV returns a promise (no immediate throw)', () => { const kh = new KeyHelper(); const p = kh.sendCtrlV(); assert.ok(p instanceof Promise); kh.dispose(); // dispose() 必须把挂起的请求结掉。否则 10 秒后的超时会变成一个 // 没人接的 rejection,并且那个 timer 会一直吊着事件循环。 return assert.rejects(p, /disposed/); }); await check('dispose() clears pending map and does not leave timers', () => { const kh = new KeyHelper(); const p = kh.sendCtrlV(); assert.strictEqual(kh.pending.size, 1); kh.dispose(); assert.strictEqual(kh.pending.size, 0); return p.catch(() => {}); }); await check('dispose() stops the reconnect loop', () => { const kh = new KeyHelper(); const before = spawnedCount; kh.dispose(); kh.ps.emit('exit', 1); // 已 dispose,不应再重连 return new Promise((r) => setTimeout(r, 700)) .then(() => assert.strictEqual(spawnedCount, before)); }); cp.spawn = origSpawn; console.log(`\n${passed} checks passed`); })();