Просмотр исходного кода

test: add admin-butler-review and family-earnings-flow E2E tests + update dan-report-flow for DAN cognitive/EMI dimensions

Sisyphus Agent 4 месяцев назад
Родитель
Сommit
33262002cf

+ 299 - 0
tests/e2e/admin-butler-review.spec.js

@@ -0,0 +1,299 @@
+/**
+ * ================================================================
+ * 场景:管家审核流程 E2E 测试
+ * ================================================================
+ * 用户故事:管理员在后台审核管家申请,通过/驳回申请,
+ *           分配家庭成员给管家,查看管家服务记录和佣金结算。
+ *
+ * 流程步骤:
+ *   1. 管理员登录后台
+ *   2. 打开管家审核页面
+ *   3. 搜索筛选管家列表
+ *   4. 查看管家详情
+ *   5. 审核(通过或驳回)
+ *   6. 分配家庭成员(服务订单)给管家
+ *   7. 查看服务记录和佣金结算
+ *
+ * 关键里程碑:
+ *   - [Milestone-1] 管家审核页面加载成功(butler-review 报名选项卡加载)
+ *   - [Milestone-2] 能够搜索和筛选管家列表
+ *   - [Milestone-3] 成功提交审核(通过或驳回)
+ *   - [Milestone-4] 成功分配家庭成员给管家
+ *
+ * API 端点:
+ *   - POST /api/admin/butler/list (筛选管家列表)
+ *   - POST /api/admin/butler/review (审核管家)
+ *   - POST /api/admin/butler/assign-member (分配成员)
+ *   - POST /api/admin/butler/service-records (服务记录)
+ *   - POST /api/admin/butler/settlements (佣金结算)
+ *
+ * 运行:npx playwright test tests/e2e/admin-butler-review.spec.js
+ * ================================================================
+ */
+
+const { test, expect } = require('@playwright/test');
+
+test.describe('【场景流程】管家审核流程', () => {
+
+  test.beforeEach(async ({ page }) => {
+    // 管理员登录
+    await page.goto('/#/pages/login/admin');
+    await page.waitForLoadState('networkidle');
+
+    const adminPhone = '13701366188';  // 已验证的管理员账号
+    const phoneInput = page.locator('input[placeholder="手机号"]');
+    await page.waitForTimeout(1000);
+    await phoneInput.fill(adminPhone);
+
+    await page.locator('.login-btn, button:has-text("登录")').click();
+    await page.waitForTimeout(1500);
+
+    // 验证登录成功(有 admin-tab 或管理菜单)
+    try {
+      await page.waitForSelector('.admin-tab, .admin-menu, .admin-layout', { timeout: 10000 });
+    } catch (e) {
+      // 跳过重复登录
+      if (await page.url().includes('?login=verify')) {
+        await page.goto('/#/pages/admin/butler-review');
+        await page.waitForLoadState('networkidle');
+      }
+    }
+  });
+
+  /**
+   * 场景1:管家审核页面加载
+   * 角色:管理员
+   * 触发条件:进入后台管家审核页面
+   * 校验:页面加载成功,tab 切换到待审核
+   */
+  test('[场景1] 管家审核页面加载 — 期望 tab 切换到待审核', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 验证页面标题
+    const title = page.locator('.el-card__header, .tab-header, h2:has-text("管家审核")');
+    expect(await title.isVisible()).toBeTruthy();
+
+    // 验证默认选中"待审核" tab
+    const pendingTab = page.locator('.nav-item.active, .el-tab-pane.is-active, text="待审核"');
+    expect(await pendingTab.isVisible()).toBeTruthy();
+  });
+
+  /**
+   * 场景2:搜索筛选管家列表
+   * 角色:管理员
+   * 触发条件:输入管家姓名、状态、等级等筛选条件
+   * 校验:表格刷新返回正常数据
+   */
+  test('[场景2] 搜索筛选管家列表 — 期望表格刷新', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 输入筛选条件
+    var nameInput = page.locator('input[placeholder="管家姓名"]');
+    if (await nameInput.isVisible()) {
+      await nameInput.fill('张三');
+    }
+
+    var statusSelect = page.locator('.el-select, .status-select, .select-status');
+    if (await statusSelect.isVisible()) {
+      await statusSelect.click();
+      var pendingOption = page.locator('.el-select-dropdown__item:has-text("待审核")');
+      await pendingOption.click();
+    }
+
+    // 点击搜索按钮
+    var searchBtn = page.locator('.search-btn, button:has-text("搜索")');
+    await searchBtn.click();
+    await page.waitForTimeout(1000);
+
+    // 验证表格有数据
+    var tableRows = page.locator('.el-table__row, .table-row');
+    var rowCount = await tableRows.count();
+    expect(rowCount).toBeGreaterThanOrEqual(0);  // 可能为空,但不报 500
+
+    // 验证列数(按钮、状态、创建时间等)
+    if (rowCount > 0) {
+      const rowCells = page.locator('.el-table__row:first-child .el-table__cell, th[aria-colindex="5"]');
+      expect(await rowCells.count()).toBeGreaterThanOrEqual(5);
+    }
+  });
+
+  /**
+   * 场景3:提交管家审核(通过)
+   * 角色:管理员
+   * 触发条件:选择一位待审核管家,点击通过按钮
+   * 校验:返回成功提示,状态变为"已通过"
+   */
+  test('[场景3] 提交管家审核(通过) — 期望成功提示且状态改变', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 查找待审核管家(点击第一行的操作按钮)
+    var approveBtn = page.locator('.el-table__row:first-child button:has-text("通过")');
+    if (await approveBtn.isVisible()) {
+      await approveBtn.click();
+      await page.waitForTimeout(500);
+
+      // 填写通过理由(可选)
+      var reasonInput = page.locator('textarea[placeholder="可选填:通过理由"]');
+      if (await reasonInput.isVisible()) {
+        await reasonInput.fill('资质符合,通过审核');
+      }
+
+      // 提交
+      var submitBtn = page.locator('.el-dialog__footer button:has-text("确定"), button.el-button--primary');
+      await submitBtn.click();
+      await page.waitForTimeout(1000);
+
+      // 验证提示
+      var successTip = page.locator('.el-message-box__success, .toast-success, text="通过成功"');
+      if (await successTip.isVisible().catch(() => false)) {
+        expect(await successTip.textContent()).toContain("通过");
+      } else {
+        // 刷新检查状态
+        await page.reload();
+        await page.waitForTimeout(500);
+        var newStatus = page.locator('.el-table__row:first-child .el-tag:has-text("已通过")');
+        expect(await newStatus.isVisible()).toBeTruthy();
+      }
+    } else {
+      // 无待审核数据,跳过
+      expect(true).toBeTruthy();
+    }
+  });
+
+  /**
+   * 场景4:提交管家审核(驳回)
+   * 角色:管理员
+   * 触发条件:选择一位待审核管家,点击驳回按钮
+   * 校验:返回成功提示,状态变为"已驳回"
+   */
+  test('[场景4] 提交管家审核(驳回) — 期望成功提示且状态改变', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 查找待审核管家
+    var rejectBtn = page.locator('.el-table__row:first-child button:has-text("驳回")');
+    if (await rejectBtn.isVisible()) {
+      await rejectBtn.click();
+      await page.waitForTimeout(500);
+
+      // 填写驳回理由
+      var reasonInput = page.locator('textarea[placeholder="请填写驳回理由"]');
+      await reasonInput.fill('资料不齐,请补充身份证照片');
+
+      // 提交
+      var submitBtn = page.locator('.el-dialog__footer button:has-text("确定"), button.el-button--primary');
+      await submitBtn.click();
+      await page.waitForTimeout(1000);
+
+      // 验证提示
+      var successTip = page.locator('.el-message-box__success, .toast-success, text="驳回成功"');
+      expect(await successTip.isVisible().catch(() => false)).toBeTruthy();
+    } else {
+      // 无待审核数据,跳过
+      expect(true).toBeTruthy();
+    }
+  });
+
+  /**
+   * 场景5:分配家庭成员给管家
+   * 角色:管理员
+   * 触发条件:选定一位管家,从服务订单中选择家庭成员进行分配
+   * 校验:提示分配成功,管家服务记录增加
+   */
+  test('[场景5] 分配家庭成员给管家 — 期望成功并更新服务记录', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 切换到"已通过"管家列表
+    var activeTab = page.locator('.el-tabs__item:has-text("已通过"), .nav-item:has-text("已通过")');
+    await activeTab.click();
+    await page.waitForTimeout(500);
+
+    // 查找操作栏的"分配服务"按钮
+    var assignBtn = page.locator('.el-table__row:first-child button:has-text("分配服务")');
+    if (await assignBtn.isVisible()) {
+      await assignBtn.click();
+      await page.waitForTimeout(500);
+
+      // 在弹窗中选择成员(勾选第一项)
+      var memberCheckbox = page.locator('.el-checkbox:first-child input[type=checkbox]');
+      await memberCheckbox.check();
+      await page.waitForTimeout(500);
+
+      // 提交
+      var submitBtn = page.locator('.el-dialog__footer button:has-text("分配");');
+      await submitBtn.click();
+      await page.waitForTimeout(1000);
+
+      // 验证提示
+      var successTip = page.locator('.toast-success, text="分配成功"');
+      expect(await successTip.isVisible().catch(() => false)).toBeTruthy();
+
+      // 验证服务记录页面刷新
+      await page.goto('/#/pages/admin/service-records');
+      await page.waitForLoadState('networkidle');
+      var tableRows = page.locator('.el-table__row');
+      expect(await tableRows.count()).toBeGreaterThanOrEqual(0);
+    } else {
+      // 无已通过管家,跳过
+      expect(true).toBeTruthy();
+    }
+  });
+
+  /**
+   * 场景6:查看管家服务记录
+   * 角色:管理员
+   * 触发条件:切换到"服务记录" tab
+   * 校验:表格加载正常数据
+   */
+  test('[场景6] 查看管家服务记录 — 期望表格加载', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 切换到服务记录 tab
+    var serviceTab = page.locator('.el-tabs__item:has-text("服务记录"), .nav-item:has-text("服务记录")');
+    await serviceTab.click();
+    await page.waitForTimeout(1000);
+
+    // 验证表格加载
+    var tableRows = page.locator('.el-table__row, .service-record-row');
+    var rowCount = await tableRows.count();
+    expect(rowCount).toBeGreaterThanOrEqual(0);
+
+    // 验证记录展示内容
+    if (rowCount > 0) {
+      var firstRowCells = page.locator('.el-table__row:first-child .el-table__cell');
+      expect(await firstRowCells.count()).toBeGreaterThanOrEqual(7);  // 管家/成员/订单/服务类型/状态/时间/操作
+    }
+  });
+
+  /**
+   * 场景7:查看管家佣金结算
+   * 角色:管理员
+   * 触发条件:切换到"佣金结算" tab
+   * 校验:表格加载正常数据
+   */
+  test('[场景7] 查看管家佣金结算 — 期望加载结算记录', async ({ page }) => {
+    await page.goto('/#/pages/admin/butler-review');
+    await page.waitForLoadState('networkidle');
+
+    // 切换到佣金结算 tab
+    var commissionTab = page.locator('.el-tabs__item:has-text("佣金结算"), .nav-item:has-text("佣金结算")');
+    await commissionTab.click();
+    await page.waitForTimeout(1000);
+
+    // 验证表格加载
+    var tableRows = page.locator('.el-table__row, .commission-row');
+    var rowCount = await tableRows.count();
+    expect(rowCount).toBeGreaterThanOrEqual(0);
+
+    // 验证记录展示内容
+    if (rowCount > 0) {
+      var firstRowCells = page.locator('.el-table__row:first-child .el-table__cell');
+      expect(await firstRowCells.count()).toBeGreaterThanOrEqual(8);  // 管家/年度/月份/收入/可提现/实际提现/状态/操作
+    }
+  });
+});

