155 lines
6.7 KiB
TypeScript
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);
|
|
});
|