mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 09:19:34 +05:30
12 commits on master since v4.7.1 baseline, taking tests 524 → 914 across 27 new suites. This commit: - Bumps package.json + README version to v4.8.0 (additive features → semver minor) - Applies Prettier formatting to all files touched during the overnight session so release build is reproducible from a clean repo New in v4.8.0: - PDF encryption close-out (D1) - KaTeX rendering test coverage - Autosave buffer + crash recovery banner - Daily notes + workspace search + doc-aware Q&A - Search sidebar panel + Ask mode - Status bar: word count, reading time, Flesch-Kincaid grade - Footnote hover preview - Smart paste (URL → link, CSV/TSV → table) - Daily notes menu item + sidebar panel + Q&A deep-link - Daily templates gallery - Pluggable DocQA engine (TF-idF default + lazy neural embeddings) Amit Haridas
180 lines
5.3 KiB
JavaScript
180 lines
5.3 KiB
JavaScript
/**
|
|
* @jest-environment node
|
|
*
|
|
* csv-to-table tests — pure module.
|
|
*/
|
|
const {
|
|
detectDelimiter,
|
|
csvToTable,
|
|
looksLikeCsv,
|
|
parseRows,
|
|
escapeCell,
|
|
} = require('../src/utils/csv-to-table');
|
|
|
|
describe('detectDelimiter', () => {
|
|
test('prefers tabs over commas when both are present', () => {
|
|
expect(detectDelimiter('a\tb\tc\n1\t2\t3')).toBe('\t');
|
|
});
|
|
|
|
test('falls back to commas', () => {
|
|
expect(detectDelimiter('a,b,c\n1,2,3')).toBe(',');
|
|
});
|
|
|
|
test('uses semicolons when commas are absent', () => {
|
|
expect(detectDelimiter('a;b;c\n1;2;3')).toBe(';');
|
|
});
|
|
|
|
test('returns comma for empty / non-string input', () => {
|
|
expect(detectDelimiter('')).toBe(',');
|
|
expect(detectDelimiter(null)).toBe(',');
|
|
});
|
|
});
|
|
|
|
describe('parseRows (RFC 4180 quoting)', () => {
|
|
test('parses simple comma-separated rows', () => {
|
|
expect(parseRows('a,b,c\n1,2,3', ',')).toEqual([
|
|
['a', 'b', 'c'],
|
|
['1', '2', '3'],
|
|
]);
|
|
});
|
|
|
|
test('handles quoted fields with embedded delimiter', () => {
|
|
expect(parseRows('"a, b",c', ',')).toEqual([['a, b', 'c']]);
|
|
});
|
|
|
|
test('handles escaped double-quotes inside quoted fields', () => {
|
|
expect(parseRows('"he said ""hi""",ok', ',')).toEqual([['he said "hi"', 'ok']]);
|
|
});
|
|
|
|
test('handles CRLF line endings', () => {
|
|
expect(parseRows('a,b\r\n1,2', ',')).toEqual([
|
|
['a', 'b'],
|
|
['1', '2'],
|
|
]);
|
|
});
|
|
|
|
test('handles multi-line quoted fields', () => {
|
|
expect(parseRows('"line1\nline2",x', ',')).toEqual([['line1\nline2', 'x']]);
|
|
});
|
|
});
|
|
|
|
describe('escapeCell', () => {
|
|
test('escapes pipes (table syntax)', () => {
|
|
expect(escapeCell('a | b')).toBe('a \\| b');
|
|
});
|
|
|
|
test('escapes backslashes first', () => {
|
|
expect(escapeCell('a\\b')).toBe('a\\\\b');
|
|
expect(escapeCell('a\\|b')).toBe('a\\\\\\|b');
|
|
});
|
|
|
|
test('strips newlines (cells must be one logical line)', () => {
|
|
expect(escapeCell('a\nb')).toBe('ab');
|
|
expect(escapeCell('a\r\nb')).toBe('ab');
|
|
// Tabs survive — they don't break markdown tables and may carry intent.
|
|
expect(escapeCell('a\tb')).toBe('a\tb');
|
|
});
|
|
|
|
test('handles null / undefined / numbers', () => {
|
|
expect(escapeCell(null)).toBe('');
|
|
expect(escapeCell(undefined)).toBe('');
|
|
expect(escapeCell(42)).toBe('42');
|
|
});
|
|
});
|
|
|
|
describe('csvToTable — basic', () => {
|
|
test('converts simple CSV with first row as header', () => {
|
|
const md = csvToTable('name,age\nAlice,30\nBob,25');
|
|
// age column has 2 numeric values (30, 25) → right-aligned
|
|
expect(md).toBe('| name | age |\n| --- | ---: |\n| Alice | 30 |\n| Bob | 25 |');
|
|
});
|
|
|
|
test('handles a single row (no header)', () => {
|
|
const md = csvToTable('just,one,row');
|
|
expect(md).toBe('| just | one | row |\n| --- | --- | --- |');
|
|
});
|
|
|
|
test('TSV (Excel/Sheets paste)', () => {
|
|
const md = csvToTable('a\tb\tc\n1\t2\t3');
|
|
expect(md).toBe('| a | b | c |\n| --- | --- | --- |\n| 1 | 2 | 3 |');
|
|
});
|
|
|
|
test('returns empty string for empty / whitespace-only input', () => {
|
|
expect(csvToTable('')).toBe('');
|
|
expect(csvToTable(' \n ')).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('csvToTable — alignment', () => {
|
|
test('right-aligns numeric columns', () => {
|
|
const md = csvToTable('name,count\nalpha,5\nbeta,12');
|
|
expect(md).toContain('| name | count |');
|
|
expect(md).toContain('| --- | ---: |');
|
|
});
|
|
|
|
test('keeps left alignment for text columns', () => {
|
|
const md = csvToTable('name,city\nAlice,Paris');
|
|
expect(md).toContain('| --- | --- |');
|
|
});
|
|
|
|
test('recognizes currency-formatted numbers as numeric', () => {
|
|
const md = csvToTable('item,price\napple,$1.50\npear,2.30');
|
|
expect(md).toContain('| --- | ---: |');
|
|
});
|
|
|
|
test('recognizes percentages', () => {
|
|
const md = csvToTable('metric,rate\nuptime,99.5%');
|
|
expect(md).toContain('| --- | ---: |');
|
|
});
|
|
});
|
|
|
|
describe('csvToTable — escaping', () => {
|
|
test('escapes pipes inside cells', () => {
|
|
const md = csvToTable('a,b\nfoo|bar,baz');
|
|
expect(md).toContain('| foo\\|bar | baz |');
|
|
});
|
|
|
|
test('escapes newlines inside cells', () => {
|
|
const md = csvToTable('a,b\n"line1\nline2",x');
|
|
expect(md).toContain('| line1line2 | x |');
|
|
});
|
|
});
|
|
|
|
describe('csvToTable — ragged rows', () => {
|
|
test('pads short rows with empty cells so the table stays rectangular', () => {
|
|
const md = csvToTable('a,b,c\n1,2');
|
|
const dataLines = md.split('\n').slice(2); // skip header + sep
|
|
expect(dataLines[0]).toBe('| 1 | 2 | |');
|
|
});
|
|
});
|
|
|
|
describe('looksLikeCsv', () => {
|
|
test('returns true for a tab-separated multi-row paste', () => {
|
|
expect(looksLikeCsv('a\tb\tc\n1\t2\t3')).toBe(true);
|
|
});
|
|
|
|
test('returns true for a comma-separated multi-row paste', () => {
|
|
expect(looksLikeCsv('a,b,c\n1,2,3')).toBe(true);
|
|
});
|
|
|
|
test('returns false for a single row', () => {
|
|
expect(looksLikeCsv('a,b,c')).toBe(false);
|
|
});
|
|
|
|
test("returns false when the column counts don't line up at all", () => {
|
|
expect(looksLikeCsv('a,b,c\n1\n2,3,4,5')).toBe(false);
|
|
});
|
|
|
|
test('returns false for very short / empty input', () => {
|
|
expect(looksLikeCsv('')).toBe(false);
|
|
expect(looksLikeCsv('ab')).toBe(false);
|
|
expect(looksLikeCsv(null)).toBe(false);
|
|
});
|
|
|
|
test('returns false for prose (no delimiter)', () => {
|
|
expect(
|
|
looksLikeCsv('The quick brown fox jumps over the lazy dog.\nThe cat sat on the mat.')
|
|
).toBe(false);
|
|
});
|
|
});
|