56 lines
2.3 KiB
TypeScript
56 lines
2.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { parseCsv, toCsv, validateCustomerRows } from '@/lib/csv';
|
|
|
|
describe('parseCsv', () => {
|
|
it('parses a header and quoted fields', () => {
|
|
const { header, rows } = parseCsv('name,email,company\n"Doe, Jane",jane@example.com,"Acme, Inc"\n');
|
|
expect(header).toEqual(['name', 'email', 'company']);
|
|
expect(rows).toEqual([{ name: 'Doe, Jane', email: 'jane@example.com', company: 'Acme, Inc' }]);
|
|
});
|
|
|
|
it('unescapes doubled quotes and tolerates CRLF', () => {
|
|
const { rows } = parseCsv('a,b\r\n"say ""hi""",2\r\n');
|
|
expect(rows[0]).toEqual({ a: 'say "hi"', b: '2' });
|
|
});
|
|
|
|
it('ignores blank lines', () => {
|
|
expect(parseCsv('a\n\n1\n').rows).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe('validateCustomerRows', () => {
|
|
const result = validateCustomerRows([
|
|
{ name: 'Jane', email: 'JANE@Example.com', company: 'Acme', status: 'lead' },
|
|
{ name: '', email: 'x@y.com', company: '', status: 'active' }, // missing name
|
|
{ name: 'Bob', email: 'not-an-email', company: '', status: 'active' }, // bad email
|
|
{ name: 'Ann', email: 'ann@example.com', company: '', status: 'vip' }, // bad status
|
|
{ name: 'Dup', email: 'jane@example.com', company: '', status: 'active' }, // duplicate email
|
|
{ name: 'Eve', email: 'eve@example.com', company: '', status: '' }, // empty status → active
|
|
]);
|
|
|
|
it('normalizes email casing and fills the default status', () => {
|
|
expect(result.valid[0]).toEqual({ name: 'Jane', email: 'jane@example.com', company: 'Acme', status: 'lead' });
|
|
expect(result.valid[1]).toEqual({ name: 'Eve', email: 'eve@example.com', company: '', status: 'active' });
|
|
});
|
|
|
|
it('reports one error per invalid row (line numbers are 1-based + header)', () => {
|
|
expect(result.errors.map((e) => e.row)).toEqual([3, 4, 5]);
|
|
});
|
|
|
|
it('flags duplicate emails within the file', () => {
|
|
expect(result.duplicatesInFile).toEqual([6]);
|
|
expect(result.total).toBe(6);
|
|
expect(result.valid).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
describe('toCsv', () => {
|
|
it('escapes commas and quotes and keeps the column order', () => {
|
|
expect(toCsv([{ a: 'x,y', b: 'say "hi"' }], ['a', 'b'])).toBe('a,b\n"x,y","say ""hi"""');
|
|
});
|
|
|
|
it('renders null/undefined as empty fields', () => {
|
|
expect(toCsv([{ a: null, b: undefined }], ['a', 'b'])).toBe('a,b\n,');
|
|
});
|
|
});
|