57 lines · 3.2 KB · TypeScript
RawDownload
import { describe, expect, it } from "vitest";import { EditorState } from "@codemirror/state";import { history, undo } from "@codemirror/commands";import { managedTableSource, newTableData, parseTableData, tableAtCursor, tableSource, validTableData } from "./table-data"; describe("data table round trips", () => {  it("imports quoted CSV, Unicode, empty fields and multiline spreadsheet cells", () => {    expect(parseTableData('方法,score,note,empty\r\n"A,B",0.0123,"a ""quote""",\r\n', ","))      .toEqual([["方法", "score", "note", "empty"], ["A,B", "0.0123", 'a "quote"', ""]]);    expect(parseTableData('a\t"two\nlines"\t\n', "\t")).toEqual([["a", "two\nlines", ""]]);    expect(parseTableData('a,b\nc', ",")).toEqual([["a", "b"], ["c", ""]]);  });  it("rejects malformed quotes and oversized data without truncating", () => {    for (const input of ['"open', '"a"x,b', Array(22).fill("a").join("\n"), Array(21).fill("a").join(",")]) {      expect(parseTableData(input, ",")).toBeNull();    }  });  it("retains a 5 by 4 experiment, raw math, formatting and caption across updates", () => {    const data = newTableData();    data.cells = parseTableData('方法\taccuracy\tnote\tformula\nA&B\t0.12345\t50%\t$x_1$\n中文\t.5\t\t\nC\t1\t_#{}\\~^$\t\nD\t0.2\t\t', "\t")!      .map((row) => row.map((text) => ({ text, latex: false })));    data.cells[1][3].latex = true;    data.align = ["l", "r", "c", "l"];    data.decimals = 3;    data.caption = "中文 & 50%";    data.label = "tab:results";    expect(validTableData(data)).toBe(true);    const source = tableSource(data);    expect(source).toContain("A\\&B & 0.123 & 50\\% & $x_1$");    expect(source).toContain("\\caption{中文 \\& 50\\%}");    expect(source).toContain("\\begin{tabular}{lrcl}");    const first = managedTableSource(data);    expect(tableAtCursor(first, first.indexOf("\\begin"))?.data).toEqual(data);    data.cells[1][1].text = "0.9";    const second = managedTableSource(data);    expect(tableAtCursor(second, second.indexOf("\\begin"))?.data).toEqual(data);    // Replace exactly one table, leaving a second copy and surrounding prose intact.    const before = `intro\n${first}middle\n${first}outro`;    const target = tableAtCursor(before, before.indexOf("\\begin"))!;    let state = EditorState.create({ doc: before, extensions: [history()] });    state = state.update({ changes: { from: target.from, to: target.to, insert: second } }).state;    expect(state.doc.toString()).toBe(`intro\n${second}middle\n${first}outro`);    undo({ state, dispatch: (transaction) => { state = transaction.state; } });    expect(state.doc.toString()).toBe(before);  });  it("refuses hand edits, malformed metadata and structural raw TeX", () => {    const data = newTableData();    const source = managedTableSource(data);    for (const changed of [source.replace("Col 1", "Manual"), source.replace("%7B", "%XX")]) {      expect(tableAtCursor(changed, changed.indexOf("\\begin"))?.data).toBeNull();    }    data.cells[0][0] = { text: "\\begin{tabular}{l}", latex: true };    expect(validTableData(data)).toBe(false);    expect(tableAtCursor(source, source.length)).toBeNull();    expect(tableAtCursor(source, source.indexOf("\\begin"))?.data).not.toBeNull();  });});