/** * 【API级】联系人管理全流程 * ================================================================ * 对应 DOM 版:e2e/contact-management.spec.js(远程无 SPA,DOM 版不可跑) * 接口契约来源:ContactController(/api/contact/*) * list : {} 联系人列表 * create : Contact 实体 { name, phone?, relationshipType?, tags?, notes?, ... } * update : Contact 实体 { id, ... } * delete : { id } * detail : { id } * import-phone: { name, phone, extra? } * interaction : { id } 记录互动 * relationshipType 合法值:family / friend / partner / colleague / other * 运行:npx playwright test -c playwright.config.api.js --project=desktop */ const { test, expect } = require('@playwright/test'); const { registerRole, apiPost, uniqueSuffix } = require('../helpers/api'); test.describe('【API级】联系人管理全流程', () => { let ctx; test.beforeAll(async ({ request }) => { ctx = await registerRole(request, 'parent', 'QA-CONTACT'); }); /** 创建一个联系人,返回 { id, name } */ async function createContact(request, tag, extra) { const name = 'QA_' + tag + '_' + uniqueSuffix(); const payload = Object.assign( { name, relationshipType: 'friend', contactSource: 'manual', notes: 'QA' }, extra || {} ); const r = await apiPost(request, '/api/contact/create', payload, ctx.token); expect(r.code, '创建联系人应成功').toBe(200); expect(r.data && r.data.id, '应返回联系人ID').toBeTruthy(); return { id: r.data.id, name: r.data.name || name }; } // ===== 场景1:创建联系人 ===== test('[场景1] 创建联系人 - 期望创建成功', async ({ request }) => { const c = await createContact(request, 'CREATE'); const list = await apiPost(request, '/api/contact/list', {}, ctx.token); expect(list.code).toBe(200); expect(Array.isArray(list.data)).toBeTruthy(); expect(list.data.some((x) => String(x.id) === String(c.id))).toBeTruthy(); }); // ===== 场景2:通讯录导入 ===== test('[场景2] 通讯录导入 - 期望导入成功', async ({ request }) => { const name = 'QA_IMPORT_' + uniqueSuffix(); const phone = '13' + String(Math.floor(Math.random() * 900000000) + 100000000); const r = await apiPost(request, '/api/contact/import-phone', { name, phone }, ctx.token); expect(r.code).toBe(200); expect(r.data).toBeTruthy(); const list = await apiPost(request, '/api/contact/list', {}, ctx.token); expect(list.data.some((x) => x.name === name)).toBeTruthy(); }); // ===== 场景3:记录互动 ===== test('[场景3] 记录互动 - 期望记录成功', async ({ request }) => { const c = await createContact(request, 'INTERACT'); const r = await apiPost(request, '/api/contact/interaction', { id: c.id }, ctx.token); expect(r.code).toBe(200); expect(r.data).toBeTruthy(); const detail = await apiPost(request, '/api/contact/detail', { id: c.id }, ctx.token); expect(detail.code).toBe(200); expect(detail.data).toBeTruthy(); }); // ===== 场景4:编辑/删除联系人 ===== test('[场景4] 编辑联系人 - 期望更新成功', async ({ request }) => { const c = await createContact(request, 'UPDATE'); const newName = 'QA_UPDATED_' + uniqueSuffix(); const r = await apiPost(request, '/api/contact/update', { id: c.id, name: newName }, ctx.token); expect(r.code).toBe(200); const detail = await apiPost(request, '/api/contact/detail', { id: c.id }, ctx.token); expect(detail.code).toBe(200); expect(detail.data.name).toBe(newName); }); test('[场景4b] 删除联系人 - 期望从列表中移除', async ({ request }) => { const c = await createContact(request, 'DELETE'); const before = await apiPost(request, '/api/contact/list', {}, ctx.token); const beforeCount = before.data.length; const r = await apiPost(request, '/api/contact/delete', { id: c.id }, ctx.token); expect(r.code).toBe(200); const after = await apiPost(request, '/api/contact/list', {}, ctx.token); expect(after.data.length).toBeLessThan(beforeCount); expect(after.data.some((x) => String(x.id) === String(c.id))).toBeFalsy(); }); // ===== 场景5:权限边界 ===== test('[场景5b] 越权操作他人联系人 - 期望被拒绝', async ({ request }) => { const c = await createContact(request, 'AUTH'); const other = await registerRole(request, 'parent', 'QA-OTHER'); const r = await apiPost(request, '/api/contact/delete', { id: c.id }, other.token); expect(r.code).not.toBe(200); }); });