import { TIMEOUTS } from '../../utils/timeouts'; import { test, expect } from '../../utils/test-setup'; import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const testUsers = JSON.parse(readFileSync(join(__dirname, '../../fixtures/test-users.json'), 'utf-8')); // 获取认证 token async function getAuthToken(request: Parameters[0]['request']): Promise { const loginResponse = await request.post('http://localhost:8080/api/v1/auth/login', { data: { username: testUsers.admin.username, password: testUsers.admin.password } }); if (!loginResponse.ok()) { console.debug('API 登录失败:', await loginResponse.text()); return null; } const loginData = await loginResponse.json(); return loginData.data?.token || loginData.token || null; } // API 调用辅助函数 - 使用 API 直接创建残疾人数据 async function createDisabledPersonViaAPI( request: Parameters[0]['request'], personData: { name: string; gender: string; idCard: string; disabilityId: string; disabilityType: string; disabilityLevel: string; idAddress: string; phone: string; province: string; city: string; } ): Promise<{ id: number; name: string } | null> { try { const token = await getAuthToken(request); if (!token) return null; const createResponse = await request.post('http://localhost:8080/api/v1/disability/createDisabledPerson', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, data: personData }); if (!createResponse.ok()) { const errorText = await createResponse.text(); console.debug('API 创建残疾人失败:', createResponse.status(), errorText); return null; } const result = await createResponse.json(); console.debug('API 创建残疾人成功:', result.name); return { id: result.id, name: result.name }; } catch (error) { console.debug('API 调用出错:', error); return null; } } // 创建测试平台 async function createPlatformViaAPI( request: Parameters[0]['request'] ): Promise<{ id: number; name: string } | null> { try { const token = await getAuthToken(request); if (!token) return null; const timestamp = Date.now(); const platformData = { platformName: `测试平台_${timestamp}`, contactPerson: '测试联系人', contactPhone: '13800138000', contactEmail: 'test@example.com' }; const createResponse = await request.post('http://localhost:8080/api/v1/platform/createPlatform', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, data: platformData }); if (!createResponse.ok()) { const errorText = await createResponse.text(); console.debug('API 创建平台失败:', createResponse.status(), errorText); return null; } const result = await createResponse.json(); console.debug('API 创建平台成功:', result.id, result.platformName); return { id: result.id, name: result.platformName }; } catch (error) { console.debug('创建平台 API 调用出错:', error); return null; } } // 创建测试公司 async function createCompanyViaAPI( request: Parameters[0]['request'], platformId: number ): Promise<{ id: number; name: string } | null> { try { const token = await getAuthToken(request); if (!token) return null; const timestamp = Date.now(); const companyName = `测试公司_${timestamp}`; const companyData = { companyName: companyName, platformId: platformId, contactPerson: '测试联系人', contactPhone: '13900139000', contactEmail: 'company@example.com' }; const createResponse = await request.post('http://localhost:8080/api/v1/company/createCompany', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, data: companyData }); if (!createResponse.ok()) { const errorText = await createResponse.text(); console.debug('API 创建公司失败:', createResponse.status(), errorText); return null; } const createResult = await createResponse.json(); if (!createResult.success) { console.debug('API 创建公司返回 success=false'); return null; } // 创建成功后,通过平台ID查询公司列表来获取公司ID const listResponse = await request.get(`http://localhost:8080/api/v1/company/getCompaniesByPlatform/${platformId}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!listResponse.ok()) { console.debug('API 获取公司列表失败'); return null; } const companies = await listResponse.json(); const createdCompany = companies.find((c: { companyName: string }) => c.companyName === companyName); if (createdCompany) { console.debug('API 创建公司成功:', createdCompany.id, createdCompany.companyName); return { id: createdCompany.id, name: createdCompany.companyName }; } console.debug('未找到创建的公司'); return null; } catch (error) { console.debug('创建公司 API 调用出错:', error); return null; } } async function selectDisabledPersonInAddDialog( page: Parameters[0]['page'], personName?: string ): Promise { const selectPersonButton = page.getByRole('button', { name: '选择残疾人' }); await selectPersonButton.click(); // 使用唯一的 test ID 精确定位残疾人选择对话框 const dialog = page.getByTestId('disabled-person-selector-dialog'); if (personName) { // 查找包含指定姓名的残疾人行 console.debug('选择残疾人:', personName); const personRow = dialog.locator('tbody tr').filter({ hasText: personName }); const rowCount = await personRow.count(); if (rowCount === 0) { console.debug('未找到残疾人:', personName); // 关闭对话框 const cancelButton = dialog.getByRole('button', { name: '取消' }).first(); await cancelButton.click().catch(() => {}); return false; } // 点击该行的复选框 const checkbox = personRow.locator('input[type="checkbox"]').first(); await checkbox.check(); console.debug('已选择残疾人:', personName); } else { // 测试环境:如果未指定姓名,组件会自动选中第一个残疾人 console.debug('未指定残疾人姓名,等待自动选择第一个残疾人...'); } // 点击确认按钮 const confirmButton = dialog.getByRole('button', { name: /^(确认|确定)$/ }); const buttonCount = await confirmButton.count(); if (buttonCount > 0) { await confirmButton.first().click(); console.debug('已点击确认按钮'); } // 等待对话框关闭 await dialog.waitFor({ state: 'hidden', timeout: TIMEOUTS.TABLE_LOAD }); console.debug('残疾人选择器对话框已关闭'); // 等待一下让状态同步 await page.waitForTimeout(TIMEOUTS.MEDIUM); return true; } // 全局计数器,确保每个测试生成唯一的数据 let testDataCounter = 0; // 生成随机身份证号,确保唯一性 function generateUniqueIdCard(): string { // 地区码(6位) const areaCode = '110101'; // 出生日期(8位)- 使用 1980-01-01 到 2005-12-31 之间的随机日期 const startYear = 1980; const endYear = 2005; const year = startYear + Math.floor(Math.random() * (endYear - startYear + 1)); const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0'); const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0'); const birthDate = `${year}${month}${day}`; // 顺序码(3位)- 使用时间戳后3位 const sequence = String(Date.now()).slice(-3).padStart(3, '0'); // 校验码(1位)- 随机数字 const checkCode = String(Math.floor(Math.random() * 10)); return areaCode + birthDate + sequence + checkCode; } function generateUniqueTestData() { const timestamp = Date.now(); const counter = ++testDataCounter; const random = Math.floor(Math.random() * 10000); const idCard = generateUniqueIdCard(); // 生成动态日期(当前日期 + 7 天) const today = new Date(); const futureDate = new Date(today); futureDate.setDate(today.getDate() + 7); const hireDate = futureDate.toISOString().split('T')[0]; // 格式: YYYY-MM-DD // 生成18位身份证号 return { orderName: '测试订单_' + timestamp + '_' + counter + '_' + random, personName: '测试残疾人_' + timestamp + '_' + counter + '_' + random, personName2: '测试残疾人2_' + timestamp + '_' + counter + '_' + random, idCard, idCard2: generateUniqueIdCard(), phone: '138' + String(counter).padStart(4, '0') + String(random).padStart(4, '0'), phone2: '139' + String(counter).padStart(4, '0') + String(random).padStart(4, '0'), gender: '男', disabilityType: '视力残疾', disabilityLevel: '一级', disabilityId: '残疾证' + String(timestamp).slice(-6) + String(random).padStart(4, '0'), idAddress: '北京市东城区测试地址' + timestamp + '_' + counter, province: '北京市', city: '北京市', hireDate, salary: 5000, platformName: `测试平台_${timestamp}_${counter}_${random}`, companyName: `测试公司_${timestamp}_${counter}_${random}`, channelName: `测试渠道_${timestamp}_${counter}_${random}`, }; } // 等待订单行出现在表格中 async function waitForOrderRow(page: Parameters[0]['page'], orderName: string, timeout = 15000) { const startTime = Date.now(); while (Date.now() - startTime < timeout) { const table = page.locator('table'); const orderRow = table.locator('tbody tr').filter({ hasText: orderName }); const count = await orderRow.count(); if (count > 0) { console.debug('找到订单行:', orderName); return true; } await page.waitForTimeout(TIMEOUTS.MEDIUM); } console.debug('等待订单行超时:', orderName); return false; } test.describe('订单完整流程测试', () => { test.describe('新增订单完整流程', () => { test('应该能完成新增订单的完整流程:创建订单 → 添加人员 → 添加附件 → 激活订单', async ({ adminLoginPage, orderManagementPage, request, page }) => { // 登录 await adminLoginPage.goto(); await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password); await adminLoginPage.expectLoginSuccess(); await orderManagementPage.goto(); // 使用 API 创建平台和公司测试数据 const createdPlatform = await createPlatformViaAPI(request); if (!createdPlatform) { test.skip(true, '无法创建平台数据'); return; } const createdCompany = await createCompanyViaAPI(request, createdPlatform.id); if (!createdCompany) { test.skip(true, '无法创建公司数据'); return; } // 生成唯一测试数据 const testData = generateUniqueTestData(); // 使用 API 创建残疾人测试数据 const personData = { name: testData.personName, gender: testData.gender, idCard: testData.idCard, disabilityId: testData.disabilityId, disabilityType: testData.disabilityType, disabilityLevel: testData.disabilityLevel, idAddress: testData.idAddress, phone: testData.phone, province: testData.province, city: testData.city, }; const createdPerson = await createDisabledPersonViaAPI(request, personData); if (!createdPerson) { test.skip(true, '无法创建残疾人数据'); return; } console.debug('已创建残疾人:', createdPerson.name, 'ID:', createdPerson.id); // 步骤 1: 创建订单(填写所有字段) await orderManagementPage.openCreateDialog(); await page.getByLabel(/订单名称|名称/).fill(testData.orderName); // 选择平台(使用 API 创建的平台名称) const platformTrigger = page.locator('[data-testid="platform-selector-create"]'); if (await platformTrigger.count() > 0) { await platformTrigger.click(); await page.waitForTimeout(TIMEOUTS.MEDIUM_LONG); const platformOption = page.getByRole('option', { name: createdPlatform.name }); const platformCount = await platformOption.count(); if (platformCount > 0) { await platformOption.click(); } await page.waitForTimeout(TIMEOUTS.VERY_SHORT); } // 选择公司(使用 API 创建的公司名称) const companyTrigger = page.locator('[data-testid="company-selector-create"]'); if (await companyTrigger.count() > 0) { await companyTrigger.click(); await page.waitForTimeout(TIMEOUTS.MEDIUM_LONG); const companyOption = page.getByRole('option', { name: createdCompany.name }); const companyCount = await companyOption.count(); if (companyCount > 0) { await companyOption.click(); } await page.waitForTimeout(TIMEOUTS.VERY_SHORT); } // 填写预计开始日期 await page.getByLabel(/预计开始日期|开始日期/).fill(testData.hireDate); // 选择残疾人 const hasPerson = await selectDisabledPersonInAddDialog(page, createdPerson.name); if (!hasPerson) { await orderManagementPage.cancelDialog(); test.skip(true, '没有可用的残疾人数据'); return; } // 提交订单 await page.waitForTimeout(TIMEOUTS.VERY_LONG); await orderManagementPage.submitForm(); await orderManagementPage.waitForDialogClosed(); // 验证订单创建成功 await page.waitForTimeout(TIMEOUTS.LONG); const successToast = page.locator('[data-sonner-toast][data-type="success"]'); const hasSuccess = await successToast.count() > 0; expect(hasSuccess).toBe(true); // 等待订单行出现在表格中 const orderFound = await waitForOrderRow(page, testData.orderName); expect(orderFound).toBe(true); // 步骤 2: 打开订单详情并验证人员已添加 await orderManagementPage.openDetailDialog(testData.orderName); // 获取人员列表 const personList = await orderManagementPage.getPersonListFromDetail(); console.debug('订单人员列表:', personList); expect(personList.length).toBeGreaterThan(0); // 步骤 3: 为人员添加附件 try { await orderManagementPage.openAddAttachmentDialog(); const fileName = 'images/photo.jpg'; await orderManagementPage.uploadAttachment(personList[0].name || createdPerson.name, fileName, 'image/jpeg', '其他'); await page.waitForTimeout(TIMEOUTS.LONG); await orderManagementPage.closeUploadDialog(); // 验证附件上传成功 const attachmentList = await orderManagementPage.getAttachmentListFromDetail(); console.debug('附件列表:', attachmentList); } catch (error) { console.debug('附件上传出错(非关键功能,继续测试):', error); // 确保对话框关闭 await page.keyboard.press('Escape').catch(() => {}); await page.waitForTimeout(TIMEOUTS.SHORT); } // 关闭订单详情对话框 await orderManagementPage.closeDetailDialog(); // 步骤 4: 激活订单(草稿 → 进行中) const activated = await orderManagementPage.activateOrder(testData.orderName); expect(activated).toBe(true); // 步骤 5: 验证订单状态(激活后变为已确认) await orderManagementPage.expectOrderStatus(testData.orderName, 'confirmed'); console.debug('新增订单完整流程测试通过'); }); }); test.describe('编辑订单完整流程', () => { test('应该能完成编辑订单的完整流程:打开订单 → 修改信息 → 添加人员 → 关闭订单', async ({ adminLoginPage, orderManagementPage, request, page }) => { // 登录 await adminLoginPage.goto(); await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password); await adminLoginPage.expectLoginSuccess(); await orderManagementPage.goto(); // 使用 API 创建平台和公司测试数据 const createdPlatform = await createPlatformViaAPI(request); if (!createdPlatform) { test.skip(true, '无法创建平台数据'); return; } const createdCompany = await createCompanyViaAPI(request, createdPlatform.id); if (!createdCompany) { test.skip(true, '无法创建公司数据'); return; } // 生成唯一测试数据 const testData = generateUniqueTestData(); // 使用 API 创建两个残疾人测试数据 const personData = { name: testData.personName, gender: testData.gender, idCard: testData.idCard, disabilityId: testData.disabilityId, disabilityType: testData.disabilityType, disabilityLevel: testData.disabilityLevel, idAddress: testData.idAddress, phone: testData.phone, province: testData.province, city: testData.city, }; const personData2 = { name: testData.personName2, gender: testData.gender, idCard: testData.idCard2, disabilityId: '残疾证' + String(Date.now()).slice(-6) + String(Math.floor(Math.random() * 10000)).padStart(4, '0'), disabilityType: testData.disabilityType, disabilityLevel: testData.disabilityLevel, idAddress: testData.idAddress, phone: testData.phone2, province: testData.province, city: testData.city, }; const createdPerson = await createDisabledPersonViaAPI(request, personData); if (!createdPerson) { test.skip(true, '无法创建残疾人数据'); return; } console.debug('已创建残疾人1:', createdPerson.name, 'ID:', createdPerson.id); const createdPerson2 = await createDisabledPersonViaAPI(request, personData2); if (!createdPerson2) { test.skip(true, '无法创建残疾人数据2'); return; } console.debug('已创建残疾人2:', createdPerson2.name, 'ID:', createdPerson2.id); // 步骤 1: 创建初始订单 await orderManagementPage.openCreateDialog(); await page.getByLabel(/订单名称|名称/).fill(testData.orderName); // 选择平台(使用 API 创建的平台名称) const platformTrigger = page.locator('[data-testid="platform-selector-create"]'); if (await platformTrigger.count() > 0) { await platformTrigger.click(); await page.waitForTimeout(TIMEOUTS.MEDIUM_LONG); const platformOption = page.getByRole('option', { name: createdPlatform.name }); const platformCount = await platformOption.count(); if (platformCount > 0) { await platformOption.click(); } await page.waitForTimeout(TIMEOUTS.VERY_SHORT); } // 选择公司(使用 API 创建的公司名称) const companyTrigger = page.locator('[data-testid="company-selector-create"]'); if (await companyTrigger.count() > 0) { await companyTrigger.click(); await page.waitForTimeout(TIMEOUTS.MEDIUM_LONG); const companyOption = page.getByRole('option', { name: createdCompany.name }); const companyCount = await companyOption.count(); if (companyCount > 0) { await companyOption.click(); } await page.waitForTimeout(TIMEOUTS.VERY_SHORT); } await page.getByLabel(/预计开始日期|开始日期/).fill(testData.hireDate); const hasPerson = await selectDisabledPersonInAddDialog(page, createdPerson.name); if (!hasPerson) { await orderManagementPage.cancelDialog(); test.skip(true, '没有可用的残疾人数据'); return; } await page.waitForTimeout(TIMEOUTS.VERY_LONG); await orderManagementPage.submitForm(); await orderManagementPage.waitForDialogClosed(); // 验证订单创建成功 await page.waitForTimeout(TIMEOUTS.LONG); const successToast = page.locator('[data-sonner-toast][data-type="success"]'); const hasSuccess = await successToast.count() > 0; expect(hasSuccess).toBe(true); const orderFound = await waitForOrderRow(page, testData.orderName); expect(orderFound).toBe(true); // 步骤 2: 修改订单信息 const newOrderName = testData.orderName + '_已修改'; const editResult = await orderManagementPage.editOrder(testData.orderName, { name: newOrderName, }); // 验证编辑成功 expect(editResult.hasSuccess).toBe(true); // 步骤 2.5: 验证订单名称已更新 const editedOrderFound = await waitForOrderRow(page, newOrderName); expect(editedOrderFound).toBe(true); console.debug('订单名称已更新为:', newOrderName); // 步骤 3: 激活订单(使其可以进行状态流转) const activated = await orderManagementPage.activateOrder(newOrderName); expect(activated).toBe(true); // 步骤 4: 打开订单详情对话框(使用新的订单名称) await orderManagementPage.openDetailDialog(newOrderName); // 步骤 3.5: 确认待添加的人员(创建订单时选择的人员在待添加列表中) // 检查是否有待添加人员 const confirmAddButton = page.getByTestId('confirm-add-persons-button'); const confirmButtonCount = await confirmAddButton.count(); if (confirmButtonCount > 0) { console.debug('发现待添加人员,点击确认添加按钮'); await confirmAddButton.click(); // 等待成功 toast await page.waitForTimeout(TIMEOUTS.LONG); const addSuccessToast = page.locator('[data-sonner-toast][data-type="success"]'); const hasAddSuccess = await addSuccessToast.count() > 0; if (hasAddSuccess) { console.debug('待添加人员已确认添加'); } } // 验证人员列表包含已确认的人员 const personListAfter = await orderManagementPage.getPersonListFromDetail(); console.debug('人员列表:', personListAfter); expect(personListAfter.length).toBeGreaterThan(0); // 至少有1个人 // 步骤 4: 关闭订单(已确认 → 已完成) await orderManagementPage.closeDetailDialog(); const closed = await orderManagementPage.closeOrder(newOrderName); expect(closed).toBe(true); // 步骤 6: 验证订单状态为已完成 await orderManagementPage.expectOrderStatus(newOrderName, 'completed'); console.debug('编辑订单完整流程测试通过'); }); }); });