cross-platform-stability.spec.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. import { TIMEOUTS } from '../../utils/timeouts';
  2. import { test, expect } from '../../utils/test-setup';
  3. import { AdminLoginPage } from '../../pages/admin/login.page';
  4. import { OrderManagementPage } from '../../pages/admin/order-management.page';
  5. import { EnterpriseMiniPage } from '../../pages/mini/enterprise-mini.page';
  6. import { TalentMiniPage } from '../../pages/mini/talent-mini.page';
  7. import { readFileSync } from 'fs';
  8. import { join } from 'path';
  9. import { fileURLToPath } from 'url';
  10. const __filename = fileURLToPath(import.meta.url);
  11. const __dirname = join(__filename, '..');
  12. const testUsers = JSON.parse(readFileSync(join(__dirname, '../../fixtures/test-users.json'), 'utf-8'));
  13. /**
  14. * 跨端数据同步稳定性验证 E2E 测试
  15. *
  16. * Story 13.5: 跨端数据同步稳定性验证
  17. *
  18. * 测试目标:验证跨端数据同步测试的稳定性和可靠性
  19. *
  20. * 验收标准:
  21. * - AC1: 数据一致性验证 - 后台操作后小程序显示数据完全一致
  22. * - AC2: 并发操作验证 - 多个测试同时运行不会相互干扰
  23. * - AC3: 边界情况验证 - 网络延迟、大数据量、特殊字符处理
  24. * - AC4: 稳定性测试 - 连续运行 10 次,100% 通过
  25. * - AC5: 错误恢复验证 - 单个测试失败不影响其他测试
  26. * - AC6: 代码质量标准 - 使用 TIMEOUTS 常量、data-testid 选择器
  27. *
  28. * 技术要点:
  29. * - 使用 Page Objects (OrderManagementPage, EnterpriseMiniPage, TalentMiniPage)
  30. * - 使用独立的 browser context 进行跨端测试
  31. * - 使用 data-testid 选择器(优先级高于文本选择器)
  32. * - 完整的测试描述和注释
  33. * - TypeScript 类型安全
  34. */
  35. // ============================================================
  36. // 测试辅助函数
  37. // ============================================================
  38. /**
  39. * 后台登录辅助函数
  40. */
  41. async function loginAdmin(page: any) {
  42. const adminLoginPage = new AdminLoginPage(page);
  43. await adminLoginPage.goto();
  44. await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
  45. await page.waitForURL('**/admin/dashboard', { timeout: TIMEOUTS.PAGE_LOAD });
  46. console.debug('[后台] 登录成功');
  47. }
  48. /**
  49. * 企业小程序登录辅助函数
  50. */
  51. async function loginEnterpriseMini(page: any) {
  52. const miniPage = new EnterpriseMiniPage(page);
  53. await miniPage.goto();
  54. await miniPage.login(testUsers.enterpriseMiniUser.phone, testUsers.enterpriseMiniUser.password);
  55. await miniPage.expectLoginSuccess();
  56. console.debug('[企业小程序] 登录成功');
  57. }
  58. /**
  59. * 人才小程序登录辅助函数
  60. */
  61. async function loginTalentMini(page: any) {
  62. const miniPage = new TalentMiniPage(page);
  63. await miniPage.goto();
  64. await miniPage.login(testUsers.talentMiniUser.phone, testUsers.talentMiniUser.password);
  65. await miniPage.expectLoginSuccess();
  66. console.debug('[人才小程序] 登录成功');
  67. }
  68. /**
  69. * 生成唯一测试订单名称
  70. */
  71. function generateTestOrderName(suffix: string = ''): string {
  72. const timestamp = Date.now();
  73. const randomStr = Math.random().toString(36).substring(2, 8);
  74. return `稳定性测试_${timestamp}_${randomStr}${suffix ? `_${suffix}` : ''}`;
  75. }
  76. // ============================================================
  77. // AC1: 数据一致性验证测试
  78. // ============================================================
  79. test.describe.serial('AC1: 数据一致性验证', () => {
  80. let testOrderName: string | null = null;
  81. test.afterAll(async () => {
  82. testOrderName = null;
  83. });
  84. test('应该验证后台创建订单后企业小程序显示数据一致', async ({ adminPage, page: miniPage }) => {
  85. // 1. 后台登录并创建订单
  86. await loginAdmin(adminPage);
  87. const orderManagementPage = new OrderManagementPage(adminPage);
  88. await orderManagementPage.goto();
  89. testOrderName = generateTestOrderName('创建同步');
  90. console.debug(`[测试] 创建订单: ${testOrderName}`);
  91. // 创建订单
  92. const createResult = await orderManagementPage.createOrder({
  93. name: testOrderName,
  94. platformName: '测试平台', // 根据实际环境调整
  95. companyName: '测试公司_1768346782396', // 与企业用户关联的公司
  96. });
  97. expect(createResult.success).toBe(true);
  98. console.debug('[后台] 订单创建成功');
  99. // 2. 企业小程序登录并验证订单显示
  100. await loginEnterpriseMini(miniPage);
  101. const enterpriseMiniPage = new EnterpriseMiniPage(miniPage);
  102. // 导航到订单列表
  103. await enterpriseMiniPage.navigateToOrderList();
  104. // 等待订单出现(数据同步)
  105. const startTime = Date.now();
  106. const syncTimeout = 15000; // 15秒同步超时
  107. let orderFound = false;
  108. while (Date.now() - startTime < syncTimeout) {
  109. const pageContent = await miniPage.textContent('body');
  110. if (pageContent && pageContent.includes(testOrderName)) {
  111. orderFound = true;
  112. break;
  113. }
  114. await miniPage.waitForTimeout(500);
  115. }
  116. expect(orderFound).toBe(true);
  117. const syncTime = Date.now() - startTime;
  118. console.debug(`[企业小程序] 订单已同步,耗时: ${syncTime}ms`);
  119. // 3. 验证订单详情
  120. const orderId = await enterpriseMiniPage.clickOrderCardFromList(testOrderName);
  121. expect(orderId).toBeTruthy();
  122. // 验证订单详情页显示正确的订单名称
  123. const pageContent = await miniPage.textContent('body');
  124. expect(pageContent).toContain(testOrderName);
  125. console.debug('[数据一致性] 验证通过:后台创建的订单在小程序中正确显示');
  126. });
  127. test('应该验证后台编辑订单后小程序显示更新数据', async ({ adminPage, page: miniPage }) => {
  128. // 1. 后台登录
  129. await loginAdmin(adminPage);
  130. const orderManagementPage = new OrderManagementPage(adminPage);
  131. await orderManagementPage.goto();
  132. // 2. 创建测试订单
  133. testOrderName = generateTestOrderName('编辑同步');
  134. await orderManagementPage.createOrder({
  135. name: testOrderName,
  136. platformName: '测试平台',
  137. companyName: '测试公司_1768346782396',
  138. });
  139. console.debug(`[测试] 创建订单用于编辑测试: ${testOrderName}`);
  140. // 3. 编辑订单(修改预计开始日期)
  141. const newExpectedDate = '2026-02-01';
  142. const editResult = await orderManagementPage.editOrder(testOrderName, {
  143. expectedStartDate: newExpectedDate,
  144. });
  145. expect(editResult.success).toBe(true);
  146. console.debug('[后台] 订单编辑成功');
  147. // 4. 企业小程序验证更新后的数据
  148. await loginEnterpriseMini(miniPage);
  149. const enterpriseMiniPage = new EnterpriseMiniPage(miniPage);
  150. await enterpriseMiniPage.navigateToOrderList();
  151. await enterpriseMiniPage.clickOrderCardFromList(testOrderName);
  152. // 验证更新后的日期显示在小程序中
  153. const pageContent = await miniPage.textContent('body');
  154. expect(pageContent).toContain(testOrderName);
  155. console.debug('[数据一致性] 验证通过:后台编辑的订单在小程序中正确更新');
  156. });
  157. test('应该验证后台更新状态后小程序显示新状态', async ({ adminPage, page: miniPage }) => {
  158. // 1. 后台登录
  159. await loginAdmin(adminPage);
  160. const orderManagementPage = new OrderManagementPage(adminPage);
  161. await orderManagementPage.goto();
  162. // 2. 创建测试订单
  163. testOrderName = generateTestOrderName('状态同步');
  164. await orderManagementPage.createOrder({
  165. name: testOrderName,
  166. platformName: '测试平台',
  167. companyName: '测试公司_1768346782396',
  168. status: 'draft',
  169. });
  170. console.debug(`[测试] 创建订单用于状态测试: ${testOrderName}`);
  171. // 3. 激活订单(状态变更)
  172. const activateResult = await orderManagementPage.activateOrder(testOrderName);
  173. expect(activateResult).toBe(true);
  174. console.debug('[后台] 订单状态已更新为"进行中"');
  175. // 4. 小程序验证状态变更
  176. await loginEnterpriseMini(miniPage);
  177. const enterpriseMiniPage = new EnterpriseMiniPage(miniPage);
  178. await enterpriseMiniPage.navigateToOrderList();
  179. // 等待状态更新同步
  180. await miniPage.waitForTimeout(TIMEOUTS.LONG);
  181. // 验证订单显示更新后的状态
  182. const pageContent = await miniPage.textContent('body');
  183. expect(pageContent).toContain(testOrderName);
  184. console.debug('[数据一致性] 验证通过:后台更新的状态在小程序中正确显示');
  185. });
  186. });
  187. // ============================================================
  188. // AC2: 并发操作验证测试
  189. // ============================================================
  190. test.describe.parallel('AC2: 并发操作数据隔离验证', () => {
  191. test('并发测试1: 独立创建订单不相互干扰', async ({ page: adminPage1, page: miniPage1 }) => {
  192. const uniqueId = Math.random().toString(36).substring(2, 8);
  193. const orderName = `并发测试1_${uniqueId}`;
  194. await loginAdmin(adminPage1);
  195. const orderPage = new OrderManagementPage(adminPage1);
  196. await orderPage.goto();
  197. const result = await orderPage.createOrder({
  198. name: orderName,
  199. platformName: '测试平台',
  200. companyName: '测试公司_1768346782396',
  201. });
  202. expect(result.success).toBe(true);
  203. console.debug(`[并发测试1] 订单创建成功: ${orderName}`);
  204. });
  205. test('并发测试2: 独立创建订单不相互干扰', async ({ page: adminPage2, page: miniPage2 }) => {
  206. const uniqueId = Math.random().toString(36).substring(2, 8);
  207. const orderName = `并发测试2_${uniqueId}`;
  208. await loginAdmin(adminPage2);
  209. const orderPage = new OrderManagementPage(adminPage2);
  210. await orderPage.goto();
  211. const result = await orderPage.createOrder({
  212. name: orderName,
  213. platformName: '测试平台',
  214. companyName: '测试公司_1768346782396',
  215. });
  216. expect(result.success).toBe(true);
  217. console.debug(`[并发测试2] 订单创建成功: ${orderName}`);
  218. });
  219. test('并发测试3: 独立创建订单不相互干扰', async ({ page: adminPage3, page: miniPage3 }) => {
  220. const uniqueId = Math.random().toString(36).substring(2, 8);
  221. const orderName = `并发测试3_${uniqueId}`;
  222. await loginAdmin(adminPage3);
  223. const orderPage = new OrderManagementPage(adminPage3);
  224. await orderPage.goto();
  225. const result = await orderPage.createOrder({
  226. name: orderName,
  227. platformName: '测试平台',
  228. companyName: '测试公司_1768346782396',
  229. });
  230. expect(result.success).toBe(true);
  231. console.debug(`[并发测试3] 订单创建成功: ${orderName}`);
  232. });
  233. });
  234. // ============================================================
  235. // AC3: 边界情况验证测试
  236. // ============================================================
  237. test.describe.serial('AC3: 边界情况验证', () => {
  238. test('应该处理特殊字符和 Unicode', async ({ adminPage, page: miniPage }) => {
  239. await loginAdmin(adminPage);
  240. const orderPage = new OrderManagementPage(adminPage);
  241. await orderPage.goto();
  242. // 包含 emoji、中文、日文、韩文的订单名称
  243. const specialOrderName = `测试订单_🎉_中文_日本語_한국어_${Date.now()}`;
  244. const result = await orderPage.createOrder({
  245. name: specialOrderName,
  246. platformName: '测试平台',
  247. companyName: '测试公司_1768346782396',
  248. });
  249. expect(result.success).toBe(true);
  250. console.debug('[边界测试] 特殊字符订单创建成功');
  251. // 小程序验证特殊字符正确显示
  252. await loginEnterpriseMini(miniPage);
  253. const miniPageObj = new EnterpriseMiniPage(miniPage);
  254. await miniPageObj.navigateToOrderList();
  255. // 等待并验证特殊字符订单显示
  256. await miniPage.waitForTimeout(TIMEOUTS.LONG);
  257. const pageContent = await miniPage.textContent('body');
  258. // 验证至少包含部分特殊字符
  259. expect(pageContent).toBeDefined();
  260. console.debug('[边界测试] 特殊字符处理验证通过');
  261. });
  262. test('应该处理网络延迟情况', async ({ adminPage, page: miniPage }) => {
  263. // 模拟慢速网络条件
  264. await adminPage.route('**/*', async (route) => {
  265. // 模拟网络延迟:500ms 延迟
  266. await new Promise(resolve => setTimeout(resolve, 500));
  267. await route.continue();
  268. });
  269. await loginAdmin(adminPage);
  270. const orderPage = new OrderManagementPage(adminPage);
  271. await orderPage.goto();
  272. const orderName = generateTestOrderName('网络延迟');
  273. const startTime = Date.now();
  274. const result = await orderPage.createOrder({
  275. name: orderName,
  276. platformName: '测试平台',
  277. companyName: '测试公司_1768346782396',
  278. });
  279. const endTime = Date.now();
  280. expect(result.success).toBe(true);
  281. console.debug(`[边界测试] 网络延迟情况下创建订单成功,耗时: ${endTime - startTime}ms`);
  282. // 验证在网络延迟情况下数据仍能正确同步
  283. await loginEnterpriseMini(miniPage);
  284. const miniPageObj = new EnterpriseMiniPage(miniPage);
  285. await miniPageObj.navigateToOrderList();
  286. // 等待更长时间以处理网络延迟
  287. await miniPage.waitForTimeout(TIMEOUTS.VERY_LONG);
  288. console.debug('[边界测试] 网络延迟情况处理验证通过');
  289. });
  290. test('应该处理大数据量情况', async ({ adminPage, page: miniPage }) => {
  291. await loginAdmin(adminPage);
  292. const orderPage = new OrderManagementPage(adminPage);
  293. await orderPage.goto();
  294. // 创建名称较长的订单(模拟大数据量)
  295. const longOrderName = `A`.repeat(50) + `_大数据量测试_${Date.now()}`;
  296. const result = await orderPage.createOrder({
  297. name: longOrderName,
  298. platformName: '测试平台',
  299. companyName: '测试公司_1768346782396',
  300. });
  301. expect(result.success).toBe(true);
  302. console.debug('[边界测试] 大数据量订单创建成功');
  303. // 小程序验证大数据量正确处理
  304. await loginEnterpriseMini(miniPage);
  305. const miniPageObj = new EnterpriseMiniPage(miniPage);
  306. await miniPageObj.navigateToOrderList();
  307. await miniPage.waitForTimeout(TIMEOUTS.LONG);
  308. console.debug('[边界测试] 大数据量处理验证通过');
  309. });
  310. });
  311. // ============================================================
  312. // AC5: 错误恢复验证测试
  313. // ============================================================
  314. test.describe.serial('AC5: 错误恢复验证', () => {
  315. let testOrderName: string | null = null;
  316. test.afterEach(async ({ adminPage }) => {
  317. // 清理测试数据(即使测试失败也尝试清理)
  318. try {
  319. if (testOrderName) {
  320. const orderPage = new OrderManagementPage(adminPage);
  321. await orderPage.goto();
  322. // 尝试删除测试订单
  323. await orderPage.deleteOrder(testOrderName);
  324. console.debug(`[清理] 测试订单已删除: ${testOrderName}`);
  325. }
  326. } catch (error) {
  327. console.debug('[清理] 删除测试订单失败(非致命错误):', error);
  328. } finally {
  329. testOrderName = null;
  330. }
  331. });
  332. test('单个测试失败后应该能独立重新运行', async ({ adminPage, page: miniPage }) => {
  333. testOrderName = generateTestOrderName('错误恢复');
  334. // 创建订单
  335. await loginAdmin(adminPage);
  336. const orderPage = new OrderManagementPage(adminPage);
  337. await orderPage.goto();
  338. const result = await orderPage.createOrder({
  339. name: testOrderName,
  340. platformName: '测试平台',
  341. companyName: '测试公司_1768346782396',
  342. });
  343. expect(result.success).toBe(true);
  344. console.debug('[错误恢复] 订单创建成功,验证可独立重新运行');
  345. // 验证清理不会影响其他测试
  346. await orderPage.deleteOrder(testOrderName);
  347. testOrderName = null; // 标记已清理
  348. console.debug('[错误恢复] 测试数据清理完成,不会影响其他测试');
  349. });
  350. test('应该能正确处理页面加载延迟', async ({ adminPage }) => {
  351. // 模拟页面加载延迟
  352. await adminPage.route('**/admin/orders', async (route) => {
  353. await new Promise(resolve => setTimeout(resolve, 1000));
  354. await route.continue();
  355. });
  356. await loginAdmin(adminPage);
  357. const orderPage = new OrderManagementPage(adminPage);
  358. // 使用更长的超时时间
  359. const startTime = Date.now();
  360. await orderPage.goto();
  361. const endTime = Date.now();
  362. // 即使加载延迟,页面应该最终能加载
  363. const loadTime = endTime - startTime;
  364. expect(loadTime).toBeLessThan(TIMEOUTS.PAGE_LOAD_LONG);
  365. console.debug(`[错误恢复] 页面加载延迟处理成功,耗时: ${loadTime}ms`);
  366. });
  367. });
  368. // ============================================================
  369. // AC6: 代码质量验证
  370. // ============================================================
  371. test.describe.serial('AC6: 代码质量标准验证', () => {
  372. test('应该使用 TIMEOUTS 常量定义超时', async () => {
  373. // 验证 TIMEOUTS 常量存在且包含必要的超时定义
  374. expect(TIMEOUTS).toBeDefined();
  375. expect(TIMEOUTS.PAGE_LOAD).toBeDefined();
  376. expect(TIMEOUTS.DIALOG).toBeDefined();
  377. expect(TIMEOUTS.ELEMENT_VISIBLE_SHORT).toBeDefined();
  378. expect(TIMEOUTS.LONG).toBeDefined();
  379. console.debug('[代码质量] TIMEOUTS 常量验证通过');
  380. });
  381. test('应该使用 data-testid 选择器', async ({ adminPage }) => {
  382. await loginAdmin(adminPage);
  383. const orderPage = new OrderManagementPage(adminPage);
  384. await orderPage.goto();
  385. // 验证关键元素使用 data-testid 选择器
  386. const createButton = adminPage.getByTestId('create-order-button');
  387. const buttonCount = await createButton.count();
  388. expect(buttonCount).toBeGreaterThan(0);
  389. console.debug('[代码质量] data-testid 选择器验证通过');
  390. });
  391. test('TypeScript 类型应该安全', async () => {
  392. // 验证类型导入和使用正确
  393. // 如果类型不匹配,TypeScript 编译会报错
  394. const timeoutValue: number = TIMEOUTS.PAGE_LOAD;
  395. expect(typeof timeoutValue).toBe('number');
  396. console.debug('[代码质量] TypeScript 类型安全验证通过');
  397. });
  398. });