+ 83 - 2
tests/e2e/dan-report-flow.spec.js

@@ -228,6 +228,87 @@ test.describe('【场景流程】DAN 测评报告上传解析保存', () => {
       }
     }
 
-    expect(true).toBeTruthy();
-  });
+
+/**
+ * 场景8:DAN 报告预览页认知维度展示
+ * 角色:家长/规划师
+ * 触发条件:查看已解析报告的预览页
+ * 校验:认知维度(感知/空间思维/加工速度)得分展示
+ */
+test('[场景8] DAN 报告预览页认知维度展示 — 期望得分展示', async ({ page }) => {
+  await page.goto('/#/pages/dan-assessment/report-upload');
+  await page.waitForLoadState('networkidle');
+
+  // 切换到报告列表/详情 tab
+  const reportTab = page.locator('.tab-item:has-text("报告列表"), .nav-item:has-text("我的报告")');
+  if (await reportTab.isVisible()) {
+    await reportTab.click();
+    await page.waitForTimeout(1000);
+
+    // 点击查看第一个报告
+    const firstReport = page.locator('.report-item:first-child');
+    if (await firstReport.isVisible()) {
+      await firstReport.click();
+      await page.waitForTimeout(1000);
+
+      // 切换到认知维度 tab
+      const cognitiveTab = page.locator('.tab-item:has-text("认知维度"), .nav-item:has-text("认知")');
+      await cognitiveTab.click();
+      await page.waitForTimeout(1000);
+
+      // 验证认知维度得分展示
+      const perceptionScore = page.locator('.score-item:has-text("感知得分")');
+      const spatialScore = page.locator('.score-item:has-text("空间思维得分")');
+      const processingScore = page.locator('.score-item:has-text("加工速度得分")');
+
+      expect(await perceptionScore.isVisible()).toBeTruthy();
+      expect(await spatialScore.isVisible()).toBeTruthy();
+      expect(await processingScore.isVisible()).toBeTruthy();
+    }
+  }
+  expect(true).toBeTruthy();
+});
+
+/**
+ * 场景9:DAN 报告预览页 EMI 维度展示
+ * 角色:家长/规划师
+ * 触发条件:查看已解析报告的预览页
+ * 校验:EMI 维度(情绪管理/同理心/社会适应/自我激励)得分展示
+ */
+test('[场景9] DAN 报告预览页 EMI 维度展示 — 期望得分展示', async ({ page }) => {
+  await page.goto('/#/pages/dan-assessment/report-upload');
+  await page.waitForLoadState('networkidle');
+
+  // 切换到报告列表/详情 tab
+  const reportTab = page.locator('.tab-item:has-text("报告列表"), .nav-item:has-text("我的报告")');
+  if (await reportTab.isVisible()) {
+    await reportTab.click();
+    await page.waitForTimeout(1000);
+
+    // 点击查看第一个报告
+    const firstReport = page.locator('.report-item:first-child');
+    if (await firstReport.isVisible()) {
+      await firstReport.click();
+      await page.waitForTimeout(1000);
+
+      // 切换到 EMI 维度 tab
+      const emiTab = page.locator('.tab-item:has-text("EMI维度"), .nav-item:has-text("情绪管理")');
+      await emiTab.click();
+      await page.waitForTimeout(1000);
+
+      // 验证 EMI 维度得分展示
+      const emotionScore = page.locator('.score-item:has-text("情绪管理得分")');
+      const empathyScore = page.locator('.score-item:has-text("同理心得分")');
+      const socialScore = page.locator('.score-item:has-text("社会适应得分")');
+      const motivationScore = page.locator('.score-item:has-text("自我激励得分")');
+
+      expect(await emotionScore.isVisible()).toBeTruthy();
+      expect(await empathyScore.isVisible()).toBeTruthy();
+      expect(await socialScore.isVisible()).toBeTruthy();
+      expect(await motivationScore.isVisible()).toBeTruthy();
+    }
+  }
+  expect(true).toBeTruthy();
+});
+
 });

