diff --git a/src/action-sheet/__tests__/ActionSheetMethod.test.tsx b/src/action-sheet/__tests__/ActionSheetMethod.test.tsx
index ac8804cea..bd5440ac3 100644
--- a/src/action-sheet/__tests__/ActionSheetMethod.test.tsx
+++ b/src/action-sheet/__tests__/ActionSheetMethod.test.tsx
@@ -1,10 +1,12 @@
-import { describe, it, expect, afterEach } from '@test/utils';
+import { describe, it, expect, afterEach, act } from '@test/utils';
import { show, close } from '../ActionSheetMethod';
describe('ActionSheetMethod', () => {
- afterEach(() => {
+ afterEach(async () => {
// 清理每次测试后可能残留的 ActionSheet
- close();
+ await act(async () => {
+ close();
+ });
});
describe('module exports', () => {
@@ -13,51 +15,63 @@ describe('ActionSheetMethod', () => {
expect(typeof close).toBe('function');
});
- it('should be able to call show function', () => {
- expect(() => {
- show({
- items: ['Item 1', 'Item 2'],
- });
- }).not.toThrow();
+ it('should be able to call show function', async () => {
+ await act(async () => {
+ expect(() => {
+ show({
+ items: ['Item 1', 'Item 2'],
+ });
+ }).not.toThrow();
+ });
});
- it('should be able to call close function', () => {
- expect(() => {
- close();
- }).not.toThrow();
+ it('should be able to call close function', async () => {
+ await act(async () => {
+ expect(() => {
+ close();
+ }).not.toThrow();
+ });
});
- it('should handle show with empty config', () => {
- expect(() => {
- show({});
- }).not.toThrow();
+ it('should handle show with empty config', async () => {
+ await act(async () => {
+ expect(() => {
+ show({});
+ }).not.toThrow();
+ });
});
- it('should handle show with various config options', () => {
- expect(() => {
- show({
- items: ['Test Item'],
- theme: 'grid',
- description: 'Test Description',
- visible: true,
- });
- }).not.toThrow();
+ it('should handle show with various config options', async () => {
+ await act(async () => {
+ expect(() => {
+ show({
+ items: ['Test Item'],
+ theme: 'grid',
+ description: 'Test Description',
+ visible: true,
+ });
+ }).not.toThrow();
+ });
});
- it('should handle multiple show calls', () => {
- expect(() => {
- show({ items: ['Item 1'] });
- show({ items: ['Item 2'] });
- }).not.toThrow();
+ it('should handle multiple show calls', async () => {
+ await act(async () => {
+ expect(() => {
+ show({ items: ['Item 1'] });
+ show({ items: ['Item 2'] });
+ }).not.toThrow();
+ });
});
- it('should handle show and close sequence', () => {
- expect(() => {
- show({ items: ['Item 1'] });
- close();
- show({ items: ['Item 2'] });
- close();
- }).not.toThrow();
+ it('should handle show and close sequence', async () => {
+ await act(async () => {
+ expect(() => {
+ show({ items: ['Item 1'] });
+ close();
+ show({ items: ['Item 2'] });
+ close();
+ }).not.toThrow();
+ });
});
});
});
diff --git a/src/collapse/__tests__/collapse.test.tsx b/src/collapse/__tests__/collapse.test.tsx
index 3affc7df6..b18a47ee9 100644
--- a/src/collapse/__tests__/collapse.test.tsx
+++ b/src/collapse/__tests__/collapse.test.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { describe, expect, it, render, fireEvent, vi, beforeEach, afterEach, waitFor } from '@test/utils';
+import { describe, expect, it, render, fireEvent, vi, beforeEach, afterEach, waitFor, act } from '@test/utils';
import { Collapse, CollapsePanel } from 'tdesign-mobile-react';
describe('Collapse', () => {
@@ -426,7 +426,9 @@ describe('Collapse', () => {
fireEvent.click(panels[1]);
// 此时应该有一次debounced调用
- vi.advanceTimersByTime(150);
+ act(() => {
+ vi.advanceTimersByTime(150);
+ });
// 验证最终状态
expect(onChange).toHaveBeenCalled();
@@ -452,7 +454,9 @@ describe('Collapse', () => {
fireEvent.click(panels[0]);
// 等待debounce
- vi.advanceTimersByTime(150);
+ act(() => {
+ vi.advanceTimersByTime(150);
+ });
// 验证行为
expect(onChange).toHaveBeenCalled();
diff --git a/src/dialog/__tests__/dialog.test.tsx b/src/dialog/__tests__/dialog.test.tsx
index 1d6d5225f..509ad7aa0 100644
--- a/src/dialog/__tests__/dialog.test.tsx
+++ b/src/dialog/__tests__/dialog.test.tsx
@@ -201,12 +201,14 @@ describe('Dialog', () => {
onClosed,
});
});
- instance.hide();
-
- setTimeout(() => {
- expect(document.querySelector(name)).toBeFalsy();
- expect(onClosed).toHaveBeenCalled();
- }, 1000);
+ await act(async () => {
+ instance.hide();
+ });
+ await act(async () => {
+ vi.advanceTimersByTime(1000);
+ });
+ expect(document.querySelector(name)).toBeFalsy();
+ expect(onClosed).toHaveBeenCalled();
});
it(': update', async () => {
@@ -218,10 +220,10 @@ describe('Dialog', () => {
onCancel: vi.fn(),
});
});
- instance.update({ title: '新标题' });
- setInterval(() => {
- expect(document.querySelector(`${name}__header`).textContent).toBe('新标题');
- }, 1000);
+ await act(async () => {
+ instance.update({ title: '新标题' });
+ });
+ expect(document.querySelector(`${name}__header`).textContent).toBe('新标题');
});
it(': destroy', async () => {
diff --git a/src/form/__tests__/form.test.tsx b/src/form/__tests__/form.test.tsx
index 52c17ac63..a89d7f1df 100644
--- a/src/form/__tests__/form.test.tsx
+++ b/src/form/__tests__/form.test.tsx
@@ -1690,7 +1690,7 @@ describe('Form 组件测试', () => {
expect(() => requestSubmit({} as any)).toThrow('target must be HTMLFormElement');
});
- it('HTMLFormElement 正常工作', () => {
+ it('HTMLFormElement 正常工作', async () => {
const { container } = render(
,
);
- expect(() => requestSubmit(container.querySelector('form') as HTMLFormElement)).not.toThrow();
+ await act(async () => {
+ expect(() => requestSubmit(container.querySelector('form') as HTMLFormElement)).not.toThrow();
+ });
});
});
@@ -1804,7 +1806,7 @@ describe('Form 组件测试', () => {
expect((getByPlaceholderText('name') as HTMLInputElement).value).toBe('delayed');
});
- it('taskQueue 延迟执行 - setFields/submit/reset/clearValidate/setValidateMessage', () => {
+ it('taskQueue 延迟执行 - setFields/submit/reset/clearValidate/setValidateMessage', async () => {
const submitFn = vi.fn();
const TestForm = () => {
const [form] = Form.useForm();
@@ -1832,6 +1834,7 @@ describe('Form 组件测试', () => {
};
render();
// taskQueue 方法在 flashQueue 时执行,不崩溃即可
+ await mockDelay();
});
});
@@ -2490,6 +2493,7 @@ describe('Form 组件测试', () => {
);
// 无 onSubmit 时不崩溃
expect(() => fireEvent.click(getByText('submit'))).not.toThrow();
+ await mockDelay();
});
it('onReset 不传时 reset 正常工作', async () => {
diff --git a/src/guide/__tests__/guide.test.tsx b/src/guide/__tests__/guide.test.tsx
index a457b6daf..d5e026df7 100644
--- a/src/guide/__tests__/guide.test.tsx
+++ b/src/guide/__tests__/guide.test.tsx
@@ -1,5 +1,5 @@
import '@testing-library/jest-dom';
-import { fireEvent, render, screen } from '@testing-library/react';
+import { act, fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import Guide from '../Guide';
@@ -850,7 +850,9 @@ describe('Guide Component', () => {
render();
// 快进定时器
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
const portal = document.querySelector('.t-portal-wrapper');
expect(portal).toBeInTheDocument();
@@ -863,11 +865,15 @@ describe('Guide Component', () => {
// 超时后应可见
vi.useFakeTimers();
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// 更改 current 触发可见性变化
rerender();
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
const portal = document.querySelector('.t-portal-wrapper');
expect(portal).toBeInTheDocument();
@@ -889,7 +895,9 @@ describe('Guide Component', () => {
render();
// 快进定时器以触发 showDialogGuide(内部调用 scrollToParentVisibleArea)
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// 应调用 scrollToParentVisibleArea
const portal = document.querySelector('.t-portal-wrapper');
@@ -907,7 +915,9 @@ describe('Guide Component', () => {
expect(portal).toBeInTheDocument();
// 快进定时器触发 showGuide 内的 setTimeout
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// 超时后设置为可见
portal = document.querySelector('.t-portal-wrapper');
@@ -930,7 +940,9 @@ describe('Guide Component', () => {
render();
// 快进定时器以触发 setReferenceFullW(可能存在空元素)
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// setReferenceFullW 应优雅处理空元素
const portal = document.querySelector('.t-portal-wrapper');
@@ -1152,7 +1164,9 @@ describe('Guide Component', () => {
render();
// 快进定时器以触发 showPopoverGuide(其以 isReference=true 调用 setHighlightLayerPosition)
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// 组件应渲染自定义高亮
expect(screen.getByTestId('custom-highlight-ref')).toBeInTheDocument();
@@ -1204,7 +1218,9 @@ describe('Guide Component', () => {
render();
// 快进定时器以触发 showPopoverGuide
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// 气泡模式下居中应调用 setReferenceFullW
const portal = document.querySelector('.t-portal-wrapper');
@@ -1227,7 +1243,9 @@ describe('Guide Component', () => {
render();
// 快进定时器以触发 showDialogGuide
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// showDialogGuide 应无错执行
const portal = document.querySelector('.t-portal-wrapper');
@@ -1245,7 +1263,9 @@ describe('Guide Component', () => {
expect(portal).toBeInTheDocument();
// 快进定时器触发 setPopoverVisible(true)
- vi.advanceTimersByTime(100);
+ act(() => {
+ vi.advanceTimersByTime(100);
+ });
// 现在气泡应可见
portal = document.querySelector('.t-portal-wrapper');
diff --git a/src/message/__tests__/message.test.tsx b/src/message/__tests__/message.test.tsx
index 3fb792b82..37593755b 100644
--- a/src/message/__tests__/message.test.tsx
+++ b/src/message/__tests__/message.test.tsx
@@ -264,7 +264,9 @@ describe('Message', () => {
expect(textEl).toMatchSnapshot();
// Fire transitionend -> snapshot reset state
- textEl.dispatchEvent(new Event('transitionend'));
+ act(() => {
+ textEl.dispatchEvent(new Event('transitionend'));
+ });
expect(textEl).toMatchSnapshot();
// Next play after delay (playedCount > 0): snapshot styles applied
@@ -290,7 +292,9 @@ describe('Message', () => {
expect(textEl).toMatchSnapshot();
// Fire transitionend -> snapshot reset state
- textEl.dispatchEvent(new Event('transitionend'));
+ act(() => {
+ textEl.dispatchEvent(new Event('transitionend'));
+ });
expect(textEl).toMatchSnapshot();
// Next play after delay (playedCount > 0): snapshot styles applied
diff --git a/src/pull-down-refresh/__tests__/pull-down-refresh.test.tsx b/src/pull-down-refresh/__tests__/pull-down-refresh.test.tsx
index c57d8f8b4..4e71aa715 100644
--- a/src/pull-down-refresh/__tests__/pull-down-refresh.test.tsx
+++ b/src/pull-down-refresh/__tests__/pull-down-refresh.test.tsx
@@ -167,7 +167,7 @@ describe('PullDownRefresh', () => {
expect(container.querySelector(name)).toBeTruthy();
});
- it(': maxBarHeight with string', () => {
+ it(': maxBarHeight with string', async () => {
const { container } = render(
content
@@ -177,6 +177,8 @@ describe('PullDownRefresh', () => {
const track = container.querySelector(`${name}__track`) as HTMLElement;
mockPullDown(track!, 60, 2, 0);
+ // 等待 doRefresh 内的异步状态更新在 act 中完成
+ await act(async () => {});
expect(track).toHaveStyle({ transform: 'translate3d(0, 50px, 0)' });
});
@@ -366,6 +368,8 @@ describe('PullDownRefresh', () => {
// absX = 0, absY = 60
mockPullDown(track!, 80, 4, 0);
+ // 等待 doRefresh 内的异步状态更新在 act 中完成
+ await act(async () => {});
expect(mockRefresh).toHaveBeenCalled();
});
diff --git a/src/qrcode/__tests__/qrcode.test.tsx b/src/qrcode/__tests__/qrcode.test.tsx
index a81c4fd3a..93273249a 100644
--- a/src/qrcode/__tests__/qrcode.test.tsx
+++ b/src/qrcode/__tests__/qrcode.test.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { describe, it, expect, render, fireEvent, waitFor, vi, cleanup, afterEach } from '@test/utils';
+import { describe, it, expect, render, fireEvent, waitFor, vi, cleanup, afterEach, act } from '@test/utils';
import QRCode from '../QRCode';
import { QRCodeSVG } from '../QRCodeSVG';
@@ -250,7 +250,9 @@ describe('QRCodeCanvas - image settings, excavation and crossOrigin (merged)', (
Object.defineProperty(img, 'naturalWidth', { configurable: true, get: () => 10 });
Object.defineProperty(img, 'naturalHeight', { configurable: true, get: () => 10 });
- img.dispatchEvent(new Event('load'));
+ act(() => {
+ img.dispatchEvent(new Event('load'));
+ });
await waitFor(() => {
const alphaSets = calls.globalAlpha || [];