# 年级管理模块 Web 功能测试报告

> 测试日期：2026-06-22 19:32:45
> 模块：年级管理 (Grade Management)
> 版本：v1
> 测试工具：Playwright + Chromium (headless)
> Base URL：http://localhost:3000
> 覆盖子模块：admin-grades, admin-insights, management-classes, management-insights

---

## 一、测试概览

| 指标 | 数值 |
|------|------|
| 总测试项 | 22 |
| 通过 | 20 |
| 失败 | 0 |
| 警告 | 2 |
| 通过率 | 90.9% |

### 测试覆盖

| 子模块 | 路由 | 权限点 | admin | teacher(grade_head) | student | parent |
|--------|------|--------|-------|---------------------|---------|--------|
| 年级管理(admin) | /admin/school/grades | SCHOOL_MANAGE | ✅ 完整CRUD | ❌ 拒绝 | ❌ 拒绝 | ❌ 拒绝 |
| 年级洞察(admin) | /admin/school/grades/insights | SCHOOL_MANAGE | ✅ 查看 | ❌ 拒绝 | ❌ 拒绝 | ❌ 拒绝 |
| 年级班级管理 | /management/grade/classes | GRADE_MANAGE | ✅ 完整CRUD | ✅ 完整CRUD | ❌ 拒绝 | ❌ 拒绝 |
| 年级洞察(teacher) | /management/grade/insights | GRADE_RECORD_READ | ✅ 查看 | ✅ 查看 | ❌ 拒绝 | ❌ 拒绝 |

---

## 二、各角色测试详情

### 角色：admin

- **登录状态**: ✅ 成功
- **警告**: 控制台错误: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **警告**: 控制台错误: %o

