- 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
294 lines
17 KiB
JSON
294 lines
17 KiB
JSON
{
|
|
"test_date": "2026-06-22 19:57:34",
|
|
"module": "备课 (lesson-preparation)",
|
|
"version": "v1",
|
|
"base_url": "http://localhost:3000",
|
|
"summary": {
|
|
"total": 4,
|
|
"passed": 2,
|
|
"failed": 0,
|
|
"warnings": 2
|
|
},
|
|
"roles": {
|
|
"admin": {
|
|
"role": "admin",
|
|
"login_success": true,
|
|
"list_page": {
|
|
"status": "passed",
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/lesson-plans",
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"new_page": {
|
|
"status": "passed",
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/lesson-plans/new",
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"checks": [
|
|
{
|
|
"name": "有权访问备课列表页",
|
|
"passed": true,
|
|
"detail": "HTTP 200"
|
|
}
|
|
],
|
|
"screenshots": [
|
|
"access_admin_list.png",
|
|
"access_admin_new.png"
|
|
]
|
|
},
|
|
"teacher": {
|
|
"role": "teacher",
|
|
"login_success": true,
|
|
"list_page": {
|
|
"status": "warning",
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/lesson-plans",
|
|
"errors": [],
|
|
"warnings": [
|
|
"控制台错误 1 条"
|
|
]
|
|
},
|
|
"new_page": {
|
|
"status": "passed",
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/teacher/lesson-plans/new",
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"checks": [
|
|
{
|
|
"name": "有权访问备课列表页",
|
|
"passed": true,
|
|
"detail": "HTTP 200"
|
|
}
|
|
],
|
|
"screenshots": [
|
|
"access_teacher_list.png",
|
|
"access_teacher_new.png"
|
|
]
|
|
},
|
|
"student": {
|
|
"role": "student",
|
|
"login_success": true,
|
|
"list_page": {
|
|
"status": "passed",
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/student/dashboard?from=%2Fteacher%2Flesson-plans&reason=forbidden",
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"new_page": {
|
|
"status": "unknown",
|
|
"http_status": null,
|
|
"final_url": null,
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"checks": [
|
|
{
|
|
"name": "无权限访问被正确拦截",
|
|
"passed": true,
|
|
"detail": "重定向到 http://localhost:3000/student/dashboard?from=%2Fteacher%2Flesson-plans&reason=forbidden"
|
|
}
|
|
],
|
|
"screenshots": [
|
|
"access_student_list.png"
|
|
]
|
|
},
|
|
"parent": {
|
|
"role": "parent",
|
|
"login_success": true,
|
|
"list_page": {
|
|
"status": "warning",
|
|
"http_status": 200,
|
|
"final_url": "http://localhost:3000/parent/dashboard?from=%2Fteacher%2Flesson-plans&reason=forbidden",
|
|
"errors": [],
|
|
"warnings": [
|
|
"控制台错误 1 条"
|
|
]
|
|
},
|
|
"new_page": {
|
|
"status": "unknown",
|
|
"http_status": null,
|
|
"final_url": null,
|
|
"errors": [],
|
|
"warnings": []
|
|
},
|
|
"checks": [
|
|
{
|
|
"name": "无权限访问被正确拦截",
|
|
"passed": true,
|
|
"detail": "重定向到 http://localhost:3000/parent/dashboard?from=%2Fteacher%2Flesson-plans&reason=forbidden"
|
|
}
|
|
],
|
|
"screenshots": [
|
|
"access_parent_list.png"
|
|
]
|
|
}
|
|
},
|
|
"console_errors_global": [
|
|
{
|
|
"role": "current",
|
|
"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 <div className=\"flex items...\">\n <GlobalSearch>\n ...\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_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\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_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
|
},
|
|
{
|
|
"role": "current",
|
|
"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 <div className=\"flex items...\">\n <GlobalSearch>\n ...\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_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\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_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
|
}
|
|
],
|
|
"teacher_features": {
|
|
"role": "teacher",
|
|
"features": {
|
|
"list": {
|
|
"name": "列表页",
|
|
"status": "passed",
|
|
"checks": [
|
|
{
|
|
"name": "页面标题包含'备课'/'Lesson Plan'",
|
|
"passed": true,
|
|
"detail": "body 长度 380020"
|
|
},
|
|
{
|
|
"name": "存在'新建课案'按钮",
|
|
"passed": true,
|
|
"detail": "找到 1 个"
|
|
},
|
|
{
|
|
"name": "存在筛选器",
|
|
"passed": true,
|
|
"detail": "找到 4 个 input/select"
|
|
},
|
|
{
|
|
"name": "课案卡片或空状态",
|
|
"passed": true,
|
|
"detail": "找到 12 个课案链接"
|
|
}
|
|
],
|
|
"errors": []
|
|
},
|
|
"create": {
|
|
"name": "新建课案",
|
|
"status": "passed",
|
|
"checks": [
|
|
{
|
|
"name": "存在模板选择按钮",
|
|
"passed": true,
|
|
"detail": "找到 11 个按钮"
|
|
},
|
|
{
|
|
"name": "模板选中状态可视化",
|
|
"passed": true,
|
|
"detail": "选中 5 个"
|
|
},
|
|
{
|
|
"name": "创建后跳转到编辑页",
|
|
"passed": true,
|
|
"detail": "planId=gpqmy3bpyts1lpnhomibnzr2"
|
|
}
|
|
],
|
|
"errors": []
|
|
},
|
|
"editor": {
|
|
"name": "编辑器",
|
|
"status": "passed",
|
|
"checks": [
|
|
{
|
|
"name": "标题输入框存在且有值",
|
|
"passed": false,
|
|
"detail": "标题=''"
|
|
},
|
|
{
|
|
"name": "React Flow 节点渲染",
|
|
"passed": true,
|
|
"detail": "渲染 8 个节点"
|
|
},
|
|
{
|
|
"name": "React Flow 边渲染",
|
|
"passed": true,
|
|
"detail": "渲染 7 条边"
|
|
},
|
|
{
|
|
"name": "存在保存版本按钮",
|
|
"passed": true,
|
|
"detail": "找到 1 个"
|
|
},
|
|
{
|
|
"name": "存在版本历史按钮",
|
|
"passed": true,
|
|
"detail": "找到 2 个"
|
|
},
|
|
{
|
|
"name": "存在添加节点按钮",
|
|
"passed": true,
|
|
"detail": "找到 1 个"
|
|
}
|
|
],
|
|
"errors": []
|
|
},
|
|
"panel": {
|
|
"name": "节点选中与侧边面板",
|
|
"status": "passed",
|
|
"checks": [
|
|
{
|
|
"name": "点击节点后出现侧边面板",
|
|
"passed": true,
|
|
"detail": "找到编辑元素: True"
|
|
}
|
|
],
|
|
"errors": []
|
|
},
|
|
"version": {
|
|
"name": "版本历史",
|
|
"status": "passed",
|
|
"checks": [
|
|
{
|
|
"name": "版本历史抽屉打开",
|
|
"passed": false,
|
|
"detail": "找到 0 个 dialog/drawer"
|
|
}
|
|
],
|
|
"errors": []
|
|
},
|
|
"duplicate_archive": {
|
|
"name": "复制与归档",
|
|
"status": "passed",
|
|
"checks": [
|
|
{
|
|
"name": "列表页存在课案卡片",
|
|
"passed": true,
|
|
"detail": "找到 13 个"
|
|
},
|
|
{
|
|
"name": "存在复制按钮",
|
|
"passed": true,
|
|
"detail": "找到 12 个"
|
|
},
|
|
{
|
|
"name": "存在归档按钮",
|
|
"passed": true,
|
|
"detail": "找到 12 个"
|
|
},
|
|
{
|
|
"name": "复制后课案数量增加",
|
|
"passed": false,
|
|
"detail": "复制前 13 → 复制后 13"
|
|
}
|
|
],
|
|
"errors": []
|
|
}
|
|
},
|
|
"screenshots": [
|
|
"teacher_feature_list.png",
|
|
"teacher_feature_create.png",
|
|
"teacher_feature_editor.png",
|
|
"teacher_feature_panel.png",
|
|
"teacher_feature_versions.png",
|
|
"teacher_feature_duplicate.png"
|
|
],
|
|
"errors": [],
|
|
"warnings": []
|
|
}
|
|
} |