feat(portal-shell): add MSW mock layer with production bundle exclusion (P1-5)
MSW v2.7.0 fallback layer covering dashboard/users/exams/grades domains. NEXT_PUBLIC_MSW=1 enables browser Service Worker + SSR route handler mock responses without backend. Production build excludes all mock data via Turbopack resolveAlias redirecting @/mocks to empty stub. Acceptance: build bundle (client+server) verified clean of mock strings; typecheck/lint/vitest (231 tests) all pass.
This commit is contained in:
@@ -757,7 +757,7 @@ export default async function ExamsPage(): Promise<React.ReactElement> {
|
||||
- **P0-7**:`eslint .` → 0 errors, 2 warnings(均在 `__generated__/types.ts` 生成文件);[eslint.config.js](file:///e:/Desktop/Edu/apps/portal-shell/eslint.config.js) 含 `no-restricted-imports`
|
||||
- **P0-8**:`git status` 干净(commit `cfb7b00`);`.env.local` 与 `tsconfig.tsbuildinfo` 已在 [.gitignore](file:///e:/Desktop/Edu/apps/portal-shell/.gitignore)
|
||||
|
||||
### P1 · 框架与数据源接通(1–2 周)— 进行中(P1-1/P1-2/P1-3/P1-4 ✅ 2026-07-22 验收)
|
||||
### P1 · 框架与数据源接通(1–2 周)— 进行中(P1-1/P1-2/P1-3/P1-4/P1-5 ✅ 2026-07-22 验收)
|
||||
|
||||
**目标**:AppFrame + 导航 + 真实数据仪表盘 + 页面模板 + MSW + i18n,页面迁移的"流水线"建成。
|
||||
|
||||
@@ -767,7 +767,7 @@ export default async function ExamsPage(): Promise<React.ReactElement> {
|
||||
| P1-2 | 仪表盘改接真实查询:`teacherDashboard`/`studentDashboard`/`parentDashboard`/`adminDashboard` + `warnings`/`errorBookStats` | 起全栈,4 角色仪表盘显示真实聚合数据(截图);widget 假契约查询(grades/homeworks/schedule/attendance/exams/announcements)全部下线或改造 | ✅ |
|
||||
| P1-3 | 页面模板四件套(list/detail/form/workbench)+ 三态规范 | Storybook 或示例页 4 张(/shell/dev/templates/*,仅 dev 可见;注:Next.js 私有文件夹 `_xxx` 不参与路由,故使用 `dev` 而非 `_dev`) | ✅ |
|
||||
| P1-4 | next-intl 接入 + messages 合并迁移 | 切换 locale 页面文案切换(截图);`useT` 自造函数删除 | ✅ |
|
||||
| P1-5 | MSW 兜底层(迁移旧 handlers,覆盖 dashboard/users/exams/grades 四域起步) | `NEXT_PUBLIC_MSW=1` 无后端启动,仪表盘 + users 页有数据(截图);生产构建 bundle 无 mocks | ⏳ |
|
||||
| P1-5 | MSW 兜底层(迁移旧 handlers,覆盖 dashboard/users/exams/grades 四域起步) | `NEXT_PUBLIC_MSW=1` 无后端启动,仪表盘 + users 页有数据(截图);生产构建 bundle 无 mocks | ✅ |
|
||||
| P1-6 | 31 widget 令牌清债(271 处机械替换)+ `border border` 去重 + 未知类检测进 arch:scan | `grep -c "text-heading-\|mt-sm\|py-xs\|p-md" src/widgets` = 0;`pnpm lint:tokens` 通过 | ⏳ |
|
||||
| P1-7 | codegen 恢复 typescript-operations(config + data-ana 两域先行关闭 skipDocumentsValidation) | 生成操作级类型;lib/api 对应域删除手写 interface;typecheck 通过 | ⏳ |
|
||||
| P1-8 | CI 增补:路由表一致性脚本 + 页面计数 + codegen diff 检查 | CI 对预埋违规报红(附 pipeline 链接) | ⏳ |
|
||||
@@ -844,6 +844,25 @@ export default async function ExamsPage(): Promise<React.ReactElement> {
|
||||
- `GET /login`(`Cookie: NEXT_LOCALE=en`)→ `<html lang="en">` + `NextIntlClientProvider locale="en"` + 英文文案("Save"/"Cancel"/"Toggle sidebar")
|
||||
- **质量校验**:`tsc --noEmit` 通过;`eslint src` → 0 errors, 2 warnings(`__generated__/types.ts` 生成文件);`vitest run` 全量 21 test files / 231 tests 全部通过
|
||||
|
||||
**P1-5 验收证据(2026-07-22)**:
|
||||
|
||||
- **MSW v2.7.0 兜底层(覆盖 dashboard/users/exams/grades 四域起步)**:
|
||||
- [src/mocks/index.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/mocks/index.ts):`initMocks()` 入口,`NEXT_PUBLIC_MSW=1` 时按环境动态启动 browser worker 或 node server
|
||||
- [src/mocks/handlers.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/mocks/handlers.ts):拦截 `POST /api/graphql` 与 `POST {APOLLO_ROUTER_URL}`,按 `operationName` 路由
|
||||
- [src/mocks/graphql-data.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/mocks/graphql-data.ts):mock 数据与 `graphqlResponse()` 函数,供 handlers.ts(MSW browser)和 route.ts(SSR)共用
|
||||
- [src/mocks/browser.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/mocks/browser.ts) / [server.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/mocks/server.ts):`setupWorker` / `setupServer`
|
||||
- [public/mockServiceWorker.js](file:///e:/Desktop/Edu/apps/portal-shell/public/mockServiceWorker.js):MSW Service Worker(`pnpm exec msw init public/ --save`)
|
||||
- **SSR 端 mock 数据(Apollo Client 走同域代理)**:
|
||||
- [src/lib/apollo-client.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/lib/apollo-client.ts):`MSW_ENABLED` 时 SSR 端 HttpLink 也指向 `/api/graphql`(不走直连 router)
|
||||
- [src/app/api/graphql/route.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/app/api/graphql/route.ts):`MSW_ENABLED` 时直接返回 mock 数据,不连接后端
|
||||
- **生产构建安全(bundle 无 mocks)**:
|
||||
- [src/mocks/empty.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/mocks/empty.ts):空 stub,导出与 `index.ts`/`graphql-data.ts` 同签名的 no-op 函数
|
||||
- [next.config.js](file:///e:/Desktop/Edu/apps/portal-shell/next.config.js):`NEXT_PUBLIC_MSW!=1` 时 Turbopack `resolveAlias` + webpack `resolve.alias` 把 `@/mocks` 与 `@/mocks/graphql-data` 重定向到 `@/mocks/empty`(注:Turbopack 不支持 Windows 绝对路径,故使用 `@/` 说明符)
|
||||
- [src/providers/MswProvider.tsx](file:///e:/Desktop/Edu/apps/portal-shell/src/providers/MswProvider.tsx):静态 `import { initMocks } from "@/mocks"`(alias 生效后指向 empty.ts),`MSW_ENABLED=false` 时 useEffect 分支被 dead-code 消除
|
||||
- **构建验证**:`NEXT_PUBLIC_MSW=0 next build` 成功(20 路由生成);`Select-String -Path ".next/static/chunks/**/*.js",".next/server/**/*.js" -Pattern "张老师","stu-001","setupWorker","dev-teacher-001","二次函数"` → **CLEAN:客户端 + 服务端 bundle 均无 mock 字符串**
|
||||
- **dev server 验证(MSW=1 无后端)**:`GET /api/graphql` 返回 `{msw:true}`;`POST /api/graphql` 返回 mock 数据(teacherDashboard: total_classes=5/total_students=142;users: 5 条记录;questions: 3 条;grades: 3 条)
|
||||
- **质量校验**:`tsc --noEmit` 通过;`eslint src` → 0 errors, 2 warnings(`__generated__/types.ts`);`vitest run` 全量 21 test files / 231 tests 全部通过
|
||||
|
||||
### P2 · 教师域页面(2–3 周,可与 P3 部分并行)
|
||||
|
||||
- 范围:§9.1 全表(~50 页)。顺序建议:exams → homework → grades → lesson-plans → questions/textbooks → attendance/classes/students → diagnostic/error-book/analytics → elective/course-plans → ai-* → practice/schedule-changes/leave。
|
||||
|
||||
Reference in New Issue
Block a user