From: Ardis Lu Date: Fri, 3 Jan 2025 04:32:41 +0000 (-0800) Subject: Refactor to delete redundant code X-Git-Tag: v0.1.0~143 X-Git-Url: https://git.ardis.lu/?a=commitdiff_plain;h=4a0994b0d61844be5713e79c3f3390ea1baab26d;p=csvio Refactor to delete redundant code --- diff --git a/test/core.test.js b/test/core.test.js index 22f51b2..b0699a0 100644 --- a/test/core.test.js +++ b/test/core.test.js @@ -2,7 +2,7 @@ import { suite, test } from 'node:test'; import { deepStrictEqual } from 'node:assert/strict'; import { unlink } from 'node:fs/promises'; -import { csvStreamEqual, csvStreamEqualWritable, createCSVMockStream, createTempFile } from './utils.js'; +import { csvStreamEqualWritable, createCSVMockStream, createTempFile } from './utils.js'; import { arrayToCSVString, createCSVReadableStream, createCSVTransformStream, createCSVWritableStream } from '../src/core.js'; suite('arrayToCSVString', { concurrency: true }, () => { @@ -24,119 +24,136 @@ suite('arrayToCSVString', { concurrency: true }, () => { }); suite('createCSVReadableStream', { concurrency: true }, () => { - test('parses simple CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/simple.csv'); - await csvStreamEqual(stream, [ - ['column1', 'column2', 'column3'], - ['abc', 'def', 'ghi'], - ['123', '456', '789'], - ['aaa', 'bbb', 'ccc'] - ]); - }); - test('parses escaping CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/escaping.csv'); - await csvStreamEqual(stream, [ - ['name', 'value'], - ['Three spaces', ' '], - ['Three commas', ',,,'], - ['Three newlines', '\r\n\r\n\r\n'], - ['Three unescaped double quotes', 'a"""'], - ['Three escaped double quotes', '"""'], - ['Unescaped double quotes around delimiter 1 "', 'a'], - ['Unescaped double quotes around delimiter 2 ""', 'a'], - ['Unescaped double quotes around delimiter 3 """', 'a'], - ['Unescaped double quotes around delimiter 4', ' "'], - ['Unescaped double quotes around delimiter 5', ' ""'], - ['Unescaped double quotes around delimiter 6', ' """'], - ['Spaces before and after a value', ' abc '], - ['Spaces before and after three unescaped double quotes', ' """ '], - ['Spaces trailing escaped double quotes', 'abc '], - ['Characters trailing escaped double quotes', 'abc def'], - ['Unescaped double quotes trailing escaped double quotes', 'abc " def 123 """ 456'], - ['Unescaped "double quotes" trailing escaped double quotes', 'abc """ def 123 """ 456'], - ['Unicode test 1', '你好'], - ['Unicode test 2', '😂👌👍'], - ['Unicode test 3', '🏴‍☠️👨‍👩‍👧‍👦'], - ['Mixed', ',\r\n",\r\n",\r\n"'] - ]); - }); - test('parses escaping edges CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/escaping-edges.csv'); - await csvStreamEqual(stream, [ - [',,,', '"""'], - ['"""', ',,,'], - ['a"b""', 'a"b""'], - ['a"b""', 'a"b""'] - ]); - }); - test('parses sparse CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/sparse.csv'); - await csvStreamEqual(stream, [ - ['column1', 'column2', 'column3'], - [''], - ['', ''], - ['', '', ''], - ['', '', '', ''], - ['', '', '', '', ''], - ['aaa', 'bbb', 'ccc'], - ['111', '222', '333', '444', '555', '666', '777', '888'], - ['', 'hhh', 'iii'], - ['ggg', '', 'iii'], - ['ggg', 'hhh', ''], - ['', 'hhh', ''], - ['ggg', '', ''], - ['', '', 'iii'], - ['', '', ''], - ['', '', ''], - ['', '', ''], - [''], - [''] - ]); - }); - test('parses line feed end-of-line CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/lf-eol.csv'); - await csvStreamEqual(stream, [ - ['column1', 'column2'], - ['a', 'b'], - ['c', 'd'], - ['1', '2'], - ['3', '4'] - ]); - }); - test('parses byte order mark CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/bom.csv'); - await csvStreamEqual(stream, [ - ['column1', 'column2'], - ['ab', 'cd'], - ['12', '34'] - ]); - }); - test('parses single value (no commas or newlines) CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/single-value.csv'); - await csvStreamEqual(stream, [ - ['abc'] - ]); - }); - test('parses single column (no commas) CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/single-column.csv'); - await csvStreamEqual(stream, [ - ['abc'], - ['def'], - ['ghi'] - ]); - }); - test('parses single row (no newlines) CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/single-row.csv'); - await csvStreamEqual(stream, [ - ['abc', 'def', 'ghi'] - ]); - }); - test('parses blank CSV', { concurrency: true }, async () => { - const stream = createCSVReadableStream('./test/data/blank.csv'); - await csvStreamEqual(stream, [ - [''] - ]); - }); + const vectors = [ + { + name: 'parses simple CSV', + input: './test/data/simple.csv', + output: [ + ['column1', 'column2', 'column3'], + ['abc', 'def', 'ghi'], + ['123', '456', '789'], + ['aaa', 'bbb', 'ccc'] + ] + }, + { + name: 'parses escaping CSV', + input: './test/data/escaping.csv', + output: [ + ['name', 'value'], + ['Three spaces', ' '], + ['Three commas', ',,,'], + ['Three newlines', '\r\n\r\n\r\n'], + ['Three unescaped double quotes', 'a"""'], + ['Three escaped double quotes', '"""'], + ['Unescaped double quotes around delimiter 1 "', 'a'], + ['Unescaped double quotes around delimiter 2 ""', 'a'], + ['Unescaped double quotes around delimiter 3 """', 'a'], + ['Unescaped double quotes around delimiter 4', ' "'], + ['Unescaped double quotes around delimiter 5', ' ""'], + ['Unescaped double quotes around delimiter 6', ' """'], + ['Spaces before and after a value', ' abc '], + ['Spaces before and after three unescaped double quotes', ' """ '], + ['Spaces trailing escaped double quotes', 'abc '], + ['Characters trailing escaped double quotes', 'abc def'], + ['Unescaped double quotes trailing escaped double quotes', 'abc " def 123 """ 456'], + ['Unescaped "double quotes" trailing escaped double quotes', 'abc """ def 123 """ 456'], + ['Unicode test 1', '你好'], + ['Unicode test 2', '😂👌👍'], + ['Unicode test 3', '🏴‍☠️👨‍👩‍👧‍👦'], + ['Mixed', ',\r\n",\r\n",\r\n"'] + ] + }, + { + name: 'parses escaping edges CSV', + input: './test/data/escaping-edges.csv', + output: [ + [',,,', '"""'], + ['"""', ',,,'], + ['a"b""', 'a"b""'], + ['a"b""', 'a"b""'] + ] + }, + { + name: 'parses sparse CSV', + input: './test/data/sparse.csv', + output: [ + ['column1', 'column2', 'column3'], + [''], + ['', ''], + ['', '', ''], + ['', '', '', ''], + ['', '', '', '', ''], + ['aaa', 'bbb', 'ccc'], + ['111', '222', '333', '444', '555', '666', '777', '888'], + ['', 'hhh', 'iii'], + ['ggg', '', 'iii'], + ['ggg', 'hhh', ''], + ['', 'hhh', ''], + ['ggg', '', ''], + ['', '', 'iii'], + ['', '', ''], + ['', '', ''], + ['', '', ''], + [''], + [''] + ] + }, + { + name: 'parses line feed end-of-line CSV', + input: './test/data/lf-eol.csv', + output: [ + ['column1', 'column2'], + ['a', 'b'], + ['c', 'd'], + ['1', '2'], + ['3', '4'] + ] + }, + { + name: 'parses byte order mark CSV', + input: './test/data/bom.csv', + output: [ + ['column1', 'column2'], + ['ab', 'cd'], + ['12', '34'] + ] + }, + { + name: 'parses single value (no commas or newlines) CSV', + input: './test/data/single-value.csv', + output: [ + ['abc'] + ] + }, + { + name: 'parses single column (no commas) CSV', + input: './test/data/single-column.csv', + output: [ + ['abc'], + ['def'], + ['ghi'] + ] + }, + { + name: 'parses single row (no newlines) CSV', + input: './test/data/single-row.csv', + output: [ + ['abc', 'def', 'ghi'] + ] + }, + { + name: 'parses blank CSV', + input: './test/data/blank.csv', + output: [ + [''] + ] + } + ]; + for (const { name, input, output } of vectors) { + test(name, { concurrency: true }, async () => { + await createCSVReadableStream(input).pipeTo(csvStreamEqualWritable(output)); + }); + } }); suite('createCSVTransformStream', { concurrency: true }, () => { @@ -144,22 +161,23 @@ suite('createCSVTransformStream', { concurrency: true }, () => { function timesTwo(row) { return [Number(row[0]) * 2, Number(row[1]) * 2]; } - const stream = createCSVMockStream([ + await createCSVMockStream([ ['columnA', 'columnB'], ['1', '1'], ['100', '100'], ['223423', '455947'], ['348553', '692708'], ['536368', '676147'] - ]).pipeThrough(createCSVTransformStream(timesTwo)); - await csvStreamEqual(stream, [ - ['columnA', 'columnB'], - [2, 2], - [200, 200], - [446846, 911894], - [697106, 1385416], - [1072736, 1352294] - ]); + ]) + .pipeThrough(createCSVTransformStream(timesTwo)) + .pipeTo(csvStreamEqualWritable([ + ['columnA', 'columnB'], + [2, 2], + [200, 200], + [446846, 911894], + [697106, 1385416], + [1072736, 1352294] + ])); }); test('can add new column', { concurrency: true }, async () => { let firstRow = true; @@ -170,132 +188,130 @@ suite('createCSVTransformStream', { concurrency: true }, () => { } return [...row, Number(row[0]) + Number(row[1])]; } - const stream = createCSVMockStream([ + await createCSVMockStream([ ['columnA', 'columnB'], ['1', '1'], ['100', '100'], ['223423', '455947'], ['348553', '692708'], ['536368', '676147'] - ]).pipeThrough(createCSVTransformStream(sum, { includeHeaders: true })); - await csvStreamEqual(stream, [ - ['columnA', 'columnB', 'sum'], - ['1', '1', 2], - ['100', '100', 200], - ['223423', '455947', 679370], - ['348553', '692708', 1041261], - ['536368', '676147', 1212515] - ]); + ]) + .pipeThrough(createCSVTransformStream(sum, { includeHeaders: true })) + .pipeTo(csvStreamEqualWritable([ + ['columnA', 'columnB', 'sum'], + ['1', '1', 2], + ['100', '100', 200], + ['223423', '455947', 679370], + ['348553', '692708', 1041261], + ['536368', '676147', 1212515] + ])); }); }); suite('createCSVWritableStream', { concurrency: true }, () => { - test('writes simple CSV', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - ['column1', 'column2', 'column3'], - ['abc', 'def', 'ghi'], - ['123', '456', '789'], - ['aaa', 'bbb', 'ccc'] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); - test('writes escaping CSV', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - ['name', 'value'], - ['Three spaces', ' '], - ['Three commas', ',,,'], - ['Three newlines', '\r\n\r\n\r\n'], - ['Three unescaped double quotes', 'a"""'], - ['Three escaped double quotes', '"""'], - ['Unescaped double quotes around delimiter 1 "', 'a'], - ['Unescaped double quotes around delimiter 2 ""', 'a'], - ['Unescaped double quotes around delimiter 3 """', 'a'], - ['Unescaped double quotes around delimiter 4', ' "'], - ['Unescaped double quotes around delimiter 5', ' ""'], - ['Unescaped double quotes around delimiter 6', ' """'], - ['Spaces before and after a value', ' abc '], - ['Spaces before and after three unescaped double quotes', ' """ '], - ['Spaces trailing escaped double quotes', 'abc '], - ['Characters trailing escaped double quotes', 'abc def'], - ['Unescaped double quotes trailing escaped double quotes', 'abc " def 123 """ 456'], - ['Unescaped "double quotes" trailing escaped double quotes', 'abc """ def 123 """ 456'], - ['Unicode test 1', '你好'], - ['Unicode test 2', '😂👌👍'], - ['Unicode test 3', '🏴‍☠️👨‍👩‍👧‍👦'], - ['Mixed', ',\r\n",\r\n",\r\n"'] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); - test('writes escaping edges CSV', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - [',,,', '"""'], - ['"""', ',,,'], - ['a"b""', 'a"b""'], - ['a"b""', 'a"b""'] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); - test('writes sparse CSV', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - ['column1', 'column2', 'column3'], - [''], - ['', ''], - ['', '', ''], - ['', '', '', ''], - ['', '', '', '', ''], - ['aaa', 'bbb', 'ccc'], - ['111', '222', '333', '444', '555', '666', '777', '888'], - ['', 'hhh', 'iii'], - ['ggg', '', 'iii'], - ['ggg', 'hhh', ''], - ['', 'hhh', ''], - ['ggg', '', ''], - ['', '', 'iii'], - ['', '', ''], - ['', '', ''], - ['', '', ''], - [''], - [''] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); - test('writes single value (no commas or newlines)', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - ['abc'] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); - test('writes single row (no newlines) CSV', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - ['abc', 'def', 'ghi'] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); - test('writes blank CSV', { concurrency: true }, async (t) => { - const temp = await createTempFile(); - t.after(async () => await unlink(temp)); - const csv = [ - [''] - ]; - await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); - await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); - }); + const vectors = [ + { + name: 'writes simple CSV', + csv: [ + ['column1', 'column2', 'column3'], + ['abc', 'def', 'ghi'], + ['123', '456', '789'], + ['aaa', 'bbb', 'ccc'] + ] + }, + { + name: 'writes escaping CSV', + csv: [ + ['name', 'value'], + ['Three spaces', ' '], + ['Three commas', ',,,'], + ['Three newlines', '\r\n\r\n\r\n'], + ['Three unescaped double quotes', 'a"""'], + ['Three escaped double quotes', '"""'], + ['Unescaped double quotes around delimiter 1 "', 'a'], + ['Unescaped double quotes around delimiter 2 ""', 'a'], + ['Unescaped double quotes around delimiter 3 """', 'a'], + ['Unescaped double quotes around delimiter 4', ' "'], + ['Unescaped double quotes around delimiter 5', ' ""'], + ['Unescaped double quotes around delimiter 6', ' """'], + ['Spaces before and after a value', ' abc '], + ['Spaces before and after three unescaped double quotes', ' """ '], + ['Spaces trailing escaped double quotes', 'abc '], + ['Characters trailing escaped double quotes', 'abc def'], + ['Unescaped double quotes trailing escaped double quotes', 'abc " def 123 """ 456'], + ['Unescaped "double quotes" trailing escaped double quotes', 'abc """ def 123 """ 456'], + ['Unicode test 1', '你好'], + ['Unicode test 2', '😂👌👍'], + ['Unicode test 3', '🏴‍☠️👨‍👩‍👧‍👦'], + ['Mixed', ',\r\n",\r\n",\r\n"'] + ] + }, + { + name: 'writes escaping edges CSV', + csv: [ + [',,,', '"""'], + ['"""', ',,,'], + ['a"b""', 'a"b""'], + ['a"b""', 'a"b""'] + ] + }, + { + name: 'writes sparse CSV', + csv: [ + ['column1', 'column2', 'column3'], + [''], + ['', ''], + ['', '', ''], + ['', '', '', ''], + ['', '', '', '', ''], + ['aaa', 'bbb', 'ccc'], + ['111', '222', '333', '444', '555', '666', '777', '888'], + ['', 'hhh', 'iii'], + ['ggg', '', 'iii'], + ['ggg', 'hhh', ''], + ['', 'hhh', ''], + ['ggg', '', ''], + ['', '', 'iii'], + ['', '', ''], + ['', '', ''], + ['', '', ''], + [''], + [''] + ] + }, + { + name: 'writes single value (no commas or newlines) CSV', + csv: [ + ['abc'] + ] + }, + { + name: 'writes single column (no commas) CSV', + csv: [ + ['abc'], + ['def'], + ['ghi'] + ] + }, + { + name: 'writes single row (no newlines) CSV', + csv: [ + ['abc', 'def', 'ghi'] + ] + }, + { + name: 'writes blank CSV', + csv: [ + [''] + ] + } + ]; + for (const { name, csv } of vectors) { + test(name, { concurrency: true }, async (t) => { + const temp = await createTempFile(); + t.after(async () => await unlink(temp)); + await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp)); + await createCSVReadableStream(temp).pipeTo(csvStreamEqualWritable(csv)); + }); + } }); \ No newline at end of file