fix(web): recover pre-render boot failures
This commit is contained in:
@@ -2,7 +2,7 @@ import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { lazy, useEffect, useState } from 'react';
|
||||
import { MemoryRouter, useSearchParams } from 'react-router-dom';
|
||||
import { isRouteChunkError, RoutePage } from './RouteBoundary';
|
||||
import { isRouteChunkError, PlatformErrorBoundary, RoutePage } from './RouteBoundary';
|
||||
import { ROUTER_FUTURE } from './routerConfig';
|
||||
|
||||
afterEach(() => {
|
||||
@@ -12,6 +12,17 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe('route recovery boundary', () => {
|
||||
it('keeps a recovery screen when the platform shell fails outside page routes', () => {
|
||||
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const BrokenShell = () => { throw new Error('shell exploded'); };
|
||||
|
||||
render(<PlatformErrorBoundary><BrokenShell /></PlatformErrorBoundary>);
|
||||
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('平台外壳运行异常');
|
||||
expect(screen.getByRole('button', { name: /重新加载平台/ })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /返回全局监控/ })).toHaveAttribute('href', '/monitor');
|
||||
});
|
||||
|
||||
it('recognizes deployment-related lazy chunk failures', () => {
|
||||
expect(isRouteChunkError(new TypeError('Failed to fetch dynamically imported module: /assets/Monitor-old.js'))).toBe(true);
|
||||
expect(isRouteChunkError(new Error('Loading chunk 18 failed'))).toBe(true);
|
||||
|
||||
@@ -73,6 +73,32 @@ class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey
|
||||
}
|
||||
}
|
||||
|
||||
export class PlatformErrorBoundary extends Component<{ children: ReactNode }, { error?: Error }> {
|
||||
state: { error?: Error } = {};
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error } = this.state;
|
||||
if (!error) return this.props.children;
|
||||
return <section className="v2-route-error v2-root-error" role="alert">
|
||||
<span><IconAlertTriangle /></span>
|
||||
<div>
|
||||
<small>平台外壳运行异常</small>
|
||||
<h2>工作台暂时无法继续显示</h2>
|
||||
<p>系统已阻止异常扩散。请重新加载最新版本;服务端数据不会因页面恢复操作而改变。</p>
|
||||
<code>{error.message || error.name}</code>
|
||||
<footer>
|
||||
<button type="button" onClick={() => window.location.reload()}><IconRefresh />重新加载平台</button>
|
||||
<a href="/monitor"><IconHome />返回全局监控</a>
|
||||
</footer>
|
||||
</div>
|
||||
</section>;
|
||||
}
|
||||
}
|
||||
|
||||
export function RoutePage({ page: Page, label }: { page: ElementType; label: string }) {
|
||||
const location = useLocation();
|
||||
const routeKey = `${location.pathname}${location.search}`;
|
||||
|
||||
Reference in New Issue
Block a user