order-create-sync.spec.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { TIMEOUTS } from '../../utils/timeouts';
  2. import { test, expect } from '../../utils/test-setup';
  3. /**
  4. * 跨端数据同步 E2E 测试
  5. *
  6. * 测试目标:验证后台创建订单后,企业小程序能否正确显示该订单
  7. *
  8. * 测试流程:
  9. * 1. 后台操作:登录 → 导航到订单管理 → 创建测试订单 → 验证创建成功
  10. * 2. 小程序验证:登录 → 导航到订单列表 → 验证订单存在 → 查看详情
  11. *
  12. * 测试要点:
  13. * - 使用两个独立的 browser context(后台和小程序)
  14. * - 记录数据同步时间
  15. * - 使用 data-testid 选择器
  16. * - 遵循项目测试规范
  17. */
  18. test.describe('跨端数据同步测试 - 后台创建订单到企业小程序', () => {
  19. let orderName: string;
  20. test.describe.serial('后台创建订单', () => {
  21. test('应该成功登录后台并创建订单', async ({ page: adminPage }) => {
  22. // 记录开始时间
  23. const startTime = Date.now();
  24. // 1. 后台登录
  25. await adminPage.goto('http://localhost:8080/admin/login');
  26. await adminPage.getByPlaceholder('请输入用户名').fill('admin');
  27. await adminPage.getByPlaceholder('请输入密码').fill('admin123');
  28. await adminPage.getByRole('button', { name: '登录' }).click();
  29. await adminPage.waitForURL('**/admin/dashboard', { timeout: TIMEOUTS.PAGE_LOAD });
  30. console.debug('[后台] 登录成功');
  31. // 2. 导航到订单管理页面
  32. await adminPage.goto('http://localhost:8080/admin/orders');
  33. await adminPage.waitForSelector('table tbody tr', { state: 'visible', timeout: TIMEOUTS.PAGE_LOAD });
  34. console.debug('[后台] 导航到订单管理页面');
  35. // 3. 点击创建订单按钮
  36. await adminPage.getByTestId('create-order-button').click();
  37. await adminPage.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  38. console.debug('[后台] 打开创建订单对话框');
  39. // 4. 填写订单表单
  40. const timestamp = Date.now();
  41. orderName = `跨端同步测试_${timestamp}`;
  42. // 填写订单名称
  43. await adminPage.getByLabel('订单名称').fill(orderName);
  44. console.debug(`[后台] 订单名称: ${orderName}`);
  45. // 选择平台(选择第一个可用平台)
  46. await adminPage.getByTestId('platform-selector-create').click();
  47. await adminPage.waitForTimeout(TIMEOUTS.SHORT);
  48. const firstPlatformOption = adminPage.locator('[role="option"]').first();
  49. const platformName = await firstPlatformOption.textContent();
  50. await firstPlatformOption.click();
  51. console.debug(`[后台] 选择平台: ${platformName}`);
  52. // 选择公司(选择第一个可用公司)
  53. await adminPage.getByTestId('company-selector-create').click();
  54. await adminPage.waitForTimeout(TIMEOUTS.SHORT);
  55. const firstCompanyOption = adminPage.locator('[role="option"]').first();
  56. const companyName = await firstCompanyOption.textContent();
  57. await firstCompanyOption.click();
  58. console.debug(`[后台] 选择公司: ${companyName}`);
  59. // 5. 点击"选择残疾人"按钮
  60. await adminPage.getByTestId('select-persons-button').click();
  61. await adminPage.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  62. console.debug('[后台] 打开选择残疾人对话框');
  63. // 等待残疾人列表加载
  64. await adminPage.waitForSelector('table tbody tr', { state: 'visible', timeout: TIMEOUTS.TABLE_LOAD });
  65. await adminPage.waitForTimeout(1000);
  66. // 使用 JavaScript 选择第一个残疾人(避免点击拦截问题)
  67. const personSelected = await adminPage.evaluate(() => {
  68. const firstRow = document.querySelector('table tbody tr');
  69. if (firstRow) {
  70. const checkbox = firstRow.querySelector('input[type="checkbox"]') as HTMLInputElement;
  71. if (checkbox) {
  72. // 尝试点击复选框的父元素
  73. const parent = checkbox.closest('[data-testid]') as HTMLElement;
  74. if (parent) {
  75. parent.click();
  76. return true;
  77. }
  78. // 备选:直接设置复选框状态
  79. checkbox.checked = true;
  80. checkbox.dispatchEvent(new Event('change', { bubbles: true }));
  81. return true;
  82. }
  83. }
  84. return false;
  85. });
  86. console.debug(`[后台] 选择残疾人: ${personSelected ? '成功' : '失败'}`);
  87. // 等待一下让 UI 更新
  88. await adminPage.waitForTimeout(2000);
  89. // 点击确认选择按钮
  90. const confirmButton = adminPage.getByTestId('confirm-batch-button');
  91. await confirmButton.click();
  92. await adminPage.waitForTimeout(TIMEOUTS.MEDIUM);
  93. console.debug('[后台] 确认选择残疾人');
  94. // 6. 提交表单
  95. await adminPage.getByTestId('order-create-submit-button').click();
  96. await adminPage.waitForLoadState('domcontentloaded');
  97. await adminPage.waitForTimeout(TIMEOUTS.LONG);
  98. console.debug('[后台] 提交订单表单');
  99. // 7. 验证创建成功的 Toast
  100. const successToast = adminPage.locator('[data-sonner-toast][data-type="success"]');
  101. const toastCount = await successToast.count();
  102. expect(toastCount).toBeGreaterThan(0);
  103. console.debug('[后台] 订单创建成功');
  104. // 8. 验证订单出现在列表中
  105. const orderExists = adminPage.locator('table tbody tr').filter({ hasText: orderName }).count() > 0;
  106. expect(orderExists).toBe(true);
  107. console.debug(`[后台] 订单 "${orderName}" 出现在列表中`);
  108. // 9. 记录创建完成时间
  109. const endTime = Date.now();
  110. const syncTime = endTime - startTime;
  111. console.debug(`[后台] 订单创建完成,耗时: ${syncTime}ms`);
  112. // 保存订单名称到环境变量,供后续测试使用
  113. process.env.__TEST_ORDER_NAME__ = orderName;
  114. });
  115. });
  116. test.describe.serial('小程序验证订单同步', () => {
  117. test.use({ storageState: undefined }); // 确保使用新的浏览器上下文
  118. test('应该在小程序中显示后台创建的订单', async ({ page: miniPage }) => {
  119. // 从环境变量获取订单名称
  120. const testOrderName = process.env.__TEST_ORDER_NAME__;
  121. if (!testOrderName) {
  122. throw new Error('未找到测试订单名称,请先运行后台创建订单测试');
  123. }
  124. console.debug(`[小程序] 查找订单: ${testOrderName}`);
  125. // 等待一段时间,确保数据同步完成
  126. await new Promise(resolve => setTimeout(resolve, 3000));
  127. // 1. 小程序登录
  128. await miniPage.goto('http://localhost:8080/mini');
  129. await miniPage.waitForLoadState('networkidle');
  130. // 填写登录表单(使用企业账号)
  131. await miniPage.getByTestId('mini-phone-input').getByPlaceholder('请输入手机号').fill('13800001111');
  132. await miniPage.getByRole('textbox', { name: '请输入密码' }).fill('password123');
  133. await miniPage.getByTestId('mini-login-button').click();
  134. console.debug('[小程序] 登录请求已发送');
  135. // 等待登录成功(跳转到 dashboard)
  136. await miniPage.waitForURL(
  137. url => url.pathname.includes('/dashboard') || url.pathname.includes('/pages/yongren/dashboard'),
  138. { timeout: TIMEOUTS.PAGE_LOAD }
  139. );
  140. console.debug('[小程序] 登录成功');
  141. // 2. 导航到订单列表页面
  142. await miniPage.getByText('订单', { exact: true }).click();
  143. await miniPage.waitForLoadState('domcontentloaded');
  144. console.debug('[小程序] 导航到订单列表页面');
  145. // 3. 等待订单列表加载
  146. await miniPage.waitForTimeout(TIMEOUTS.LONG);
  147. // 4. 验证订单出现在小程序列表中
  148. // 使用多种方法验证订单存在
  149. const orderExistsByLocator = await miniPage.locator('text=' + testOrderName).count() > 0;
  150. const orderExistsByGetByText = await miniPage.getByText(testOrderName).count() > 0;
  151. const orderExists = orderExistsByLocator || orderExistsByGetByText;
  152. expect(orderExists).toBe(true);
  153. console.debug(`[小程序] 订单 "${testOrderName}" 已同步到小程序`);
  154. // 5. 点击查看详情
  155. const detailButton = miniPage.getByText('查看详情').first();
  156. await detailButton.click();
  157. await miniPage.waitForURL(/\/detail/, { timeout: TIMEOUTS.PAGE_LOAD });
  158. console.debug('[小程序] 打开订单详情');
  159. // 6. 验证订单详情信息
  160. // 验证订单名称显示
  161. const orderNameElement = miniPage.getByText(testOrderName);
  162. await expect(orderNameElement).toBeVisible();
  163. console.debug('[小程序] 订单详情显示正确');
  164. // 7. 记录数据同步完成时间
  165. const syncEndTime = Date.now();
  166. console.debug(`[小程序] 数据同步验证完成,时间戳: ${syncEndTime}`);
  167. // 清理环境变量
  168. delete process.env.__TEST_ORDER_NAME__;
  169. });
  170. });
  171. });