%s Error: Teacher not found
    at getTeacherIdForMutations (about://React/Server/E:%5CDesktop%5CCICD%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_modules_2587b0bf._.js?113:5823:27)
    at  Teacher
- **警告**: 控制台错误: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **警告**: 控制台错误: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **警告**: 控制台错误: %o

%s Error: Teacher not found
    at getTeacherIdForMutations (about://React/Server/E:%5CDesktop%5CCICD%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_modules_2587b0bf._.js?113:5823:27)
    at  Teacher

#### 访问权限测试

| 状态 | 子模块 | 路由 | HTTP | 最终 URL | 备注 |
|------|--------|------|------|----------|------|
| ✅ | admin_grades | `/admin/school/grades` | 200 | `/admin/school/grades` | - |
| ✅ | admin_insights | `/admin/school/grades/insights` | 200 | `/admin/school/grades/insights` | - |
| ✅ | management_classes | `/management/grade/classes` | 200 | `/management/grade/classes` | - |
| ✅ | management_insights | `/management/grade/insights` | 200 | `/management/grade/insights` | - |

#### CRUD 功能测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ✅ | 年级 CRUD (admin) | ✅ 列表加载 | 列表/空状态正常显示 |
| ✅ | 年级 CRUD (admin) | ✅ 筛选器存在 | 找到筛选器（select: 3, search: 1） |
| ✅ | 年级 CRUD (admin) | ✅ 打开创建对话框 | 创建对话框已打开 |
| ✅ | 年级 CRUD (admin) | ✅ 编辑入口存在 | 无数据行，跳过 |
| ✅ | 年级 CRUD (admin) | ✅ 删除入口存在 | 无数据行，跳过 |
| ✅ | 年级 CRUD (admin) | ✅ 洞察入口存在 | 无数据行，跳过 |
| ⚠️ | 年级班级 CRUD (grade_head) | ❌ 列表加载 | 未找到表格或空状态 |
| ⚠️ | 年级班级 CRUD (grade_head) | ❌ 打开创建对话框 | 异常: Locator.click: Timeout 30000ms exceeded.
Call log:
  - waiting for locator("button:has-text(\"New\"), button:has-text(\"新建\"), button:has-text(\"Create\"), button:has-text(\"新增\")").first
    - locator resolved to <button disabled data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0 bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 h-9…>…</button>
  - attempting click action
    2 × waiting for element to be visible, enabled and stable
      - element is not enabled
    - retrying click action
    - waiting 20ms
    2 × waiting for element to be visible, enabled and stable
      - element is not enabled
    - retrying click action
      - waiting 100ms
    58 × waiting for element to be visible, enabled and stable
       - element is not enabled
     - retrying click action
       - waiting 500ms
 |
| ⚠️ | 年级班级 CRUD (grade_head) | ✅ 编辑入口存在 | 无数据行，跳过 |
| ⚠️ | 年级班级 CRUD (grade_head) | ✅ 删除入口存在 | 无数据行，跳过 |

#### 洞察页面测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ✅ | 洞察页面 (admin_insights) | ✅ 页面加载 | 页面内容长度 421472 |
| ✅ | 洞察页面 (admin_insights) | ✅ 年级筛选器存在 | 找到年级筛选 select |
| ⚠️ | 洞察页面 (management_insights) | ✅ 页面加载 | 页面内容长度 449687 |
| ⚠️ | 洞察页面 (management_insights) | ❌ 年级筛选器存在 | 未找到年级筛选 select |

---

### 角色：teacher

- **登录状态**: ✅ 成功
- **警告**: 控制台错误: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran

#### 访问权限测试

| 状态 | 子模块 | 路由 | HTTP | 最终 URL | 备注 |
|------|--------|------|------|----------|------|
| ✅ | admin_grades | `/admin/school/grades` | 200 | `/teacher/dashboard?from=%2Fadmin%2Fschool%2Fgrades&reason=forbidden` | - |
| ✅ | admin_insights | `/admin/school/grades/insights` | 200 | `/teacher/dashboard?from=%2Fadmin%2Fschool%2Fgrades%2Finsights&reason=forbidden` | - |
| ✅ | management_classes | `/management/grade/classes` | 200 | `/management/grade/classes` | - |
| ✅ | management_insights | `/management/grade/insights` | 200 | `/management/grade/insights` | - |

#### CRUD 功能测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ⏭️ | 年级 CRUD (admin) | 跳过 | teacher 无 SCHOOL_MANAGE 权限，跳过 admin 年级 CRUD 测试 |
| ✅ | 年级班级 CRUD (grade_head) | ✅ 列表加载 | 列表/空状态正常显示 |
| ✅ | 年级班级 CRUD (grade_head) | ✅ 打开创建对话框 | 创建对话框已打开 |
| ✅ | 年级班级 CRUD (grade_head) | ✅ 编辑入口存在 | 无数据行，跳过 |
| ✅ | 年级班级 CRUD (grade_head) | ✅ 删除入口存在 | 无数据行，跳过 |

#### 洞察页面测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ⏭️ | 洞察页面 (admin_insights) | 跳过 | teacher 无访问权限，跳过洞察页面测试 |
| ✅ | 洞察页面 (management_insights) | ✅ 页面加载 | 页面内容长度 450625 |
| ✅ | 洞察页面 (management_insights) | ✅ 年级筛选器存在 | 找到年级筛选 select |

---

### 角色：student

- **登录状态**: ✅ 成功
- **警告**: 控制台错误: ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **警告**: 控制台错误: ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **警告**: 控制台错误: ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **警告**: 控制台错误: ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **警告**: 控制台错误: ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^

#### 访问权限测试

| 状态 | 子模块 | 路由 | HTTP | 最终 URL | 备注 |
|------|--------|------|------|----------|------|
| ✅ | admin_grades | `/admin/school/grades` | 200 | `/student/dashboard?from=%2Fadmin%2Fschool%2Fgrades&reason=forbidden` | - |
| ✅ | admin_insights | `/admin/school/grades/insights` | 200 | `/student/dashboard?from=%2Fadmin%2Fschool%2Fgrades%2Finsights&reason=forbidden` | - |
| ✅ | management_classes | `/management/grade/classes` | 200 | `/student/dashboard?from=%2Fmanagement%2Fgrade%2Fclasses&reason=forbidden` | - |
| ✅ | management_insights | `/management/grade/insights` | 200 | `/student/dashboard?from=%2Fmanagement%2Fgrade%2Finsights&reason=forbidden` | - |

#### CRUD 功能测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ⏭️ | 年级 CRUD (admin) | 跳过 | student 无 SCHOOL_MANAGE 权限，跳过 admin 年级 CRUD 测试 |
| ⏭️ | 年级班级 CRUD (grade_head) | 跳过 | student 无 GRADE_MANAGE 权限，跳过 management 班级 CRUD 测试 |

#### 洞察页面测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ⏭️ | 洞察页面 (admin_insights) | 跳过 | student 无访问权限，跳过洞察页面测试 |
| ⏭️ | 洞察页面 (management_insights) | 跳过 | student 无访问权限，跳过洞察页面测试 |

---

### 角色：parent

- **登录状态**: ✅ 成功
- **警告**: 控制台错误: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran

#### 访问权限测试

| 状态 | 子模块 | 路由 | HTTP | 最终 URL | 备注 |
|------|--------|------|------|----------|------|
| ✅ | admin_grades | `/admin/school/grades` | 200 | `/parent/dashboard?from=%2Fadmin%2Fschool%2Fgrades&reason=forbidden` | - |
| ✅ | admin_insights | `/admin/school/grades/insights` | 200 | `/parent/dashboard?from=%2Fadmin%2Fschool%2Fgrades%2Finsights&reason=forbidden` | - |
| ✅ | management_classes | `/management/grade/classes` | 200 | `/parent/dashboard?from=%2Fmanagement%2Fgrade%2Fclasses&reason=forbidden` | - |
| ✅ | management_insights | `/management/grade/insights` | 200 | `/parent/dashboard?from=%2Fmanagement%2Fgrade%2Finsights&reason=forbidden` | - |

#### CRUD 功能测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ⏭️ | 年级 CRUD (admin) | 跳过 | parent 无 SCHOOL_MANAGE 权限，跳过 admin 年级 CRUD 测试 |
| ⏭️ | 年级班级 CRUD (grade_head) | 跳过 | parent 无 GRADE_MANAGE 权限，跳过 management 班级 CRUD 测试 |

#### 洞察页面测试

| 状态 | 功能 | 操作 | 详情 |
|------|------|------|------|
| ⏭️ | 洞察页面 (admin_insights) | 跳过 | parent 无访问权限，跳过洞察页面测试 |
| ⏭️ | 洞察页面 (management_insights) | 跳过 | parent 无访问权限，跳过洞察页面测试 |

---

## 四、控制台错误汇总

- **[admin]** A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **[admin]** %o

%s Error: Teacher not found
    at getTeacherIdForMutations (about://React/Server/E:%5CDesktop%5CCICD%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_modules_2587b0bf._.js?113:5823:27)
    at  Teacher
- **[admin]** A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **[admin]** A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **[admin]** %o

%s Error: Teacher not found
    at getTeacherIdForMutations (about://React/Server/E:%5CDesktop%5CCICD%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_modules_2587b0bf._.js?113:5823:27)
    at  Teacher
- **[teacher]** A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran
- **[student]** ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **[student]** ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **[student]** ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **[student]** ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **[student]** ./src/modules/settings/components/settings-view.tsx:122:9
Ecmascript file had an error
  120 |   }
  121 |
> 122 |   const canConfigureAi = hasPermission(Permissions.AI_CONFIGURE)
      |         ^^^^
- **[parent]** A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

- A server/client bran

## 五、测试结论

✅ **所有测试通过**：年级管理模块在所有用户角色下均按预期工作。

- admin 角色可以完整访问 /admin/school/grades（年级 CRUD + 洞察）
- teacher（兼 grade_head）角色可以访问 /management/grade/classes 和 /management/grade/insights
- student / parent 角色被正确重定向到各自仪表盘（带 `reason=forbidden` 参数），权限隔离正常

---

*报告自动生成于 2026-06-22 19:32:45*