/** * ================================================================ * 场景:规划师仪表盘 E2E 测试 * ================================================================ * 目标:规划师通过 cfc-web TeacherDashboard 查看统计数据、快捷操作、家庭/团队列表 * 部署地址:http://cfc.iwintrue.com * * 测试场景: * 1. 统计卡片渲染(服务家庭、活跃家庭、本月销售额、我的佣金) * 2. 快捷操作导航(管理家庭、消息中心、查看订单、DAN测评) * 3. 今日概览数字展示(新增家庭、完成任务、新订单、待审核) * 4. 我的家庭列表渲染 * 5. 空状态显示(无家庭/无团队) * 6. 我的团队列表渲染 * 7. 最近订单表格渲染 * 8. 家庭任务完成率进度条 * 9. 团队成员等级标签 * 10. 页面整体加载和标题 * * 前置条件:已登录规划师账号 * ================================================================ */ const { test, expect } = require('@playwright/test'); /** * 确保已登录到管理后台(规划师角色) */ async function ensureLoggedIn(page) { const sidebar = page.locator('.sidebar-container, .el-menu-vertical-demo, .el-aside, #app > div'); try { await sidebar.waitFor({ state: 'visible', timeout: 5000 }); return; } catch { // 未登录,继续执行登录流程 } await page.goto('/login', { waitUntil: 'networkidle', timeout: 30000 }); await page.waitForTimeout(2000); // 尝试多种输入框选择器 const inputSelectors = [ '.el-input__inner', 'input[type="text"]', 'input[placeholder*="账号"]', 'input[placeholder*="用户名"]', 'input[name="username"]', '.el-input input', ]; let inputFound = false; for (const sel of inputSelectors) { try { const el = page.locator(sel).first(); if (await el.isVisible({ timeout: 3000 })) { await el.fill('teacher'); inputFound = true; break; } } catch { // 继续尝试下一个选择器 } } if (!inputFound) { const allInputs = page.locator('input'); const count = await allInputs.count(); if (count > 0) { await allInputs.first().fill('teacher'); if (count > 1) { await allInputs.nth(1).fill('teacher123'); } } } // 填密码 const pwdInputs = page.locator('input[type="password"]'); if (await pwdInputs.count() > 0) { await pwdInputs.first().fill('teacher123'); } else { const allInputs = page.locator('input'); const count = await allInputs.count(); if (count >= 2) { await allInputs.nth(1).fill('teacher123'); } } // 点登录按钮 const btnSelectors = [ '.el-button--primary', 'button[type="submit"]', '.login-btn', '.el-button:has-text("登录")', '.el-button:has-text("登 录")', ]; for (const sel of btnSelectors) { try { const btn = page.locator(sel).first(); if (await btn.isVisible({ timeout: 2000 })) { await btn.click(); break; } } catch { // 继续尝试 } } // 等待登录完成 await page.waitForTimeout(3000); try { await sidebar.waitFor({ state: 'visible', timeout: 10000 }); } catch { // 超时仍继续,可能页面已跳转 } } test.describe('【场景流程】规划师仪表盘 TeacherDashboard', () => { test('[场景1] 统计卡片渲染 - 期望显示4个统计卡片(服务家庭、活跃家庭、销售额、佣金)', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(3000); await page.screenshot({ path: 'test-artifacts/teacher-dashboard-stats.png' }).catch(() => {}); const statRow = page.locator('.td-stat-row'); await expect(statRow).toBeVisible({ timeout: 15000 }); // 应有4个统计卡片 const statCards = page.locator('.td-stat-card'); const count = await statCards.count(); expect(count).toBeGreaterThanOrEqual(4); // 验证每个卡片包含数值和标签 const statContents = page.locator('.td-stat-content'); await expect(statContents.first()).toBeVisible(); }); test('[场景2] 快捷操作导航 - 期望显示4个快捷操作入口', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const quickCard = page.locator('.td-quick-card'); await expect(quickCard).toBeVisible({ timeout: 15000 }); const actionItems = page.locator('.td-action-item'); const count = await actionItems.count(); expect(count).toBeGreaterThanOrEqual(4); }); test('[场景3] 今日概览 - 期望显示4个今日统计数据', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const todayStats = page.locator('.td-today-stats'); await expect(todayStats).toBeVisible({ timeout: 15000 }); const statItems = page.locator('.td-today-stat-item'); const count = await statItems.count(); expect(count).toBeGreaterThanOrEqual(4); }); test('[场景4] 我的家庭列表 - 期望显示家庭列表或空状态', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const familyListCard = page.locator('.td-list-card').first(); await expect(familyListCard).toBeVisible({ timeout: 15000 }); // 要么有家庭列表项,要么显示空状态 const familyItems = page.locator('.td-family-item'); const emptyState = page.locator('.td-empty-state'); const hasItems = await familyItems.count(); const hasEmpty = await emptyState.count(); expect(hasItems > 0 || hasEmpty > 0).toBeTruthy(); }); test('[场景5] 空状态显示 - 无家庭或无团队时显示空状态图标和提示', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const emptyIcons = page.locator('.td-empty-state i'); const emptyTexts = page.locator('.td-empty-state p'); const count = await emptyIcons.count(); // 如果有空状态,验证它有图标和文本 if (count > 0) { await expect(emptyIcons.first()).toBeVisible(); await expect(emptyTexts.first()).toBeVisible(); } }); test('[场景6] 我的团队列表 - 期望显示团队成员或空状态', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); // 第二个td-list-card为团队列表 const teamCards = page.locator('.td-list-card'); const count = await teamCards.count(); expect(count).toBeGreaterThanOrEqual(2); const teamItems = page.locator('.td-team-item'); const emptyState = page.locator('.td-empty-state'); const hasItems = await teamItems.count(); const hasEmpty = await emptyState.count(); expect(hasItems > 0 || hasEmpty > 0).toBeTruthy(); }); test('[场景7] 最近订单表格 - 期望显示订单表格', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const ordersCard = page.locator('.td-orders-card'); await expect(ordersCard).toBeVisible({ timeout: 15000 }); const ordersTable = page.locator('.td-orders-table, .el-table'); await expect(ordersTable.first()).toBeVisible(); }); test('[场景8] 团队成员等级标签 - 期望显示规划师等级标签', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const levelTags = page.locator('.td-member-level .el-tag'); const count = await levelTags.count(); // 如果有团队成员,验证有等级标签 if (count > 0) { await expect(levelTags.first()).toBeVisible(); } }); test('[场景9] 统计卡片数值格式 - 期望销售额和佣金显示货币格式', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const statValues = page.locator('.td-stat-value'); const count = await statValues.count(); expect(count).toBeGreaterThanOrEqual(4); }); test('[场景10] 页面标题 - 期望页面标题包含"规划师首页"', async ({ page }) => { await ensureLoggedIn(page); await page.goto('/teacher-dashboard', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); await expect(page).toHaveTitle(/规划师|teacher/i); }); });