新增七个预生成分类路由、工具卡片网格与空状态。启用侧栏分类导航和当前分类选中态,修复 Windows 静态导出 RSC 路径并补充产物验证,保持静态导出与安全外链约束。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
202 lines
5.1 KiB
TypeScript
202 lines
5.1 KiB
TypeScript
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
|
|
import type { Category } from "@/types/navigation"
|
|
|
|
import { AppShell } from "./app-shell"
|
|
|
|
const categories: Category[] = [
|
|
{ id: "dev", title: "开发工具", description: "开发", icon: "code", sort: 10 },
|
|
]
|
|
|
|
describe("AppShell", () => {
|
|
beforeEach(() => window.localStorage.clear())
|
|
afterEach(() => vi.restoreAllMocks())
|
|
|
|
it("切换侧栏并持久化状态", () => {
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
>
|
|
<main>首页内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
const toggle = screen.getByRole("button", { name: "折叠侧边栏" })
|
|
fireEvent.click(toggle)
|
|
|
|
expect(screen.getByTestId("app-shell")).toHaveAttribute(
|
|
"data-sidebar-collapsed",
|
|
"true",
|
|
)
|
|
expect(window.localStorage.getItem("polaris.sidebar.collapsed")).toBe(
|
|
"true",
|
|
)
|
|
})
|
|
|
|
it("hydration 后恢复持久化状态", async () => {
|
|
window.localStorage.setItem("polaris.sidebar.collapsed", "true")
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
>
|
|
<main>首页内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId("app-shell")).toHaveAttribute(
|
|
"data-sidebar-collapsed",
|
|
"true",
|
|
)
|
|
expect(screen.getByTestId("app-shell")).toHaveAttribute(
|
|
"data-hydrated",
|
|
"true",
|
|
)
|
|
})
|
|
})
|
|
|
|
it("写入失败时内存偏好优先于旧持久化值", () => {
|
|
window.localStorage.setItem("polaris.sidebar.collapsed", "false")
|
|
vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
|
|
throw new Error("storage unavailable")
|
|
})
|
|
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
>
|
|
<main>首页内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "折叠侧边栏" }))
|
|
expect(screen.getByTestId("app-shell")).toHaveAttribute(
|
|
"data-sidebar-collapsed",
|
|
"true",
|
|
)
|
|
})
|
|
|
|
it("忽略 sessionStorage 的同名 storage 事件", () => {
|
|
window.localStorage.setItem("polaris.sidebar.collapsed", "false")
|
|
vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
|
|
throw new Error("storage unavailable")
|
|
})
|
|
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
>
|
|
<main>首页内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "折叠侧边栏" }))
|
|
fireEvent(
|
|
window,
|
|
new StorageEvent("storage", {
|
|
key: "polaris.sidebar.collapsed",
|
|
storageArea: window.sessionStorage,
|
|
}),
|
|
)
|
|
|
|
expect(screen.getByTestId("app-shell")).toHaveAttribute(
|
|
"data-sidebar-collapsed",
|
|
"true",
|
|
)
|
|
})
|
|
|
|
it("存储不可用时仍保留当前会话内交互", () => {
|
|
vi.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
|
|
throw new Error("storage unavailable")
|
|
})
|
|
vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
|
|
throw new Error("storage unavailable")
|
|
})
|
|
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
>
|
|
<main>首页内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "折叠侧边栏" }))
|
|
expect(screen.getByTestId("app-shell")).toHaveAttribute(
|
|
"data-sidebar-collapsed",
|
|
"true",
|
|
)
|
|
})
|
|
|
|
it("首页默认选中并启用分类链接", () => {
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
>
|
|
<main>首页内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
expect(screen.getByRole("link", { name: "首页" })).toHaveAttribute(
|
|
"aria-current",
|
|
"page",
|
|
)
|
|
expect(screen.getByRole("link", { name: "开发工具" })).toHaveAttribute(
|
|
"href",
|
|
"/categories/dev",
|
|
)
|
|
})
|
|
|
|
it("分类页只选中当前分类", () => {
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={false}
|
|
activeCategoryId="dev"
|
|
>
|
|
<main>分类内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
expect(screen.getByRole("link", { name: "首页" })).not.toHaveAttribute(
|
|
"aria-current",
|
|
)
|
|
expect(screen.getByRole("link", { name: "开发工具" })).toHaveAttribute(
|
|
"aria-current",
|
|
"page",
|
|
)
|
|
})
|
|
|
|
it("折叠状态仍保留分类链接的可访问名称", () => {
|
|
render(
|
|
<AppShell
|
|
categories={categories}
|
|
siteName="Polaris"
|
|
defaultCollapsed={true}
|
|
activeCategoryId="dev"
|
|
>
|
|
<main>分类内容</main>
|
|
</AppShell>,
|
|
)
|
|
|
|
expect(screen.getByRole("link", { name: "开发工具" })).toHaveAttribute(
|
|
"aria-current",
|
|
"page",
|
|
)
|
|
})
|
|
})
|