| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /**
- * ================================================================
- * 场景:规划师仪表盘 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);
- });
- });
|