/** * 删除订单 E2E 测试 * * 测试范围: * - 删除草稿状态订单 * - 删除有关联人员的订单 * - 取消删除操作 * - 删除后列表更新验证 * - Toast 消息验证 * * @packageDocumentation */ import { TIMEOUTS } from '../../utils/timeouts'; import { test, expect, Page } from '../../utils/test-setup'; /** * 辅助函数:获取订单列表中第一个订单的名称 * * @param page - Playwright Page 对象 * @returns 第一个订单的名称,如果没有则返回 null */ async function getFirstOrderName(page: Page): Promise { const table = page.locator('table tbody tr'); // 等待表格数据加载完成(跳过"加载中"等占位符文本) await page.waitForTimeout(TIMEOUTS.LONG); const count = await table.count(); if (count === 0) { return null; } // 查找第一个有效的订单行(排除"加载中"等占位符) for (let i = 0; i < count; i++) { const row = table.nth(i); const nameCell = row.locator('td').first(); const name = await nameCell.textContent(); const trimmedName = name?.trim() || ''; // 跳过占位符文本 if (trimmedName && trimmedName !== '加载中...' && trimmedName !== '暂无数据' && !trimmedName.includes('加载')) { return trimmedName; } } return null; } /** * 辅助函数:在创建订单对话框中选择残疾人 * * @param page - Playwright Page 对象 * @returns 是否成功选择了残疾人 */ async function selectDisabledPersonForOrder(page: Page): Promise { const selectPersonButton = page.getByRole('button', { name: '选择残疾人' }); await selectPersonButton.click(); await page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG }); let hasData = false; try { const firstCheckbox = page.locator('table tbody tr').first().locator('input[type="checkbox"]').first(); await firstCheckbox.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT }); await firstCheckbox.check(); console.debug('✓ 已选择第一个残疾人'); hasData = true; } catch (error) { console.debug('没有可用的残疾人数据'); hasData = false; } if (hasData) { const confirmButton = page.getByRole('button', { name: /^(确定|确认|选择)$/ }); await confirmButton.click().catch(() => { console.debug('没有找到确认按钮,尝试关闭对话框'); page.keyboard.press('Escape'); }); } else { await page.keyboard.press('Escape').catch(() => { console.debug('无法关闭对话框,可能已经自动关闭'); }); } await page.waitForTimeout(TIMEOUTS.MEDIUM); return hasData; } /** * 辅助函数:创建测试订单 * * @param page - Playwright Page 对象 * @param orderName - 订单名称 * @returns 是否成功创建 */ async function createTestOrder(page: Page, orderName: string): Promise { // 打开创建对话框 const addOrderButton = page.getByTestId('create-order-button'); await addOrderButton.click(); await page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG }); // 填写必填字段 await page.getByLabel(/订单名称|名称/).fill(orderName); await page.getByLabel(/预计开始日期|开始日期/).fill('2025-01-15'); // 选择残疾人(必填) const hasDisabledPerson = await selectDisabledPersonForOrder(page); if (!hasDisabledPerson) { await page.keyboard.press('Escape'); return false; } // 提交表单 const submitButton = page.getByRole('button', { name: /^(创建|更新|保存)$/ }); await submitButton.click(); // 等待网络请求完成 try { await page.waitForLoadState('networkidle', { timeout: TIMEOUTS.DIALOG }); } catch { console.debug('networkidle 超时,继续检查 Toast 消息'); } await page.waitForTimeout(TIMEOUTS.VERY_LONG); // 检查成功消息 const successToast = page.locator('[data-sonner-toast][data-type="success"]'); const hasSuccess = await successToast.count() > 0; // 等待对话框关闭 const dialog = page.locator('[role="dialog"]'); await dialog.waitFor({ state: 'hidden', timeout: TIMEOUTS.DIALOG }).catch(() => { console.debug('对话框关闭超时,可能已经关闭'); }); return hasSuccess; } test.describe('删除订单测试', () => { let testOrderName: string; test.beforeEach(async ({ adminLoginPage, orderManagementPage, testUsers }) => { // 以管理员身份登录后台 await adminLoginPage.goto(); await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password); await adminLoginPage.expectLoginSuccess(); await orderManagementPage.goto(); // 尝试使用现有订单 const existingOrder = await getFirstOrderName(orderManagementPage.page); if (existingOrder) { testOrderName = existingOrder; console.debug(`✓ 使用现有订单: ${testOrderName}`); } else { // 如果没有现有订单,创建一个 const timestamp = Date.now(); testOrderName = `删除测试_${timestamp}`; const created = await createTestOrder(orderManagementPage.page, testOrderName); if (!created) { // 没有残疾人数据时,跳过测试 test.skip(true, '没有残疾人数据,无法创建测试订单'); } // 验证订单出现在列表中 await expect(async () => { const exists = await orderManagementPage.orderExists(testOrderName); expect(exists).toBe(true); }).toPass({ timeout: TIMEOUTS.DIALOG }); } }); test.describe('删除草稿状态订单', () => { test('应该成功删除草稿订单', async ({ orderManagementPage }) => { // 打开删除确认对话框 await orderManagementPage.openDeleteDialog(testOrderName); // 确认删除 await orderManagementPage.confirmDelete(); // 验证订单不再存在 await expect(async () => { const exists = await orderManagementPage.orderExists(testOrderName); expect(exists).toBe(false); }).toPass({ timeout: TIMEOUTS.DIALOG }); }); test('应该在删除后显示成功提示', async ({ orderManagementPage }) => { await orderManagementPage.deleteOrder(testOrderName); // 验证 Toast 成功消息 - 检查更具体的消息内容 const successToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="success"]'); await expect(successToast).toBeVisible(); const message = await successToast.textContent(); expect(message).toBeDefined(); expect(message?.length).toBeGreaterThan(0); // 验证消息包含删除相关的关键词 expect(message).toMatch(/删除|成功|已删除/); // 验证订单被删除 const exists = await orderManagementPage.orderExists(testOrderName); expect(exists).toBe(false); }); test('删除确认对话框应该正确显示', async ({ orderManagementPage, page }) => { await orderManagementPage.openDeleteDialog(testOrderName); // 验证对话框可见 const dialog = page.locator('[role="alertdialog"]'); await expect(dialog).toBeVisible(); // 验证确认按钮存在(支持多种可能的按钮名称) const confirmButton = page.locator('[role="alertdialog"]').getByRole('button', { name: /^(确认删除|删除|确定|确认)$/ }); await expect(confirmButton).toBeVisible(); // 验证取消按钮存在 const cancelButton = page.locator('[role="alertdialog"]').getByRole('button', { name: '取消' }); await expect(cancelButton).toBeVisible(); // 取消删除以清理 await orderManagementPage.cancelDelete(); }); }); test.describe('取消删除', () => { test('应该能在确认对话框中取消删除', async ({ orderManagementPage }) => { // 打开删除确认对话框 await orderManagementPage.openDeleteDialog(testOrderName); // 取消删除 await orderManagementPage.cancelDelete(); // 验证订单仍然存在 const exists = await orderManagementPage.orderExists(testOrderName); expect(exists).toBe(true); }); test('取消删除后订单应该保持不变', async ({ orderManagementPage, page }) => { // 获取删除前的订单行(用于后续验证) const orderRowBefore = page.locator('table tbody tr').filter({ hasText: testOrderName }); await expect(orderRowBefore).toBeVisible(); // 打开删除确认对话框 await orderManagementPage.openDeleteDialog(testOrderName); // 取消删除 await orderManagementPage.cancelDelete(); // 等待对话框关闭 await page.waitForTimeout(TIMEOUTS.MEDIUM); // 验证订单仍然在列表中 const orderRowAfter = page.locator('table tbody tr').filter({ hasText: testOrderName }); await expect(orderRowAfter).toBeVisible(); }); test('应该能通过关闭对话框取消删除', async ({ orderManagementPage, page }) => { // 打开删除确认对话框 await orderManagementPage.openDeleteDialog(testOrderName); // 按 Escape 键关闭对话框 await page.keyboard.press('Escape'); // 等待对话框关闭 const dialog = page.locator('[role="alertdialog"]'); await dialog.waitFor({ state: 'hidden', timeout: TIMEOUTS.DIALOG }).catch(() => { console.debug('对话框关闭超时,可能已经关闭'); }); // 验证订单仍然存在 const exists = await orderManagementPage.orderExists(testOrderName); expect(exists).toBe(true); }); }); test.describe('删除有关联人员的订单', () => { let orderWithPersonName: string; test.beforeEach(async ({ orderManagementPage }) => { // 创建专用于此测试套件的订单(确保测试隔离) const timestamp = Date.now(); orderWithPersonName = `删除测试_人员_${timestamp}`; const created = await createTestOrder(orderManagementPage.page, orderWithPersonName); if (!created) { test.skip(true, '无法创建测试订单用于人员关联测试'); } // 验证订单出现在列表中 await expect(async () => { const exists = await orderManagementPage.orderExists(orderWithPersonName); expect(exists).toBe(true); }).toPass({ timeout: TIMEOUTS.DIALOG }); // 打开人员管理对话框并添加人员 await orderManagementPage.openPersonManagementDialog(orderWithPersonName); // 检查是否已经有人员(如果有则使用现有人员,否则尝试添加) const personTable = orderManagementPage.page.locator('[role="dialog"]').locator('table tbody tr'); const personCount = await personTable.count(); if (personCount === 0) { // 尝试添加人员 try { await orderManagementPage.addPersonToOrder({ disabledPersonName: '测试', // 使用名称而不是硬编码 ID }); console.debug('✓ 已添加人员到订单'); } catch (error) { console.debug('添加人员失败,可能没有可用数据:', error); } } else { console.debug(`✓ 订单已有 ${personCount} 个关联人员`); } // 关闭人员管理对话框 await orderManagementPage.page.keyboard.press('Escape'); await orderManagementPage.page.waitForTimeout(TIMEOUTS.MEDIUM); }); test('应该能删除有人员的订单(级联删除)', async ({ orderManagementPage }) => { // 尝试删除有关联人员的订单 await orderManagementPage.deleteOrder(orderWithPersonName); // 验证结果 - 根据实际业务逻辑,可能成功或失败 const successToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="success"]'); const errorToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="error"]'); const hasSuccess = await successToast.count() > 0; const hasError = await errorToast.count() > 0; if (hasSuccess) { // 级联删除成功 console.debug('✓ 订单及其关联人员已被删除'); const exists = await orderManagementPage.orderExists(orderWithPersonName); expect(exists).toBe(false); } else if (hasError) { // 禁止删除,显示错误消息 const errorMessage = await errorToast.textContent(); console.debug('删除失败消息:', errorMessage); expect(errorMessage).toBeDefined(); // 订单应该仍然存在 const exists = await orderManagementPage.orderExists(orderWithPersonName); expect(exists).toBe(true); } else { // 如果没有明确的成功或失败消息,检查订单状态 const exists = await orderManagementPage.orderExists(orderWithPersonName); console.debug(`删除后订单状态: ${exists ? '仍存在' : '已删除'}`); } }); test('删除失败应该显示错误消息', async ({ orderManagementPage }) => { // 打开删除对话框 await orderManagementPage.openDeleteDialog(orderWithPersonName); // 确认删除 await orderManagementPage.confirmDelete(); // 等待响应 await orderManagementPage.page.waitForTimeout(TIMEOUTS.VERY_LONG); // 检查结果 const successToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="success"]'); const errorToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="error"]'); const hasSuccess = await successToast.count() > 0; const hasError = await errorToast.count() > 0; // 根据业务逻辑验证结果 if (hasError) { const errorMessage = await errorToast.textContent(); console.debug('预期错误消息:', errorMessage); expect(errorMessage).toBeDefined(); expect(errorMessage?.length).toBeGreaterThan(0); } else if (!hasSuccess) { // 如果没有明确的 Toast 消息,检查其他形式的反馈 console.debug('没有检测到 Toast 消息,检查其他反馈形式'); } // 清理:如果删除成功,则不需要操作;如果删除失败,订单仍在 const exists = await orderManagementPage.orderExists(orderWithPersonName); if (exists) { console.debug('订单仍然存在,业务规则可能禁止删除有人员的订单'); } }); }); test.describe('删除后列表更新验证', () => { test('删除后列表应该不再显示该订单', async ({ orderManagementPage }) => { // 验证订单在删除前存在 const existsBefore = await orderManagementPage.orderExists(testOrderName); expect(existsBefore).toBe(true); // 执行删除 await orderManagementPage.deleteOrder(testOrderName); // 验证订单在删除后不存在 await expect(async () => { const existsAfter = await orderManagementPage.orderExists(testOrderName); expect(existsAfter).toBe(false); }).toPass({ timeout: TIMEOUTS.DIALOG }); }); test('删除后列表应该正确更新', async ({ orderManagementPage, page }) => { // 获取删除前的行数 const tableBody = page.locator('table tbody'); const rowsBefore = await tableBody.locator('tr').count(); // 执行删除 await orderManagementPage.deleteOrder(testOrderName); // 等待列表更新 await page.waitForTimeout(TIMEOUTS.LONG); // 验证行数减少 const rowsAfter = await tableBody.locator('tr').count(); expect(rowsAfter).toBe(rowsBefore - 1); }); }); test.describe('Toast 消息验证', () => { test('成功删除应该显示正确的成功消息', async ({ orderManagementPage }) => { await orderManagementPage.deleteOrder(testOrderName); const successToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="success"]'); await expect(successToast).toBeVisible(); const message = await successToast.textContent(); expect(message).toBeDefined(); expect(message?.length).toBeGreaterThan(0); // 验证消息包含删除相关的关键词 expect(message).toMatch(/删除|成功|已删除/); console.debug('删除成功消息:', message); }); test('Toast 消息应该自动消失', async ({ orderManagementPage }) => { await orderManagementPage.deleteOrder(testOrderName); const successToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="success"]'); // 等待 Toast 消息消失 - 使用更合理的超时时间 await successToast.waitFor({ state: 'hidden', timeout: TIMEOUTS.TOAST_LONG }).catch(() => { console.debug('Toast 消息可能在 6 秒内未消失'); }); // 验证消息不再可见 const isVisible = await successToast.isVisible().catch(() => false); expect(isVisible).toBe(false); }); }); });