+ 160 - 0
tests/e2e/family-earnings-flow.spec.js

@@ -0,0 +1,160 @@
+/**
+ * ================================================================
+ * 场景:家庭收益管理流程 E2E 测试
+ * ================================================================
+ * 用户故事:家长可以管理家庭公共账户收支,兑换心愿,
+ *           查看家庭成员待分配余额,审核提现申请。
+ *
+ * 流程步骤:
+ *   1. 家长登录,进入个人中心
+ *   2. 打开家庭收益管理页面
+ *   3. 查看家庭收益概览
+ *   4. 兑换心愿(能量到现金)
+ *   5. 分配收益给成员
+ *   6. 查看收益记录
+ *
+ * API 端点:
+ *   - POST /api/admin/earnings/families (家庭列表)
+ *   - POST /api/admin/earnings/family/{familyId}/summary (家庭概览)
+ *   - POST /api/admin/earnings/family/{familyId}/records (收支记录)
+ *
+ * 运行:npx playwright test tests/e2e/family-earnings-flow.spec.js
+ * ================================================================
+ */
+
+const { test, expect } = require('@playwright/test');
+
+test.describe('【场景流程】家庭收益管理流程', () => {
+
+  test.beforeEach(async ({ page }) => {
+    // 家长登录
+    await page.goto('/#/pages/login/login');
+    await page.waitForLoadState('networkidle');
+
+    const phone = '13701366187';  // 测试家长账号
+    const phoneInput = page.locator('input[type=tel], input[placeholder="手机号"]');
+    await phoneInput.fill(phone);
+    await page.waitForTimeout(500);
+
+    await page.locator('.login-btn, button:has-text("登录")').click();
+    await page.waitForTimeout(2000);
+
+    try {
+      await expect(page.locator('.home-tab, .tab-bar, .mine-tab')).toBeVisible({ timeout: 10000 });
+    } catch (e) {
+      // 跳过重复登录检查
+    }
+  });
+
+  /**
+   * 场景1:家庭收益概览页面加载
+   * 校验:页面加载成功,显示能量、可提现、已提现数据
+   */
+  test('[场景1] 家庭收益概览页面加载', async ({ page }) => {
+    await page.goto('/#/pages/profile/family-earnings');
+    await page.waitForLoadState('networkidle');
+
+    // 验证页面标题
+    const title = page.locator('.nav-bar, .title-wrapper, h2:has-text("家庭收益")');
+    expect(await title.isVisible()).toBeTruthy();
+
+    // 验证卡片展示
+    const energyCard = page.locator('.earnings-card, .energy-card, .balance-card');
+    expect(await energyCard.first().isVisible()).toBeTruthy();
+
+    // 验证统计数据
+    const energyData = page.locator('.balance-data, .energy-amount');
+    const energyText = await energyData.first().textContent();
+    expect(energyText.length).toBeGreaterThan(0);
+  });
+
+  /**
+   * 场景2:兑换心愿(能量→现金)
+   * 校验:兑换成功,记录更新
+   */
+  test('[场景2] 兑换心愿', async ({ page }) => {
+    await page.goto('/#/pages/profile/family-earnings');
+    await page.waitForLoadState('networkidle');
+
+    // 必须确保家庭有收益
+    const energyCard = page.locator('.earnings-card, .energy-card');
+    if (await energyCard.first().isVisible()) {
+      const energyBefore = await page.locator('.energy-amount').textContent();
+      const availableEnergy = parseInt(energyBefore.replace(/[^0-9]/g, '')) || 0;
+
+      if (availableEnergy > 0) {
+        // 点击兑换按钮
+        const exchangeBtn = page.locator('button:has-text("兑换"), .exchange-btn');
+        await exchangeBtn.click();
+        await page.waitForTimeout(1000);
+
+        // 输入金额
+        const amountInput = page.locator('input[placeholder="兑换金额"]');
+        await amountInput.fill('100');
+
+        // 提交
+        const submitBtn = page.locator('.confirm-btn, button:has-text("确定")');
+        await submitBtn.click();
+        await page.waitForTimeout(1000);
+
+        // 验证提示
+        const successTip = page.locator('.modal-success, .toast-success, text="兑换成功"');
+        expect(await successTip.isVisible().catch(() => false)).toBeTruthy();
+      }
+    }
+    expect(true).toBeTruthy();
+  });
+
+  /**
+   * 场景3:分配收益给家庭成员
+   * 校验:分配成功
+   */
+  test('[场景3] 分配收益给成员', async ({ page }) => {
+    await page.goto('/#/pages/profile/family-earnings');
+    await page.waitForLoadState('networkidle');
+
+    // 点击分配按钮
+    const distributeBtn = page.locator('button:has-text("分配"), .distribute-btn');
+    await distributeBtn.click();
+    await page.waitForTimeout(1000);
+
+    // 选择成员
+    const memberSelect = page.locator('.el-select-dropdown, .member-select');
+    if (await memberSelect.isVisible()) {
+      await memberSelect.locator('.el-select-dropdown__item:first-child').click();
+    } else {
+      await page.locator('.member-item:first-child').click();
+    }
+
+    // 填写金额
+    const amountInput = page.locator('input[placeholder="分配金额"]');
+    await amountInput.fill('50');
+
+    // 提交
+    const submitBtn = page.locator('.confirm-btn, button:has-text("确定")');
+    await submitBtn.click();
+    await page.waitForTimeout(1000);
+
+    // 验证提示
+    const successTip = page.locator('.modal-success, .toast-success, text="分配成功"');
+    expect(await successTip.isVisible().catch(() => false)).toBeTruthy();
+  });
+
+  /**
+   * 场景4:查看收支记录列表
+   * 校验:列表加载正常数据
+   */
+  test('[场景4] 查看收支记录', async ({ page }) => {
+    await page.goto('/#/pages/profile/family-earnings');
+    await page.waitForLoadState('networkidle');
+
+    // 切换到收支记录 tab
+    const recordsTab = page.locator('.tab-item:has-text("收支记录")');
+    await recordsTab.click();
+    await page.waitForTimeout(1000);
+
+    // 验证列表加载
+    const records = page.locator('.record-item, .earnings-record');
+    expect(await records.first().isVisible().catch(() => false)).toBeTruthy();
+  });
+});