test(editor-ext): extract shared ProseMirror table-test fixture (F1)
The schema + cell/row/table/doc builders + grid/stateFor/trFor were copied verbatim into the 3 new table-utils test files (and the pre-existing table-utils.test.ts) — a schema change would have to be synced across all four. Move them into a shared table-test-helpers.ts (test-only, excluded from the build like footnote-corpus.ts) and import it everywhere; cell uses the (txt, attrs?) superset (a drop-in for the bare (txt) copies). No assertion changes — test counts unchanged (223 passed + 3 expected-fail). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { Schema } from "@tiptap/pm/model";
|
||||
import type { Node as PMNode } from "@tiptap/pm/model";
|
||||
import { tableNodes } from "@tiptap/pm/tables";
|
||||
import { EditorState, Selection } from "@tiptap/pm/state";
|
||||
import { getSelectionRangeInColumn } from "./get-selection-range-in-column";
|
||||
import { cell, row, table, doc, trFor } from "./table-test-helpers";
|
||||
|
||||
/**
|
||||
* getSelectionRangeInColumn computes the rectangular column range (the set of
|
||||
@@ -13,36 +10,6 @@ import { getSelectionRangeInColumn } from "./get-selection-range-in-column";
|
||||
* real EditorState whose selection sits inside the table.
|
||||
*/
|
||||
|
||||
// Real ProseMirror table schema (same primitives the editor uses) so TableMap /
|
||||
// cellsInRect behave exactly as in production.
|
||||
const tNodes = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "inline*",
|
||||
cellAttributes: {},
|
||||
});
|
||||
const schema = new Schema({
|
||||
nodes: {
|
||||
doc: { content: "block+" },
|
||||
paragraph: { group: "block", content: "inline*", toDOM: () => ["p", 0] },
|
||||
text: { group: "inline" },
|
||||
...tNodes,
|
||||
},
|
||||
marks: {},
|
||||
});
|
||||
const cell = (txt: string, attrs?: Record<string, unknown>): PMNode =>
|
||||
schema.nodes.table_cell.createChecked(attrs ?? null, schema.text(txt));
|
||||
const row = (...cells: PMNode[]): PMNode =>
|
||||
schema.nodes.table_row.createChecked(null, cells);
|
||||
const table = (...rows: PMNode[]): PMNode =>
|
||||
schema.nodes.table.createChecked(null, rows);
|
||||
const doc = (...content: PMNode[]): PMNode =>
|
||||
schema.nodes.doc.createChecked(null, content);
|
||||
|
||||
// Build a transaction whose selection is inside the table (the function locates
|
||||
// the table via `tr.selection.$from`).
|
||||
const trFor = (d: PMNode) =>
|
||||
EditorState.create({ doc: d, selection: Selection.atStart(d) }).tr;
|
||||
|
||||
// A 2-row x 3-col grid; each column is identifiable by its top-row letter.
|
||||
const grid3x2 = () =>
|
||||
doc(
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { Schema } from "@tiptap/pm/model";
|
||||
import type { Node as PMNode } from "@tiptap/pm/model";
|
||||
import { tableNodes, CellSelection } from "@tiptap/pm/tables";
|
||||
import { EditorState, Selection } from "@tiptap/pm/state";
|
||||
import { CellSelection } from "@tiptap/pm/tables";
|
||||
import { moveColumn } from "./move-column";
|
||||
import { convertTableNodeToArrayOfRows } from "./convert-table-node-to-array-of-rows";
|
||||
import { findTable } from "./query";
|
||||
import {
|
||||
schema,
|
||||
cell,
|
||||
row,
|
||||
table,
|
||||
doc,
|
||||
grid,
|
||||
stateFor,
|
||||
} from "./table-test-helpers";
|
||||
|
||||
/**
|
||||
* moveColumn reorders whole columns of a real ProseMirror table by mutating a
|
||||
@@ -14,36 +18,6 @@ import { findTable } from "./query";
|
||||
* cell's content preserved and nothing dropped or duplicated.
|
||||
*/
|
||||
|
||||
const tNodes = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "inline*",
|
||||
cellAttributes: {},
|
||||
});
|
||||
const schema = new Schema({
|
||||
nodes: {
|
||||
doc: { content: "block+" },
|
||||
paragraph: { group: "block", content: "inline*", toDOM: () => ["p", 0] },
|
||||
text: { group: "inline" },
|
||||
...tNodes,
|
||||
},
|
||||
marks: {},
|
||||
});
|
||||
const cell = (txt: string): PMNode =>
|
||||
schema.nodes.table_cell.createChecked(null, schema.text(txt));
|
||||
const row = (...cells: PMNode[]): PMNode =>
|
||||
schema.nodes.table_row.createChecked(null, cells);
|
||||
const table = (...rows: PMNode[]): PMNode =>
|
||||
schema.nodes.table.createChecked(null, rows);
|
||||
const doc = (...content: PMNode[]): PMNode =>
|
||||
schema.nodes.doc.createChecked(null, content);
|
||||
|
||||
const grid = (tr: any): string[][] => {
|
||||
const t = findTable(tr.doc.resolve(tr.selection.from))!;
|
||||
return convertTableNodeToArrayOfRows(t.node).map((r) =>
|
||||
r.map((c) => (c ? c.textContent : "")),
|
||||
);
|
||||
};
|
||||
|
||||
// 2-row x 3-col table; column k is (rowX-col-k). Columns: 0=(a,d) 1=(b,e) 2=(c,f).
|
||||
const grid3x2 = () =>
|
||||
doc(
|
||||
@@ -53,9 +27,6 @@ const grid3x2 = () =>
|
||||
),
|
||||
);
|
||||
|
||||
const stateFor = (d: PMNode) =>
|
||||
EditorState.create({ doc: d, selection: Selection.atStart(d) });
|
||||
|
||||
describe("moveColumn", () => {
|
||||
it("moves the first column to the last index, preserving column content", () => {
|
||||
// origin 0 -> target 2 sends column (a,d) to the right: cols become 1,2,0.
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { Schema } from "@tiptap/pm/model";
|
||||
import type { Node as PMNode } from "@tiptap/pm/model";
|
||||
import { tableNodes, CellSelection } from "@tiptap/pm/tables";
|
||||
import { EditorState, Selection } from "@tiptap/pm/state";
|
||||
import { CellSelection } from "@tiptap/pm/tables";
|
||||
import { moveRow } from "./move-row";
|
||||
import { convertTableNodeToArrayOfRows } from "./convert-table-node-to-array-of-rows";
|
||||
import { findTable } from "./query";
|
||||
import {
|
||||
schema,
|
||||
cell,
|
||||
row,
|
||||
table,
|
||||
doc,
|
||||
grid,
|
||||
stateFor,
|
||||
} from "./table-test-helpers";
|
||||
|
||||
/**
|
||||
* moveRow reorders whole rows of a real ProseMirror table by mutating a
|
||||
@@ -15,38 +19,6 @@ import { findTable } from "./query";
|
||||
* content preserved, and no rows are dropped or duplicated.
|
||||
*/
|
||||
|
||||
const tNodes = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "inline*",
|
||||
cellAttributes: {},
|
||||
});
|
||||
const schema = new Schema({
|
||||
nodes: {
|
||||
doc: { content: "block+" },
|
||||
paragraph: { group: "block", content: "inline*", toDOM: () => ["p", 0] },
|
||||
text: { group: "inline" },
|
||||
...tNodes,
|
||||
},
|
||||
marks: {},
|
||||
});
|
||||
const cell = (txt: string): PMNode =>
|
||||
schema.nodes.table_cell.createChecked(null, schema.text(txt));
|
||||
const row = (...cells: PMNode[]): PMNode =>
|
||||
schema.nodes.table_row.createChecked(null, cells);
|
||||
const table = (...rows: PMNode[]): PMNode =>
|
||||
schema.nodes.table.createChecked(null, rows);
|
||||
const doc = (...content: PMNode[]): PMNode =>
|
||||
schema.nodes.doc.createChecked(null, content);
|
||||
|
||||
// Read the table's content as a grid of cell texts (rows x cols) from whatever
|
||||
// table currently lives in `tr.doc`.
|
||||
const grid = (tr: any): string[][] => {
|
||||
const t = findTable(tr.doc.resolve(tr.selection.from))!;
|
||||
return convertTableNodeToArrayOfRows(t.node).map((r) =>
|
||||
r.map((c) => (c ? c.textContent : "")),
|
||||
);
|
||||
};
|
||||
|
||||
// 3-row x 2-col table; each row identifiable by its cells.
|
||||
const grid2x3 = () =>
|
||||
doc(
|
||||
@@ -57,9 +29,6 @@ const grid2x3 = () =>
|
||||
),
|
||||
);
|
||||
|
||||
const stateFor = (d: PMNode) =>
|
||||
EditorState.create({ doc: d, selection: Selection.atStart(d) });
|
||||
|
||||
describe("moveRow", () => {
|
||||
it("moves the first row down to the last index, preserving content", () => {
|
||||
// origin 0 -> target 2 makes row 0 land after the other rows: [r1, r2, r0].
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Schema } from "@tiptap/pm/model";
|
||||
import type { Node as PMNode } from "@tiptap/pm/model";
|
||||
import { tableNodes } from "@tiptap/pm/tables";
|
||||
import { EditorState, Selection } from "@tiptap/pm/state";
|
||||
import { findTable } from "./query";
|
||||
import { convertTableNodeToArrayOfRows } from "./convert-table-node-to-array-of-rows";
|
||||
|
||||
/**
|
||||
* Shared test fixtures for the table utility tests. Several test files exercise
|
||||
* the row/column move and selection helpers against a real ProseMirror table
|
||||
* schema (the same primitives the editor uses) so TableMap / cellsInRect behave
|
||||
* exactly as in production. Keeping the schema and node builders in one place
|
||||
* means a schema change (e.g. cellAttributes) is applied once instead of being
|
||||
* copied across every test file.
|
||||
*
|
||||
* This is a test-only helper (not shipped) and intentionally contains no test
|
||||
* cases, so vitest does not pick it up as a spec file.
|
||||
*/
|
||||
|
||||
const tNodes = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "inline*",
|
||||
cellAttributes: {},
|
||||
});
|
||||
|
||||
export const schema = new Schema({
|
||||
nodes: {
|
||||
doc: { content: "block+" },
|
||||
paragraph: { group: "block", content: "inline*", toDOM: () => ["p", 0] },
|
||||
text: { group: "inline" },
|
||||
...tNodes,
|
||||
},
|
||||
marks: {},
|
||||
});
|
||||
|
||||
export const cell = (txt: string, attrs?: Record<string, unknown>): PMNode =>
|
||||
schema.nodes.table_cell.createChecked(attrs ?? null, schema.text(txt));
|
||||
export const row = (...cells: PMNode[]): PMNode =>
|
||||
schema.nodes.table_row.createChecked(null, cells);
|
||||
export const table = (...rows: PMNode[]): PMNode =>
|
||||
schema.nodes.table.createChecked(null, rows);
|
||||
export const doc = (...content: PMNode[]): PMNode =>
|
||||
schema.nodes.doc.createChecked(null, content);
|
||||
|
||||
// Read the table's content as a grid of cell texts (rows x cols) from whatever
|
||||
// table currently lives in `tr.doc`.
|
||||
export const grid = (tr: any): string[][] => {
|
||||
const t = findTable(tr.doc.resolve(tr.selection.from))!;
|
||||
return convertTableNodeToArrayOfRows(t.node).map((r) =>
|
||||
r.map((c) => (c ? c.textContent : "")),
|
||||
);
|
||||
};
|
||||
|
||||
export const stateFor = (d: PMNode) =>
|
||||
EditorState.create({ doc: d, selection: Selection.atStart(d) });
|
||||
|
||||
// Build a transaction whose selection is inside the doc (helpers locate the
|
||||
// table via `tr.selection.$from`).
|
||||
export const trFor = (d: PMNode) => stateFor(d).tr;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { Schema } from "@tiptap/pm/model";
|
||||
import type { Node as PMNode } from "@tiptap/pm/model";
|
||||
import { tableNodes, TableMap } from "@tiptap/pm/tables";
|
||||
import { TableMap } from "@tiptap/pm/tables";
|
||||
import { transpose } from "./transpose";
|
||||
import { moveRowInArrayOfRows } from "./move-row-in-array-of-rows";
|
||||
import { convertTableNodeToArrayOfRows } from "./convert-table-node-to-array-of-rows";
|
||||
import { convertArrayOfRowsToTableNode } from "./convert-array-of-rows-to-table-node";
|
||||
import { cell, row, table } from "./table-test-helpers";
|
||||
|
||||
/**
|
||||
* Unit tests for the pure table data-transformation utilities. These functions
|
||||
@@ -14,30 +14,6 @@ import { convertArrayOfRowsToTableNode } from "./convert-array-of-rows-to-table-
|
||||
* ProseMirror table schema (the same primitives the editor uses).
|
||||
*/
|
||||
|
||||
// Minimal schema containing real ProseMirror table nodes so TableMap behaves
|
||||
// exactly as it does in the editor (merged cells, colspan, etc.).
|
||||
const tNodes = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "inline*",
|
||||
cellAttributes: {},
|
||||
});
|
||||
const schema = new Schema({
|
||||
nodes: {
|
||||
doc: { content: "block+" },
|
||||
paragraph: { group: "block", content: "inline*", toDOM: () => ["p", 0] },
|
||||
text: { group: "inline" },
|
||||
...tNodes,
|
||||
},
|
||||
marks: {},
|
||||
});
|
||||
|
||||
const cell = (txt: string, attrs?: Record<string, unknown>): PMNode =>
|
||||
schema.nodes.table_cell.createChecked(attrs ?? null, schema.text(txt));
|
||||
const row = (...cells: PMNode[]): PMNode =>
|
||||
schema.nodes.table_row.createChecked(null, cells);
|
||||
const table = (...rows: PMNode[]): PMNode =>
|
||||
schema.nodes.table.createChecked(null, rows);
|
||||
|
||||
// Read the text content of each (non-null) cell so we can compare structure
|
||||
// without depending on ProseMirror node identity.
|
||||
const textGrid = (rows: (PMNode | null)[][]): (string | null)[][] =>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"dist",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/lib/footnote/footnote-corpus.ts"
|
||||
"src/lib/footnote/footnote-corpus.ts",
|
||||
"src/lib/table/utils/table-test-helpers.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user