import { expect, test } from './fixtures'; const moicenUnionid = process.env.MOICEN_E2E_UNIONID?.trim(); test.skip(!moicenUnionid, 'MOICEN_E2E_UNIONID 未设置'); async function loginAsTeacher(page: any) { const q = new URLSearchParams({ unionid: moicenUnionid!, status: '2' }); await page.goto(`/?${q.toString()}`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); // 逐层处理选择页:机构选择 → 角色选择(可能有 0~n 层) let roleSelected = false; for (let i = 0; i < 5 && !roleSelected; i++) { const path = await page.evaluate(() => window.location.pathname); if (path === '/org/select') { await page.locator('.van-cell').first().click(); await page.waitForTimeout(3_000); } else if (path === '/') { const rs = page.getByText('请选择您的登录身份'); try { await rs.waitFor({ state: 'visible', timeout: 10_000 }); const items = page.locator('.van-grid-item'); const count = await items.count(); if (count >= 3) { await items.nth(2).click(); // TEACHER role } else { await items.first().click(); } await page.waitForTimeout(3_000); roleSelected = true; } catch { // 无角色选择器 → 角色已自动选中 break; } } else { // 已在目标页面 break; } } } // CI 环境下 /clazz 页加载较慢,给予更宽松的超时 test.setTimeout(240_000); test.describe('排课双视图切换(阿难账号)', () => { test('教师端排课页显示视图切换按钮', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Check toggle buttons exist const toggleBar = page.locator('.view-toolbar'); await expect(toggleBar).toBeVisible({ timeout: 10_000 }); const calBtn = toggleBar.locator('button', { hasText: '日历' }); const matrixBtn = toggleBar.locator('button', { hasText: '矩阵' }); await expect(calBtn).toBeVisible(); await expect(matrixBtn).toBeVisible(); // Default is calendar — 日历 should be primary await expect(calBtn).toHaveClass(/van-button--primary/); }); test('切换到矩阵视图后显示时段列和日期行', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Switch to matrix view const matrixBtn = page.locator('.view-toolbar button', { hasText: '矩阵' }); await matrixBtn.click(); await page.waitForTimeout(1500); // Matrix container should be visible const matrixContainer = page.locator('.matrix-container'); await expect(matrixContainer).toBeVisible({ timeout: 10_000 }); // Header should show time slot labels (第一节 ~ 第四节) const headerCells = page.locator('.header-cell'); await expect(headerCells.first()).toBeVisible(); const count = await headerCells.count(); await expect(count).toBeGreaterThanOrEqual(4); // Each header cell should have slot label + time const firstHeader = headerCells.first(); await expect(firstHeader.locator('.header-label')).toBeVisible(); await expect(firstHeader.locator('.header-time')).toBeVisible(); // Day sidebar should show weekday labels (周一 ~ 周日) const dayCells = page.locator('.sidebar-row'); await expect(dayCells.first()).toBeVisible(); expect(await dayCells.count()).toBe(7); // Each day cell should have day name + date const firstDay = dayCells.first(); await expect(firstDay.locator('.day-name')).toBeVisible(); await expect(firstDay.locator('.day-date')).toBeVisible(); }); test('矩阵视图导航栏可操作(上一周/本周/下一周)', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Switch to matrix await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); // Nav buttons should exist const nav = page.locator('.view-toolbar__nav'); await expect(nav).toBeVisible({ timeout: 10_000 }); // Click "本周" to reset to current week await nav.locator('button', { hasText: '本周' }).click(); await page.waitForTimeout(2000); // Should show date range const range = page.locator('.view-toolbar__range'); await expect(range).toBeVisible(); }); test('矩阵视图课块显示课程名、老师、学生信息', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Switch to matrix await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); // Check event blocks exist in the grid const eventBlocks = page.locator('.event-block'); const count = await eventBlocks.count(); if (count === 0) { test.skip(true, '无排课数据'); return; } // First block should show course name const first = eventBlocks.first(); await expect(first.locator('.ev-title')).toBeVisible(); // At least one block should have teacher name const hasTeacher = await page.locator('.ev-teacher').count(); expect(hasTeacher).toBeGreaterThanOrEqual(0); // At least one block should have student names const hasStudents = await page.locator('.ev-students').count(); expect(hasStudents).toBeGreaterThanOrEqual(0); }); test('矩阵视图同一时段多节课横向排列', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Switch to matrix await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); // Find a cell with 2+ event blocks and verify it uses flex row const isHorizontal = await page.evaluate(() => { const cells = document.querySelectorAll('.event-cell'); for (const cell of cells) { const blocks = cell.querySelectorAll('.event-block'); if (blocks.length >= 2) { const style = window.getComputedStyle(cell); return style.display === 'flex' && style.flexDirection === 'row'; } } return false; }); if (!isHorizontal) { const hasMulti = await page.evaluate(() => { const cells = document.querySelectorAll('.event-cell'); return Array.from(cells).some(c => c.querySelectorAll('.event-block').length >= 2); }); if (!hasMulti) { test.skip(true, '无同一时段多节课数据'); return; } } expect(isHorizontal).toBe(true); }); test('从矩阵视图切换回日历视图后日历正常显示', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // Switch to matrix await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); await expect(page.locator('.matrix-container')).toBeVisible({ timeout: 10_000 }); // Switch back to calendar await page.locator('.view-toolbar button', { hasText: '日历' }).click(); await page.waitForTimeout(1500); // Calendar (FullCalendar) should be visible await expect(page.locator('.fc')).toBeVisible({ timeout: 10_000 }); }); test('日历与矩阵视图起始日一致(周一)', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // 切换到矩阵 await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); await expect(page.locator('.matrix-container')).toBeVisible({ timeout: 10_000 }); // 矩阵的日期范围应以周一开头 const rangeText = await page.locator('.view-toolbar__range').textContent(); expect(rangeText).toBeTruthy(); const startDate = rangeText?.split('~')[0]?.trim(); expect(startDate).toBeTruthy(); if (startDate) { const day = new Date(startDate).getDay(); expect(day).toBe(1); // Monday = 1 } }); test('来回切换视图后数据仍正常加载', async ({ page }) => { await loginAsTeacher(page); await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); await page.waitForTimeout(3000); // 无排课数据则跳过 const hasEvents = await page.locator('.fc-event').first().isVisible().catch(() => false); if (!hasEvents) { test.skip(true, '当前账号无可视事件'); return; } // 切换到矩阵 await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); await expect(page.locator('.matrix-container')).toBeVisible({ timeout: 10_000 }); // 矩阵应有事件 const matrixFirst = await page.locator('.event-block').count(); // 切回日历 await page.locator('.view-toolbar button', { hasText: '日历' }).click(); await page.waitForTimeout(1500); await expect(page.locator('.fc')).toBeVisible({ timeout: 10_000 }); // 日历仍有事件(说明数据加载正常) await expect(async () => { const calEvents = await page.locator('.fc-event').count(); expect(calEvents).toBeGreaterThan(0); }).toPass({ timeout: 10_000 }); // 再次切到矩阵 await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); await expect(page.locator('.matrix-container')).toBeVisible({ timeout: 10_000 }); // 矩阵仍有事件 const matrixSecond = await page.locator('.event-block').count(); expect(matrixSecond).toBeGreaterThanOrEqual(0); // 导航到下一周再回来 await page.locator('.view-toolbar__nav button', { hasText: '›' }).click(); await page.waitForTimeout(2000); // 切回日历 await page.locator('.view-toolbar button', { hasText: '日历' }).click(); await page.waitForTimeout(1500); // 再切回矩阵,回到本周 await page.locator('.view-toolbar button', { hasText: '矩阵' }).click(); await page.waitForTimeout(1500); await page.locator('.view-toolbar__nav button', { hasText: '本周' }).click(); await page.waitForTimeout(2000); // 回到本周后应有事件 const currentWeekEvents = await page.locator('.event-block').count(); expect(currentWeekEvents).toBeGreaterThanOrEqual(0); }); });