growth-record-sync.spec.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /**
  2. * ================================================================
  3. * 场景:成长记录同步全流程 E2E 测试
  4. * ================================================================
  5. * 用户故事:作为家长,我希望记录孩子的成长轨迹,
  6. * 上传健康报告并与测评结果关联,获得全面的成长分析。
  7. *
  8. * 流程步骤:
  9. * 1. 家长创建孩子的成长档案(基本信息、学校、年级等)
  10. * 2. 家长上传孩子的健康体检报告
  11. * 3. 系统解析报告,提取身高、体重、视力等数据
  12. * 4. 成长记录与认知测评结果关联
  13. * 5. 系统更新孩子的五维能量数据
  14. *
  15. * 关键里程碑:
  16. * - [Milestone-1] 成长档案创建成功
  17. * - [Milestone-2] 健康报告上传并解析成功
  18. * - [Milestone-3] 成长记录与测评报告关联
  19. * - [Milestone-4] 五维能量数据更新
  20. * ================================================================
  21. *
  22. * 运行:npx playwright test tests/e2e/growth-record-sync.spec.js
  23. */
  24. const { test, expect } = require('@playwright/test');
  25. test.describe('【场景流程】成长记录同步全流程', () => {
  26. // ===== 场景1:创建成长档案 =====
  27. /**
  28. * 场景:家长为孩子创建一个成长档案
  29. * 角色:家长
  30. * 触发条件:家长首次使用成长记录功能
  31. */
  32. test('[场景1] 创建孩子成长档案 - 期望档案创建成功', async ({ page }) => {
  33. // ========== Given:进入创建成长档案页面 ==========
  34. await page.goto('/#/pages/growth/create');
  35. await page.waitForLoadState('networkidle');
  36. // ========== Step 1:选择孩子 ==========
  37. const childPicker = page.locator('.child-picker, .select-child');
  38. if (await childPicker.isVisible()) {
  39. await childPicker.click();
  40. await page.waitForSelector('.child-option, .option-item', { timeout: 5000 });
  41. await page.click('.child-option:first-child, .option-item:first-child');
  42. }
  43. // ========== Step 2:填写基本信息 ==========
  44. // 输入学校名称
  45. const schoolInput = page.locator('input[placeholder*="学校"], .school-input, input[name="school"]');
  46. if (await schoolInput.isVisible()) {
  47. await schoolInput.fill('北京市第一小学');
  48. }
  49. // 选择年级
  50. const gradePicker = page.locator('.grade-picker, .select-grade');
  51. if (await gradePicker.isVisible()) {
  52. await gradePicker.click();
  53. await page.waitForSelector('.grade-option, .option-item', { timeout: 5000 });
  54. await page.click('.grade-option:has-text("三年级"), .grade-option:nth-child(3)');
  55. }
  56. // ========== When:提交创建 ==========
  57. await page.click('.submit-btn, .create-btn');
  58. // ========== Then:Milestone-1 - 档案创建成功 ========
  59. await page.waitForSelector('.success-tip, .created, .record-success', { timeout: 10000 });
  60. const successTip = page.locator('.success-tip, .created, .record-success');
  61. expect(await successTip.isVisible()).toBeTruthy();
  62. });
  63. /**
  64. * 场景:家长创建档案时未选择孩子
  65. * 角色:家长
  66. * 触发条件:表单中孩子ID为空
  67. */
  68. test('[场景1b] 创建档案时未选择孩子 - 期望返回错误', async ({ page }) => {
  69. // ========== Given:进入创建页面 ==========
  70. await page.goto('/#/pages/growth/create');
  71. await page.waitForLoadState('networkidle');
  72. // ========== Step 1:填写学校但选择孩子 ==========
  73. const schoolInput = page.locator('input[placeholder*="学校"], .school-input');
  74. if (await schoolInput.isVisible()) {
  75. await schoolInput.fill('北京市第一小学');
  76. }
  77. // ========== When:尝试提交 ==========
  78. await page.click('.submit-btn');
  79. // ========== Then:返回错误提示 ========
  80. await page.waitForSelector('.error-tip, .van-field__error, .form-error', { timeout: 5000 });
  81. const errorTip = page.locator('.error-tip, .van-field__error');
  82. expect(await errorTip.isVisible()).toBeTruthy();
  83. expect(await errorTip.textContent()).toMatch(/孩子|选择/);
  84. });
  85. // ===== 场景2:创建档案并购买测评 =====
  86. /**
  87. * 场景:家长创建成长档案的同时购买认知测评服务
  88. * 角色:家长
  89. * 触发条件:勾选"同时购买测评"选项
  90. */
  91. test('[场景2] 创建档案并购买测评套餐 - 期望档案和订单同时创建', async ({ page }) => {
  92. // ========== Given:进入创建档案页面 ==========
  93. await page.goto('/#/pages/growth/create-with-order');
  94. await page.waitForLoadState('networkidle');
  95. // ========== Step 1:选择孩子 ==========
  96. const childPicker = page.locator('.child-picker');
  97. if (await childPicker.isVisible()) {
  98. await childPicker.click();
  99. await page.waitForSelector('.child-option', { timeout: 5000 });
  100. await page.click('.child-option:first-child');
  101. }
  102. // ========== Step 2:填写学校信息 ==========
  103. const schoolInput = page.locator('input[name="school"], .school-input');
  104. if (await schoolInput.isVisible()) {
  105. await schoolInput.fill('北京市第二小学');
  106. }
  107. // ========== Step 3:勾选同时购买测评 ==========
  108. const purchaseCheckbox = page.locator('.purchase-checkbox, .also-buy-assessment');
  109. if (await purchaseCheckbox.isVisible()) {
  110. await purchaseCheckbox.click();
  111. }
  112. // 选择套餐
  113. const packageOption = page.locator('.package-option:first-child');
  114. if (await packageOption.isVisible()) {
  115. await packageOption.click();
  116. }
  117. // ========== When:提交 ==========
  118. await page.click('.submit-btn, .create-with-order-btn');
  119. // ========== Then:档案和订单都创建成功 ========
  120. await page.waitForSelector('.success-tip, .record-created', { timeout: 10000 });
  121. // 验证跳转或显示订单信息
  122. const orderInfo = page.locator('.order-info, .order-no');
  123. // 可能跳转到了订单页面或留在创建页但显示了订单信息
  124. expect(await page.locator('.success-tip, .record-created, .order-info').isVisible()).toBeTruthy();
  125. });
  126. // ===== 场景3:上传健康报告并解析 =====
  127. /**
  128. * 场景:家长上传孩子的健康体检报告,系统自动解析
  129. * 角色:家长
  130. * 触发条件:上传体检报告文件
  131. */
  132. test('[场景3] 上传并解析健康报告 - 期望解析出健康数据', async ({ page }) => {
  133. // ========== Given:进入上传报告页面 ==========
  134. await page.goto('/#/pages/growth/upload-report');
  135. await page.waitForLoadState('networkidle');
  136. // ========== Step 1:选择孩子 ==========
  137. const childPicker = page.locator('.child-picker');
  138. if (await childPicker.isVisible()) {
  139. await childPicker.click();
  140. await page.waitForSelector('.child-option', { timeout: 5000 });
  141. await page.click('.child-option:first-child');
  142. }
  143. // ========== Step 2:上传报告文件 ==========
  144. // 点击上传按钮
  145. const uploadBtn = page.locator('.upload-btn, .upload-report-btn');
  146. if (await uploadBtn.isVisible()) {
  147. await uploadBtn.click();
  148. }
  149. // 选择文件(模拟)
  150. const fileInput = page.locator('input[type="file"], .file-input');
  151. if (await fileInput.isVisible()) {
  152. // 在实际测试中,这里会上传真实的PDF文件
  153. // await fileInput.setInputFiles('/path/to/test-report.pdf');
  154. }
  155. // ========== When:确认上传 ==========
  156. const confirmBtn = page.locator('.confirm-upload-btn, .submit-upload-btn');
  157. if (await confirmBtn.isVisible()) {
  158. await confirmBtn.click();
  159. }
  160. // ========== Then:Milestone-2 - 报告解析成功 ========
  161. // 等待解析完成(可能需要几秒钟)
  162. await page.waitForSelector('.parsed-result, .report-data, .parse-success', { timeout: 30000 });
  163. // 验证解析结果显示
  164. const parsedData = page.locator('.parsed-result, .health-data');
  165. expect(await parsedData.isVisible()).toBeTruthy();
  166. // 验证关键数据提取
  167. const heightData = page.locator('.height-value, .parsed-height');
  168. const weightData = page.locator('.weight-value, .parsed-weight');
  169. if (await heightData.isVisible()) {
  170. expect(await heightData.textContent()).toMatch(/\d+/);
  171. }
  172. });
  173. // ===== 场景4:关联成长记录与测评结果 =====
  174. /**
  175. * 场景:成长记录与认知测评结果进行关联
  176. * 角色:系统(自动关联)
  177. * 触发条件:测评报告生成后
  178. */
  179. test('[场景4] 成长记录关联测评报告 - 期望关联成功', async ({ page }) => {
  180. // ========== Given:进入成长记录详情 ==========
  181. await page.goto('/#/pages/growth/record-detail/1');
  182. await page.waitForLoadState('networkidle');
  183. // ========== When:查看关联状态 ==========
  184. await page.waitForSelector('.record-detail, .growth-record', { timeout: 10000 });
  185. // ========== Then:Milestone-3 - 关联成功 ========
  186. // 查看是否有测评报告关联卡片
  187. const assessmentCard = page.locator('.assessment-link, .related-assessment, .linked-report');
  188. if (await assessmentCard.isVisible()) {
  189. // 验证关联的测评报告显示
  190. expect(await assessmentCard.isVisible()).toBeTruthy();
  191. } else {
  192. // 验证关联按钮存在,可以进行关联
  193. const linkBtn = page.locator('.link-assessment-btn, .add-assessment-btn');
  194. expect(await linkBtn.isVisible()).toBeTruthy();
  195. }
  196. });
  197. // ===== 场景5:查询孩子的成长记录列表 =====
  198. /**
  199. * 场景:家长查看孩子的历史成长记录
  200. * 角色:家长
  201. * 触发条件:进入成长记录历史页面
  202. */
  203. test('[场景5] 查询成长记录历史 - 期望返回记录列表', async ({ page }) => {
  204. // ========== Given:进入成长记录列表 ==========
  205. await page.goto('/#/pages/growth/records');
  206. await page.waitForLoadState('networkidle');
  207. // ========== Step 1:选择孩子 ==========
  208. const childPicker = page.locator('.child-picker');
  209. if (await childPicker.isVisible()) {
  210. await childPicker.click();
  211. await page.waitForSelector('.child-option', { timeout: 5000 });
  212. await page.click('.child-option:first-child');
  213. }
  214. // ========== When:查询记录列表 ==========
  215. await page.waitForSelector('.record-item, .growth-item', { timeout: 10000 });
  216. // ========== Then:返回历史记录列表 ========
  217. const recordItems = page.locator('.record-item, .growth-item');
  218. const count = await recordItems.count();
  219. expect(count).toBeGreaterThan(0);
  220. // 验证记录包含日期
  221. const firstRecord = recordItems.first();
  222. const recordDate = firstRecord.locator('.record-date, .date-text');
  223. expect(await recordDate.isVisible()).toBeTruthy();
  224. });
  225. // ===== 场景6:补充成长记录 =====
  226. /**
  227. * 场景:家长为已有记录补充信息
  228. * 角色:家长
  229. * 触发条件:点击记录的"补充"按钮
  230. */
  231. test('[场景6] 补充成长记录 - 期望补充成功', async ({ page }) => {
  232. // ========== Given:进入记录详情 ==========
  233. await page.goto('/#/pages/growth/record-detail/1');
  234. await page.waitForLoadState('networkidle');
  235. // ========== When:点击补充按钮 ==========
  236. const supplementBtn = page.locator('.supplement-btn, .add-note-btn');
  237. if (await supplementBtn.isVisible()) {
  238. await supplementBtn.click();
  239. // 填写补充内容
  240. const noteInput = page.locator('textarea[name="note"], .note-input');
  241. if (await noteInput.isVisible()) {
  242. await noteInput.fill('本学期孩子在数学方面有明显进步,建议加强逻辑思维训练。');
  243. }
  244. await page.click('.submit-supplement-btn');
  245. // ========== Then:补充成功 ========
  246. await page.waitForSelector('.success-tip, .supplement-success', { timeout: 10000 });
  247. // 验证补充内容显示
  248. const supplementList = page.locator('.supplement-list, .note-list');
  249. expect(await supplementList.isVisible()).toBeTruthy();
  250. }
  251. });
  252. // ===== 场景7:完整成长记录流程(端到端)=====
  253. test('[场景7] 完整成长记录生命周期 - 期望全流程成功', async ({ page }) => {
  254. // ========== Step 1:创建成长档案 ==========
  255. await page.goto('/#/pages/growth/create');
  256. await page.waitForLoadState('networkidle');
  257. // 选择孩子
  258. const childPicker = page.locator('.child-picker');
  259. if (await childPicker.isVisible()) {
  260. await childPicker.click();
  261. await page.waitForSelector('.child-option', { timeout: 5000 });
  262. await page.click('.child-option:first-child');
  263. }
  264. // 填写学校
  265. const schoolInput = page.locator('input[name="school"], .school-input');
  266. if (await schoolInput.isVisible()) {
  267. await schoolInput.fill('北京市第一小学');
  268. }
  269. // 选择年级
  270. const gradePicker = page.locator('.grade-picker');
  271. if (await gradePicker.isVisible()) {
  272. await gradePicker.click();
  273. await page.waitForSelector('.grade-option', { timeout: 5000 });
  274. await page.click('.grade-option:nth-child(3)');
  275. }
  276. await page.click('.submit-btn');
  277. await page.waitForSelector('.success-tip, .record-success', { timeout: 10000 });
  278. // ========== Step 2:查询记录列表 ==========
  279. await page.goto('/#/pages/growth/records');
  280. await page.waitForLoadState('networkidle');
  281. await page.waitForSelector('.record-item', { timeout: 10000 });
  282. // 验证记录存在
  283. const recordCount = await page.locator('.record-item').count();
  284. expect(recordCount).toBeGreaterThan(0);
  285. // ========== Step 3:查看详情 ==========
  286. const firstRecord = page.locator('.record-item').first();
  287. await firstRecord.click();
  288. await page.waitForSelector('.record-detail, .growth-detail', { timeout: 10000 });
  289. // 验证详情页面显示
  290. expect(await page.locator('.record-detail, .growth-detail').isVisible()).toBeTruthy();
  291. });
  292. });