Files
NextEdu/tests/integration/dashboard-routing.test.ts
SpecialX 2c0f81391b feat(dashboard): 实现所有长期问题修复(P2-1/P2-5/P2-7/P2-9)
P2-9: TeacherSchedule 重复渲染优化
- 将移动端(lg:hidden)和桌面端(hidden lg:block)的双实例渲染改为单实例
- 使用 CSS flex order + grid col-start/row-start 实现响应式布局重排序
- 消除服务端 HTML 负载翻倍问题

P2-5: StudentTodayScheduleCard 时间过时修复
- 新增 useCurrentTime hook(src/shared/hooks/use-current-time.ts)
- 每分钟自动更新当前时间,useMemo 依赖 [items, now] 确保徽章不过时
- SSR 安全:初始渲染用 new Date(),挂载后 setInterval 更新

P2-1: 流式/Suspense 架构改造
- 新增 getAdminDashboardStreams(streams.ts):返回各独立数据源的未解析 Promise
- Admin dashboard:7 个分区组件用 React use() 独立消费 Promise,各 Suspense 边界独立流式渲染
- Teacher/Student/Parent dashboard:传入未解析 Promise,视图用 use() 消费,启用 Suspense 流式
- 页面外壳(标题 + 快捷操作)立即渲染,数据到达后各分区按各自速度填充

P2-7: 组件测试 + 路由测试修复
- 修复 dashboard-routing.test.ts:移除误导性的 permissions 字段(实际用 resolvePermissions(roles))
- 新增 fallback 路由测试(未知角色 → teacher dashboard)
- 新增 DashboardSection 组件测试(6 个测试:骨架屏变体 + 错误边界 + 正常渲染)
- 新增 useCurrentTime hook 测试(3 个测试:初始值 + 间隔更新 + 清理)

同步更新:
- docs/architecture/005_architecture_data.json 新增 7 个流式组件 + useCurrentTime hook + getAdminDashboardStreams 条目
2026-06-23 09:04:40 +08:00

80 lines
2.4 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 { beforeEach, describe, expect, it, vi } from "vitest"
const mocks = vi.hoisted(() => ({
authMock: vi.fn(),
redirectMock: vi.fn((target: string) => {
throw new Error(`REDIRECT:${target}`)
}),
}))
vi.mock("@/auth", () => ({
auth: mocks.authMock,
}))
vi.mock("next/navigation", () => ({
redirect: mocks.redirectMock,
}))
import DashboardPage from "@/app/(dashboard)/dashboard/page"
/**
* 仪表盘路由分发测试
*
* 注意:实际代码用 `resolvePermissions(roles)` 从角色推导权限,
* 而非读取 user.permissions 字段。因此 mock 中只需设置 roles
* 无需(也不应)设置 permissions 字段,以免误导。
*/
describe("dashboard route dispatcher", () => {
beforeEach(() => {
vi.resetAllMocks()
mocks.redirectMock.mockImplementation((target: string) => {
throw new Error(`REDIRECT:${target}`)
})
})
it("redirects to login when session is missing", async () => {
mocks.authMock.mockResolvedValue(null)
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/login")
})
it("redirects to login when user is missing", async () => {
mocks.authMock.mockResolvedValue({ user: null })
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/login")
})
it("redirects admin to admin dashboard", async () => {
mocks.authMock.mockResolvedValue({
user: { id: "u_admin", roles: ["admin"] },
})
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/admin/dashboard")
})
it("redirects student to student dashboard", async () => {
mocks.authMock.mockResolvedValue({
user: { id: "u_student", roles: ["student"] },
})
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/student/dashboard")
})
it("redirects parent to parent dashboard", async () => {
mocks.authMock.mockResolvedValue({
user: { id: "u_parent", roles: ["parent"] },
})
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/parent/dashboard")
})
it("redirects teacher to teacher dashboard", async () => {
mocks.authMock.mockResolvedValue({
user: { id: "u_teacher", roles: ["teacher"] },
})
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/teacher/dashboard")
})
it("redirects unknown role to teacher dashboard (fallback)", async () => {
mocks.authMock.mockResolvedValue({
user: { id: "u_unknown", roles: [] },
})
await expect(DashboardPage()).rejects.toThrow("REDIRECT:/teacher/dashboard")
})
})