diff --git a/tests/clazz-dual-view.spec.ts b/tests/clazz-dual-view.spec.ts index 12712d8..ec78a25 100644 --- a/tests/clazz-dual-view.spec.ts +++ b/tests/clazz-dual-view.spec.ts @@ -71,7 +71,7 @@ test.describe('排课双视图切换(阿难账号)', () => { await expect(firstHeader.locator('.header-time')).toBeVisible(); // Day sidebar should show weekday labels (周一 ~ 周日) - const dayCells = page.locator('.day-cell'); + const dayCells = page.locator('.sidebar-row'); await expect(dayCells.first()).toBeVisible(); expect(await dayCells.count()).toBe(7); @@ -136,6 +136,42 @@ test.describe('排课双视图切换(阿难账号)', () => { 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 }); diff --git a/tests/clazz-supervisor-matrix.spec.ts b/tests/clazz-supervisor-matrix.spec.ts new file mode 100644 index 0000000..b19f120 --- /dev/null +++ b/tests/clazz-supervisor-matrix.spec.ts @@ -0,0 +1,194 @@ +import { expect, test } from './fixtures'; + +const moicenUnionid = process.env.MOICEN_E2E_UNIONID?.trim(); +test.skip(!moicenUnionid, 'MOICEN_E2E_UNIONID 未设置'); + +/** + * 角色选择器在 action-sheet 中的索引: + * ["测试员","学生","教师","管理员","主管教师"] + */ +const ROLE_TEACHER = 2; +const ROLE_SUPERVISOR = 4; + +const SUBSIDIARY_TEACHER_NAME = '周晓慧'; + +test.describe('主管老师矩阵视图', () => { + + async function loginAsTeacher(page: any) { + 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(3000); + + const rs = page.getByText('请选择您的登录身份'); + if (await rs.isVisible().catch(() => false)) { + const items = page.locator('.van-grid-item'); + const count = await items.count(); + if (count >= 3) { + await items.nth(ROLE_TEACHER).click(); + } else { + await items.first().click(); + } + await page.waitForTimeout(3000); + } + } + + async function switchToRole(page: any, roleIndex: number) { + const roleSwitcher = page.locator('.van-icon-exchange'); + await expect(roleSwitcher).toBeVisible({ timeout: 15_000 }); + await roleSwitcher.click(); + await page.locator('.van-action-sheet').waitFor({ state: 'visible', timeout: 10_000 }); + await page.waitForTimeout(1000); + + const roleBtn = page.locator('.van-action-sheet__item').nth(roleIndex); + await expect(roleBtn).toBeVisible(); + await roleBtn.click(); + await page.waitForTimeout(1500); + + const confirmDialog = page.locator('.van-dialog__confirm'); + if (await confirmDialog.isVisible().catch(() => false)) { + await confirmDialog.click(); + await page.waitForTimeout(3000); + } + await page.waitForTimeout(2000); + } + + async function switchToMatrixView(page: any) { + const matrixBtn = page.locator('.view-toolbar button', { hasText: '矩阵' }); + await expect(matrixBtn).toBeVisible({ timeout: 10_000 }); + await matrixBtn.click(); + await page.waitForTimeout(1500); + await expect(page.locator('.matrix-container')).toBeVisible({ timeout: 10_000 }); + } + + test('教师角色下矩阵视图不显示下属老师(周晓慧)的课程', async ({ page }) => { + test.setTimeout(120_000); + + 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 switchToMatrixView(page); + + // Collect all teacher names from event blocks + const teacherNames = await page.evaluate(() => { + return Array.from(document.querySelectorAll('.ev-teacher')) + .map(el => el.textContent?.trim()) + .filter(Boolean); + }); + + // In TEACHER role, 周晓慧's courses should NOT appear + const hasZhouXiaohui = teacherNames.includes(SUBSIDIARY_TEACHER_NAME); + expect(hasZhouXiaohui).toBe(false); + + // footer-subsidiary should not exist in TEACHER role + const footerSubsidiary = page.locator('.footer-subsidiary'); + await expect(footerSubsidiary).not.toBeVisible(); + }); + + test('切换到主管老师角色后矩阵视图显示周晓慧的课程(不同颜色)', async ({ page }) => { + test.setTimeout(120_000); + + await loginAsTeacher(page); + + // Go to TEACHER profile to open role switcher + await page.goto('/teacher/profile', { waitUntil: 'domcontentloaded', timeout: 60_000 }); + await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); + await page.waitForTimeout(3000); + + // Switch to SUPERVISOR role + await switchToRole(page, ROLE_SUPERVISOR); + + // Navigate to clazz page + await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); + await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); + await page.waitForTimeout(5000); + + // Verify subsidiary teacher tags in footer + const footerSubsidiary = page.locator('.footer-subsidiary'); + await expect(footerSubsidiary).toBeVisible({ timeout: 15_000 }); + const tagCount = await footerSubsidiary.locator('.van-tag').count(); + expect(tagCount).toBeGreaterThanOrEqual(1); + + // Verify 周晓慧 tag is present + const zhouTag = footerSubsidiary.locator('.van-tag', { hasText: SUBSIDIARY_TEACHER_NAME }); + await expect(zhouTag).toBeVisible(); + + // Switch to matrix view + await switchToMatrixView(page); + + // Verify 周晓慧's courses appear + const teacherNames = await page.evaluate(() => { + return Array.from(document.querySelectorAll('.ev-teacher')) + .map(el => el.textContent?.trim()) + .filter(Boolean); + }); + + if (teacherNames.length === 0) { + test.skip(true, '排课数据为空'); + return; + } + + expect(teacherNames).toContain(SUBSIDIARY_TEACHER_NAME); + + // Verify different courses have different background colors + const teacherColorMap = await page.evaluate(() => { + const map: Record = {}; + document.querySelectorAll('.event-block').forEach(block => { + const teacher = block.querySelector('.ev-teacher')?.textContent?.trim(); + const bgColor = getComputedStyle(block).backgroundColor; + if (teacher && bgColor && teacher !== '未设置老师') { + map[teacher] = map[teacher] || bgColor; + } + }); + return map; + }); + + // At least 2 different colors should be used (own + subsidiary) + const colorSet = new Set(Object.values(teacherColorMap)); + expect(colorSet.size).toBeGreaterThanOrEqual(2); + }); + + test('从主管老师切回教师角色后周晓慧的课程消失', async ({ page }) => { + test.setTimeout(180_000); + + await loginAsTeacher(page); + + // Switch to SUPERVISOR first + await page.goto('/teacher/profile', { waitUntil: 'domcontentloaded', timeout: 60_000 }); + await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); + await page.waitForTimeout(3000); + await switchToRole(page, ROLE_SUPERVISOR); + + // Verify clazz page shows subsidiary data + await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); + await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); + await page.waitForTimeout(5000); + + const footerSubsidiary = page.locator('.footer-subsidiary'); + await expect(footerSubsidiary).toBeVisible({ timeout: 15_000 }); + + // Switch back to TEACHER via supervisor profile + await page.goto('/supervisor/profile', { waitUntil: 'domcontentloaded', timeout: 60_000 }); + await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); + await page.waitForTimeout(3000); + await switchToRole(page, ROLE_TEACHER); + + // Verify clazz page no longer shows subsidiary data + await page.goto('/clazz', { waitUntil: 'domcontentloaded', timeout: 60_000 }); + await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); + await page.waitForTimeout(5000); + + await expect(footerSubsidiary).not.toBeVisible(); + + // 周晓慧's name should not appear in event blocks + const teacherNames = await page.evaluate(() => { + return Array.from(document.querySelectorAll('.ev-teacher')) + .map(el => el.textContent?.trim()) + .filter(Boolean); + }); + + expect(teacherNames).not.toContain(SUBSIDIARY_TEACHER_NAME); + }); +});