Files
emdash-cms--emdash/packages/admin/tests/components/PortableTextEditor.table.test.ts
T
wehub-resource-sync b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:23:53 +08:00

402 lines
9.2 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
_portableTextToProsemirror,
_prosemirrorToPortableText,
} from "../../src/components/PortableTextEditor";
describe("Table conversion: PortableText ↔ ProseMirror", () => {
describe("PortableText → ProseMirror", () => {
it("converts simple table with text", () => {
const ptBlocks = [
{
_type: "table",
_key: "t1",
hasHeaderRow: true,
rows: [
{
_type: "tableRow",
_key: "r1",
cells: [
{
_type: "tableCell",
_key: "c1",
isHeader: true,
content: [{ _type: "span", _key: "s1", text: "Header 1" }],
},
{
_type: "tableCell",
_key: "c2",
isHeader: true,
content: [{ _type: "span", _key: "s2", text: "Header 2" }],
},
],
},
{
_type: "tableRow",
_key: "r2",
cells: [
{
_type: "tableCell",
_key: "c3",
content: [{ _type: "span", _key: "s3", text: "Cell 1" }],
},
{
_type: "tableCell",
_key: "c4",
content: [{ _type: "span", _key: "s4", text: "Cell 2" }],
},
],
},
],
},
];
const result = _portableTextToProsemirror(ptBlocks);
expect(result.type).toBe("doc");
expect(result.content).toHaveLength(1);
const table = result.content[0];
expect(table.type).toBe("table");
expect(table.content).toHaveLength(2);
const headerRow = table.content[0];
expect(headerRow.type).toBe("tableRow");
expect(headerRow.content[0].type).toBe("tableHeader");
expect(headerRow.content[1].type).toBe("tableHeader");
const dataRow = table.content[1];
expect(dataRow.type).toBe("tableRow");
expect(dataRow.content[0].type).toBe("tableCell");
});
it("converts table with text formatting marks", () => {
const ptBlocks = [
{
_type: "table",
_key: "t1",
rows: [
{
_type: "tableRow",
_key: "r1",
cells: [
{
_type: "tableCell",
_key: "c1",
content: [{ _type: "span", _key: "s1", text: "Bold", marks: ["strong"] }],
},
{
_type: "tableCell",
_key: "c2",
content: [{ _type: "span", _key: "s2", text: "Italic", marks: ["em"] }],
},
],
},
],
},
];
const result = _portableTextToProsemirror(ptBlocks);
const table = result.content[0];
const cell1 = table.content[0].content[0];
const cell1Para = cell1.content[0];
const cell1Text = cell1Para.content[0];
expect(cell1Text.text).toBe("Bold");
expect(cell1Text.marks).toContainEqual({ type: "bold" });
const cell2 = table.content[0].content[1];
const cell2Para = cell2.content[0];
const cell2Text = cell2Para.content[0];
expect(cell2Text.text).toBe("Italic");
expect(cell2Text.marks).toContainEqual({ type: "italic" });
});
it("converts table with links (markDefs)", () => {
const ptBlocks = [
{
_type: "table",
_key: "t1",
markDefs: [
{
_type: "link",
_key: "link1",
href: "https://example.com",
blank: true,
},
],
rows: [
{
_type: "tableRow",
_key: "r1",
cells: [
{
_type: "tableCell",
_key: "c1",
content: [{ _type: "span", _key: "s1", text: "Click here", marks: ["link1"] }],
},
],
},
],
},
];
const result = _portableTextToProsemirror(ptBlocks);
const table = result.content[0];
const cell = table.content[0].content[0];
const para = cell.content[0];
const text = para.content[0];
expect(text.text).toBe("Click here");
expect(text.marks).toContainEqual({
type: "link",
attrs: { href: "https://example.com", target: "_blank" },
});
});
});
describe("ProseMirror → PortableText", () => {
it("converts simple table to PT", () => {
const pmDoc = {
type: "doc",
content: [
{
type: "table",
content: [
{
type: "tableRow",
content: [
{
type: "tableHeader",
content: [
{
type: "paragraph",
content: [{ type: "text", text: "Header 1" }],
},
],
},
{
type: "tableHeader",
content: [
{
type: "paragraph",
content: [{ type: "text", text: "Header 2" }],
},
],
},
],
},
{
type: "tableRow",
content: [
{
type: "tableCell",
content: [
{
type: "paragraph",
content: [{ type: "text", text: "Cell 1" }],
},
],
},
{
type: "tableCell",
content: [
{
type: "paragraph",
content: [{ type: "text", text: "Cell 2" }],
},
],
},
],
},
],
},
],
};
const result = _prosemirrorToPortableText(pmDoc);
expect(result).toHaveLength(1);
const table = result[0] as {
_type: string;
rows: Array<{
cells: Array<{ content: Array<{ text: string }>; isHeader: boolean }>;
}>;
hasHeaderRow: boolean;
};
expect(table._type).toBe("table");
expect(table.hasHeaderRow).toBe(true);
expect(table.rows).toHaveLength(2);
expect(table.rows[0].cells[0].isHeader).toBe(true);
expect(table.rows[0].cells[0].content[0].text).toBe("Header 1");
expect(table.rows[1].cells[0].isHeader).toBe(false);
expect(table.rows[1].cells[0].content[0].text).toBe("Cell 1");
});
it("converts table with text marks to PT", () => {
const pmDoc = {
type: "doc",
content: [
{
type: "table",
content: [
{
type: "tableRow",
content: [
{
type: "tableCell",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "Bold text",
marks: [{ type: "bold" }],
},
],
},
],
},
],
},
],
},
],
};
const result = _prosemirrorToPortableText(pmDoc);
const table = result[0] as {
rows: Array<{
cells: Array<{ content: Array<{ text: string; marks?: string[] }> }>;
}>;
};
expect(table.rows[0].cells[0].content[0].text).toBe("Bold text");
expect(table.rows[0].cells[0].content[0].marks).toContain("strong");
});
it("converts table with links to PT (preserves markDefs)", () => {
const pmDoc = {
type: "doc",
content: [
{
type: "table",
content: [
{
type: "tableRow",
content: [
{
type: "tableCell",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "Link text",
marks: [
{
type: "link",
attrs: {
href: "https://example.com",
target: "_blank",
},
},
],
},
],
},
],
},
],
},
],
},
],
};
const result = _prosemirrorToPortableText(pmDoc);
const table = result[0] as {
rows: Array<{
cells: Array<{
content: Array<{ text: string; marks?: string[] }>;
markDefs?: Array<{ _key: string; _type: string; href: string; blank?: boolean }>;
}>;
}>;
};
const cell = table.rows[0].cells[0];
expect(cell.markDefs).toBeDefined();
expect(cell.markDefs).toHaveLength(1);
expect(cell.markDefs![0]._type).toBe("link");
expect(cell.markDefs![0].href).toBe("https://example.com");
const linkMarkKey = cell.markDefs![0]._key;
expect(cell.content[0].marks).toContain(linkMarkKey);
});
});
describe("Round-trip conversion", () => {
it("PT → PM → PT preserves table structure", () => {
const original = [
{
_type: "table",
_key: "t1",
hasHeaderRow: true,
rows: [
{
_type: "tableRow",
_key: "r1",
cells: [
{
_type: "tableCell",
_key: "c1",
isHeader: true,
content: [{ _type: "span", _key: "s1", text: "Header" }],
},
],
},
{
_type: "tableRow",
_key: "r2",
cells: [
{
_type: "tableCell",
_key: "c2",
content: [{ _type: "span", _key: "s2", text: "Data" }],
},
],
},
],
},
];
const pm = _portableTextToProsemirror(original);
const roundTripped = _prosemirrorToPortableText(pm);
const table = roundTripped[0] as {
_type: string;
hasHeaderRow: boolean;
rows: Array<{
cells: Array<{ content: Array<{ text: string }>; isHeader: boolean }>;
}>;
};
expect(table._type).toBe("table");
expect(table.hasHeaderRow).toBe(true);
expect(table.rows[0].cells[0].isHeader).toBe(true);
expect(table.rows[0].cells[0].content[0].text).toBe("Header");
expect(table.rows[1].cells[0].isHeader).toBe(false);
expect(table.rows[1].cells[0].content[0].text).toBe("Data");
});
});
});