Files
lislgosms/src/apps/admin/ReportMaterialImportModal.test.tsx
T

155 lines
6.7 KiB
TypeScript

import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ReportMaterialImportModal } from './ReportMaterialImportModal';
const { adminApi } = vi.hoisted(() => ({
adminApi: {
analyzeReportMaterialImport: vi.fn(),
commitReportMaterialImport: vi.fn(),
getReportMaterialImportAnalysisStatus: vi.fn(),
listDrainageFields: vi.fn(),
listEnterpriseApplicationOptions: vi.fn(),
listReportImportProfiles: vi.fn(),
listTenantOptions: vi.fn(),
},
}));
vi.mock('@/api/adminApi', () => ({ adminApi }));
describe('ReportMaterialImportModal mapping profile action', () => {
beforeEach(() => {
Object.values(adminApi).forEach((method) => method.mockReset());
adminApi.listTenantOptions.mockResolvedValue([
{ id: 'tenant-1', name: '测试企业', code: 'T001', status: 'active' },
]);
adminApi.listEnterpriseApplicationOptions.mockResolvedValue([
{ id: 'app-1', tenantId: 'tenant-1', name: '测试应用', status: 'active' },
]);
adminApi.listDrainageFields.mockResolvedValue([]);
adminApi.listReportImportProfiles.mockResolvedValue([]);
adminApi.analyzeReportMaterialImport.mockResolvedValue({
id: 'analysis-1',
status: 'queued',
progress: 10,
progressStage: '文件已上传,等待解析',
});
adminApi.getReportMaterialImportAnalysisStatus.mockResolvedValue({
id: 'analysis-1',
status: 'analyzed',
progress: 100,
progressStage: '解析完成',
columns: [
{ sourceColumnIndex: 0, columnLetter: 'A', sourceHeader: '签名', sourceHeaderPath: '签名', imageCount: 0 },
],
rows: [],
suggestedMappings: [],
});
adminApi.commitReportMaterialImport.mockResolvedValue({
id: 'analysis-1',
reportType: 'signature',
status: 'pending_review',
successCount: 1,
failedCount: 0,
});
});
it('renders the reusable mapping choice as a clear pressed-state shared button', async () => {
const user = userEvent.setup();
render(<ReportMaterialImportModal onClose={vi.fn()} onCompleted={vi.fn()} />);
await waitFor(() => expect(adminApi.listTenantOptions).toHaveBeenCalledTimes(1));
const tenantSelect = screen.getByText('所属企业').closest('label')?.querySelector('button');
expect(tenantSelect).not.toBeNull();
await user.click(tenantSelect!);
await user.click(screen.getByRole('option', { name: /测试企业/ }));
const applicationSelect = screen.getByText('企业应用(必选)').closest('label')?.querySelector('button');
await user.click(applicationSelect!);
await user.click(screen.getByRole('option', { name: '测试应用' }));
const fileInput = document.querySelector('input[type="file"]');
expect(fileInput).not.toBeNull();
fireEvent.change(fileInput!, { target: { files: [new File(['xlsx'], 'mapping.xlsx')] } });
await user.click(screen.getByRole('button', { name: '解析文件并配置映射' }));
const toggle = await screen.findByRole('button', { name: '保存为可复用映射方案' });
expect(toggle).toHaveClass('ui-button', 'report-import-profile__toggle');
expect(toggle).toHaveAttribute('aria-pressed', 'false');
await user.click(toggle);
await waitFor(() =>
expect(screen.getByRole('button', { name: '本次将保存/更新映射方案' })).toHaveAttribute('aria-pressed', 'true'),
);
expect(screen.getByLabelText('映射方案名称')).toBeVisible();
}, 10_000);
it('keeps signature category separate from the signature name and submits the review batch', async () => {
adminApi.getReportMaterialImportAnalysisStatus.mockResolvedValue({
id: 'analysis-1',
status: 'analyzed',
progress: 100,
progressStage: '解析完成',
columns: [
{ sourceColumnIndex: 1, columnLetter: 'A', sourceHeader: '签名*', sourceHeaderPath: '签名*', imageCount: 0 },
{
sourceColumnIndex: 2,
columnLetter: 'B',
sourceHeader: '签名类别*',
sourceHeaderPath: '签名类别*\n1-营业执照\n2-商标\n3-APP',
imageCount: 0,
},
],
rows: [{ rowNumber: 2, values: { 1: '【真实签名】', 2: '1' }, imageColumns: [] }],
suggestedMappings: [
{
sourceHeader: '签名*',
sourceHeaderPath: '签名*',
sourceColumnIndex: 1,
targetFieldCode: 'signature_name',
targetKind: 'signatureName',
fieldType: 'string',
required: true,
},
{
sourceHeader: '签名类别*',
sourceHeaderPath: '签名类别*\n1-营业执照\n2-商标\n3-APP',
sourceColumnIndex: 2,
targetFieldCode: 'purpose',
targetKind: 'purpose',
fieldType: 'string',
},
],
});
const user = userEvent.setup();
const onCompleted = vi.fn();
const refresh = vi.fn();
window.addEventListener('cmpp-audit-count-refresh', refresh);
render(<ReportMaterialImportModal onClose={vi.fn()} onCompleted={onCompleted} />);
await waitFor(() => expect(adminApi.listTenantOptions).toHaveBeenCalledTimes(1));
await user.click(screen.getByText('所属企业').closest('label')!.querySelector('button')!);
await user.click(screen.getByRole('option', { name: /测试企业/ }));
await user.click(screen.getByText('企业应用(必选)').closest('label')!.querySelector('button')!);
await user.click(screen.getByRole('option', { name: '测试应用' }));
fireEvent.change(document.querySelector('input[type="file"]')!, {
target: { files: [new File(['xlsx'], 'mapping.xlsx')] },
});
await user.click(screen.getByRole('button', { name: '解析文件并配置映射' }));
const preview = await screen.findByText('查看前 1 行解析预览');
await user.click(preview);
expect(preview.closest('details')).toHaveAttribute('open');
await user.click(screen.getByRole('button', { name: '提交导入审核' }));
await waitFor(() =>
expect(adminApi.commitReportMaterialImport).toHaveBeenCalledWith(
'analysis-1',
expect.objectContaining({
mappings: expect.arrayContaining([
expect.objectContaining({ targetKind: 'signatureName', targetFieldCode: 'signature_name' }),
expect.objectContaining({ targetKind: 'purpose', targetFieldCode: 'purpose' }),
]),
}),
),
);
expect(onCompleted).toHaveBeenCalledWith(expect.objectContaining({ id: 'analysis-1', status: 'pending_review' }));
expect(refresh).toHaveBeenCalledTimes(1);
window.removeEventListener('cmpp-audit-count-refresh', refresh);
}, 10_000);
});