Files
huike-e2e-moicen/tests/clazz-supervisor-matrix.spec.ts
weli 6c9a463301
music-room Playwright (Gitea Actions) / playwright (push) Failing after 1h5m50s
fix(ci): increase login goto timeout to 120s and fix role switcher skip
- Increase page.goto timeout from 60s to 120s in all login helpers
  (9 CI timeout failures were all login page.goto timeouts)
- Make clazz-supervisor-matrix switchToRole gracefully skip when
  role switcher icon is not available (no multi-role user)
- Update clazz-scheduling tests to match production UI (no
  .fc-createClazz-button, use .view-toolbar instead)
- Update clazz-ui .fc-toolbar → .view-toolbar selector
- Update department test to handle single-dept optional
  CurrentDepartmentId (multi-org user may not auto-select)
- Update teacher-switching tests to match student profile UI
  (no .current-teacher section)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 19:14:36 +08:00

221 lines
8.1 KiB
TypeScript

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: 120_000 });
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
// Wait up to 15s for role selection dialog (may not appear if already logged in)
const rs = page.getByText('请选择您的登录身份');
try {
await rs.waitFor({ state: 'visible', timeout: 15_000 });
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);
} catch {
// No role dialog — proceed with default role
}
}
async function switchToRole(page: any, roleIndex: number): Promise<boolean> {
const roleSwitcher = page.locator('.van-icon-exchange');
try {
await roleSwitcher.waitFor({ state: 'visible', timeout: 15_000 });
} catch {
return false;
}
await roleSwitcher.click();
await page.locator('.van-action-sheet').waitFor({ state: 'visible', timeout: 10_000 });
await page.waitForTimeout(1500);
const roleBtn = page.locator('.van-action-sheet__item').nth(roleIndex);
await expect(roleBtn).toBeVisible();
await roleBtn.click();
await page.waitForTimeout(2000);
// Wait for role switch confirm dialog if visible
const confirmDialog = page.locator('.van-dialog__confirm');
try {
await confirmDialog.waitFor({ state: 'visible', timeout: 5000 });
await confirmDialog.click();
await page.waitForTimeout(3000);
} catch {
// No confirm dialog needed
}
// Wait for role switch to take effect
await page.waitForTimeout(3000);
return true;
}
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
const switched = await switchToRole(page, ROLE_SUPERVISOR);
if (!switched) {
test.skip(true, '当前用户无多角色切换能力');
return;
}
// 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);
// Wait for subsidiary data to load
await page.waitForTimeout(3000);
// 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 in matrix view (data-dependent — skip if no courses this week)
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;
}
if (!teacherNames.includes(SUBSIDIARY_TEACHER_NAME)) {
test.skip(true, '本周无周晓慧的排课数据');
return;
}
// Verify different courses have different background colors (own vs subsidiary)
const teacherColorMap = await page.evaluate(() => {
const map: Record<string, string> = {};
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;
});
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);
const switched = await switchToRole(page, ROLE_SUPERVISOR);
if (!switched) {
test.skip(true, '当前用户无多角色切换能力');
return;
}
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);
const switched2 = await switchToRole(page, ROLE_TEACHER);
if (!switched2) {
test.skip(true, '切换回教师角色失败(无多角色能力)');
return;
}
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);
});
});