feat(ts): 实现 Polaris 可交互首页

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-02 23:53:28 +08:00
co-authored by Claude Opus 4.8
parent 95c3da0f71
commit c3d2a40c1f
71 changed files with 15613 additions and 46 deletions
@@ -0,0 +1,162 @@
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("button", {
name: "开发工具,分类页将在下一阶段提供",
}),
).toHaveAttribute("aria-disabled", "true")
expect(
screen.queryByRole("link", { name: /开发工具/ }),
).not.toBeInTheDocument()
})
})