|
|
@@ -0,0 +1,365 @@
|
|
|
+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'));
|
|
|
+
|
|
|
+async function getAuthToken(request: Parameters<typeof test>[0]['request']): Promise<string | null> {
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+async function createDisabledPersonViaAPI(
|
|
|
+ request: Parameters<typeof test>[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 ' + String(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<typeof test>[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: '测试平台_' + String(timestamp),
|
|
|
+ contactPerson: '测试联系人',
|
|
|
+ contactPhone: '13800138000',
|
|
|
+ contactEmail: 'test@example.com'
|
|
|
+ };
|
|
|
+
|
|
|
+ const createResponse = await request.post('http://localhost:8080/api/v1/platform/createPlatform', {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': 'Bearer ' + String(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<typeof test>[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 = '测试公司_' + String(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 ' + String(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;
|
|
|
+ }
|
|
|
+
|
|
|
+ const listResponse = await request.get('http://localhost:8080/api/v1/company/getCompaniesByPlatform/' + String(platformId), {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': 'Bearer ' + String(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 createOrderViaAPI(
|
|
|
+ request: Parameters<typeof test>[0]['request'],
|
|
|
+ orderData: {
|
|
|
+ orderName: string;
|
|
|
+ platformId: number;
|
|
|
+ companyId: number;
|
|
|
+ expectedStartDate: 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/order/create', {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': 'Bearer ' + String(token),
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ },
|
|
|
+ data: orderData
|
|
|
+ });
|
|
|
+
|
|
|
+ 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.orderName);
|
|
|
+ return { id: result.id, name: result.orderName };
|
|
|
+ } catch (error) {
|
|
|
+ console.debug('创建订单 API 调用出错:', error);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function bindPersonToOrderViaAPI(
|
|
|
+ request: Parameters<typeof test>[0]['request'],
|
|
|
+ orderId: number,
|
|
|
+ personId: number,
|
|
|
+ joinDate: string
|
|
|
+): Promise<boolean> {
|
|
|
+ try {
|
|
|
+ const token = await getAuthToken(request);
|
|
|
+ if (!token) return false;
|
|
|
+
|
|
|
+ const url = 'http://localhost:8080/api/v1/order/' + String(orderId) + '/persons/batch';
|
|
|
+ const createResponse = await request.post(url, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': 'Bearer ' + String(token),
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ },
|
|
|
+ data: {
|
|
|
+ persons: [
|
|
|
+ {
|
|
|
+ personId: personId,
|
|
|
+ joinDate: joinDate,
|
|
|
+ salaryDetail: 5000
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!createResponse.ok()) {
|
|
|
+ const errorText = await createResponse.text();
|
|
|
+ console.debug('API 绑定人员失败:', createResponse.status(), errorText);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await createResponse.json();
|
|
|
+ if (result.success) {
|
|
|
+ console.debug('API 绑定人员成功:', personId);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ } catch (error) {
|
|
|
+ console.debug('绑定人员 API 调用出错:', error);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function generateUniqueTestData() {
|
|
|
+ const timestamp = Date.now();
|
|
|
+ const counter = Math.floor(Math.random() * 10000);
|
|
|
+
|
|
|
+ return {
|
|
|
+ orderName: '日期测试订单_' + String(timestamp),
|
|
|
+ personName: '日期测试残疾人_' + String(timestamp),
|
|
|
+ gender: '男',
|
|
|
+ idCard: '110101' + String(timestamp).slice(-8) + String(counter).slice(-4),
|
|
|
+ disabilityId: '残疾证' + String(timestamp).slice(-6) + String(counter),
|
|
|
+ disabilityType: '视力残疾',
|
|
|
+ disabilityLevel: '一级',
|
|
|
+ idAddress: '北京市东城区测试地址' + String(timestamp),
|
|
|
+ phone: '138' + String(counter).padStart(8, '0'),
|
|
|
+ province: '北京市',
|
|
|
+ city: '北京市',
|
|
|
+ joinDate: '2026-01-01',
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+test.describe.serial('订单管理 - 人员入职/离职日期编辑功能 (Story 15.7)', () => {
|
|
|
+ test.beforeEach(async ({ adminLoginPage }) => {
|
|
|
+ await adminLoginPage.goto();
|
|
|
+ await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
|
|
|
+ });
|
|
|
+
|
|
|
+ test('AC1: 订单人员详情页中入职日期和离职日期可点击编辑', async ({ page, request }) => {
|
|
|
+ console.debug('========== Story 15.7 AC1: 入职/离职日期可编辑 ==========');
|
|
|
+
|
|
|
+ const testData = generateUniqueTestData();
|
|
|
+ console.debug('测试数据已生成:', testData.personName);
|
|
|
+
|
|
|
+ const platform = await createPlatformViaAPI(request);
|
|
|
+ expect(platform).not.toBeNull();
|
|
|
+
|
|
|
+ const company = await createCompanyViaAPI(request, platform!.id);
|
|
|
+ expect(company).not.toBeNull();
|
|
|
+
|
|
|
+ const order = await createOrderViaAPI(request, {
|
|
|
+ orderName: testData.orderName,
|
|
|
+ platformId: platform!.id,
|
|
|
+ companyId: company!.id,
|
|
|
+ expectedStartDate: testData.joinDate
|
|
|
+ });
|
|
|
+ expect(order).not.toBeNull();
|
|
|
+
|
|
|
+ const person = await createDisabledPersonViaAPI(request, {
|
|
|
+ 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,
|
|
|
+ });
|
|
|
+ expect(person).not.toBeNull();
|
|
|
+
|
|
|
+ const bound = await bindPersonToOrderViaAPI(request, order!.id, person!.id, testData.joinDate);
|
|
|
+ expect(bound).toBe(true);
|
|
|
+
|
|
|
+ await page.goto('/admin/orders');
|
|
|
+ await page.waitForLoadState('networkidle');
|
|
|
+
|
|
|
+ await page.fill('input[placeholder*="搜索"]', testData.orderName);
|
|
|
+ await page.waitForTimeout(TIMEOUTS.MEDIUM);
|
|
|
+ const orderRow = page.locator('tbody tr').filter({ hasText: testData.orderName });
|
|
|
+ await orderRow.waitFor({ state: 'visible', timeout: TIMEOUTS.TABLE_LOAD });
|
|
|
+
|
|
|
+ // 点击"打开菜单"按钮
|
|
|
+ const menuTrigger = orderRow.getByRole('button', { name: /打开菜单/ });
|
|
|
+ await menuTrigger.click();
|
|
|
+ await page.waitForTimeout(TIMEOUTS.VERY_SHORT);
|
|
|
+
|
|
|
+ // 点击"查看详情"菜单项
|
|
|
+ const detailButton = page.getByRole('menuitem', { name: /查看详情/ });
|
|
|
+ await detailButton.click();
|
|
|
+
|
|
|
+ await page.waitForSelector('[data-testid="order-detail-dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG_OPEN });
|
|
|
+
|
|
|
+ const joinDateButton = page.locator('[data-testid="edit-join-date-' + String(person!.id) + '"]');
|
|
|
+ await expect(joinDateButton).toBeVisible();
|
|
|
+ console.debug('入职日期按钮可见');
|
|
|
+
|
|
|
+ const leaveDateButton = page.locator('[data-testid="edit-leave-date-' + String(person!.id) + '"]');
|
|
|
+ await expect(leaveDateButton).toBeVisible();
|
|
|
+ console.debug('离职日期按钮可见');
|
|
|
+
|
|
|
+ await joinDateButton.click();
|
|
|
+
|
|
|
+ await expect(page.locator('[data-testid="person-date-edit-dialog"]')).toBeVisible();
|
|
|
+ console.debug('日期编辑对话框已打开');
|
|
|
+
|
|
|
+ const dialogContent = page.locator('[data-testid="person-date-edit-dialog"] .text-sm.text-muted-foreground');
|
|
|
+ await expect(dialogContent).toContainText(testData.personName);
|
|
|
+ console.debug('对话框显示正确的人员名称');
|
|
|
+
|
|
|
+ await page.locator('[data-testid="cancel-button"]').click();
|
|
|
+ await expect(page.locator('[data-testid="person-date-edit-dialog"]')).not.toBeVisible();
|
|
|
+ console.debug('对话框已关闭');
|
|
|
+
|
|
|
+ await leaveDateButton.click();
|
|
|
+
|
|
|
+ await expect(page.locator('[data-testid="person-date-edit-dialog"]')).toBeVisible();
|
|
|
+ console.debug('通过离职日期按钮打开了对话框');
|
|
|
+
|
|
|
+ await page.locator('[data-testid="cancel-button"]').click();
|
|
|
+
|
|
|
+ console.debug('========== AC1 测试完成 ==========');
|
|
|
+ });
|
|
|
+});
|