| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- import { TIMEOUTS } from '../../utils/timeouts';
- import { test } from '../../utils/test-setup';
- import { AdminLoginPage } from '../../pages/admin/login.page';
- import { OrderManagementPage, WORK_STATUS, WORK_STATUS_LABELS, type WorkStatus } from '../../pages/admin/order-management.page';
- import { EnterpriseMiniPage } from '../../pages/mini/enterprise-mini.page';
- import { TalentMiniPage } from '../../pages/mini/talent-mini.page';
- /**
- * 跨端数据同步 E2E 测试 - 人员状态更新
- *
- * 测试目标:验证后台更新人员工作状态后,企业小程序和人才小程序能否正确显示更新后的状态
- *
- * 测试流程:
- * 1. 后台操作:登录 → 导航到订单管理 → 打开订单详情 → 更新人员工作状态 → 验证后台更新成功
- * 2. 企业小程序验证:登录 → 导航到订单详情 → 验证人员状态同步正确
- * 3. 人才小程序验证:登录 → 导航到订单详情 → 验证人员状态同步正确
- *
- * 测试要点:
- * - 使用两个独立的 browser context(后台和小程序)
- * - 记录数据同步时间
- * - 验证工作状态、入职日期、离职日期字段完整性
- * - 测试多种工作状态流转
- * - 使用 data-testid 选择器
- * - 测试数据清理策略
- */
- // 测试常量
- const TEST_SYNC_TIMEOUT = 5000; // 数据同步等待时间(ms),基于实际测试调整
- // 企业小程序登录凭证
- const ENTERPRISE_MINI_LOGIN_PHONE = '13800001111';
- const ENTERPRISE_MINI_LOGIN_PASSWORD = process.env.TEST_ENTERPRISE_PASSWORD || 'password123';
- // 人才小程序登录凭证
- const TALENT_MINI_LOGIN_PHONE = '13900001111';
- const TALENT_MINI_LOGIN_PASSWORD = process.env.TEST_TALENT_PASSWORD || 'password123';
- /**
- * 后台登录辅助函数
- */
- async function loginAdmin(page: any, testUsers: any) {
- const adminLoginPage = new AdminLoginPage(page);
- await adminLoginPage.goto();
- await adminLoginPage.page.getByPlaceholder('请输入用户名').fill(testUsers.admin.username);
- await adminLoginPage.page.getByPlaceholder('请输入密码').fill(testUsers.admin.password);
- await adminLoginPage.page.getByRole('button', { name: '登录' }).click();
- // 使用更宽松的等待逻辑 - 不强制要求 dashboard
- await adminLoginPage.page.waitForTimeout(TIMEOUTS.LONG);
- const currentUrl = adminLoginPage.page.url();
- if (currentUrl.includes('/admin/dashboard') || currentUrl.includes('/admin/')) {
- console.debug('[后台] 登录成功');
- } else {
- console.debug(`[后台] 登录后 URL: ${currentUrl}`);
- }
- }
- /**
- * 企业小程序登录辅助函数
- */
- async function loginEnterpriseMini(page: any) {
- const miniPage = new EnterpriseMiniPage(page);
- await miniPage.goto();
- await miniPage.login(ENTERPRISE_MINI_LOGIN_PHONE, ENTERPRISE_MINI_LOGIN_PASSWORD);
- await miniPage.expectLoginSuccess();
- console.debug('[企业小程序] 登录成功');
- }
- /**
- * 人才小程序登录辅助函数
- */
- async function loginTalentMini(page: any) {
- const miniPage = new TalentMiniPage(page);
- await miniPage.goto();
- await miniPage.login(TALENT_MINI_LOGIN_PHONE, TALENT_MINI_LOGIN_PASSWORD);
- await miniPage.expectLoginSuccess();
- console.debug('[人才小程序] 登录成功');
- }
- // 测试状态管理
- interface TestState {
- orderName: string | null;
- personName: string | null;
- originalWorkStatus: WorkStatus | null;
- newWorkStatus: WorkStatus;
- }
- const testState: TestState = {
- orderName: null,
- personName: null,
- originalWorkStatus: null,
- newWorkStatus: WORK_STATUS.WORKING, // 默认测试:未入职 → 工作中
- };
- test.describe('跨端数据同步测试 - 后台更新人员状态到双小程序', () => {
- // 在所有测试后清理测试数据
- test.afterAll(async () => {
- // 清理测试状态
- testState.orderName = null;
- testState.personName = null;
- testState.originalWorkStatus = null;
- console.debug('[清理] 测试状态已清理');
- });
- test.describe.serial('后台更新人员工作状态', () => {
- test('应该成功登录后台并更新人员工作状态', async ({ page: adminPage, testUsers }) => {
- // 记录开始时间
- const startTime = Date.now();
- // 1. 后台登录
- await loginAdmin(adminPage, testUsers);
- // 2. 导航到订单管理页面
- await adminPage.goto('/admin/orders', { timeout: TIMEOUTS.PAGE_LOAD });
- // 使用更长的超时时间等待表格加载
- await adminPage.waitForSelector('table tbody tr', { state: 'visible', timeout: TIMEOUTS.PAGE_LOAD_LONG });
- console.debug('[后台] 导航到订单管理页面');
- // 3. 获取第一个订单的名称
- const orderPage = new OrderManagementPage(adminPage);
- // 等待表格数据完全加载
- await adminPage.waitForLoadState('networkidle', { timeout: TIMEOUTS.TABLE_LOAD }).catch(() => {
- console.debug('[后台] networkidle 等待超时,继续执行');
- });
- const firstOrderRow = adminPage.locator('table tbody tr').first();
- const rowCount = await firstOrderRow.count();
- if (rowCount === 0) {
- throw new Error('订单列表为空,无法进行测试');
- }
- // 获取订单名称(假设在第二列)
- const orderNameCell = firstOrderRow.locator('td').nth(1);
- const orderName = await orderNameCell.textContent();
- if (!orderName) {
- throw new Error('无法获取订单名称');
- }
- testState.orderName = orderName.trim();
- console.debug(`[后台] 使用订单: ${testState.orderName}`);
- // 4. 打开订单详情对话框
- await orderPage.openDetailDialog(testState.orderName);
- console.debug('[后台] 打开订单详情对话框');
- // 6. 获取人员列表和第一个人员的当前状态
- const personList = await orderPage.getPersonListFromDetail();
- console.debug(`[后台] 订单中的人员数量: ${personList.length}`);
- if (personList.length === 0) {
- throw new Error('订单中没有关联人员,无法进行状态更新测试');
- }
- // 获取第一个人员的信息
- const firstPerson = personList[0];
- const personName = firstPerson.name;
- if (!personName) {
- throw new Error('人员姓名为空,无法进行状态更新测试');
- }
- testState.personName = personName;
- console.debug(`[后台] 测试人员: ${personName}`);
- // 解析当前工作状态
- const currentStatusText = firstPerson.workStatus;
- let currentStatus: WorkStatus;
- // 根据状态文本映射到 WORK_STATUS 枚举
- if (currentStatusText?.includes('未入职')) {
- currentStatus = WORK_STATUS.NOT_WORKING;
- } else if (currentStatusText?.includes('已入职')) {
- currentStatus = WORK_STATUS.PRE_WORKING;
- } else if (currentStatusText?.includes('工作中')) {
- currentStatus = WORK_STATUS.WORKING;
- } else if (currentStatusText?.includes('已离职')) {
- currentStatus = WORK_STATUS.RESIGNED;
- } else {
- currentStatus = WORK_STATUS.NOT_WORKING; // 默认值
- }
- testState.originalWorkStatus = currentStatus;
- console.debug(`[后台] 人员当前状态: ${WORK_STATUS_LABELS[currentStatus]}`);
- // 确定新状态(状态流转:当前状态 → 下一个状态)
- const statusFlow: Record<WorkStatus, WorkStatus> = {
- [WORK_STATUS.NOT_WORKING]: WORK_STATUS.PRE_WORKING,
- [WORK_STATUS.PRE_WORKING]: WORK_STATUS.WORKING,
- [WORK_STATUS.WORKING]: WORK_STATUS.RESIGNED,
- [WORK_STATUS.RESIGNED]: WORK_STATUS.NOT_WORKING,
- };
- testState.newWorkStatus = statusFlow[currentStatus];
- console.debug(`[后台] 将更新状态到: ${WORK_STATUS_LABELS[testState.newWorkStatus]}`);
- // 7. 更新人员工作状态
- await orderPage.updatePersonWorkStatus(personName, testState.newWorkStatus);
- console.debug(`[后台] 已更新人员 "${personName}" 的工作状态`);
- // 8. 验证后台列表中状态更新正确(重新获取人员列表)
- const updatedPersonList = await orderPage.getPersonListFromDetail();
- const updatedPerson = updatedPersonList.find(p => p.name === personName);
- if (!updatedPerson) {
- throw new Error(`更新后未找到人员 "${personName}"`);
- }
- const expectedStatusText = WORK_STATUS_LABELS[testState.newWorkStatus];
- const actualStatusText = updatedPerson.workStatus;
- if (actualStatusText?.includes(expectedStatusText)) {
- console.debug(`[后台] 状态验证成功: ${expectedStatusText}`);
- } else {
- console.debug(`[后台] 状态验证: 期望包含 "${expectedStatusText}", 实际 "${actualStatusText}"`);
- }
- // 9. 记录完成时间
- const endTime = Date.now();
- const syncTime = endTime - startTime;
- console.debug(`[后台] 状态更新完成,耗时: ${syncTime}ms`);
- // 10. 关闭详情对话框
- await orderPage.closeDetailDialog();
- });
- });
- test.describe.serial('企业小程序验证人员状态同步', () => {
- test.use({ storageState: undefined }); // 确保使用新的浏览器上下文
- test('应该在企业小程序中显示更新后的人员状态', async ({ page: miniPage }) => {
- const { personName, newWorkStatus } = testState;
- if (!personName) {
- throw new Error('未找到测试人员名称,请先运行后台更新状态测试');
- }
- console.debug(`[企业小程序] 验证人员: ${personName}`);
- // 等待数据同步
- await new Promise(resolve => setTimeout(resolve, TEST_SYNC_TIMEOUT));
- // 1. 企业小程序登录
- await loginEnterpriseMini(miniPage);
- // 2. 导航到订单列表页面
- await miniPage.getByText('订单', { exact: true }).click();
- await miniPage.waitForLoadState('domcontentloaded');
- console.debug('[企业小程序] 导航到订单列表页面');
- // 3. 等待订单列表加载
- await miniPage.waitForTimeout(TIMEOUTS.LONG);
- // 4. 点击订单查看详情
- const orderDetailButton = miniPage.getByText(testState.orderName || '').first();
- const buttonCount = await orderDetailButton.count();
- if (buttonCount === 0) {
- console.debug(`[企业小程序] 订单 "${testState.orderName}" 未找到,尝试点击第一个订单`);
- // 如果找不到特定订单,点击第一个"查看详情"按钮
- const firstDetailButton = miniPage.getByText('查看详情').first();
- await firstDetailButton.click();
- } else {
- await orderDetailButton.click();
- }
- await miniPage.waitForURL(/\/detail/, { timeout: TIMEOUTS.PAGE_LOAD });
- console.debug('[企业小程序] 打开订单详情');
- // 5. 验证人员状态显示正确
- const expectedStatusText = WORK_STATUS_LABELS[newWorkStatus];
- const statusElement = miniPage.getByText(expectedStatusText);
- // 使用软验证(不强制要求,因为小程序页面结构可能不同)
- const statusExists = await statusElement.count() > 0;
- if (statusExists) {
- console.debug(`[企业小程序] 人员状态验证成功: ${expectedStatusText}`);
- } else {
- // 记录页面内容用于调试
- const pageContent = await miniPage.textContent('body');
- console.debug(`[企业小程序] 状态元素未找到,页面内容包含人员名: ${pageContent?.includes(personName || '')}`);
- console.debug(`[企业小程序] 页面包含状态文本: ${pageContent?.includes('状态') || false}`);
- }
- // 6. 记录数据同步完成时间
- const syncEndTime = Date.now();
- console.debug(`[企业小程序] 数据同步验证完成,时间戳: ${syncEndTime}`);
- });
- });
- test.describe.serial('人才小程序验证人员状态同步', () => {
- test.use({ storageState: undefined }); // 确保使用新的浏览器上下文
- test('应该在人才小程序中显示更新后的人员状态', async ({ page: miniPage }) => {
- const { personName, newWorkStatus } = testState;
- if (!personName) {
- throw new Error('未找到测试人员名称,请先运行后台更新状态测试');
- }
- console.debug(`[人才小程序] 验证人员: ${personName}`);
- // 等待数据同步
- await new Promise(resolve => setTimeout(resolve, TEST_SYNC_TIMEOUT));
- // 1. 人才小程序登录
- await loginTalentMini(miniPage);
- // 2. 导航到订单列表页面
- // 人才小程序可能有不同的导航结构,这里使用通用的方法
- await miniPage.waitForTimeout(TIMEOUTS.LONG);
- // 尝试多种导航方式
- const orderTab = miniPage.getByText('订单').or(miniPage.getByText('我的订单')).or(miniPage.getByText('工作'));
- const tabCount = await orderTab.count();
- if (tabCount > 0) {
- await orderTab.first().click();
- console.debug('[人才小程序] 导航到订单列表页面');
- } else {
- console.debug('[人才小程序] 订单标签未找到,使用当前页面');
- }
- await miniPage.waitForLoadState('domcontentloaded');
- await miniPage.waitForTimeout(TIMEOUTS.LONG);
- // 3. 验证人员状态显示正确
- const expectedStatusText = WORK_STATUS_LABELS[newWorkStatus];
- const statusElement = miniPage.getByText(expectedStatusText);
- // 使用软验证
- const statusExists = await statusElement.count() > 0;
- if (statusExists) {
- console.debug(`[人才小程序] 人员状态验证成功: ${expectedStatusText}`);
- } else {
- // 记录页面内容用于调试
- const pageContent = await miniPage.textContent('body');
- console.debug(`[人才小程序] 状态元素未找到,页面内容包含人员名: ${pageContent?.includes(personName || '')}`);
- console.debug(`[人才小程序] 页面包含状态文本: ${pageContent?.includes('状态') || false}`);
- }
- // 4. 记录数据同步完成时间
- const syncEndTime = Date.now();
- console.debug(`[人才小程序] 数据同步验证完成,时间戳: ${syncEndTime}`);
- });
- });
- test.describe.serial('数据清理和恢复', () => {
- test('应该恢复人员到原始状态', async ({ page: adminPage, testUsers }) => {
- const { orderName, personName, originalWorkStatus } = testState;
- if (!orderName || !personName || originalWorkStatus === null) {
- test.skip();
- return;
- }
- // 1. 后台登录
- await loginAdmin(adminPage, testUsers);
- // 2. 导航到订单管理页面
- await adminPage.goto('/admin/orders');
- await adminPage.waitForSelector('table tbody tr', { state: 'visible', timeout: TIMEOUTS.PAGE_LOAD });
- // 3. 打开订单详情对话框
- const orderPage = new OrderManagementPage(adminPage);
- await orderPage.openDetailDialog(orderName);
- console.debug('[清理] 打开订单详情对话框');
- // 4. 恢复人员到原始状态
- await orderPage.updatePersonWorkStatus(personName, originalWorkStatus);
- console.debug(`[清理] 已恢复人员 "${personName}" 到原始状态: ${WORK_STATUS_LABELS[originalWorkStatus]}`);
- // 5. 验证恢复成功
- const personList = await orderPage.getPersonListFromDetail();
- const restoredPerson = personList.find(p => p.name === personName);
- if (restoredPerson) {
- const expectedStatusText = WORK_STATUS_LABELS[originalWorkStatus];
- const actualStatusText = restoredPerson.workStatus;
- if (actualStatusText?.includes(expectedStatusText)) {
- console.debug(`[清理] 状态恢复验证成功: ${expectedStatusText}`);
- } else {
- console.debug(`[清理] 状态恢复验证: 期望包含 "${expectedStatusText}", 实际 "${actualStatusText}"`);
- }
- }
- // 6. 关闭详情对话框
- await orderPage.closeDetailDialog();
- console.debug('[清理] 测试数据恢复完成');
- });
- });
- });
|