diff --git a/packages/editor-ext/src/lib/table/utils/get-selection-range-in-column.test.ts b/packages/editor-ext/src/lib/table/utils/get-selection-range-in-column.test.ts index 7e623c60..70e0eed6 100644 --- a/packages/editor-ext/src/lib/table/utils/get-selection-range-in-column.test.ts +++ b/packages/editor-ext/src/lib/table/utils/get-selection-range-in-column.test.ts @@ -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): 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( diff --git a/packages/editor-ext/src/lib/table/utils/move-column.test.ts b/packages/editor-ext/src/lib/table/utils/move-column.test.ts index 5b2d60e5..da524b8f 100644 --- a/packages/editor-ext/src/lib/table/utils/move-column.test.ts +++ b/packages/editor-ext/src/lib/table/utils/move-column.test.ts @@ -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. diff --git a/packages/editor-ext/src/lib/table/utils/move-row.test.ts b/packages/editor-ext/src/lib/table/utils/move-row.test.ts index 3a0c481a..462d98fd 100644 --- a/packages/editor-ext/src/lib/table/utils/move-row.test.ts +++ b/packages/editor-ext/src/lib/table/utils/move-row.test.ts @@ -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]. diff --git a/packages/editor-ext/src/lib/table/utils/table-test-helpers.ts b/packages/editor-ext/src/lib/table/utils/table-test-helpers.ts new file mode 100644 index 00000000..035689df --- /dev/null +++ b/packages/editor-ext/src/lib/table/utils/table-test-helpers.ts @@ -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): 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; diff --git a/packages/editor-ext/src/lib/table/utils/table-utils.test.ts b/packages/editor-ext/src/lib/table/utils/table-utils.test.ts index d8c964a2..70f59996 100644 --- a/packages/editor-ext/src/lib/table/utils/table-utils.test.ts +++ b/packages/editor-ext/src/lib/table/utils/table-utils.test.ts @@ -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): 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)[][] => diff --git a/packages/editor-ext/tsconfig.json b/packages/editor-ext/tsconfig.json index 5fcc2435..e9c235bd 100644 --- a/packages/editor-ext/tsconfig.json +++ b/packages/editor-ext/tsconfig.json @@ -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" ] }