import assert from 'node:assert/strict' import { describe, it } from 'node:test' import { buildExcelSnapshot, buildExcelTsv, excelCell, isCopyableColumn, isRowSelectorColumn, resolveCopyAxes, snapshotToUniverCellData, toggleAxis } from './list-excel-copy' describe('isCopyableColumn', () => { it('keeps ordinary data columns', () => { assert.equal(isCopyableColumn({ id: 'name', label: '联系人' }), true) }) it('drops selection, index, expand, label 操作, and className containing col-actions', () => { assert.equal(isCopyableColumn({ id: 's', label: '', type: 'selection' }), false) assert.equal(isCopyableColumn({ id: 'i', label: '', type: 'index' }), false) assert.equal(isCopyableColumn({ id: 'e', label: '', type: 'expand' }), false) assert.equal(isCopyableColumn({ id: 'a', label: '操作' }), false) assert.equal( isCopyableColumn({ id: 'act', label: '编辑', className: 'col-actions fixed' }), false ) }) }) describe('isRowSelectorColumn', () => { it('treats index type and 序号/# labels as the row head', () => { assert.equal(isRowSelectorColumn({ id: 'i', label: '', type: 'index' }), true) assert.equal(isRowSelectorColumn({ id: 'i', label: '序号' }), true) assert.equal(isRowSelectorColumn({ id: 'i', label: '#' }), true) assert.equal(isRowSelectorColumn({ id: 'n', label: '客户名称' }), false) }) }) describe('excelCell', () => { it('trims and turns tabs and line breaks into spaces', () => { assert.equal(excelCell(' 合作中 \n'), '合作中') assert.equal(excelCell('重庆\t长寿\r\n区'), '重庆 长寿 区') assert.equal(excelCell('重庆\r长寿'), '重庆 长寿') }) }) describe('toggleAxis', () => { it('toggles one index and remembers anchor; second toggle of same index removes it but keeps anchor', () => { let selected = new Set() let anchor: number | null = null const first = toggleAxis(selected, 2, false, anchor) selected = first.selected anchor = first.anchor assert.deepEqual([...selected].sort(), [2]) assert.equal(anchor, 2) const second = toggleAxis(selected, 2, false, anchor) selected = second.selected anchor = second.anchor assert.deepEqual([...selected], []) assert.equal(anchor, 2) }) it('shift-selects inclusive span and keeps earlier picks: start with set containing 0 and anchor 0, shift-click 3 yields 0,1,2,3 and anchor 3', () => { const selected = new Set([0]) const result = toggleAxis(selected, 3, true, 0) assert.deepEqual( [...result.selected].sort((a, b) => a - b), [0, 1, 2, 3] ) assert.equal(result.anchor, 3) }) it('shift without an anchor toggles the clicked index', () => { const selected = new Set() const result = toggleAxis(selected, 1, true, null) assert.deepEqual([...result.selected], [1]) assert.equal(result.anchor, 1) }) }) describe('resolveCopyAxes', () => { it('returns null when nothing selected', () => { assert.equal( resolveCopyAxes({ selectedCols: [], selectedRows: [], columnCount: 3, rowCount: 2 }), null ) }) it('expands columns to every current row, sorting cols: selectedCols [2,0], rowCount 2, columnCount 3 => cols [0,2] rows [0,1]', () => { const result = resolveCopyAxes({ selectedCols: [2, 0], selectedRows: [], columnCount: 3, rowCount: 2 }) assert.deepEqual(result, { cols: [0, 2], rows: [0, 1] }) }) it('expands rows to every copyable column: selectedRows [3,1], columnCount 2, rowCount 4 => cols [0,1] rows [1,3]', () => { const result = resolveCopyAxes({ selectedCols: [], selectedRows: [3, 1], columnCount: 2, rowCount: 4 }) assert.deepEqual(result, { cols: [0, 1], rows: [1, 3] }) }) it('intersection when both selected: selectedCols [1], selectedRows [0,2], columnCount 3, rowCount 4 => cols [1] rows [0,2]', () => { const result = resolveCopyAxes({ selectedCols: [1], selectedRows: [0, 2], columnCount: 3, rowCount: 4 }) assert.deepEqual(result, { cols: [1], rows: [0, 2] }) }) }) describe('buildExcelTsv', () => { it('builds CRLF-separated TSV with header row', () => { const tsv = buildExcelTsv( ['联系人', '状态'], [ ['张三', '合作中'], ['李四', '跟进中'] ] ) assert.equal(tsv, '联系人\t状态\r\n张三\t合作中\r\n李四\t跟进中') }) }) describe('buildExcelSnapshot', () => { it('keeps copyable columns, cleans cells, and drops 操作', () => { const snapshot = buildExcelSnapshot({ columns: [ { id: 'name', label: ' 联系人\n' }, { id: 'act', label: '操作' }, { id: 'status', label: '状态' } ], rowValues: [ ['张三\t甲', '编辑', '合作中'], ['李四', '删除', '跟进中'] ] }) assert.deepEqual(snapshot.headers, ['联系人', '状态']) assert.deepEqual(snapshot.rows, [ ['张三 甲', '合作中'], ['李四', '跟进中'] ]) }) it('returns empty snapshot when no copyable columns', () => { const snapshot = buildExcelSnapshot({ columns: [{ id: 'a', label: '操作' }], rowValues: [['x']] }) assert.deepEqual(snapshot, { headers: [], rows: [] }) }) }) describe('snapshotToUniverCellData', () => { it('puts headers on row 0 and data from row 1', () => { const cellData = snapshotToUniverCellData({ headers: ['A', 'B'], rows: [['1', '2']] }) assert.deepEqual(cellData[0][0], { v: 'A', t: 1 }) assert.deepEqual(cellData[0][1], { v: 'B', t: 1 }) assert.deepEqual(cellData[1][0], { v: '1', t: 1 }) assert.deepEqual(cellData[1][1], { v: '2', t: 1 }) }) })