SurfSense/surfsense_web/lib/playground/csv.selfcheck.ts

43 lines
1.5 KiB
TypeScript
Raw Normal View History

2026-07-06 16:45:04 -07:00
/**
* Runnable self-check for the playground CSV serializer. No test framework
* run with: ``npx tsx lib/playground/csv.selfcheck.ts``
* Exits non-zero on the first failed assertion.
*/
import assert from "node:assert/strict";
import { rowsToCsv } from "./csv";
// Union of keys, stable order, header + rows joined with CRLF.
assert.equal(
rowsToCsv([
{ a: 1, b: "x" },
{ a: 2, c: true },
]),
"a,b,c\r\n1,x,\r\n2,,true"
);
// Explicit columns fix order and subset.
assert.equal(rowsToCsv([{ a: 1, b: 2, c: 3 }], ["c", "a"]), "c,a\r\n3,1");
// RFC 4180 quoting: commas, quotes, and newlines.
assert.equal(rowsToCsv([{ v: 'he said "hi", bye' }]), 'v\r\n"he said ""hi"", bye"');
assert.equal(rowsToCsv([{ v: "line1\nline2" }]), 'v\r\n"line1\nline2"');
// Objects/arrays are JSON-stringified (then quoted for the comma).
assert.equal(rowsToCsv([{ v: { k: 1 } }]), 'v\r\n"{""k"":1}"');
// Null/undefined become empty cells.
assert.equal(rowsToCsv([{ v: null }]), "v\r\n");
// Formula-injection guard: dangerous leads get a ' prefix, real numbers don't.
assert.equal(rowsToCsv([{ v: "=1+2" }]), "v\r\n'=1+2");
assert.equal(rowsToCsv([{ v: "+1-800" }]), "v\r\n'+1-800");
assert.equal(rowsToCsv([{ v: "@handle" }]), "v\r\n'@handle");
assert.equal(rowsToCsv([{ v: "-cmd" }]), "v\r\n'-cmd");
assert.equal(rowsToCsv([{ v: "-5" }]), "v\r\n-5");
assert.equal(rowsToCsv([{ v: -5 }]), "v\r\n-5");
// Header only when there are no rows.
assert.equal(rowsToCsv([], ["a", "b"]), "a,b");
console.log("csv.selfcheck: all assertions passed");