import { expect, test } from './fixtures'; const moicenUnionid = process.env.MOICEN_E2E_UNIONID?.trim(); test.describe('学生老师切换', () => { async function loginAsStudent(page: import('@playwright/test').Page) { const q = new URLSearchParams({ unionid: moicenUnionid!, status: '2' }); await page.goto(`/?${q.toString()}`, { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); // Wait for SPA to settle and login/role resolution to complete await page.waitForTimeout(5000); // Pick STUDENT role explicitly const roleSelect = page.getByText('请选择您的登录身份'); if (await roleSelect.isVisible().catch(() => false)) { const studentRole = page.locator('.van-grid-item').filter({ hasText: '学生' }); if (await studentRole.isVisible().catch(() => false)) { await studentRole.click(); await page.waitForTimeout(5000); } } // Handle org select if needed if (page.url().includes('/org/select')) { const guest = page.getByText('请返回微信小程序完成登录'); if (await guest.isVisible().catch(() => false)) { test.skip(true, '会话不可用'); return; } const orgCells = page.locator('#app .van-cell-group .van-cell'); const n = await orgCells.count(); expect(n).toBeGreaterThan(0); await orgCells.first().click(); await page.waitForTimeout(5000); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); if (await guest.isVisible().catch(() => false)) { test.skip(true, 'org switch 后会话不可用'); return; } await page.goto('/student/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); } // Ensure we're on a student page const currentPath = new URL(page.url()).pathname; if (!currentPath.startsWith('/student/')) { await page.goto('/student/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); } // Skip if session lost if ( await page .getByText('请返回微信小程序完成登录') .isVisible() .catch(() => false) ) { test.skip(true, '无法以学生身份访问 profile'); return; } } test('profile 页显示当前老师且可切换', async ({ page }) => { test.setTimeout(180_000); await loginAsStudent(page); // Navigate to student profile await page.goto('/student/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Find the teacher section const teacherSection = page.locator('.current-teacher'); const teacherVisible = await teacherSection .isVisible() .catch(() => false); if (!teacherVisible) { const profileHeader = page.locator('.info'); const profileVisible = await profileHeader.isVisible().catch(() => false); if (!profileVisible) { test.skip(true, '未到 profile 页'); return; } test.skip(true, '学生角色无老师信息'); return; } const teacherText = await teacherSection.locator('span').first().textContent(); expect(teacherText).toContain('当前老师'); // Verify auto-select: teacher name should be displayed (not "未选择老师") expect(teacherText).not.toContain('未选择老师'); // Check switch button const switchBtn = teacherSection.locator('.van-button'); await expect(switchBtn).toBeVisible({ timeout: 10_000 }); // Click switch and verify teacher list await switchBtn.click(); await page.waitForTimeout(2000); const teacherCells = page.locator('.van-cell'); const teacherCount = await teacherCells.count(); expect(teacherCount).toBeGreaterThan(0); if (teacherCount > 1) { await teacherCells.nth(1).click(); await page.waitForTimeout(1500); const updatedSection = page.locator('.current-teacher'); await expect(updatedSection).toBeVisible({ timeout: 15_000 }); const newText = await updatedSection.locator('span').first().textContent(); expect(newText).not.toContain('未选择老师'); } }); test('「我的老师」列表显示本机构老师', async ({ page }) => { test.setTimeout(180_000); await loginAsStudent(page); // Navigate to my teachers page await page.goto('/student/teachers', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); if ( await page .getByText('请返回微信小程序完成登录') .isVisible() .catch(() => false) ) { test.skip(true, '会话已过期'); return; } const teacherCells = page.locator('.van-cell'); const teacherCount = await teacherCells.count(); expect(teacherCount).toBeGreaterThan(0); }); test('角色切换后自动选择老师且不会回机构选择页', async ({ page }) => { test.setTimeout(180_000); // Login as student to establish session await loginAsStudent(page); // Navigate to profile await page.goto('/student/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); const roleSwitcher = page.locator('.van-icon-exchange'); await roleSwitcher.click(); // Wait for action sheet to appear and settle await page.locator('.van-action-sheet').waitFor({ state: 'visible', timeout: 10_000 }); await page.waitForTimeout(1000); // Select TEACHER role (index 2 in ["测试员","学生","教师","管理员","主管教师"]) const teacherRoleBtn = page.locator('.van-action-sheet__item').nth(2); if (!(await teacherRoleBtn.isVisible().catch(() => false))) { test.skip(true, '未找到教师角色选项'); return; } await teacherRoleBtn.click(); await page.waitForTimeout(1500); // Handle confirm dialog if shown const confirmDialog = page.locator('.van-dialog__confirm'); if (await confirmDialog.isVisible().catch(() => false)) { await confirmDialog.click(); await page.waitForTimeout(5000); } // Wait for role switch to complete await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Navigate to teacher profile to ensure we're in teacher context await page.goto('/teacher/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); if (await page.getByText('请返回微信小程序完成登录').isVisible().catch(() => false)) { test.skip(true, '老师会话不可用'); return; } // Now switch back to STUDENT role const roleSwitcher2 = page.locator('.van-icon-exchange'); await roleSwitcher2.click(); // Wait for action sheet to appear and settle await page.locator('.van-action-sheet').waitFor({ state: 'visible', timeout: 10_000 }); await page.waitForTimeout(1000); // Select STUDENT role (index 1 in ["测试员","学生","教师","管理员","主管教师"]) const studentRoleBtn = page.locator('.van-action-sheet__item').nth(1); if (!(await studentRoleBtn.isVisible().catch(() => false))) { test.skip(true, '未找到学生角色选项'); return; } await studentRoleBtn.click(); await page.waitForTimeout(1500); // Handle confirm dialog const confirmDialog2 = page.locator('.van-dialog__confirm'); if (await confirmDialog2.isVisible().catch(() => false)) { await confirmDialog2.click(); await page.waitForTimeout(5000); } // Wait for role switch and auto-select to complete await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(5000); // Auto-select should have picked a teacher and redirected to /student/home const currentPath = new URL(page.url()).pathname; // Verify we did NOT end up on org-select page expect(currentPath).not.toBe('/org/select'); // Should be on a valid student page (not org-select) const isStudentPage = currentPath.startsWith('/student/') || currentPath === '/'; expect(isStudentPage).toBe(true); // Verify teacher is auto-selected: navigate to profile and check await page.goto('/student/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); const teacherSection = page.locator('.current-teacher'); if (await teacherSection.isVisible().catch(() => false)) { const teacherText = await teacherSection.locator('span').first().textContent(); // Teacher should be selected (not "未选择老师") expect(teacherText).not.toContain('未选择老师'); } }); test('老师选择后不会回到机构选择页面', async ({ page }) => { test.setTimeout(180_000); // Login as student await loginAsStudent(page); // Navigate to profile and find teacher section await page.goto('/student/profile', { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Click switch teacher button const switchBtn = page.locator('.current-teacher .van-button'); if (!(await switchBtn.isVisible().catch(() => false))) { test.skip(true, '未找到切换老师按钮'); return; } await switchBtn.click(); await page.waitForTimeout(2000); // Verify teacher list shows const teacherCells = page.locator('.van-cell'); const teacherCount = await teacherCells.count(); expect(teacherCount).toBeGreaterThan(0); // Select a teacher await teacherCells.first().click(); await page.waitForTimeout(2000); // Verify we didn't end up on org-select const afterUrl = page.url(); expect(afterUrl).not.toContain('/org/select'); // Should still be on a valid student page await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); }); test('无老师学生自动跳转到选择老师页面', async ({ page }) => { test.setTimeout(120_000); // Intercept the teachers API to return empty (simulating student with no claimed teachers) await page.route('**/find_all_teachers_by_student_id/**', async route => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ r: true, d: [], e: null }) }); }); const q = new URLSearchParams({ unionid: moicenUnionid!, status: '2' }); await page.goto(`/?${q.toString()}`, { waitUntil: 'domcontentloaded', timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(5000); // Pick STUDENT role explicitly const roleSelect = page.getByText('请选择您的登录身份'); if (await roleSelect.isVisible().catch(() => false)) { const studentRole = page.locator('.van-grid-item').filter({ hasText: '学生' }); if (await studentRole.isVisible().catch(() => false)) { await studentRole.click(); await page.waitForTimeout(5000); } } // Handle org select if needed if (page.url().includes('/org/select')) { const orgCells = page.locator('#app .van-cell-group .van-cell'); const n = await orgCells.count(); expect(n).toBeGreaterThan(0); await orgCells.first().click(); await page.waitForTimeout(5000); } // Student with no teachers should be redirected to teacher-select page // The redirect happens asynchronously via watchEffect await expect(async () => { const url = page.url(); expect(url).toContain('/student/teacher-select'); }).toPass({ timeout: 30_000 }); }); });