forked from lunanting/cangku
75 lines
3.1 KiB
JavaScript
75 lines
3.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import {
|
|
buildCsvContent,
|
|
buildWorksheetXml,
|
|
escapeXmlText,
|
|
resolveExportRows,
|
|
spreadsheetColumnName,
|
|
} from '../src/utils/tableExport.js';
|
|
|
|
test('spreadsheetColumnName maps indexes to excel column letters', () => {
|
|
assert.equal(spreadsheetColumnName(0), 'A');
|
|
assert.equal(spreadsheetColumnName(25), 'Z');
|
|
assert.equal(spreadsheetColumnName(26), 'AA');
|
|
assert.equal(spreadsheetColumnName(51), 'AZ');
|
|
assert.equal(spreadsheetColumnName(52), 'BA');
|
|
assert.equal(spreadsheetColumnName(701), 'ZZ');
|
|
});
|
|
|
|
test('escapeXmlText escapes xml special characters', () => {
|
|
assert.equal(escapeXmlText('a<b>&"c"\''), 'a<b>&"c"'');
|
|
});
|
|
|
|
test('resolveExportRows applies exportValue and falls back to dataIndex', () => {
|
|
const columns = [
|
|
{ title: '名称', dataIndex: 'name' },
|
|
{ title: '达标', dataIndex: 'qualified', exportValue: (value) => (value ? '达标' : '未达标') },
|
|
];
|
|
const rows = [{ name: '商品<1>', qualified: true }, { name: '商品,2', qualified: false }];
|
|
assert.deepEqual(resolveExportRows(columns, rows), [
|
|
['商品<1>', '达标'],
|
|
['商品,2', '未达标'],
|
|
]);
|
|
});
|
|
|
|
test('exportValue receives the cell value like antd render, not the row object', () => {
|
|
// 回归:导出 Invalid Date / NaN% 的根因是 exportValue 曾被传入整行对象
|
|
const formatDate = (value) => (value ? `D:${value.slice(0, 10)}` : '--');
|
|
const percent = (value) => (value === null || value === undefined ? '--' : `${(Number(value) * 100).toFixed(2)}%`);
|
|
const columns = [
|
|
{ title: '上架时间', dataIndex: 'firstCreatedAt', exportValue: formatDate },
|
|
{ title: '动销率', dataIndex: 'rate', exportValue: percent },
|
|
{ title: '是否达标', dataIndex: 'qualified', exportValue: (value) => (value ? '达标' : '未达标') },
|
|
];
|
|
const rows = [
|
|
{ firstCreatedAt: '2026-07-28T00:00:00.000Z', rate: 0.9667, qualified: true },
|
|
{ firstCreatedAt: null, rate: null, qualified: false },
|
|
];
|
|
assert.deepEqual(resolveExportRows(columns, rows), [
|
|
['D:2026-07-28', '96.67%', '达标'],
|
|
['--', '--', '未达标'],
|
|
]);
|
|
});
|
|
|
|
test('buildCsvContent quotes cells containing delimiters and keeps CRLF rows', () => {
|
|
const columns = [{ title: '商品,名称', dataIndex: 'name' }, { title: '数量', dataIndex: 'count' }];
|
|
const rows = [{ name: '含"引号"和\n换行', count: 3 }];
|
|
const content = buildCsvContent(columns, rows);
|
|
const lines = content.split('\r\n');
|
|
assert.equal(lines[0], '"商品,名称",数量');
|
|
assert.equal(lines[1], '"含""引号""和\n换行",3');
|
|
});
|
|
|
|
test('buildWorksheetXml emits header row, inline strings and numeric cells', () => {
|
|
const columns = [{ title: '名称<测试>', dataIndex: 'name' }, { title: '奖金', dataIndex: 'bonus' }];
|
|
const rows = [{ name: '商品&1', bonus: 400 }];
|
|
const xml = buildWorksheetXml(columns, rows);
|
|
assert.match(xml, /<sheetData>/);
|
|
assert.match(xml, /t="inlineStr"><is><t xml:space="preserve">名称<测试><\/t><\/is><\/c>/);
|
|
assert.match(xml, /商品&1/);
|
|
assert.match(xml, /<c r="B2"><v>400<\/v><\/c>/);
|
|
assert.match(xml, /<c r="A1"/);
|
|
});
|