Files
xiaoxia-saas/apps/web/src/test/components/ChunkErrorBoundary.test.tsx
T
xiaoxia 9a289e1e1f
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 4s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 5s
CI/CD Pipeline / Check push changed paths (push) Successful in 16s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 12s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 13s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 15s
CI/CD Pipeline / Build Staging API Image (push) Successful in 21s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 23s
CI/CD Pipeline / CI Gate (pull_request) Successful in 9s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 2m7s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m23s
CI/CD Pipeline / Validate - Style (push) Successful in 2m50s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m37s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m6s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m36s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m31s
CI/CD Pipeline / Validate - Security (push) Successful in 5m21s
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 241h44m7s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 241h46m52s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 241h46m53s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 241h47m15s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 241h47m22s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 241h47m25s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 241h47m26s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 241h47m27s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 241h47m38s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 241h47m27s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 241h47m39s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 241h47m41s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 241h47m39s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 241h47m42s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 241h48m8s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 241h48m9s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 241h48m21s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 241h48m22s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 241h48m22s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 241h48m23s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 241h48m24s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 241h48m24s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 241h48m25s
CI/CD Pipeline / PR Build Web Image (push) Failing after 241h48m25s
CI/CD Pipeline / Frontend Lint (push) Failing after 241h48m27s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 241h48m41s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 241h48m43s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 242h21m52s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 242h22m16s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 242h22m43s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 242h22m57s
CI/CD Pipeline / PR Build API Image (push) Failing after 242h23m2s
fix: 发版后懒加载 chunk 失效白屏——ErrorBoundary 自动刷新 + lazy 重试 (#1732)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-06 13:35:35 +08:00

80 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
import { render, screen, fireEvent } from "@testing-library/react"
import { Button } from "antd"
import { useState } from "react"
import ChunkErrorBoundary from "@/components/common/ChunkErrorBoundary"
import * as chunkUtils from "@/utils/chunkLoadError"
// reload 函数 mock 掉(jsdom 不支持真实 window.location.reload)
vi.mock("@/utils/chunkLoadError", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/utils/chunkLoadError")>()
return {
...actual,
reloadForChunkError: vi.fn(),
goHomeRecover: vi.fn(),
}
})
const { reloadForChunkError, goHomeRecover } = vi.mocked(chunkUtils)
/** 渲染时直接抛错的子组件 */
const Boom: React.FC<{ error: Error }> = ({ error }) => {
throw error
}
/** 点击按钮后才抛 chunk 错误的子组件 */
const ChunkBoomButton: React.FC = () => {
const [boom, setBoom] = useState(false)
if (boom) {
throw new TypeError("Failed to fetch dynamically imported module: /assets/x.js")
}
return <Button onClick={() => setBoom(true)}>boom</Button>
}
const renderBoundary = (ui: React.ReactNode) =>
render(<ChunkErrorBoundary>{ui}</ChunkErrorBoundary>)
beforeEach(() => {
sessionStorage.clear()
vi.clearAllMocks()
// error boundary 捕获后 React 会打 error log,静默掉
vi.spyOn(console, "error").mockImplementation(() => {})
})
afterEach(() => {
vi.restoreAllMocks()
sessionStorage.clear()
})
describe("ChunkErrorBoundary", () => {
it("正常渲染 children", () => {
renderBoundary(<div>hello-child</div>)
expect(screen.getByText("hello-child")).toBeInTheDocument()
})
it("首次捕获 chunk 错误 → 自动刷新(reloadForChunkError)并显示自动刷新提示", () => {
renderBoundary(<ChunkBoomButton />)
fireEvent.click(screen.getByText("boom"))
expect(reloadForChunkError).toHaveBeenCalledTimes(1)
expect(screen.getByText(/正在自动刷新/)).toBeInTheDocument()
})
it("已刷新过仍失败 → 不再自动刷新,显示手动兜底按钮", () => {
// 模拟"本会话已经自动刷新过一次"
sessionStorage.setItem("chunk_error_reloaded_at", String(Date.now()))
renderBoundary(
<Boom error={new TypeError("Failed to fetch dynamically imported module: /assets/y.js")} />,
)
expect(reloadForChunkError).not.toHaveBeenCalled()
expect(screen.getByText("系统已更新")).toBeInTheDocument()
// 点击兜底按钮 → goHomeRecover(跳首页,不刷新当前 URL)
fireEvent.click(screen.getByText("刷新并返回首页"))
expect(goHomeRecover).toHaveBeenCalledTimes(1)
})
it("非 chunk 错误 → 显示通用错误页,不触发 chunk 自动刷新", () => {
renderBoundary(<Boom error={new Error("普通业务报错")} />)
expect(reloadForChunkError).not.toHaveBeenCalled()
expect(screen.getByText("页面出现异常")).toBeInTheDocument()
})
})