From 69a2f59ac4e8108b84e7e3704499b7dae9336a6d Mon Sep 17 00:00:00 2001 From: wangqiao258 <631391830@qq.com> Date: Tue, 28 Jul 2026 20:53:51 +0800 Subject: [PATCH] improve(pages): simplify ErrorBoundary reload UX and add unit tests Closes #544 - Change RouteErrorFallback reload to call window.location.reload() directly after clearing the session guard - Add vitest + @testing-library/react + jsdom test infrastructure for pages/ - Add ErrorBoundary.test.tsx covering 4 scenarios (chunk error reload, guard prevents double reload, non-chunk error shows fallback, reset clears guard and reloads) --- pages/package.json | 7 ++ pages/src/components/ErrorBoundary.test.tsx | 71 +++++++++++++++++++++ pages/src/components/ErrorBoundary.tsx | 2 +- pages/vitest.config.ts | 11 ++++ pages/vitest.setup.ts | 1 + 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 pages/src/components/ErrorBoundary.test.tsx create mode 100644 pages/vitest.config.ts create mode 100644 pages/vitest.setup.ts diff --git a/pages/package.json b/pages/package.json index c9ffceb4..2733e101 100644 --- a/pages/package.json +++ b/pages/package.json @@ -7,6 +7,7 @@ "dev": "webpack serve", "build": "NODE_ENV=production webpack --mode production", "typecheck": "tsc --noEmit", + "test": "vitest run", "lint": "eslint src/", "size": "size-limit" }, @@ -36,10 +37,14 @@ "@babel/preset-typescript": "^7.23.3", "@eslint/js": "^10.0.1", "@size-limit/file": "^13.0.1", + "@testing-library/jest-dom": "^7.0.0", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", "@types/dompurify": "^3.0.5", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "@types/three": "^0.185.0", + "@vitejs/plugin-react": "^6.0.4", "autoprefixer": "^10.4.16", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^14.0.0", @@ -47,6 +52,7 @@ "eslint": "^10.8.0", "eslint-plugin-react-hooks": "^7.1.1", "html-webpack-plugin": "^5.5.3", + "jsdom": "^30.0.0", "postcss": "^8.5.15", "postcss-loader": "^7.3.3", "size-limit": "^13.0.1", @@ -54,6 +60,7 @@ "tailwindcss": "^3.3.5", "typescript": "^5.3.2", "typescript-eslint": "^8.65.0", + "vitest": "^4.1.10", "webpack": "^5.89.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.2.4" diff --git a/pages/src/components/ErrorBoundary.test.tsx b/pages/src/components/ErrorBoundary.test.tsx new file mode 100644 index 00000000..e4ccf09d --- /dev/null +++ b/pages/src/components/ErrorBoundary.test.tsx @@ -0,0 +1,71 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import ErrorBoundary from './ErrorBoundary'; + +const Thrower: React.FC<{ msg: string }> = ({ msg }) => { + throw new Error(msg); +}; + +function mockReload(): ReturnType { + const reloadSpy = vi.fn(); + vi.spyOn(window, 'location', 'get').mockReturnValue({ + reload: reloadSpy, + href: 'http://localhost/', + } as unknown as Location); + return reloadSpy; +} + +describe('ErrorBoundary', () => { + beforeEach(() => { + sessionStorage.clear(); + vi.restoreAllMocks(); + }); + + it('reloads the page on a chunk load error', () => { + const reloadSpy = mockReload(); + render( + + + + ); + expect(reloadSpy).toHaveBeenCalled(); + }); + + it('does not reload twice when the guard is already set', () => { + const reloadSpy = mockReload(); + sessionStorage.setItem('ocr-chunk-reload-attempted', 'true'); + render( + + + + ); + expect(reloadSpy).not.toHaveBeenCalled(); + }); + + it('renders fallback without reloading on a non-chunk error', () => { + const reloadSpy = mockReload(); + render( + + + + ); + expect(screen.getByText('fallback-ui')).toBeTruthy(); + expect(reloadSpy).not.toHaveBeenCalled(); + }); + + it('reset() clears the guard and reloads the page', () => { + const reloadSpy = mockReload(); + const fallback = (_e: Error, reset: () => void) => ( + + ); + render( + + + + ); + sessionStorage.setItem('ocr-chunk-reload-attempted', 'true'); + fireEvent.click(screen.getByText('reload')); + expect(sessionStorage.getItem('ocr-chunk-reload-attempted')).toBeNull(); + expect(reloadSpy).toHaveBeenCalled(); + }); +}); diff --git a/pages/src/components/ErrorBoundary.tsx b/pages/src/components/ErrorBoundary.tsx index 843ac353..6343a32f 100644 --- a/pages/src/components/ErrorBoundary.tsx +++ b/pages/src/components/ErrorBoundary.tsx @@ -50,7 +50,7 @@ export default class ErrorBoundary extends React.Component