diff --git a/src/pages/TestDataGenerator/__tests__/index.test.tsx b/src/pages/TestDataGenerator/__tests__/index.test.tsx
new file mode 100644
index 0000000..7ba77b7
--- /dev/null
+++ b/src/pages/TestDataGenerator/__tests__/index.test.tsx
@@ -0,0 +1,82 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+vi.mock('../hooks/useGenerator', () => ({
+ useGenerator: () => ({
+ isGenerating: false,
+ progress: null,
+ result: null,
+ error: null,
+ generate: vi.fn(),
+ cancel: vi.fn(),
+ clearResult: vi.fn(),
+ }),
+}));
+
+vi.mock('@/utils/ruleStorage', () => ({
+ MAX_RULES: 20,
+ getAll: vi.fn(() => []),
+ save: vi.fn(),
+ deleteRule: vi.fn(),
+ duplicate: vi.fn(),
+ recordUse: vi.fn(),
+ search: vi.fn(() => []),
+ exportRules: vi.fn(() => '[]'),
+ importRules: vi.fn(() => ({ success: 0, failed: 0, errors: [] })),
+}));
+
+vi.mock('sonner', () => ({
+ toast: {
+ success: vi.fn(),
+ error: vi.fn(),
+ },
+}));
+
+import TestDataGeneratorPage from '../index';
+import * as ruleStorage from '@/utils/ruleStorage';
+import { toast } from 'sonner';
+import type { DataRule, FieldConfig } from '@/types/testDataGenerator';
+
+const mockedRuleStorage = vi.mocked(ruleStorage);
+const mockedToast = vi.mocked(toast);
+
+const mockFields: FieldConfig[] = [
+ {
+ id: 'field-1',
+ name: 'name',
+ generatorId: 'string',
+ params: {},
+ required: true,
+ nullRate: 0,
+ unique: false,
+ },
+];
+
+const mockRule: DataRule = {
+ id: 'rule-1',
+ name: '用户数据规则',
+ description: '测试规则',
+ fields: mockFields,
+ createdAt: Date.now(),
+ updatedAt: Date.now(),
+ useCount: 0,
+};
+
+describe('TestDataGeneratorPage', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockedRuleStorage.getAll.mockReturnValue([mockRule]);
+ });
+
+ it('编辑规则时应显示包含规则名的 toast', async () => {
+ const user = userEvent.setup();
+ render();
+
+ await user.click(screen.getByText('规则管理'));
+ await user.click(screen.getByTitle('编辑'));
+
+ expect(mockedToast.success).toHaveBeenCalledWith('正在编辑规则「用户数据规则」');
+ expect(mockedToast.success).not.toHaveBeenCalledWith(expect.stringContaining('{{name}}'));
+ });
+});
diff --git a/src/pages/TestDataGenerator/components/ResultPanel.tsx b/src/pages/TestDataGenerator/components/ResultPanel.tsx
index 3bb3100..818ae09 100644
--- a/src/pages/TestDataGenerator/components/ResultPanel.tsx
+++ b/src/pages/TestDataGenerator/components/ResultPanel.tsx
@@ -84,7 +84,9 @@ export default function ResultPanel({ result }: ResultPanelProps) {
))}
{result.warnings.length > 5 && (
-
... {'还有 {{count}} 条警告'}
+
+ {`... 还有 ${result.warnings.length - 5} 条警告`}
+
)}
diff --git a/src/pages/TestDataGenerator/components/__tests__/ResultPanel.test.tsx b/src/pages/TestDataGenerator/components/__tests__/ResultPanel.test.tsx
new file mode 100644
index 0000000..0e6e124
--- /dev/null
+++ b/src/pages/TestDataGenerator/components/__tests__/ResultPanel.test.tsx
@@ -0,0 +1,42 @@
+import { describe, expect, it } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import ResultPanel from '../ResultPanel';
+import type { GenerateResult } from '@/types/testDataGenerator';
+
+function createResult(overrides: Partial = {}): GenerateResult {
+ return {
+ success: true,
+ warnings: [],
+ stats: {
+ total: 100,
+ success: 100,
+ failed: 0,
+ duration: 1200,
+ },
+ ...overrides,
+ };
+}
+
+describe('ResultPanel', () => {
+ it('警告超过 5 条时应显示剩余警告数量', () => {
+ const warnings = Array.from({ length: 8 }, (_, index) => `警告 ${index + 1}`);
+
+ render();
+
+ expect(screen.getByText('警告 1')).toBeInTheDocument();
+ expect(screen.getByText('警告 5')).toBeInTheDocument();
+ expect(screen.queryByText('警告 6')).not.toBeInTheDocument();
+ expect(screen.getByText('... 还有 3 条警告')).toBeInTheDocument();
+ expect(screen.queryByText(/\{\{count\}\}/)).not.toBeInTheDocument();
+ });
+
+ it('警告不超过 5 条时不应显示剩余提示', () => {
+ const warnings = ['警告 1', '警告 2', '警告 3'];
+
+ render();
+
+ expect(screen.getByText('警告 1')).toBeInTheDocument();
+ expect(screen.getByText('警告 3')).toBeInTheDocument();
+ expect(screen.queryByText(/还有 \d+ 条警告/)).not.toBeInTheDocument();
+ });
+});
diff --git a/src/pages/TestDataGenerator/components/__tests__/RuleManager.test.tsx b/src/pages/TestDataGenerator/components/__tests__/RuleManager.test.tsx
index b84cba5..a3ec838 100644
--- a/src/pages/TestDataGenerator/components/__tests__/RuleManager.test.tsx
+++ b/src/pages/TestDataGenerator/components/__tests__/RuleManager.test.tsx
@@ -178,6 +178,18 @@ describe('RuleManager', () => {
vi.restoreAllMocks();
});
+ it('should call onEdit when edit button clicked', async () => {
+ const user = userEvent.setup();
+ const onEdit = vi.fn();
+ mockedRuleStorage.getAll.mockReturnValue([mockRule]);
+
+ render();
+
+ await user.click(screen.getByTitle('编辑'));
+
+ expect(onEdit).toHaveBeenCalledWith(mockRule);
+ });
+
it('should show saved rule count', () => {
mockedRuleStorage.getAll.mockReturnValue([mockRule]);
diff --git a/src/pages/TestDataGenerator/index.tsx b/src/pages/TestDataGenerator/index.tsx
index 41efd20..fd0700e 100644
--- a/src/pages/TestDataGenerator/index.tsx
+++ b/src/pages/TestDataGenerator/index.tsx
@@ -130,7 +130,7 @@ export default function TestDataGeneratorPage() {
setEditingRule(rule);
setActiveTab('fields');
clearResult();
- toast.success('正在编辑规则「{{name}}」');
+ toast.success(`正在编辑规则「${rule.name}」`);
},
[clearResult],
);