product-purchase.spec.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /**
  2. * ================================================================
  3. * 场景:商品购买支付全流程 E2E 测试
  4. * ================================================================
  5. * 用户故事:作为家长,我希望在商城购买孩子的用品,
  6. * 完成支付后等待商品配送到家。
  7. *
  8. * 流程步骤:
  9. * 1. 家长浏览商品列表,选择商品
  10. * 2. 家长创建商品订单
  11. * 3. 家长选择支付方式并完成支付
  12. * 4. 服务商发货,填写物流信息
  13. * 5. 家长确认收货,订单完成
  14. * 6. 如有问题,家长申请退款
  15. *
  16. * 关键里程碑:
  17. * - [Milestone-1] 订单创建成功,获得订单号
  18. * - [Milestone-2] 支付成功,订单状态变更为"已支付"
  19. * - [Milestone-3] 服务商发货,订单状态变更为"已发货"
  20. * - [Milestone-4] 家长确认收货,订单完成
  21. * - [Milestone-5] 退款申请提交成功
  22. * ================================================================
  23. *
  24. * 运行:npx playwright test tests/e2e/product-purchase.spec.js
  25. */
  26. const { test, expect } = require('@playwright/test');
  27. test.describe('【场景流程】商品购买支付全流程', () => {
  28. // ===== 场景1:创建商品订单 =====
  29. /**
  30. * 场景:家长选购商品后创建订单
  31. * 角色:家长
  32. * 触发条件:点击"立即购买"或"加入购物车结算"
  33. */
  34. test('[场景1] 创建商品订单 - 期望订单创建成功', async ({ page }) => {
  35. // ========== Given:进入商品详情页 ==========
  36. await page.goto('/#/pages/shop/product/1');
  37. await page.waitForLoadState('networkidle');
  38. // ========== Step 1:选择数量 ==========
  39. const quantityInput = page.locator('.quantity-input, input[name="quantity"]');
  40. if (await quantityInput.isVisible()) {
  41. await quantityInput.fill('1');
  42. } else {
  43. // 尝试点击增加按钮
  44. const increaseBtn = page.locator('.increase-btn, .btn-increase');
  45. if (await increaseBtn.isVisible()) {
  46. await increaseBtn.click();
  47. }
  48. }
  49. // ========== Step 2:选择收货地址 ==========
  50. const addressPicker = page.locator('.address-picker, .select-address');
  51. if (await addressPicker.isVisible()) {
  52. await addressPicker.click();
  53. await page.waitForSelector('.address-option, .address-item', { timeout: 5000 });
  54. await page.click('.address-option:first-child, .address-item:first-child');
  55. }
  56. // ========== When:点击立即购买 ==========
  57. await page.click('.buy-now-btn, .purchase-btn');
  58. // ========== Then:Milestone-1 - 订单创建成功 ========
  59. await page.waitForSelector('.order-created, .order-success, .order-no', { timeout: 10000 });
  60. // 验证订单号显示
  61. const orderNo = page.locator('.order-no, .order-number');
  62. expect(await orderNo.isVisible()).toBeTruthy();
  63. const orderNoText = await orderNo.textContent();
  64. expect(orderNoText).toMatch(/ORD|PRD|\d+/);
  65. });
  66. // ===== 场景2:支付订单 =====
  67. /**
  68. * 场景:家长选择微信支付完成订单支付
  69. * 角色:家长
  70. * 触发条件:点击"去支付"并选择支付方式
  71. */
  72. test('[场景2] 支付商品订单 - 期望支付成功', async ({ page }) => {
  73. // ========== Given:进入订单支付页面 ==========
  74. await page.goto('/#/pages/shop/pay?orderNo=ORD20260628001');
  75. await page.waitForLoadState('networkidle');
  76. // ========== Step 1:选择支付方式 ==========
  77. const wechatPayOption = page.locator('.pay-method:has-text("微信支付"), .wechat-pay');
  78. if (await wechatPayOption.isVisible()) {
  79. await wechatPayOption.click();
  80. }
  81. // ========== When:确认支付 ==========
  82. await page.click('.confirm-pay-btn, .pay-now-btn');
  83. // ========== Then:Milestone-2 - 支付成功 ========
  84. await page.waitForSelector('.pay-success, .payment-success, .success-tip', { timeout: 30000 });
  85. // 验证支付成功提示
  86. const successTip = page.locator('.pay-success, .payment-success');
  87. expect(await successTip.isVisible()).toBeTruthy();
  88. });
  89. // ===== 场景3:服务商发货 =====
  90. /**
  91. * 场景:服务商收到订单后发货,填写物流信息
  92. * 角色:服务商/供应商
  93. * 触发条件:商家点击"发货"并填写物流单号
  94. */
  95. test('[场景3] 服务商发货 - 期望订单状态更新为已发货', async ({ page }) => {
  96. // ========== Given:进入商家订单管理 ==========
  97. await page.goto('/#/pages/vendor/orders');
  98. await page.waitForLoadState('networkidle');
  99. // ========== Step 1:找到待发货订单 ==========
  100. await page.waitForSelector('.order-item', { timeout: 10000 });
  101. const paidOrder = page.locator('.order-item:has-text("已支付"), .order-item:has-text("待发货")').first();
  102. await paidOrder.click();
  103. // ========== Step 2:点击发货按钮 ==========
  104. await page.waitForSelector('.ship-btn, .deliver-btn', { timeout: 5000 });
  105. await page.click('.ship-btn');
  106. // ========== Step 3:填写物流信息 ==========
  107. await page.waitForSelector('.express-company, .tracking-company', { timeout: 5000 });
  108. // 选择快递公司
  109. const companyPicker = page.locator('.express-company, .tracking-company');
  110. if (await companyPicker.isVisible()) {
  111. await companyPicker.click();
  112. await page.waitForSelector('.company-option', { timeout: 5000 });
  113. await page.click('.company-option:first-child');
  114. }
  115. // 输入运单号
  116. const trackingInput = page.locator('input[name="trackingNumber"], .tracking-input');
  117. if (await trackingInput.isVisible()) {
  118. await trackingInput.fill('SF' + Date.now());
  119. }
  120. // ========== When:确认发货 ==========
  121. await page.click('.confirm-ship-btn, .submit-btn');
  122. // ========== Then:Milestone-3 - 发货成功 ========
  123. await page.waitForSelector('.ship-success, .shipped', { timeout: 10000 });
  124. // 验证订单状态变更为"已发货"
  125. const orderStatus = page.locator('.order-status, .status-tag');
  126. if (await orderStatus.isVisible()) {
  127. const statusText = await orderStatus.textContent();
  128. expect(statusText).toMatch(/已发货|待收货/);
  129. }
  130. // 验证运单号显示
  131. const trackingNo = page.locator('.tracking-number, .tracking-no');
  132. if (await trackingNo.isVisible()) {
  133. const trackingText = await trackingNo.textContent();
  134. expect(trackingText).toMatch(/SF|\d{10,}/);
  135. }
  136. });
  137. // ===== 场景4:家长确认收货 =====
  138. /**
  139. * 场景:家长收到商品后确认收货
  140. * 角色:家长
  141. * 触发条件:确认收到商品无误
  142. */
  143. test('[场景4] 家长确认收货 - 期望订单完成', async ({ page }) => {
  144. // ========== Given:进入待收货订单 ==========
  145. await page.goto('/#/pages/shop/orders?to=received');
  146. await page.waitForLoadState('networkidle');
  147. // ========== Step 1:找到待收货订单 ==========
  148. await page.waitForSelector('.order-item', { timeout: 10000 });
  149. const shippedOrder = page.locator('.order-item:has-text("已发货"), .order-item:has-text("待收货")').first();
  150. await shippedOrder.click();
  151. // ========== When:点击确认收货 ==========
  152. await page.waitForSelector('.confirm-receive-btn, .receive-btn', { timeout: 5000 });
  153. await page.click('.confirm-receive-btn, .receive-btn');
  154. // ========== Then:Milestone-4 - 订单完成 ========
  155. await page.waitForSelector('.receive-success, .completed', { timeout: 10000 });
  156. // 验证订单状态变更为"已完成"
  157. const orderStatus = page.locator('.order-status, .status-tag');
  158. if (await orderStatus.isVisible()) {
  159. const statusText = await orderStatus.textContent();
  160. expect(statusText).toMatch(/已完成|已收货/);
  161. }
  162. });
  163. // ===== 场景5:申请退款 =====
  164. /**
  165. * 场景:家长发现问题,申请退款
  166. * 角色:家长
  167. * 触发条件:点击"申请退款"并填写原因
  168. */
  169. test('[场景5] 申请退款 - 期望退款申请提交成功', async ({ page }) => {
  170. // ========== Given:进入订单详情 ==========
  171. await page.goto('/#/pages/shop/order-detail?orderNo=ORD20260628002');
  172. await page.waitForLoadState('networkidle');
  173. // ========== Step 1:点击申请退款 ==========
  174. await page.waitForSelector('.refund-btn, .apply-refund-btn', { timeout: 10000 });
  175. await page.click('.refund-btn');
  176. // ========== Step 2:填写退款原因 ==========
  177. await page.waitForSelector('.refund-reason-input, .reason-textarea', { timeout: 5000 });
  178. const reasonInput = page.locator('.refund-reason-input textarea, .reason-textarea');
  179. if (await reasonInput.isVisible()) {
  180. await reasonInput.fill('商品与描述不符,收到后发现质量问题');
  181. }
  182. // ========== When:提交退款申请 ==========
  183. await page.click('.submit-refund-btn, .confirm-refund-btn');
  184. // ========== Then:Milestone-5 - 退款申请成功 ========
  185. await page.waitForSelector('.refund-submitted, .refund-success', { timeout: 10000 });
  186. // 验证退款状态
  187. const refundStatus = page.locator('.refund-status, .status-tag');
  188. if (await refundStatus.isVisible()) {
  189. const statusText = await refundStatus.textContent();
  190. expect(statusText).toMatch(/退款|审核中/);
  191. }
  192. });
  193. // ===== 场景6:取消未支付订单 =====
  194. /**
  195. * 场景:家长改变主意,取消未支付的订单
  196. * 角色:家长
  197. * 触发条件:点击"取消订单"
  198. */
  199. test('[场景6] 取消未支付订单 - 期望订单关闭', async ({ page }) => {
  200. // ========== Given:进入我的订单 ==========
  201. await page.goto('/#/pages/shop/orders');
  202. await page.waitForLoadState('networkidle');
  203. // ========== Step 1:找到待支付订单 ==========
  204. await page.waitForSelector('.order-item', { timeout: 10000 });
  205. const pendingOrder = page.locator('.order-item:has-text("待支付"), .order-item:has-text("待付款")').first();
  206. // 点击进入订单详情
  207. await pendingOrder.click();
  208. // ========== Step 2:点击取消订单 ==========
  209. await page.waitForSelector('.cancel-order-btn', { timeout: 5000 });
  210. await page.click('.cancel-order-btn');
  211. // 确认取消
  212. await page.waitForSelector('.confirm-dialog, .van-dialog', { timeout: 5000 });
  213. await page.click('.confirm-dialog .van-button--primary, .confirm-cancel');
  214. // ========== Then:订单关闭 ========
  215. await page.waitForSelector('.cancel-success, .order-closed', { timeout: 10000 });
  216. const orderStatus = page.locator('.order-status, .status-tag');
  217. if (await orderStatus.isVisible()) {
  218. const statusText = await orderStatus.textContent();
  219. expect(statusText).toMatch(/已取消|已关闭/);
  220. }
  221. });
  222. // ===== 场景7:查看我的订单列表 =====
  223. /**
  224. * 场景:家长查看自己的订单列表
  225. * 角色:家长
  226. * 触发条件:进入"我的订单"页面
  227. */
  228. test('[场景7] 查看我的订单列表 - 期望返回订单列表', async ({ page }) => {
  229. // ========== Given:进入我的订单页面 ==========
  230. await page.goto('/#/pages/shop/orders');
  231. await page.waitForLoadState('networkidle');
  232. // ========== When:查看订单列表 ==========
  233. await page.waitForSelector('.order-item', { timeout: 10000 });
  234. // ========== Then:返回订单列表 ========
  235. const orderItems = page.locator('.order-item');
  236. const count = await orderItems.count();
  237. expect(count).toBeGreaterThan(0);
  238. // 验证订单状态显示
  239. const firstOrder = orderItems.first();
  240. const statusTag = firstOrder.locator('.status-tag, .order-status');
  241. expect(await statusTag.isVisible()).toBeTruthy();
  242. });
  243. // ===== 场景8:查看订单详情 =====
  244. /**
  245. * 场景:家长查看具体订单的详细信息
  246. * 角色:家长
  247. * 触发条件:点击某个订单查看详情
  248. */
  249. test('[场景8] 查看订单详情 - 期望返回完整订单信息', async ({ page }) => {
  250. // ========== Given:进入订单详情 ==========
  251. await page.goto('/#/pages/shop/order-detail?orderNo=ORD20260628001');
  252. await page.waitForLoadState('networkidle');
  253. // ========== When:查看订单详情 ==========
  254. await page.waitForSelector('.order-detail, .detail-container', { timeout: 10000 });
  255. // ========== Then:返回完整订单信息 ========
  256. // 验证订单号显示
  257. const orderNo = page.locator('.order-no, .order-number');
  258. expect(await orderNo.isVisible()).toBeTruthy();
  259. // 验证商品信息显示
  260. const productInfo = page.locator('.product-info, .product-name');
  261. expect(await productInfo.isVisible()).toBeTruthy();
  262. // 验证金额显示
  263. const amountInfo = page.locator('.total-amount, .order-amount');
  264. expect(await amountInfo.isVisible()).toBeTruthy();
  265. // 验证物流信息(如果有)
  266. const logisticsInfo = page.locator('.logistics-info, .tracking-info');
  267. // 物流信息可能不显示(未发货时),但不应该报错
  268. });
  269. // ===== 场景9:完整商品购买流程(端到端)=====
  270. test('[场景9] 完整商品购买流程 - 期望全流程成功', async ({ page }) => {
  271. // ========== Step 1:浏览商品 ==========
  272. await page.goto('/#/pages/shop/list');
  273. await page.waitForLoadState('networkidle');
  274. await page.waitForSelector('.product-item', { timeout: 10000 });
  275. // 点击第一个商品
  276. await page.locator('.product-item').first().click();
  277. await page.waitForLoadState('networkidle');
  278. // ========== Step 2:创建订单 ==========
  279. const buyBtn = page.locator('.buy-now-btn, .purchase-btn');
  280. if (await buyBtn.isVisible()) {
  281. await buyBtn.click();
  282. await page.waitForSelector('.order-no, .order-created', { timeout: 10000 });
  283. }
  284. // 获取订单号
  285. let orderNo = '';
  286. const orderNoEl = page.locator('.order-no');
  287. if (await orderNoEl.isVisible()) {
  288. orderNo = await orderNoEl.textContent();
  289. }
  290. // ========== Step 3:支付订单 ==========
  291. const payBtn = page.locator('.go-pay-btn, .pay-now-btn');
  292. if (await payBtn.isVisible()) {
  293. await payBtn.click();
  294. // 选择微信支付
  295. const wechatPay = page.locator('.pay-method:has-text("微信")');
  296. if (await wechatPay.isVisible()) {
  297. await wechatPay.click();
  298. }
  299. await page.click('.confirm-pay-btn');
  300. await page.waitForSelector('.pay-success, .payment-success', { timeout: 30000 });
  301. }
  302. // ========== Step 4:查看我的订单 ==========
  303. await page.goto('/#/pages/shop/orders');
  304. await page.waitForLoadState('networkidle');
  305. await page.waitForSelector('.order-item', { timeout: 10000 });
  306. // 验证订单存在
  307. const orderItems = page.locator('.order-item');
  308. const count = await orderItems.count();
  309. expect(count).toBeGreaterThan(0);
  310. // 如果有已发货的订单,确认收货
  311. const shippedOrder = page.locator('.order-item:has-text("已发货")').first();
  312. if (await shippedOrder.isVisible()) {
  313. await shippedOrder.click();
  314. const receiveBtn = page.locator('.confirm-receive-btn');
  315. if (await receiveBtn.isVisible()) {
  316. await receiveBtn.click();
  317. await page.waitForSelector('.receive-success', { timeout: 10000 });
  318. }
  319. }
  320. });
  321. });