Skip to content

Commit 96cc3b7

Browse files
committed
chore: add test to document-sections updateSectionById
1 parent 7407524 commit 96cc3b7

2 files changed

Lines changed: 174 additions & 1 deletion

File tree

packages/super-editor/src/core/inputRules/html/html-helpers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ListHelpers } from '@helpers/list-numbering-helpers.js';
77
export function flattenListsInHtml(html, editor) {
88
// pick the right parser & Node interface
99
let parser, NodeInterface;
10-
if (editor.options.mockDocument) {
10+
if (editor.options?.mockDocument) {
1111
const win = editor.options.mockDocument.defaultView;
1212
parser = new win.DOMParser();
1313
NodeInterface = win.Node;
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { Schema, DOMSerializer } from 'prosemirror-model';
3+
import { EditorState } from 'prosemirror-state';
4+
import { SectionHelpers } from '@helpers/index.js';
5+
import { DocumentSection } from './document-section.js';
6+
7+
vi.mock('@helpers/index.js', async () => {
8+
return {
9+
SectionHelpers: {
10+
getAllSections: vi.fn(),
11+
},
12+
findParentNode: () => () => null,
13+
};
14+
});
15+
16+
// NodeView referenced by addNodeView, but not executed in these tests
17+
vi.mock('../src/document-section/DocumentSectionView.js', async () => {
18+
return { DocumentSectionView: class {} };
19+
});
20+
21+
// Minimal shims for Node.create / Attribute.mergeAttributes
22+
vi.mock('@core/index.js', async () => {
23+
return {
24+
Node: {
25+
create(spec) {
26+
// Return spec so addCommands/parseDOM/etc are available
27+
return spec;
28+
},
29+
},
30+
Attribute: {
31+
mergeAttributes(a, b) {
32+
return { ...(a || {}), ...(b || {}) };
33+
},
34+
},
35+
};
36+
});
37+
38+
function makeSchema() {
39+
const nodes = {
40+
doc: { content: 'block+' },
41+
text: { group: 'inline' },
42+
paragraph: {
43+
content: 'inline*',
44+
group: 'block',
45+
parseDOM: [{ tag: 'p' }],
46+
toDOM() {
47+
return ['p', 0];
48+
},
49+
},
50+
documentSection: {
51+
group: 'block',
52+
content: 'block*',
53+
atom: true,
54+
isolating: true,
55+
attrs: {
56+
id: { default: null },
57+
title: { default: '' },
58+
description: { default: '' },
59+
sectionType: { default: '' },
60+
isLocked: { default: false },
61+
},
62+
parseDOM: [{ tag: 'div.sd-document-section-block' }],
63+
toDOM(node) {
64+
return ['div', { class: 'sd-document-section-block', 'data-id': node.attrs.id }, 0];
65+
},
66+
},
67+
};
68+
return new Schema({ nodes });
69+
}
70+
71+
function p(schema, text) {
72+
return schema.nodes.paragraph.createAndFill(null, schema.text(text));
73+
}
74+
75+
function section(schema, attrs, contentNodes) {
76+
return schema.nodes.documentSection.createAndFill(attrs, contentNodes);
77+
}
78+
79+
function docHTML(schema, doc) {
80+
const serializer = DOMSerializer.fromSchema(schema);
81+
const wrap = document.createElement('div');
82+
wrap.appendChild(serializer.serializeFragment(doc.content));
83+
return wrap.innerHTML;
84+
}
85+
86+
describe('DocumentSection.updateSectionById (JS only)', () => {
87+
let schema;
88+
89+
beforeEach(() => {
90+
schema = makeSchema();
91+
vi.clearAllMocks();
92+
});
93+
94+
it('updates attributes (attrs-only)', () => {
95+
// initial doc: one section with id:1 and a paragraph
96+
const initialDoc = schema.nodes.doc.create(null, [
97+
section(schema, { id: 1, title: 'Old Title' }, [p(schema, 'Hello')]),
98+
]);
99+
const state = EditorState.create({ schema, doc: initialDoc });
100+
101+
const tr = state.tr;
102+
const dispatch = () => {}; // attrs-only path doesn't need us to capture the doc
103+
104+
// Mock getAllSections to find the section at pos 0 (single top-level node)
105+
const secNode = state.doc.firstChild;
106+
SectionHelpers.getAllSections.mockReturnValue([{ pos: 0, node: secNode }]);
107+
108+
// Build the command
109+
const cmdFactory = DocumentSection.addCommands().updateSectionById;
110+
const cmd = cmdFactory({ id: 1, attrs: { title: 'New Title', isLocked: true } }).bind({
111+
editor: { schema },
112+
});
113+
114+
const ok = cmd({ tr, dispatch, editor: { schema } });
115+
expect(ok).toBe(true);
116+
117+
const updated = tr.doc.firstChild;
118+
expect(updated.type.name).toBe('documentSection');
119+
expect(updated.attrs.title).toBe('New Title');
120+
expect(updated.attrs.isLocked).toBe(true);
121+
});
122+
123+
it('updates content from JSON (JSON takes precedence over HTML)', () => {
124+
const initialDoc = schema.nodes.doc.create(null, [section(schema, { id: 1 }, [p(schema, 'Old')])]);
125+
const state = EditorState.create({ schema, doc: initialDoc });
126+
127+
let nextDoc = state.doc;
128+
const dispatch = (t) => {
129+
nextDoc = t.doc;
130+
};
131+
132+
const secNode = state.doc.firstChild;
133+
SectionHelpers.getAllSections.mockReturnValue([{ pos: 0, node: secNode }]);
134+
135+
const json = { type: 'paragraph', content: [{ type: 'text', text: 'From JSON' }] };
136+
137+
const cmdFactory = DocumentSection.addCommands().updateSectionById;
138+
const cmd = cmdFactory({ id: 1, html: '<p>From HTML</p>', json }).bind({
139+
editor: { schema },
140+
});
141+
142+
const ok = cmd({ tr: state.tr, dispatch, editor: { schema } });
143+
expect(ok).toBe(true);
144+
145+
const html = docHTML(schema, nextDoc);
146+
expect(html).toContain('<p>From JSON</p>');
147+
expect(html).not.toContain('From HTML');
148+
});
149+
150+
it('updates content from HTML when JSON not provided', () => {
151+
const initialDoc = schema.nodes.doc.create(null, [section(schema, { id: 1 }, [p(schema, 'Old')])]);
152+
const state = EditorState.create({ schema, doc: initialDoc });
153+
154+
let nextDoc = state.doc;
155+
const dispatch = (t) => {
156+
nextDoc = t.doc;
157+
};
158+
159+
const secNode = state.doc.firstChild;
160+
SectionHelpers.getAllSections.mockReturnValue([{ pos: 0, node: secNode }]);
161+
162+
const cmdFactory = DocumentSection.addCommands().updateSectionById;
163+
const cmd = cmdFactory({ id: 1, html: '<p>From HTML</p>' }).bind({
164+
editor: { schema },
165+
});
166+
167+
const ok = cmd({ tr: state.tr, dispatch, editor: { schema } });
168+
expect(ok).toBe(true);
169+
170+
const htmlOut = docHTML(schema, nextDoc);
171+
expect(htmlOut).toContain('<p>From HTML</p>');
172+
});
173+
});

0 commit comments

Comments
 (0)