/** * ================================================================ * 场景:成长规划师家庭管理 E2E 测试 * ================================================================ * 用户故事:成长规划师可以查看自己管理的家庭列表, * 了解每个家庭的成员构成和服务记录。 * * 流程步骤: * 1. 规划师登录后台 * 2. 进入家庭管理页面 * 3. 查看家庭列表 * 4. 查看家庭详情 * 5. 查看服务记录 * * 关键里程碑: * - [Milestone-1] 家庭管理页面加载成功 * - [Milestone-2] 家庭列表加载正常 * - [Milestone-3] 家庭详情可查看 * - [Milestone-4] 服务记录可查询 * * API 端点: * - POST /api/guide/family/list (家庭列表) * * 运行:npx playwright test tests/e2e/guide-family-list.spec.js * ================================================================ */ const { test, expect } = require('@playwright/test'); test.describe('【场景流程】成长规划师家庭管理', () => { test.beforeEach(async ({ page }) => { // 规划师登录(admin 后台) await page.goto('http://cfc.iwintrue.com/admin/login'); await page.waitForLoadState('networkidle'); const guidePhone = '13701366189'; // 测试规划师账号 const phoneInput = page.locator('input[placeholder="请输入手机号"]'); await phoneInput.fill(guidePhone); await page.locator('.login-btn, button:has-text("登录")').click(); await page.waitForTimeout(1500); // 验证登录成功 try { await page.waitForSelector('.guide-tab, .teacher-tab, .admin-layout', { timeout: 10000 }); } catch (e) { if (await page.url().includes('/login')) { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); } } }); /** * 场景1:家庭管理页面加载 * 角色:成长规划师 * 触发条件:进入家庭管理页面 * 校验:页面加载成功,tab 切换到家庭列表 */ test('[场景1] 家庭管理页面加载 — 期望 tab 切换到家庭列表', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 验证页面标题 const title = page.locator('.el-card__header, .tab-header, h2:has-text("家庭管理")'); expect(await title.isVisible()).toBeTruthy(); // 验证默认选中"家庭列表" tab const listTab = page.locator('.nav-item.active, .el-tab-pane.is-active, text="家庭列表"'); expect(await listTab.isVisible()).toBeTruthy(); }); /** * 场景2:家庭列表加载 * 角色:成长规划师 * 触发条件:进入家庭管理页面 * 校验:列表加载正常数据 */ test('[场景2] 家庭列表加载 — 期望数据加载', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 验证列表加载 const familyRows = page.locator('.el-table__row, .family-row'); const rowCount = await familyRows.count(); expect(rowCount).toBeGreaterThanOrEqual(0); // 验证列数 if (rowCount > 0) { const firstRow = familyRows.first(); const cells = firstRow.locator('.el-table__cell'); expect(await cells.count()).toBeGreaterThanOrEqual(5); } }); /** * 场景3:搜索筛选家庭 * 角色:成长规划师 * 触发条件:输入家庭名称、状态等筛选条件 * 校验:列表刷新返回正常数据 */ test('[场景3] 搜索筛选家庭 — 期望列表刷新', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 输入筛选条件 const nameInput = page.locator('input[placeholder="家庭名称"]'); if (await nameInput.isVisible()) { await nameInput.fill('测试家庭'); } // 点击搜索按钮 const searchBtn = page.locator('.search-btn, button:has-text("搜索")'); await searchBtn.click(); await page.waitForTimeout(1000); // 验证列表刷新 const familyRows = page.locator('.el-table__row'); expect(await familyRows.count()).toBeGreaterThanOrEqual(0); }); /** * 场景4:查看家庭详情 * 角色:成长规划师 * 触发条件:点击家庭列表中的"详情"按钮 * 校验:家庭详情页面加载成功 */ test('[场景4] 查看家庭详情 — 期望详情加载', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 查找第一个家庭的详情按钮 const detailBtn = page.locator('.el-table__row:first-child button:has-text("详情")'); if (await detailBtn.isVisible()) { await detailBtn.click(); await page.waitForTimeout(1000); // 验证详情加载 const familyInfo = page.locator('.family-info, .detail-card'); expect(await familyInfo.isVisible()).toBeTruthy(); // 验证家庭成员列表 const memberRows = page.locator('.member-item, .el-table__row'); expect(await memberRows.count()).toBeGreaterThanOrEqual(0); } else { // 无家庭数据,跳过 expect(true).toBeTruthy(); } }); /** * 场景5:查看家庭服务记录 * 角色:成长规划师 * 触发条件:在家庭详情页切换到服务记录 tab * 校验:服务记录加载正常 */ test('[场景5] 查看家庭服务记录 — 期望记录加载', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 查找第一个家庭的详情按钮 const detailBtn = page.locator('.el-table__row:first-child button:has-text("详情")'); if (await detailBtn.isVisible()) { await detailBtn.click(); await page.waitForTimeout(1000); // 切换到服务记录 tab const recordTab = page.locator('.nav-item:has-text("服务记录"), .tab-item:has-text("服务")'); await recordTab.click(); await page.waitForTimeout(1000); // 验证记录加载 const recordRows = page.locator('.el-table__row, .service-record'); expect(await recordRows.count()).toBeGreaterThanOrEqual(0); } else { // 无家庭数据,跳过 expect(true).toBeTruthy(); } }); /** * 场景6:家庭变更日志 * 角色:成长规划师 * 触发条件:在家庭详情页切换到变更日志 tab * 校验:变更日志加载正常 */ test('[场景6] 查看家庭变更日志 — 期望日志加载', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 查找第一个家庭的详情按钮 const detailBtn = page.locator('.el-table__row:first-child button:has-text("详情")'); if (await detailBtn.isVisible()) { await detailBtn.click(); await page.waitForTimeout(1000); // 切换到变更日志 tab const logTab = page.locator('.nav-item:has-text("变更日志"), .tab-item:has-text("日志")'); await logTab.click(); await page.waitForTimeout(1000); // 验证日志加载 const logRows = page.locator('.el-table__row, .log-item'); expect(await logRows.count()).toBeGreaterThanOrEqual(0); } else { // 无家庭数据,跳过 expect(true).toBeTruthy(); } }); /** * 场景7:家庭关系图谱 * 角色:成长规划师 * 触发条件:在家庭详情页切换到关系图谱 tab * 校验:关系图谱加载正常 */ test('[场景7] 查看家庭关系图谱 — 期望图谱加载', async ({ page }) => { await page.goto('http://cfc.iwintrue.com/admin/guide-family'); await page.waitForLoadState('networkidle'); // 查找第一个家庭的详情按钮 const detailBtn = page.locator('.el-table__row:first-child button:has-text("详情")'); if (await detailBtn.isVisible()) { await detailBtn.click(); await page.waitForTimeout(1000); // 切换到关系图谱 tab const relationTab = page.locator('.nav-item:has-text("关系图谱"), .tab-item:has-text("关系")'); await relationTab.click(); await page.waitForTimeout(1000); // 验证图谱加载 const relationGraph = page.locator('.relation-graph, .graph-container, svg'); expect(await relationGraph.isVisible().catch(() => false)).toBeTruthy(); } else { // 无家庭数据,跳过 expect(true).toBeTruthy(); } }); });