- Update E2E tests: announcements, auth, auth-business-flow, full-route-regression, grades, navigation, smoke-auth, teacher-web-test - Update integration tests: api-ai-chat, api-onboarding-complete, api-onboarding-status, proxy-guard, integration setup - Update visual regression tests: admin-dashboard, homepage, student-dashboard, teacher-dashboard, visual config, helpers - Update webapp tests: admin, parent, student full tests and debug scripts - Add new webapp tests: announcements_messages, settings_profile, debug scripts - Add webtest directory with test plans, screenshots, and diagnostic scripts
438 lines
19 KiB
JSON
438 lines
19 KiB
JSON
{
|
|
"test_date": "2026-06-22 19:42:53",
|
|
"module": "学校管理 (School Management)",
|
|
"version": "v1",
|
|
"base_url": "http://localhost:3000",
|
|
"covered_submodules": [
|
|
"schools",
|
|
"departments",
|
|
"academic-year"
|
|
],
|
|
"summary": {
|
|
"total": 15,
|
|
"passed": 15,
|
|
"failed": 0,
|
|
"warnings": 0
|
|
},
|
|
"roles": {
|
|
"admin": {
|
|
"role": "admin",
|
|
"login_success": true,
|
|
"access_tests": [
|
|
{
|
|
"route_key": "schools_list",
|
|
"url": "http://localhost:3000/admin/school/schools",
|
|
"expected_access": true,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/admin/school/schools",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "页面正常加载",
|
|
"passed": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "departments_list",
|
|
"url": "http://localhost:3000/admin/school/departments",
|
|
"expected_access": true,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/admin/school/departments",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "页面正常加载",
|
|
"passed": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "academic_year_list",
|
|
"url": "http://localhost:3000/admin/school/academic-year",
|
|
"expected_access": true,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/admin/school/academic-year",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "页面正常加载",
|
|
"passed": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"crud_tests": [
|
|
{
|
|
"feature": "学校 CRUD",
|
|
"role": "admin",
|
|
"operations": [
|
|
{
|
|
"name": "列表加载",
|
|
"passed": true,
|
|
"detail": "列表/空状态正常显示"
|
|
},
|
|
{
|
|
"name": "打开创建对话框",
|
|
"passed": true,
|
|
"detail": "创建对话框已打开"
|
|
},
|
|
{
|
|
"name": "编辑入口存在",
|
|
"passed": true,
|
|
"detail": "无数据行,跳过编辑入口检查"
|
|
},
|
|
{
|
|
"name": "删除入口存在",
|
|
"passed": true,
|
|
"detail": "无数据行,跳过删除入口检查"
|
|
}
|
|
],
|
|
"status": "passed",
|
|
"errors": []
|
|
},
|
|
{
|
|
"feature": "院系 CRUD",
|
|
"role": "admin",
|
|
"operations": [
|
|
{
|
|
"name": "列表加载",
|
|
"passed": true,
|
|
"detail": "列表/空状态正常显示"
|
|
},
|
|
{
|
|
"name": "打开创建对话框",
|
|
"passed": true,
|
|
"detail": "创建对话框已打开"
|
|
},
|
|
{
|
|
"name": "编辑入口存在",
|
|
"passed": true,
|
|
"detail": "无数据行,跳过"
|
|
},
|
|
{
|
|
"name": "删除入口存在",
|
|
"passed": true,
|
|
"detail": "无数据行,跳过"
|
|
}
|
|
],
|
|
"status": "passed",
|
|
"errors": []
|
|
},
|
|
{
|
|
"feature": "学年 CRUD",
|
|
"role": "admin",
|
|
"operations": [
|
|
{
|
|
"name": "列表加载",
|
|
"passed": true,
|
|
"detail": "列表/空状态正常显示"
|
|
},
|
|
{
|
|
"name": "打开创建对话框",
|
|
"passed": true,
|
|
"detail": "创建对话框已打开"
|
|
},
|
|
{
|
|
"name": "编辑入口存在",
|
|
"passed": true,
|
|
"detail": "无数据行,跳过"
|
|
},
|
|
{
|
|
"name": "删除入口存在",
|
|
"passed": true,
|
|
"detail": "无数据行,跳过"
|
|
}
|
|
],
|
|
"status": "passed",
|
|
"errors": []
|
|
}
|
|
],
|
|
"errors": [],
|
|
"warnings": [
|
|
"控制台错误: 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:\n\n- 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:\n\n- A server/client bran"
|
|
]
|
|
},
|
|
"teacher": {
|
|
"role": "teacher",
|
|
"login_success": true,
|
|
"access_tests": [
|
|
{
|
|
"route_key": "schools_list",
|
|
"url": "http://localhost:3000/admin/school/schools",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fschool%2Fschools&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fschool%2Fschools&reason=forbidden"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "departments_list",
|
|
"url": "http://localhost:3000/admin/school/departments",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fschool%2Fdepartments&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fschool%2Fdepartments&reason=forbidden"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "academic_year_list",
|
|
"url": "http://localhost:3000/admin/school/academic-year",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fschool%2Facademic-year&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fschool%2Facademic-year&reason=forbidden"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"crud_tests": [
|
|
{
|
|
"feature": "学校 CRUD",
|
|
"role": "teacher",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"teacher 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
},
|
|
{
|
|
"feature": "院系 CRUD",
|
|
"role": "teacher",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"teacher 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
},
|
|
{
|
|
"feature": "学年 CRUD",
|
|
"role": "teacher",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"teacher 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
}
|
|
],
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"student": {
|
|
"role": "student",
|
|
"login_success": true,
|
|
"access_tests": [
|
|
{
|
|
"route_key": "schools_list",
|
|
"url": "http://localhost:3000/admin/school/schools",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/student/dashboard?from=%2Fadmin%2Fschool%2Fschools&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/student/dashboard?from=%2Fadmin%2Fschool%2Fschools&reason=forbidden"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "departments_list",
|
|
"url": "http://localhost:3000/admin/school/departments",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/student/dashboard?from=%2Fadmin%2Fschool%2Fdepartments&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/student/dashboard?from=%2Fadmin%2Fschool%2Fdepartments&reason=forbidden"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "academic_year_list",
|
|
"url": "http://localhost:3000/admin/school/academic-year",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/student/dashboard?from=%2Fadmin%2Fschool%2Facademic-year&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/student/dashboard?from=%2Fadmin%2Fschool%2Facademic-year&reason=forbidden"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"crud_tests": [
|
|
{
|
|
"feature": "学校 CRUD",
|
|
"role": "student",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"student 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
},
|
|
{
|
|
"feature": "院系 CRUD",
|
|
"role": "student",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"student 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
},
|
|
{
|
|
"feature": "学年 CRUD",
|
|
"role": "student",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"student 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
}
|
|
],
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"parent": {
|
|
"role": "parent",
|
|
"login_success": true,
|
|
"access_tests": [
|
|
{
|
|
"route_key": "schools_list",
|
|
"url": "http://localhost:3000/admin/school/schools",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fschool%2Fschools&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fschool%2Fschools&reason=forbidden"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "departments_list",
|
|
"url": "http://localhost:3000/admin/school/departments",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fschool%2Fdepartments&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fschool%2Fdepartments&reason=forbidden"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"route_key": "academic_year_list",
|
|
"url": "http://localhost:3000/admin/school/academic-year",
|
|
"expected_access": false,
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fschool%2Facademic-year&reason=forbidden",
|
|
"status": "passed",
|
|
"errors": [],
|
|
"warnings": [],
|
|
"checks": [
|
|
{
|
|
"name": "权限拒绝重定向",
|
|
"passed": true,
|
|
"detail": "正确重定向到 http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fschool%2Facademic-year&reason=forbidden"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"crud_tests": [
|
|
{
|
|
"feature": "学校 CRUD",
|
|
"role": "parent",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"parent 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
},
|
|
{
|
|
"feature": "院系 CRUD",
|
|
"role": "parent",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"parent 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
},
|
|
{
|
|
"feature": "学年 CRUD",
|
|
"role": "parent",
|
|
"operations": [],
|
|
"status": "skipped",
|
|
"errors": [
|
|
"parent 无 SCHOOL_MANAGE 权限,跳过 CRUD 测试"
|
|
]
|
|
}
|
|
],
|
|
"errors": [],
|
|
"warnings": []
|
|
}
|
|
},
|
|
"console_errors_global": [
|
|
{
|
|
"role": "admin",
|
|
"error": "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:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <MenuProvider scope={{Menu:[...], ...}} open={false} onOpenChange={function useCallbackRef.useMemo} content={null} ...>\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_2...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_2...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_2...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-8 w-8\" disabled={false} type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-m...\"}\n disabled={false}\n type=\"button\"\n+ id=\"radix-_R_2d59bn5ritpevqbn6lb_\"\n- id=\"radix-_R_9kl5esnebn9evqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
|
},
|
|
{
|
|
"role": "admin",
|
|
"error": "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:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <MenuProvider scope={{Menu:[...], ...}} open={false} onOpenChange={function useCallbackRef.useMemo} content={null} ...>\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_j...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_j...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_j...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-8 w-8\" disabled={false} type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-m...\"}\n disabled={false}\n type=\"button\"\n+ id=\"radix-_R_j59bn5ritpevqbn6lb_\"\n- id=\"radix-_R_2cl5esnebn9evqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
|
}
|
|
]
|
|
} |