b79fe7dd49
Move all source code directories into src/ for cleaner project structure: - pages/, components/, utils/, config/, providers/, types/, lib/, assets/, entrypoints/ → src/ - Use WXT srcDir config to resolve @/ alias to src/ - Update tsconfig, vitest, eslint, tailwind configs - Remove scattered README.md files from subdirectories - Update documentation (AGENTS.md, CODING_STANDARDS.md, README.md) - Fix pre-existing lint error in RouterProvider.tsx Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
import { PageErrorBoundary } from '@/components/PageErrorBoundary';
|
|
|
|
// 用于触发错误的测试子组件
|
|
function ThrowError({ message }: { message: string }): never {
|
|
throw new Error(message);
|
|
}
|
|
|
|
// 正常渲染的子组件
|
|
function NormalComponent({ text }: { text: string }) {
|
|
return <div data-testid="normal-content">{text}</div>;
|
|
}
|
|
|
|
describe('PageErrorBoundary', () => {
|
|
beforeEach(() => {
|
|
// 抑制测试中故意抛出的错误日志
|
|
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
});
|
|
|
|
it('正常渲染子组件', () => {
|
|
render(
|
|
<PageErrorBoundary>
|
|
<NormalComponent text="正常内容" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
expect(screen.getByTestId('normal-content')).toHaveTextContent('正常内容');
|
|
});
|
|
|
|
it('子组件抛出错误时显示错误卡片 UI', () => {
|
|
render(
|
|
<PageErrorBoundary>
|
|
<ThrowError message="测试错误" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
|
expect(screen.getByText(/测试错误/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('点击重试按钮后恢复', async () => {
|
|
const { rerender } = render(
|
|
<PageErrorBoundary>
|
|
<ThrowError message="可恢复错误" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
|
|
|
// 将子组件替换为正常组件,然后点击重试
|
|
rerender(
|
|
<PageErrorBoundary>
|
|
<NormalComponent text="恢复后的内容" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
const retryButton = screen.getByRole('button', { name: /重新尝试/ });
|
|
retryButton.click();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('normal-content')).toHaveTextContent('恢复后的内容');
|
|
});
|
|
|
|
expect(screen.queryByText('该功能运行异常')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('resetKey 变化时自动重置错误状态', async () => {
|
|
const { rerender } = render(
|
|
<PageErrorBoundary resetKey="page-a">
|
|
<ThrowError message="页面 A 错误" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
|
|
|
// 切换 resetKey,同时提供正常子组件
|
|
rerender(
|
|
<PageErrorBoundary resetKey="page-b">
|
|
<NormalComponent text="页面 B 内容" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('normal-content')).toHaveTextContent('页面 B 内容');
|
|
});
|
|
|
|
expect(screen.queryByText('该功能运行异常')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('resetKey 不变时保持错误状态', () => {
|
|
const { rerender } = render(
|
|
<PageErrorBoundary resetKey="page-a">
|
|
<ThrowError message="初始错误" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
|
|
|
// 仅 children 变化,resetKey 不变,错误应保持
|
|
rerender(
|
|
<PageErrorBoundary resetKey="page-a">
|
|
<NormalComponent text="新内容" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
|
});
|
|
|
|
it('错误 UI 包含重试按钮', () => {
|
|
render(
|
|
<PageErrorBoundary>
|
|
<ThrowError message="按钮测试" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
const retryButton = screen.getByRole('button', { name: /重新尝试/ });
|
|
expect(retryButton).toBeInTheDocument();
|
|
});
|
|
|
|
it('错误信息以 monospace 格式显示', () => {
|
|
render(
|
|
<PageErrorBoundary>
|
|
<ThrowError message="格式化测试" />
|
|
</PageErrorBoundary>,
|
|
);
|
|
|
|
const errorText = screen.getByText(/格式化测试/);
|
|
expect(errorText).toBeInTheDocument();
|
|
expect(errorText.tagName.toLowerCase()).toBe('pre');
|
|
});
|
|
});
|