exportUtils.test.ts
4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// REQ-USR-003: 页面常量 + 前端 CSV 导出工具单测(D-PLAN-1)
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { buildUserCsv, downloadCsv } from '../../src/pages/usr/UserList/exportUtils';
import {
DEFAULT_QUERY,
PAGE_SIZE_OPTIONS,
QUERY_FIELD_OPTIONS,
MATCH_TYPE_OPTIONS,
} from '../../src/pages/usr/UserList/constants';
import type { UserVO } from '../../src/api/types';
const rowVoid0: UserVO = {
id: 1,
sUserName: 'admin',
employeeName: null,
sUserNo: 'U001',
departmentName: '技术部',
sUserType: '超级管理员',
sLanguage: '中文',
iIsVoid: 0,
tLastLoginDate: null,
sCreator: '系统',
tCreateDate: '2024-01-01T00:00:00',
};
const rowVoid1: UserVO = {
id: 2,
sUserName: '李丹',
employeeName: '李丹',
sUserNo: 'U002',
departmentName: '客服部',
sUserType: '普通用户',
sLanguage: '中文',
iIsVoid: 1,
tLastLoginDate: '2024-02-01T10:00:00',
sCreator: 'admin',
tCreateDate: '2024-01-02T00:00:00',
};
describe('exportUtils.buildUserCsv', () => {
it('buildUserCsv has header row and maps 作废 0/1', () => {
const csv = buildUserCsv([rowVoid0, rowVoid1]);
const lines = csv.split('\n');
const header = lines[0];
// 中文表头逐字(不含序号列由前端生成,导出含中文列名)
expect(header).toContain('用户名');
expect(header).toContain('员工名');
expect(header).toContain('作废');
expect(header).toContain('用户号');
expect(header).toContain('部门');
expect(header).toContain('用户类型');
expect(header).toContain('语言');
expect(header).toContain('登录日期');
expect(header).toContain('制单人');
expect(header).toContain('制单日期');
// 作废列 0→否、1→是
expect(lines[1]).toContain('否');
expect(lines[2]).toContain('是');
// 空值字段(employeeName=null)渲染为空串,不出现 "null"
expect(csv).not.toContain('null');
});
});
describe('exportUtils.downloadCsv', () => {
let createSpy: ReturnType<typeof vi.fn>;
let clickSpy: ReturnType<typeof vi.fn>;
let blobArg: Blob | null;
beforeEach(() => {
blobArg = null;
createSpy = vi.fn((b: Blob) => {
blobArg = b;
return 'blob:mock';
});
clickSpy = vi.fn();
// jsdom 未实现 URL.createObjectURL / revokeObjectURL
(URL as unknown as { createObjectURL: unknown }).createObjectURL = createSpy;
(URL as unknown as { revokeObjectURL: unknown }).revokeObjectURL = vi.fn();
vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(clickSpy);
});
afterEach(() => {
vi.restoreAllMocks();
});
function readBlobBytes(blob: Blob): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(new Uint8Array(reader.result as ArrayBuffer));
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(blob);
});
}
it('downloadCsv triggers blob download with UTF-8 BOM', async () => {
downloadCsv('users.csv', 'x');
expect(createSpy).toHaveBeenCalledTimes(1);
expect(clickSpy).toHaveBeenCalledTimes(1);
expect(blobArg).toBeInstanceOf(Blob);
const bytes = await readBlobBytes(blobArg!);
// UTF-8 BOM 字节序列 EF BB BF
expect(bytes[0]).toBe(0xef);
expect(bytes[1]).toBe(0xbb);
expect(bytes[2]).toBe(0xbf);
// 内容 'x' 紧随 BOM 之后
expect(bytes[3]).toBe('x'.charCodeAt(0));
});
});
describe('UserList constants', () => {
it('DEFAULT_QUERY defaults match BR2/BR3/D4', () => {
expect(DEFAULT_QUERY.queryField).toBe('用户名');
expect(DEFAULT_QUERY.matchType).toBe('包含');
expect(DEFAULT_QUERY.queryValue).toBe('');
expect(DEFAULT_QUERY.pageNum).toBe(1);
expect(DEFAULT_QUERY.pageSize).toBe(10);
});
it('PAGE_SIZE_OPTIONS upper bound is 100', () => {
expect(PAGE_SIZE_OPTIONS).toEqual([10, 20, 50, 100]);
});
it('QUERY_FIELD_OPTIONS has 8 fields starting 用户名', () => {
expect(QUERY_FIELD_OPTIONS).toHaveLength(8);
expect(QUERY_FIELD_OPTIONS[0]).toBe('用户名');
expect(QUERY_FIELD_OPTIONS).toEqual([
'用户名',
'员工名',
'用户号',
'部门',
'用户类型',
'作废',
'登录日期',
'制单人',
]);
});
it('MATCH_TYPE_OPTIONS is 包含/不包含/等于', () => {
expect(MATCH_TYPE_OPTIONS).toEqual(['包含', '不包含', '等于']);
});
});