| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- /**
- * 【API级】能量系统收支全流程
- * ================================================================
- * 对应 DOM 版:e2e/energy-system.spec.js(远程无 SPA,DOM 版不可跑)
- * 接口契约来源:EnergyController(/api/energy/*)
- * sandbox : {} 家庭能量沙盘(五维)
- * overview : { memberId? } 能量概览
- * logs : { memberId?, dimensionCode?, page?, size? } 能量流水(Page)
- * grant : { memberId, taskId, amount, dimensionCode } 任务完成发放能量
- * deduct-for-wish: { memberId, wishId, amount } 心愿兑换扣能量
- * 五维维度代码:body(身) / mind(心) / wisdom(智) / action(行) / wealth(富)
- * 运行:npx playwright test -c playwright.config.api.js --project=desktop
- */
- const { test, expect } = require('@playwright/test');
- const { registerRole, apiPost, pickMemberByUserId, uniqueSuffix } = require('../helpers/api');
- const DIMENSION = 'body'; // 身
- test.describe('【API级】能量系统收支全流程', () => {
- let ctx;
- let selfMemberId;
- test.beforeAll(async ({ request }) => {
- ctx = await registerRole(request, 'parent', 'QA-ENERGY');
- const list = await apiPost(request, '/api/family/member/list', {}, ctx.token);
- expect(list.code).toBe(200);
- const self = pickMemberByUserId(list.data, ctx.userId);
- expect(self, '成员列表应包含登录用户自身').toBeTruthy();
- selfMemberId = self.id;
- });
- // ===== 场景1:获取家庭能量沙盘 =====
- test('[场景1] 获取家庭能量沙盘 - 期望返回五维能量数据', async ({ request }) => {
- const r = await apiPost(request, '/api/energy/sandbox', {}, ctx.token);
- expect(r.code).toBe(200);
- expect(r.data).toBeTruthy();
- expect(typeof r.data).toBe('object');
- });
- test('[场景1b] 未加入家庭/越权访问 - 期望返回错误提示', async ({ request }) => {
- const r = await apiPost(request, '/api/family/member/list', { familyId: 999999999 }, ctx.token);
- expect(r.code).toBe(403);
- expect(String(r.message)).toContain('无权访问');
- });
- // ===== 场景2:任务完成能量发放 =====
- /**
- * CFC-EN-001:/api/energy/grant 已修复(维度由调用方显式指定,能量直接发放到该维度)。
- * 用例断言成功路径(200 + data + 流水记录),不再以 fixme 标记阻塞。
- */
- test('[场景2] 任务完成能量发放 - 期望能量增加且流水记录', async ({ request }) => {
- const amount = 10;
- const taskId = Number(uniqueSuffix());
- const grant = await apiPost(
- request,
- '/api/energy/grant',
- { memberId: selfMemberId, taskId, amount, dimensionCode: DIMENSION },
- ctx.token
- );
- expect(grant.code, '能量发放应成功').toBe(200);
- expect(grant.data).toBeTruthy();
- // 流水应记录本次发放
- const logs = await apiPost(request, '/api/energy/logs', { memberId: selfMemberId }, ctx.token);
- expect(logs.code).toBe(200);
- const records = (logs.data && logs.data.records) || [];
- expect(Array.isArray(records)).toBeTruthy();
- expect(records.some((r) => String(r.sourceId) === String(taskId))).toBeTruthy();
- });
- test('[场景2b] 能量发放缺参 - 期望返回错误提示', async ({ request }) => {
- const r = await apiPost(request, '/api/energy/grant', { memberId: selfMemberId }, ctx.token);
- expect(r.code).not.toBe(200);
- expect(String(r.message)).toContain('不能为空');
- });
- // ===== 场景3:能量兑换心愿 =====
- test('[场景3] 能量兑换心愿 - 期望参数校验生效且未扣减能量', async ({ request }) => {
- // 3a) 缺参必须被拒
- const missing = await apiPost(
- request,
- '/api/energy/deduct-for-wish',
- { memberId: selfMemberId },
- ctx.token
- );
- expect(missing.code).not.toBe(200);
- expect(String(missing.message)).toContain('不能为空');
- // 3b) 心愿不存在时,接口以 code=200 + data=false 表达「未扣减」(布尔返回语义)
- const absent = await apiPost(
- request,
- '/api/energy/deduct-for-wish',
- { memberId: selfMemberId, wishId: 999999999, amount: 1 },
- ctx.token
- );
- expect(absent.code).toBe(200);
- expect(absent.data).toBe(false);
- });
- // ===== 场景4:查询能量流水 =====
- test('[场景4] 查询能量流水 - 期望返回分页记录', async ({ request }) => {
- const r = await apiPost(request, '/api/energy/logs', { memberId: selfMemberId, page: 1, size: 10 }, ctx.token);
- expect(r.code).toBe(200);
- expect(r.data).toBeTruthy();
- expect(Array.isArray(r.data.records)).toBeTruthy();
- expect(r.data).toHaveProperty('total');
- });
- // ===== 场景5:五维能量概览 =====
- test('[场景5] 获取五维能量概览 - 期望返回五维汇总数据', async ({ request }) => {
- const r = await apiPost(request, '/api/energy/overview', { memberId: selfMemberId }, ctx.token);
- expect(r.code).toBe(200);
- expect(r.data).toBeTruthy();
- // 五维:身/心/智/行/富 → body / mind / wisdom / action / wealth
- const dims = (r.data && r.data.dimensions) || [];
- expect(Array.isArray(dims)).toBeTruthy();
- const codes = dims.map((d) => d.code);
- for (const c of ['body', 'mind', 'wisdom', 'action', 'wealth']) {
- expect(codes, '五维概览应包含维度 ' + c).toContain(c);
- }
- });
- // ===== 场景6:完整能量收支流程 =====
- test('[场景6] 完整能量收支流程 - 期望全流程成功', async ({ request }) => {
- const taskId = Number(uniqueSuffix());
- const amount = 5;
- // 1) 发放
- const grant = await apiPost(
- request,
- '/api/energy/grant',
- { memberId: selfMemberId, taskId, amount, dimensionCode: DIMENSION },
- ctx.token
- );
- expect(grant.code).toBe(200);
- // 2) 流水可查
- const logs = await apiPost(request, '/api/energy/logs', { memberId: selfMemberId }, ctx.token);
- expect(logs.code).toBe(200);
- expect(logs.data.records.some((r) => String(r.sourceId) === String(taskId))).toBeTruthy();
- // 3) 概览可查
- const overview = await apiPost(request, '/api/energy/overview', { memberId: selfMemberId }, ctx.token);
- expect(overview.code).toBe(200);
- });
- });
|