| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /**
- * ================================================================
- * 场景:心愿兑换审批全流程 E2E 测试
- * ================================================================
- * 用户故事:作为孩子,我希望创建心愿并通过积累积分兑换,
- * 以获得家长的认可和实际的奖励物品。
- *
- * 流程步骤:
- * 1. 孩子创建心愿(如"想要一套科学实验套装")
- * 2. 家长查看心愿并设定所需积分(如 500 积分)
- * 3. 孩子通过完成任务积累积分后,申请兑换心愿
- * 4. 家长审批通过,系统自动扣除积分
- * 5. 家长购买物品后标记"已购买"
- * 6. 孩子确认收货,心愿流程完成
- *
- * 关键里程碑:
- * - [Milestone-1] 心愿创建成功,获得心愿ID
- * - [Milestone-2] 家长定价成功,心愿状态变更为"已定价"
- * - [Milestone-3] 兑换申请提交,状态变更为"兑换中"
- * - [Milestone-4] 家长审批通过,积分立即扣减
- * - [Milestone-5] 标记已购买,等待孩子确认
- * - [Milestone-6] 孩子确认收货,心愿完成
- * ================================================================
- *
- * 运行:npx playwright test tests/e2e/wish-exchange-flow.spec.js
- */
- const { test, expect } = require('@playwright/test');
- test.describe('【场景流程】心愿兑换审批全流程', () => {
- // ===== 场景1:孩子创建心愿 =====
- /**
- * 场景:孩子创建一个新的心愿
- * 角色:孩子
- * 触发条件:孩子点击"创建心愿"并填写表单
- */
- test('[场景1] 孩子创建心愿 - 期望心愿创建成功', async ({ page }) => {
- // ========== Given:进入创建心愿页面 ==========
- await page.goto('/#/pages/wishes/create');
- await page.waitForLoadState('networkidle');
- // ========== When:填写心愿信息 ==========
- // 输入心愿标题
- const titleInput = page.locator('input[placeholder*="心愿"], .wish-title-input, input[name="title"]');
- if (await titleInput.isVisible()) {
- await titleInput.fill('科学实验套装');
- }
- // 输入心愿描述
- const descInput = page.locator('textarea[placeholder*="描述"], .wish-desc-input');
- if (await descInput.isVisible()) {
- await descInput.fill('包含50个实验的科学实验套装,寓教于乐');
- }
- // 输入预估价格
- const priceInput = page.locator('input[placeholder*="价格"], .wish-price-input, input[name="estimatedPrice"]');
- if (await priceInput.isVisible()) {
- await priceInput.fill('299');
- }
- // ========== Then:提交创建 ==========
- await page.click('.submit-btn, .create-btn');
- // ========== Then:Milestone-1 - 心愿创建成功 ========
- await page.waitForSelector('.success-tip, .wish-created, .create-success', { timeout: 10000 });
- // 验证心愿创建成功
- const successTip = page.locator('.success-tip, .wish-created, .create-success');
- expect(await successTip.isVisible()).toBeTruthy();
- });
- // ===== 场景2:家长定价 =====
- /**
- * 场景:家长为孩子的心愿设定所需积分
- * 角色:家长
- * 触发条件:家长打开心愿详情,决定设定多少积分可兑换
- */
- test('[场景2] 家长设定心愿积分 - 期望积分设定成功且状态更新', async ({ page }) => {
- // ========== Given:进入待定价心愿列表 ==========
- await page.goto('/#/pages/wishes/pending');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:找到待定价心愿 ==========
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const pendingWish = page.locator('.wish-item:has-text("待定价"), .wish-item:has-text("待设置")').first();
- await pendingWish.click();
- // ========== Step 2:设定积分值 ==========
- await page.waitForSelector('.price-input, .points-input', { timeout: 5000 });
- const priceInput = page.locator('.price-input, .points-input, input[name="pointsRequired"]');
- await priceInput.fill('500');
- // ========== When:提交定价 ==========
- await page.click('.set-price-btn, .confirm-price-btn, .submit-btn');
- // ========== Then:Milestone-2 - 定价成功 ========
- await page.waitForSelector('.success-tip, .price-set, .set-success', { timeout: 10000 });
- // 验证状态变更
- const wishStatus = page.locator('.wish-status, .status-tag');
- if (await wishStatus.isVisible()) {
- const statusText = await wishStatus.textContent();
- expect(statusText).toMatch(/已定价|待兑换/);
- }
- });
- /**
- * 场景:家长设定的积分值为负数或零
- * 角色:家长
- * 触发条件:表单校验未通过
- */
- test('[场景2b] 家长设定无效积分值 - 期望返回错误提示', async ({ page }) => {
- // ========== Given:进入心愿编辑页面 ==========
- await page.goto('/#/pages/wishes/edit/1');
- await page.waitForLoadState('networkidle');
- // ========== When:设定无效积分值 ==========
- const priceInput = page.locator('.price-input, .points-input, input[name="pointsRequired"]');
- if (await priceInput.isVisible()) {
- await priceInput.fill('0');
- }
- await page.click('.submit-btn');
- // ========== Then:返回错误提示 ========
- await page.waitForSelector('.error-tip, .van-field__error, .form-error', { timeout: 5000 });
- const errorTip = page.locator('.error-tip, .van-field__error, .form-error');
- expect(await errorTip.isVisible()).toBeTruthy();
- });
- // ===== 场景3:孩子申请兑换 =====
- /**
- * 场景:孩子积累足够积分后,申请兑换心愿
- * 角色:孩子
- * 触发条件:孩子点击心愿的"申请兑换"按钮
- */
- test('[场景3] 孩子申请兑换心愿 - 期望兑换申请提交成功', async ({ page }) => {
- // ========== Given:进入我的心愿列表 ==========
- await page.goto('/#/pages/wishes/my');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:找到已定价心愿 ==========
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const pricedWish = page.locator('.wish-item:has-text("已定价"), .wish-item:has-text("可兑换")').first();
- await pricedWish.click();
- // ========== Step 2:点击兑换按钮 ==========
- await page.waitForSelector('.exchange-btn, .apply-exchange-btn', { timeout: 5000 });
- await page.click('.exchange-btn, .apply-exchange-btn');
- // ========== Then:Milestone-3 - 兑换申请提交成功 ========
- await page.waitForSelector('.success-tip, .exchange-applied, .apply-success', { timeout: 10000 });
- const successTip = page.locator('.success-tip, .exchange-applied, .apply-success');
- expect(await successTip.isVisible()).toBeTruthy();
- });
- /**
- * 场景:孩子积分不足时尝试兑换
- * 角色:孩子
- * 触发条件:孩子积分不足以兑换该心愿
- */
- test('[场景3b] 积分不足时申请兑换 - 期望返回错误提示', async ({ page }) => {
- // ========== Given:进入心愿详情 ==========
- await page.goto('/#/pages/wishes/detail/1');
- await page.waitForLoadState('networkidle');
- // ========== When:点击兑换 ==========
- const exchangeBtn = page.locator('.exchange-btn, .apply-exchange-btn');
- if (await exchangeBtn.isVisible()) {
- await exchangeBtn.click();
- // ========== Then:返回积分不足提示 ========
- await page.waitForSelector('.error-tip, .insufficient-points, .points-error', { timeout: 5000 });
- const errorTip = page.locator('.error-tip, .insufficient-points, .points-error');
- expect(await errorTip.isVisible()).toBeTruthy();
- // 验证错误信息包含积分相关字样
- const errorText = await errorTip.textContent();
- expect(errorText).toMatch(/积分|不足/);
- }
- });
- // ===== 场景4:家长审批兑换 =====
- /**
- * 场景:家长审批通过孩子的兑换申请,积分立即扣除
- * 角色:家长
- * 触发条件:家长查看兑换申请并点击"批准"
- */
- test('[场景4] 家长审批通过兑换 - 期望审批成功且积分扣除', async ({ page }) => {
- // ========== Given:进入待审批兑换列表 ==========
- await page.goto('/#/pages/wishes/pending-approval');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:找到待审批的兑换申请 ==========
- await page.waitForSelector('.exchange-item, .approval-item', { timeout: 10000 });
- const pendingApproval = page.locator('.exchange-item:has-text("申请中"), .approval-item:has-text("待审批")').first();
- await pendingApproval.click();
- // ========== Step 2:查看积分信息 ==========
- await page.waitForSelector('.points-required, .wish-points', { timeout: 5000 });
- // ========== When:点击批准 ==========
- await page.click('.approve-btn, .confirm-btn, .approve-exchange-btn');
- // ========== Then:Milestone-4 - 审批通过 + 积分扣减 ========
- await page.waitForSelector('.success-tip, .approved, .approve-success', { timeout: 10000 });
- // 验证积分已扣减(如果有显示)
- const remainingPoints = page.locator('.remaining-points, .current-points');
- if (await remainingPoints.isVisible()) {
- const pointsText = await remainingPoints.textContent();
- expect(pointsText).toMatch(/\d+/);
- }
- });
- /**
- * 场景:家长拒绝孩子的兑换申请
- * 角色:家长
- * 触发条件:家长认为该心愿不合适,点击"拒绝"
- */
- test('[场景4b] 家长拒绝兑换申请 - 期望心愿状态回退', async ({ page }) => {
- // ========== Given:进入待审批兑换详情 ==========
- await page.goto('/#/pages/wishes/approval/1');
- await page.waitForLoadState('networkidle');
- // ========== When:点击拒绝 ==========
- await page.click('.reject-btn, .reject-exchange-btn');
- // 填写拒绝原因
- const reasonInput = page.locator('textarea[placeholder*="原因"], .reject-reason-input');
- if (await reasonInput.isVisible()) {
- await reasonInput.fill('本月预算已用完,下个月再兑换好吗?');
- }
- // 确认拒绝
- await page.click('.confirm-reject-btn, .submit-btn');
- // ========== Then:拒绝成功,心愿状态回退 ========
- await page.waitForSelector('.rejected, .reject-success', { timeout: 10000 });
- // 验证心愿状态回退到"已定价"
- const wishStatus = page.locator('.wish-status, .status-tag');
- if (await wishStatus.isVisible()) {
- const statusText = await wishStatus.textContent();
- expect(statusText).toMatch(/已定价|待兑换/);
- }
- });
- // ===== 场景5:标记已购买 =====
- /**
- * 场景:家长购买物品后标记心愿为"已购买"
- * 角色:家长
- * 触发条件:家长完成购买操作
- */
- test('[场景5] 家长标记心愿已购买 - 期望状态变更为待收货', async ({ page }) => {
- // ========== Given:进入已批准兑换列表 ==========
- await page.goto('/#/pages/wishes/approved');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:找到已批准的兑换 ==========
- await page.waitForSelector('.exchange-item, .approved-item', { timeout: 10000 });
- const approvedItem = page.locator('.exchange-item:has-text("已批准"), .approved-item').first();
- await approvedItem.click();
- // ========== When:点击"已购买" ==========
- await page.click('.mark-purchased-btn, .fulfill-btn');
- // ========== Then:Milestone-5 - 标记成功 ========
- await page.waitForSelector('.success-tip, .purchased, .fulfill-success', { timeout: 10000 });
- // 验证状态变更为"待收货"
- const wishStatus = page.locator('.wish-status, .status-tag');
- if (await wishStatus.isVisible()) {
- const statusText = await wishStatus.textContent();
- expect(statusText).toMatch(/待收货|已购买/);
- }
- });
- // ===== 场景6:孩子确认收货 =====
- /**
- * 场景:孩子收到物品后确认收货,心愿流程完成
- * 角色:孩子
- * 触发条件:孩子收到实物并确认无误
- */
- test('[场景6] 孩子确认收货 - 期望心愿流程完成', async ({ page }) => {
- // ========== Given:进入待确认收货列表 ==========
- await page.goto('/#/pages/wishes/to-confirm');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:找到待确认的心愿 ==========
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const toConfirm = page.locator('.wish-item:has-text("待收货"), .wish-item:has-text("已购买")').first();
- await toConfirm.click();
- // ========== When:点击"确认收货" ==========
- await page.click('.confirm-receive-btn, .confirm-btn');
- // ========== Then:Milestone-6 - 心愿完成 ========
- await page.waitForSelector('.success-tip, .completed, .confirm-success', { timeout: 10000 });
- // 验证状态变更为"已完成"
- const wishStatus = page.locator('.wish-status, .status-tag');
- if (await wishStatus.isVisible()) {
- const statusText = await wishStatus.textContent();
- expect(statusText).toMatch(/已完成|已收货/);
- }
- });
- // ===== 场景7:完整心愿流程(端到端)=====
- test('[场景7] 完整心愿生命周期 - 期望全流程成功', async ({ page }) => {
- // ========== Step 1:孩子创建心愿 ==========
- await page.goto('/#/pages/wishes/create');
- await page.waitForLoadState('networkidle');
- const titleInput = page.locator('input[name="title"], .wish-title-input');
- if (await titleInput.isVisible()) {
- await titleInput.fill('科学杂志订阅_' + Date.now());
- }
- const descInput = page.locator('textarea[name="description"], .wish-desc-input');
- if (await descInput.isVisible()) {
- await descInput.fill('儿童科学爱好者订阅一年');
- }
- await page.click('.submit-btn');
- await page.waitForSelector('.success-tip, .wish-created', { timeout: 10000 });
- // ========== Step 2:家长定价 ==========
- await page.goto('/#/pages/wishes/pending');
- await page.waitForLoadState('networkidle');
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const wishItem = page.locator('.wish-item').first();
- const editBtn = wishItem.locator('.edit-btn, .price-btn');
- if (await editBtn.isVisible()) {
- await editBtn.click();
- await page.waitForSelector('.price-input', { timeout: 5000 });
- const priceInput = page.locator('.price-input, input[name="pointsRequired"]');
- await priceInput.fill('100');
- await page.click('.set-price-btn');
- await page.waitForSelector('.success-tip', { timeout: 10000 });
- }
- // ========== Step 3:孩子申请兑换 ==========
- await page.goto('/#/pages/wishes/my');
- await page.waitForLoadState('networkidle');
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const exchangeBtn = page.locator('.exchange-btn').first();
- if (await exchangeBtn.isVisible()) {
- await exchangeBtn.click();
- await page.waitForSelector('.success-tip, .exchange-applied', { timeout: 10000 });
- }
- // ========== Step 4:家长审批通过 ==========
- await page.goto('/#/pages/wishes/pending-approval');
- await page.waitForLoadState('networkidle');
- const approvalItem = page.locator('.exchange-item, .approval-item').first();
- if (await approvalItem.isVisible()) {
- await approvalItem.click();
- await page.waitForSelector('.approve-btn', { timeout: 5000 });
- await page.click('.approve-btn');
- await page.waitForSelector('.success-tip, .approved', { timeout: 10000 });
- }
- // ========== Step 5:家长标记已购买 ==========
- await page.goto('/#/pages/wishes/approved');
- await page.waitForLoadState('networkidle');
- const purchasedBtn = page.locator('.mark-purchased-btn, .fulfill-btn').first();
- if (await purchasedBtn.isVisible()) {
- await purchasedBtn.click();
- await page.waitForSelector('.success-tip, .purchased', { timeout: 10000 });
- }
- // ========== Step 6:孩子确认收货 ==========
- await page.goto('/#/pages/wishes/to-confirm');
- await page.waitForLoadState('networkidle');
- const confirmBtn = page.locator('.confirm-receive-btn').first();
- if (await confirmBtn.isVisible()) {
- await confirmBtn.click();
- await page.waitForSelector('.success-tip, .completed', { timeout: 10000 });
- }
- // 验证最终状态
- const finalStatus = page.locator('.wish-status');
- if (await finalStatus.isVisible()) {
- const statusText = await finalStatus.textContent();
- expect(statusText).toMatch(/已完成/);
- }
- });
- // ===== 场景8:家长拒绝心愿 =====
- test('[场景8] 家长直接拒绝心愿 - 期望心愿被关闭', async ({ page }) => {
- // ========== Given:进入待处理心愿 ==========
- await page.goto('/#/pages/wishes/pending');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:选择心愿 ==========
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const wishItem = page.locator('.wish-item').first();
- await wishItem.click();
- // ========== When:点击拒绝 ==========
- await page.waitForSelector('.reject-btn, .refuse-btn', { timeout: 5000 });
- await page.click('.reject-btn');
- // 填写原因
- const reasonInput = page.locator('textarea[name="reason"], .reject-reason-input');
- if (await reasonInput.isVisible()) {
- await reasonInput.fill('这套书家里已经有了,换个其他的吧');
- }
- await page.click('.confirm-reject-btn');
- // ========== Then:心愿被关闭 ========
- await page.waitForSelector('.rejected, .reject-success', { timeout: 10000 });
- const wishStatus = page.locator('.wish-status, .status-tag');
- if (await wishStatus.isVisible()) {
- const statusText = await wishStatus.textContent();
- expect(statusText).toMatch(/已拒绝|已关闭/);
- }
- });
- // ===== 场景9:孩子取消心愿 =====
- test('[场景9] 孩子取消心愿 - 期望心愿被取消', async ({ page }) => {
- // ========== Given:进入我的心愿 ==========
- await page.goto('/#/pages/wishes/my');
- await page.waitForLoadState('networkidle');
- // ========== Step 1:选择心愿 ==========
- await page.waitForSelector('.wish-item', { timeout: 10000 });
- const wishItem = page.locator('.wish-item').first();
- // 点击取消按钮
- const cancelBtn = wishItem.locator('.cancel-btn, .cancel-wish-btn');
- if (await cancelBtn.isVisible()) {
- await cancelBtn.click();
- // 确认取消
- await page.waitForSelector('.confirm-dialog, .van-dialog', { timeout: 5000 });
- await page.click('.confirm-dialog .van-button--primary, .confirm-cancel-btn');
- // ========== Then:心愿被取消 ========
- await page.waitForTimeout(1000);
- // 验证心愿不在列表中或状态变更
- const statusTag = wishItem.locator('.status-tag');
- if (await statusTag.isVisible()) {
- const statusText = await statusTag.textContent();
- expect(statusText).toMatch(/已取消|已关闭/);
- }
- }
- });
- });
|