fix: 重构所有定时器测试为 Jest fake timers,彻底解决 CI Run tests 卡死
CI / test (20.x) (push) Canceled after 0s
CI / test (22.x) (push) Canceled after 0s
CI / test (24.x) (push) Canceled after 0s
CI / test (18.x) (push) Canceled after 18m34s

tests/utils.test.ts: debounce/throttle/sleep 使用 useFakeTimers + advanceTimersByTime; tests/core.test.ts: 历史栈防抖合并测试用 fake timers 替代 400ms setTimeout; tests/plugins.test.ts: autoSave 三个测试用 fake timers 替代 setTimeout/done; README.md: 更新测试徽章为 353 passed; site/index.html, site/demo.html: 更新测试数为 353
This commit is contained in:
2026-07-28 21:18:26 +08:00
parent b7a3da8176
commit cf0bc45055
6 changed files with 87 additions and 72 deletions
+39 -38
View File
@@ -61,27 +61,26 @@ describe('prefersDark', () => {
});
describe('debounce', () => {
test('延迟执行', (done) => {
beforeEach(() => jest.useFakeTimers());
afterEach(() => jest.useRealTimers());
test('延迟执行', () => {
let called = 0;
const fn = debounce(() => { called++; }, 50);
fn();
expect(called).toBe(0);
setTimeout(() => {
expect(called).toBe(1);
done();
}, 100);
jest.advanceTimersByTime(50);
expect(called).toBe(1);
});
test('多次调用只执行最后一次', (done) => {
test('多次调用只执行最后一次', () => {
let result = 0;
const fn = debounce((v) => { result = v; }, 50);
fn(1);
fn(2);
fn(3);
setTimeout(() => {
expect(result).toBe(3);
done();
}, 100);
jest.advanceTimersByTime(50);
expect(result).toBe(3);
});
test('immediate 模式立即执行', () => {
@@ -91,31 +90,30 @@ describe('debounce', () => {
expect(called).toBe(1);
});
test('保留 this 上下文', (done) => {
test('保留 this 上下文', () => {
const obj = {
val: 42,
getVal: debounce(function () { return this.val; }, 30),
};
const p = obj.getVal();
// immediate=false 时首次不返回,但 this 应正确
setTimeout(() => {
expect(typeof p).toBe('undefined'); // debounce 返回的是 undefined(异步执行)
done();
}, 60);
expect(typeof p).toBe('undefined');
jest.advanceTimersByTime(30);
});
test('传递参数', (done) => {
test('传递参数', () => {
let captured = null;
const fn = debounce((a, b) => { captured = a + b; }, 30);
fn(1, 2);
setTimeout(() => {
expect(captured).toBe(3);
done();
}, 60);
jest.advanceTimersByTime(30);
expect(captured).toBe(3);
});
});
describe('throttle', () => {
beforeEach(() => jest.useFakeTimers());
afterEach(() => jest.useRealTimers());
test('首次立即执行', () => {
let called = 0;
const fn = throttle(() => { called++; }, 50);
@@ -132,25 +130,22 @@ describe('throttle', () => {
expect(called).toBe(1);
});
test('限流期后可再次执行', (done) => {
test('限流期后可再次执行', () => {
let called = 0;
const fn = throttle(() => { called++; }, 40);
fn();
setTimeout(() => {
fn();
expect(called).toBe(2);
done();
}, 60);
jest.advanceTimersByTime(40);
fn();
expect(called).toBe(2);
});
test('保留 this 与参数', (done) => {
test('保留 this 与参数', () => {
const obj = {
n: 10,
run: throttle(function (x) { this.result = this.n + x; }, 30),
};
obj.run(5);
expect(obj.result).toBe(15);
done();
});
});
@@ -225,22 +220,28 @@ describe('formatFileSize', () => {
});
describe('sleep', () => {
beforeEach(() => jest.useFakeTimers());
afterEach(() => jest.useRealTimers());
test('返回 Promise', () => {
expect(sleep(10)).toBeInstanceOf(Promise);
});
test('延迟后 resolve', (done) => {
const start = Date.now();
sleep(50).then(() => {
const elapsed = Date.now() - start;
expect(elapsed).toBeGreaterThanOrEqual(40);
done();
});
test('延迟后 resolve', async () => {
let resolved = false;
sleep(50).then(() => { resolved = true; });
expect(resolved).toBe(false);
jest.advanceTimersByTime(50);
// 让微任务队列执行
await Promise.resolve();
expect(resolved).toBe(true);
});
test('async/await 用法', async () => {
const start = Date.now();
await sleep(30);
expect(Date.now() - start).toBeGreaterThanOrEqual(20);
let resolved = false;
const p = sleep(30).then(() => { resolved = true; });
jest.advanceTimersByTime(30);
await p;
expect(resolved).toBe(true);
});
});