409 lines
14 KiB
TypeScript
409 lines
14 KiB
TypeScript
|
|
/**
|
||
|
|
* 教师端 E2E 测试 - 所有功能接口全面测试
|
||
|
|
* 覆盖所有后端 API 接口并验证前端适配
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { test, expect } from '@playwright/test';
|
||
|
|
import { loginAsTeacher, clickSubMenu, waitForPageLoad } from './helpers';
|
||
|
|
|
||
|
|
test.describe('教师端 - 所有功能接口全面测试', () => {
|
||
|
|
let authContext: { authToken: string };
|
||
|
|
|
||
|
|
test.beforeEach(async ({ page }) => {
|
||
|
|
await loginAsTeacher(page);
|
||
|
|
await page.waitForTimeout(2000);
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 仪表盘测试 ====================
|
||
|
|
test.describe('1. 仪表盘功能测试', () => {
|
||
|
|
test('验证仪表盘页面加载和 API 调用', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/dashboard');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 验证页面元素
|
||
|
|
const dashboardContent = page.locator('.ant-layout-content');
|
||
|
|
await expect(dashboardContent).toBeVisible();
|
||
|
|
|
||
|
|
// 等待并验证 API 响应
|
||
|
|
const responsePromise = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/dashboard') || res.url().includes('/teacher/stats'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await responsePromise;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `仪表盘 API: status=${response.status()}, data=${JSON.stringify(data).substring(0, 100)}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '仪表盘页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 我的课表测试 ====================
|
||
|
|
test.describe('2. 我的课表功能测试', () => {
|
||
|
|
test('验证课表页面加载和列表 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/schedule');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 验证课表视图
|
||
|
|
const scheduleView = page.locator('[class*="schedule"], [class*="timetable"]');
|
||
|
|
await expect(scheduleView).toBeVisible({ timeout: 10000 }).catch(() => {
|
||
|
|
test.info().annotations.push({ type: 'warning', description: '课表视图未找到,可能使用其他布局' });
|
||
|
|
});
|
||
|
|
|
||
|
|
// 等待并验证排课 API
|
||
|
|
const scheduleResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/schedules'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await scheduleResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `排课列表 API: status=${response.status()}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '课表页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('验证课程表 API(timetable)', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/schedule');
|
||
|
|
|
||
|
|
// 等待 timetable API 调用
|
||
|
|
const timetableResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/schedules/timetable'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await timetableResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `课程表 API: status=${response.status()}, records=${Array.isArray(data?.data) ? data.data.length : 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 课程列表测试 ====================
|
||
|
|
test.describe('3. 课程列表功能测试', () => {
|
||
|
|
test('验证课程列表页面和 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/courses');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 等待课程 API
|
||
|
|
const coursesResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/courses') && !res.url().includes('/courses/'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await coursesResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `课程列表 API: status=${response.status()}, total=${data?.data?.total ?? 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 验证课程卡片显示
|
||
|
|
const courseCards = page.locator('[class*="course-card"], [class*="course"] .ant-card');
|
||
|
|
const count = await courseCards.count();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: `课程列表页面加载成功,显示 ${count} 个课程`,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('验证班级 API 调用', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/courses');
|
||
|
|
|
||
|
|
const classesResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/classes'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await classesResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `班级列表 API: status=${response.status()}, count=${Array.isArray(data?.data) ? data.data.length : 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 授课记录测试 ====================
|
||
|
|
test.describe('4. 授课记录功能测试', () => {
|
||
|
|
test('验证授课记录页面和列表 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/lessons');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 等待授课记录 API
|
||
|
|
const lessonsResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/lessons') && !res.url().includes('/lessons/'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await lessonsResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `授课记录 API: status=${response.status()}, total=${data?.data?.total ?? 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '授课记录页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('验证今日授课 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/lessons');
|
||
|
|
|
||
|
|
const todayResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/lessons/today'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await todayResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `今日授课 API: status=${response.status()}, count=${Array.isArray(data?.data) ? data.data.length : 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 班级管理测试 ====================
|
||
|
|
test.describe('5. 班级管理功能测试', () => {
|
||
|
|
test('验证班级列表页面和 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/classes');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 验证班级表格
|
||
|
|
const table = page.locator('table, .ant-table');
|
||
|
|
await expect(table).toBeVisible({ timeout: 10000 });
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '班级列表页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('验证班级学生 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/classes');
|
||
|
|
|
||
|
|
// 等待学生 API(如果有)
|
||
|
|
const studentsResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/classes/') && res.url().includes('/students'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await studentsResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `班级学生 API: status=${response.status()}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 学生管理测试 ====================
|
||
|
|
test.describe('6. 学生管理功能测试', () => {
|
||
|
|
test('验证学生列表页面和 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/students');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 等待学生 API
|
||
|
|
const studentsResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/students'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await studentsResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `学生列表 API: status=${response.status()}, total=${data?.data?.total ?? 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '学生列表页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 任务管理测试 ====================
|
||
|
|
test.describe('7. 任务管理功能测试', () => {
|
||
|
|
test('验证任务列表页面和 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/tasks');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 等待任务 API
|
||
|
|
const tasksResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/tasks') && !res.url().includes('/tasks/'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await tasksResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `任务列表 API: status=${response.status()}, total=${data?.data?.total ?? 'N/A'}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '任务列表页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('验证任务模板 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/tasks');
|
||
|
|
|
||
|
|
// 等待任务模板 API(如果有)
|
||
|
|
const templatesResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/task-templates') || res.url().includes('/task/template'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await templatesResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `任务模板 API: status=${response.status()}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 成长记录测试 ====================
|
||
|
|
test.describe('8. 成长记录功能测试', () => {
|
||
|
|
test('验证成长记录页面和 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/growth-records');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 等待成长记录 API
|
||
|
|
const growthResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/growth') || res.url().includes('/teacher/records'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await growthResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `成长记录 API: status=${response.status()}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '成长记录页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 教学反馈测试 ====================
|
||
|
|
test.describe('9. 教学反馈功能测试', () => {
|
||
|
|
test('验证反馈页面和 API', async ({ page }) => {
|
||
|
|
await page.goto('/teacher/feedback');
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
|
||
|
|
// 等待反馈 API
|
||
|
|
const feedbackResponse = page.waitForResponse(
|
||
|
|
async (res) => res.url().includes('/teacher/feedback') || res.url().includes('/feedback'),
|
||
|
|
{ timeout: 10000 }
|
||
|
|
).catch(() => null);
|
||
|
|
|
||
|
|
const response = await feedbackResponse;
|
||
|
|
if (response) {
|
||
|
|
expect(response.status()).toBe(200);
|
||
|
|
const data = await response.json();
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'API 验证',
|
||
|
|
description: `反馈 API: status=${response.status()}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '反馈页面加载成功',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ==================== 综合流程测试 ====================
|
||
|
|
test.describe('10. 综合流程测试', () => {
|
||
|
|
test('完整教学流程:课表 → 课程 → 授课 → 反馈', async ({ page }) => {
|
||
|
|
const steps = [
|
||
|
|
{ name: '仪表盘', path: '/teacher/dashboard' },
|
||
|
|
{ name: '我的课表', path: '/teacher/schedule' },
|
||
|
|
{ name: '课程列表', path: '/teacher/courses' },
|
||
|
|
{ name: '授课记录', path: '/teacher/lessons' },
|
||
|
|
{ name: '班级管理', path: '/teacher/classes' },
|
||
|
|
{ name: '学生列表', path: '/teacher/students' },
|
||
|
|
{ name: '任务管理', path: '/teacher/tasks' },
|
||
|
|
{ name: '成长记录', path: '/teacher/growth-records' },
|
||
|
|
{ name: '教学反馈', path: '/teacher/feedback' },
|
||
|
|
];
|
||
|
|
|
||
|
|
for (const step of steps) {
|
||
|
|
await page.goto(step.path);
|
||
|
|
await waitForPageLoad(page);
|
||
|
|
await page.waitForTimeout(1000);
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: `访问${step.name}(${step.path}) - OK`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test.info().annotations.push({
|
||
|
|
type: 'success',
|
||
|
|
description: '完整教学流程测试通过!所有页面均可正常访问。',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|