@platecms/delta-cast 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -4
- package/src/lib/invalid-cast.error.ts +6 -0
- package/src/lib/normalize-cast.spec.ts +2123 -0
- package/src/lib/normalize-cast.ts +390 -0
- package/src/lib/schemas/schema.json +80 -0
- package/src/lib/schemas/schema.ts +277 -0
- package/src/lib/validate-cast.spec.ts +395 -0
- package/src/lib/validate-cast.ts +231 -0
- package/src/index.js +0 -7
- package/src/index.js.map +0 -1
- package/src/lib/invalid-cast.error.d.ts +0 -3
- package/src/lib/invalid-cast.error.js +0 -11
- package/src/lib/invalid-cast.error.js.map +0 -1
- package/src/lib/normalize-cast.d.ts +0 -9
- package/src/lib/normalize-cast.js +0 -190
- package/src/lib/normalize-cast.js.map +0 -1
- package/src/lib/schemas/schema.d.ts +0 -105
- package/src/lib/schemas/schema.js +0 -3
- package/src/lib/schemas/schema.js.map +0 -1
- package/src/lib/validate-cast.d.ts +0 -2
- package/src/lib/validate-cast.js +0 -149
- package/src/lib/validate-cast.js.map +0 -1
- /package/src/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,2123 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { normalizeCast } from "./normalize-cast";
|
|
3
|
+
import { Root } from "./schemas/schema";
|
|
4
|
+
|
|
5
|
+
describe("normalizeCast", () => {
|
|
6
|
+
describe("adjacent node merging", () => {
|
|
7
|
+
describe("text node merging", () => {
|
|
8
|
+
it("should merge adjacent text nodes in a paragraph", () => {
|
|
9
|
+
const input: Root = {
|
|
10
|
+
type: "root",
|
|
11
|
+
children: [
|
|
12
|
+
{
|
|
13
|
+
type: "paragraph",
|
|
14
|
+
children: [
|
|
15
|
+
{ type: "text", value: "Hello" },
|
|
16
|
+
{ type: "text", value: " " },
|
|
17
|
+
{ type: "text", value: "world" },
|
|
18
|
+
{ type: "text", value: "!" },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const expected: Root = {
|
|
25
|
+
type: "root",
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
type: "paragraph",
|
|
29
|
+
children: [{ type: "text", value: "Hello world!" }],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const result = normalizeCast(input);
|
|
35
|
+
expect(result).toEqual(expected);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should merge text nodes across different containers", () => {
|
|
39
|
+
const input: Root = {
|
|
40
|
+
type: "root",
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
type: "paragraph",
|
|
44
|
+
children: [
|
|
45
|
+
{ type: "text", value: "Start" },
|
|
46
|
+
{ type: "text", value: " " },
|
|
47
|
+
{ type: "bold", children: [{ type: "text", value: "bold" }] },
|
|
48
|
+
{ type: "text", value: " " },
|
|
49
|
+
{ type: "text", value: "end" },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const expected: Root = {
|
|
56
|
+
type: "root",
|
|
57
|
+
children: [
|
|
58
|
+
{
|
|
59
|
+
type: "paragraph",
|
|
60
|
+
children: [
|
|
61
|
+
{ type: "text", value: "Start " },
|
|
62
|
+
{ type: "bold", children: [{ type: "text", value: "bold" }] },
|
|
63
|
+
{ type: "text", value: " end" },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const result = normalizeCast(input);
|
|
70
|
+
expect(result).toEqual(expected);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should handle empty text nodes", () => {
|
|
74
|
+
const input: Root = {
|
|
75
|
+
type: "root",
|
|
76
|
+
children: [
|
|
77
|
+
{
|
|
78
|
+
type: "paragraph",
|
|
79
|
+
children: [
|
|
80
|
+
{ type: "text", value: "Hello" },
|
|
81
|
+
{ type: "text", value: "" },
|
|
82
|
+
{ type: "text", value: "world" },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const expected: Root = {
|
|
89
|
+
type: "root",
|
|
90
|
+
children: [
|
|
91
|
+
{
|
|
92
|
+
type: "paragraph",
|
|
93
|
+
children: [{ type: "text", value: "Helloworld" }],
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const result = normalizeCast(input);
|
|
99
|
+
expect(result).toEqual(expected);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("formatting node merging", () => {
|
|
104
|
+
it("should merge adjacent bold nodes", () => {
|
|
105
|
+
const input: Root = {
|
|
106
|
+
type: "root",
|
|
107
|
+
children: [
|
|
108
|
+
{
|
|
109
|
+
type: "paragraph",
|
|
110
|
+
children: [
|
|
111
|
+
{ type: "bold", children: [{ type: "text", value: "Hello" }] },
|
|
112
|
+
{ type: "bold", children: [{ type: "text", value: " world" }] },
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const expected: Root = {
|
|
119
|
+
type: "root",
|
|
120
|
+
children: [
|
|
121
|
+
{
|
|
122
|
+
type: "paragraph",
|
|
123
|
+
children: [{ type: "bold", children: [{ type: "text", value: "Hello world" }] }],
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const result = normalizeCast(input);
|
|
129
|
+
expect(result).toEqual(expected);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should merge adjacent italic nodes", () => {
|
|
133
|
+
const input: Root = {
|
|
134
|
+
type: "root",
|
|
135
|
+
children: [
|
|
136
|
+
{
|
|
137
|
+
type: "paragraph",
|
|
138
|
+
children: [
|
|
139
|
+
{ type: "italic", children: [{ type: "text", value: "Hello" }] },
|
|
140
|
+
{ type: "italic", children: [{ type: "text", value: " world" }] },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const expected: Root = {
|
|
147
|
+
type: "root",
|
|
148
|
+
children: [
|
|
149
|
+
{
|
|
150
|
+
type: "paragraph",
|
|
151
|
+
children: [{ type: "italic", children: [{ type: "text", value: "Hello world" }] }],
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result = normalizeCast(input);
|
|
157
|
+
expect(result).toEqual(expected);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should merge adjacent underline nodes", () => {
|
|
161
|
+
const input: Root = {
|
|
162
|
+
type: "root",
|
|
163
|
+
children: [
|
|
164
|
+
{
|
|
165
|
+
type: "paragraph",
|
|
166
|
+
children: [
|
|
167
|
+
{ type: "underline", children: [{ type: "text", value: "Hello" }] },
|
|
168
|
+
{ type: "underline", children: [{ type: "text", value: " world" }] },
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const expected: Root = {
|
|
175
|
+
type: "root",
|
|
176
|
+
children: [
|
|
177
|
+
{
|
|
178
|
+
type: "paragraph",
|
|
179
|
+
children: [{ type: "underline", children: [{ type: "text", value: "Hello world" }] }],
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const result = normalizeCast(input);
|
|
185
|
+
expect(result).toEqual(expected);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should merge adjacent strikethrough nodes", () => {
|
|
189
|
+
const input: Root = {
|
|
190
|
+
type: "root",
|
|
191
|
+
children: [
|
|
192
|
+
{
|
|
193
|
+
type: "paragraph",
|
|
194
|
+
children: [
|
|
195
|
+
{ type: "strikethrough", children: [{ type: "text", value: "Hello" }] },
|
|
196
|
+
{ type: "strikethrough", children: [{ type: "text", value: " world" }] },
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const expected: Root = {
|
|
203
|
+
type: "root",
|
|
204
|
+
children: [
|
|
205
|
+
{
|
|
206
|
+
type: "paragraph",
|
|
207
|
+
children: [{ type: "strikethrough", children: [{ type: "text", value: "Hello world" }] }],
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const result = normalizeCast(input);
|
|
213
|
+
expect(result).toEqual(expected);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should merge adjacent highlight nodes", () => {
|
|
217
|
+
const input: Root = {
|
|
218
|
+
type: "root",
|
|
219
|
+
children: [
|
|
220
|
+
{
|
|
221
|
+
type: "paragraph",
|
|
222
|
+
children: [
|
|
223
|
+
{ type: "highlight", children: [{ type: "text", value: "Hello" }] },
|
|
224
|
+
{ type: "highlight", children: [{ type: "text", value: " world" }] },
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const expected: Root = {
|
|
231
|
+
type: "root",
|
|
232
|
+
children: [
|
|
233
|
+
{
|
|
234
|
+
type: "paragraph",
|
|
235
|
+
children: [{ type: "highlight", children: [{ type: "text", value: "Hello world" }] }],
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const result = normalizeCast(input);
|
|
241
|
+
expect(result).toEqual(expected);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should merge multiple adjacent formatting nodes of the same type", () => {
|
|
245
|
+
const input: Root = {
|
|
246
|
+
type: "root",
|
|
247
|
+
children: [
|
|
248
|
+
{
|
|
249
|
+
type: "paragraph",
|
|
250
|
+
children: [
|
|
251
|
+
{ type: "bold", children: [{ type: "text", value: "A" }] },
|
|
252
|
+
{ type: "bold", children: [{ type: "text", value: "B" }] },
|
|
253
|
+
{ type: "bold", children: [{ type: "text", value: "C" }] },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const expected: Root = {
|
|
260
|
+
type: "root",
|
|
261
|
+
children: [
|
|
262
|
+
{
|
|
263
|
+
type: "paragraph",
|
|
264
|
+
children: [{ type: "bold", children: [{ type: "text", value: "ABC" }] }],
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const result = normalizeCast(input);
|
|
270
|
+
expect(result).toEqual(expected);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should merge formatting nodes with nested content", () => {
|
|
274
|
+
const input: Root = {
|
|
275
|
+
type: "root",
|
|
276
|
+
children: [
|
|
277
|
+
{
|
|
278
|
+
type: "paragraph",
|
|
279
|
+
children: [
|
|
280
|
+
{
|
|
281
|
+
type: "bold",
|
|
282
|
+
children: [
|
|
283
|
+
{ type: "text", value: "Hello" },
|
|
284
|
+
{ type: "italic", children: [{ type: "text", value: "italic" }] },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
{ type: "bold", children: [{ type: "text", value: " world" }] },
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const expected: Root = {
|
|
294
|
+
type: "root",
|
|
295
|
+
children: [
|
|
296
|
+
{
|
|
297
|
+
type: "paragraph",
|
|
298
|
+
children: [
|
|
299
|
+
{
|
|
300
|
+
type: "bold",
|
|
301
|
+
children: [
|
|
302
|
+
{ type: "text", value: "Hello" },
|
|
303
|
+
{ type: "italic", children: [{ type: "text", value: "italic" }] },
|
|
304
|
+
{ type: "text", value: " world" },
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const result = normalizeCast(input);
|
|
313
|
+
expect(result).toEqual(expected);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe("non-mergeable nodes", () => {
|
|
318
|
+
it("should not merge adjacent contentValue nodes", () => {
|
|
319
|
+
const input: Root = {
|
|
320
|
+
type: "root",
|
|
321
|
+
children: [
|
|
322
|
+
{
|
|
323
|
+
type: "paragraph",
|
|
324
|
+
children: [
|
|
325
|
+
{ type: "contentValue", prn: "prn:content-value:123" },
|
|
326
|
+
{ type: "contentValue", prn: "prn:content-value:456" },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const result = normalizeCast(input);
|
|
333
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should not merge adjacent externalLink nodes", () => {
|
|
337
|
+
const input: Root = {
|
|
338
|
+
type: "root",
|
|
339
|
+
children: [
|
|
340
|
+
{
|
|
341
|
+
type: "paragraph",
|
|
342
|
+
children: [
|
|
343
|
+
{ type: "externalLink", url: "https://example.com", children: [{ type: "text", value: "Link 1" }] },
|
|
344
|
+
{ type: "externalLink", url: "https://other.com", children: [{ type: "text", value: "Link 2" }] },
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const result = normalizeCast(input);
|
|
351
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("should not merge adjacent internalLink nodes", () => {
|
|
355
|
+
const input: Root = {
|
|
356
|
+
type: "root",
|
|
357
|
+
children: [
|
|
358
|
+
{
|
|
359
|
+
type: "paragraph",
|
|
360
|
+
children: [
|
|
361
|
+
{ type: "internalLink", prn: "prn:path-part:123", children: [{ type: "text", value: "Link 1" }] },
|
|
362
|
+
{ type: "internalLink", prn: "prn:path-part:456", children: [{ type: "text", value: "Link 2" }] },
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const result = normalizeCast(input);
|
|
369
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should not merge adjacent inlineCode nodes", () => {
|
|
373
|
+
const input: Root = {
|
|
374
|
+
type: "root",
|
|
375
|
+
children: [
|
|
376
|
+
{
|
|
377
|
+
type: "paragraph",
|
|
378
|
+
children: [
|
|
379
|
+
{ type: "inlineCode", value: "code1" },
|
|
380
|
+
{ type: "inlineCode", value: "code2" },
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const result = normalizeCast(input);
|
|
387
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("should not merge different types of formatting nodes", () => {
|
|
391
|
+
const input: Root = {
|
|
392
|
+
type: "root",
|
|
393
|
+
children: [
|
|
394
|
+
{
|
|
395
|
+
type: "paragraph",
|
|
396
|
+
children: [
|
|
397
|
+
{ type: "bold", children: [{ type: "text", value: "bold" }] },
|
|
398
|
+
{ type: "italic", children: [{ type: "text", value: "italic" }] },
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const result = normalizeCast(input);
|
|
405
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe("mixed scenarios", () => {
|
|
410
|
+
it("should handle complex mixed merging scenarios", () => {
|
|
411
|
+
const input: Root = {
|
|
412
|
+
type: "root",
|
|
413
|
+
children: [
|
|
414
|
+
{
|
|
415
|
+
type: "paragraph",
|
|
416
|
+
children: [
|
|
417
|
+
{ type: "text", value: "Start " },
|
|
418
|
+
{ type: "bold", children: [{ type: "text", value: "bold1" }] },
|
|
419
|
+
{ type: "bold", children: [{ type: "text", value: "bold2" }] },
|
|
420
|
+
{ type: "text", value: " " },
|
|
421
|
+
{ type: "text", value: "middle " },
|
|
422
|
+
{ type: "italic", children: [{ type: "text", value: "italic1" }] },
|
|
423
|
+
{ type: "italic", children: [{ type: "text", value: "italic2" }] },
|
|
424
|
+
{ type: "text", value: " end" },
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const expected: Root = {
|
|
431
|
+
type: "root",
|
|
432
|
+
children: [
|
|
433
|
+
{
|
|
434
|
+
type: "paragraph",
|
|
435
|
+
children: [
|
|
436
|
+
{ type: "text", value: "Start " },
|
|
437
|
+
{ type: "bold", children: [{ type: "text", value: "bold1bold2" }] },
|
|
438
|
+
{ type: "text", value: " middle " },
|
|
439
|
+
{ type: "italic", children: [{ type: "text", value: "italic1italic2" }] },
|
|
440
|
+
{ type: "text", value: " end" },
|
|
441
|
+
],
|
|
442
|
+
},
|
|
443
|
+
],
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const result = normalizeCast(input);
|
|
447
|
+
expect(result).toEqual(expected);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("should merge across different block containers", () => {
|
|
451
|
+
const input: Root = {
|
|
452
|
+
type: "root",
|
|
453
|
+
children: [
|
|
454
|
+
{
|
|
455
|
+
type: "paragraph",
|
|
456
|
+
children: [
|
|
457
|
+
{ type: "text", value: "Para1 " },
|
|
458
|
+
{ type: "text", value: "text" },
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
type: "paragraph",
|
|
463
|
+
children: [
|
|
464
|
+
{ type: "text", value: "Para2 " },
|
|
465
|
+
{ type: "text", value: "text" },
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const expected: Root = {
|
|
472
|
+
type: "root",
|
|
473
|
+
children: [
|
|
474
|
+
{
|
|
475
|
+
type: "paragraph",
|
|
476
|
+
children: [{ type: "text", value: "Para1 text" }],
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
type: "paragraph",
|
|
480
|
+
children: [{ type: "text", value: "Para2 text" }],
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const result = normalizeCast(input);
|
|
486
|
+
expect(result).toEqual(expected);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe("formatting node nesting order", () => {
|
|
492
|
+
it("should reorder formatting nodes to follow the standard hierarchy", () => {
|
|
493
|
+
const input: Root = {
|
|
494
|
+
type: "root",
|
|
495
|
+
children: [
|
|
496
|
+
{
|
|
497
|
+
type: "paragraph",
|
|
498
|
+
children: [
|
|
499
|
+
{
|
|
500
|
+
type: "highlight",
|
|
501
|
+
children: [
|
|
502
|
+
{
|
|
503
|
+
type: "bold",
|
|
504
|
+
children: [
|
|
505
|
+
{
|
|
506
|
+
type: "italic",
|
|
507
|
+
children: [{ type: "text", value: "text" }],
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
},
|
|
513
|
+
],
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const expected: Root = {
|
|
519
|
+
type: "root",
|
|
520
|
+
children: [
|
|
521
|
+
{
|
|
522
|
+
type: "paragraph",
|
|
523
|
+
children: [
|
|
524
|
+
{
|
|
525
|
+
type: "bold",
|
|
526
|
+
children: [
|
|
527
|
+
{
|
|
528
|
+
type: "italic",
|
|
529
|
+
children: [
|
|
530
|
+
{
|
|
531
|
+
type: "highlight",
|
|
532
|
+
children: [{ type: "text", value: "text" }],
|
|
533
|
+
},
|
|
534
|
+
],
|
|
535
|
+
},
|
|
536
|
+
],
|
|
537
|
+
},
|
|
538
|
+
],
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const result = normalizeCast(input);
|
|
544
|
+
expect(result).toEqual(expected);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it("should reorder multiple levels of mixed formatting", () => {
|
|
548
|
+
const input: Root = {
|
|
549
|
+
type: "root",
|
|
550
|
+
children: [
|
|
551
|
+
{
|
|
552
|
+
type: "paragraph",
|
|
553
|
+
children: [
|
|
554
|
+
{
|
|
555
|
+
type: "strikethrough",
|
|
556
|
+
children: [
|
|
557
|
+
{
|
|
558
|
+
type: "underline",
|
|
559
|
+
children: [
|
|
560
|
+
{
|
|
561
|
+
type: "bold",
|
|
562
|
+
children: [
|
|
563
|
+
{
|
|
564
|
+
type: "highlight",
|
|
565
|
+
children: [
|
|
566
|
+
{
|
|
567
|
+
type: "italic",
|
|
568
|
+
children: [{ type: "text", value: "complex text" }],
|
|
569
|
+
},
|
|
570
|
+
],
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
},
|
|
574
|
+
],
|
|
575
|
+
},
|
|
576
|
+
],
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const expected: Root = {
|
|
584
|
+
type: "root",
|
|
585
|
+
children: [
|
|
586
|
+
{
|
|
587
|
+
type: "paragraph",
|
|
588
|
+
children: [
|
|
589
|
+
{
|
|
590
|
+
type: "bold",
|
|
591
|
+
children: [
|
|
592
|
+
{
|
|
593
|
+
type: "italic",
|
|
594
|
+
children: [
|
|
595
|
+
{
|
|
596
|
+
type: "underline",
|
|
597
|
+
children: [
|
|
598
|
+
{
|
|
599
|
+
type: "strikethrough",
|
|
600
|
+
children: [
|
|
601
|
+
{
|
|
602
|
+
type: "highlight",
|
|
603
|
+
children: [{ type: "text", value: "complex text" }],
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
],
|
|
612
|
+
},
|
|
613
|
+
],
|
|
614
|
+
},
|
|
615
|
+
],
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
const result = normalizeCast(input);
|
|
619
|
+
expect(result).toEqual(expected);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("should handle already correctly ordered formatting", () => {
|
|
623
|
+
const input: Root = {
|
|
624
|
+
type: "root",
|
|
625
|
+
children: [
|
|
626
|
+
{
|
|
627
|
+
type: "paragraph",
|
|
628
|
+
children: [
|
|
629
|
+
{
|
|
630
|
+
type: "bold",
|
|
631
|
+
children: [
|
|
632
|
+
{
|
|
633
|
+
type: "italic",
|
|
634
|
+
children: [
|
|
635
|
+
{
|
|
636
|
+
type: "underline",
|
|
637
|
+
children: [
|
|
638
|
+
{
|
|
639
|
+
type: "strikethrough",
|
|
640
|
+
children: [
|
|
641
|
+
{
|
|
642
|
+
type: "highlight",
|
|
643
|
+
children: [{ type: "text", value: "correctly ordered" }],
|
|
644
|
+
},
|
|
645
|
+
],
|
|
646
|
+
},
|
|
647
|
+
],
|
|
648
|
+
},
|
|
649
|
+
],
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
},
|
|
655
|
+
],
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
const result = normalizeCast(input);
|
|
659
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it("should reorder formatting nodes with mixed content", () => {
|
|
663
|
+
const input: Root = {
|
|
664
|
+
type: "root",
|
|
665
|
+
children: [
|
|
666
|
+
{
|
|
667
|
+
type: "paragraph",
|
|
668
|
+
children: [
|
|
669
|
+
{
|
|
670
|
+
type: "highlight",
|
|
671
|
+
children: [
|
|
672
|
+
{ type: "text", value: "start " },
|
|
673
|
+
{
|
|
674
|
+
type: "bold",
|
|
675
|
+
children: [
|
|
676
|
+
{
|
|
677
|
+
type: "italic",
|
|
678
|
+
children: [{ type: "text", value: "bold italic" }],
|
|
679
|
+
},
|
|
680
|
+
],
|
|
681
|
+
},
|
|
682
|
+
{ type: "text", value: " end" },
|
|
683
|
+
],
|
|
684
|
+
},
|
|
685
|
+
],
|
|
686
|
+
},
|
|
687
|
+
],
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
const expected: Root = {
|
|
691
|
+
type: "root",
|
|
692
|
+
children: [
|
|
693
|
+
{
|
|
694
|
+
type: "paragraph",
|
|
695
|
+
children: [
|
|
696
|
+
{
|
|
697
|
+
type: "highlight",
|
|
698
|
+
children: [{ type: "text", value: "start " }],
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
type: "bold",
|
|
702
|
+
children: [
|
|
703
|
+
{
|
|
704
|
+
type: "italic",
|
|
705
|
+
children: [
|
|
706
|
+
{
|
|
707
|
+
type: "highlight",
|
|
708
|
+
children: [{ type: "text", value: "bold italic" }],
|
|
709
|
+
},
|
|
710
|
+
],
|
|
711
|
+
},
|
|
712
|
+
],
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
type: "highlight",
|
|
716
|
+
children: [{ type: "text", value: " end" }],
|
|
717
|
+
},
|
|
718
|
+
],
|
|
719
|
+
},
|
|
720
|
+
],
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
const result = normalizeCast(input);
|
|
724
|
+
expect(result).toEqual(expected);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it("should handle single-level formatting nodes", () => {
|
|
728
|
+
const input: Root = {
|
|
729
|
+
type: "root",
|
|
730
|
+
children: [
|
|
731
|
+
{
|
|
732
|
+
type: "paragraph",
|
|
733
|
+
children: [
|
|
734
|
+
{
|
|
735
|
+
type: "highlight",
|
|
736
|
+
children: [{ type: "text", value: "highlighted" }],
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
type: "bold",
|
|
740
|
+
children: [{ type: "text", value: "bold" }],
|
|
741
|
+
},
|
|
742
|
+
],
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
const result = normalizeCast(input);
|
|
748
|
+
expect(result).toEqual(input); // Should remain unchanged (no nesting)
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it("should preserve non-formatting nodes during reordering", () => {
|
|
752
|
+
const input: Root = {
|
|
753
|
+
type: "root",
|
|
754
|
+
children: [
|
|
755
|
+
{
|
|
756
|
+
type: "paragraph",
|
|
757
|
+
children: [
|
|
758
|
+
{
|
|
759
|
+
type: "highlight",
|
|
760
|
+
children: [
|
|
761
|
+
{
|
|
762
|
+
type: "bold",
|
|
763
|
+
children: [
|
|
764
|
+
{ type: "text", value: "formatted " },
|
|
765
|
+
{ type: "inlineCode", value: "code" },
|
|
766
|
+
{ type: "text", value: " text" },
|
|
767
|
+
],
|
|
768
|
+
},
|
|
769
|
+
],
|
|
770
|
+
},
|
|
771
|
+
],
|
|
772
|
+
},
|
|
773
|
+
],
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
const expected: Root = {
|
|
777
|
+
type: "root",
|
|
778
|
+
children: [
|
|
779
|
+
{
|
|
780
|
+
type: "paragraph",
|
|
781
|
+
children: [
|
|
782
|
+
{
|
|
783
|
+
type: "bold",
|
|
784
|
+
children: [
|
|
785
|
+
{
|
|
786
|
+
type: "highlight",
|
|
787
|
+
children: [
|
|
788
|
+
{ type: "text", value: "formatted " },
|
|
789
|
+
{ type: "inlineCode", value: "code" },
|
|
790
|
+
{ type: "text", value: " text" },
|
|
791
|
+
],
|
|
792
|
+
},
|
|
793
|
+
],
|
|
794
|
+
},
|
|
795
|
+
],
|
|
796
|
+
},
|
|
797
|
+
],
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
const result = normalizeCast(input);
|
|
801
|
+
expect(result).toEqual(expected);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it("should handle complex overlapping formatting with multiple subtrees with worst case scenario", () => {
|
|
805
|
+
// Input: _Hello 'world *how*' *are* you?_
|
|
806
|
+
const input: Root = {
|
|
807
|
+
type: "root",
|
|
808
|
+
children: [
|
|
809
|
+
{
|
|
810
|
+
type: "paragraph",
|
|
811
|
+
children: [
|
|
812
|
+
{
|
|
813
|
+
type: "underline",
|
|
814
|
+
children: [
|
|
815
|
+
{ type: "text", value: "Hello " },
|
|
816
|
+
{
|
|
817
|
+
type: "italic",
|
|
818
|
+
children: [
|
|
819
|
+
{ type: "text", value: "world " },
|
|
820
|
+
{
|
|
821
|
+
type: "bold",
|
|
822
|
+
children: [{ type: "text", value: "how" }],
|
|
823
|
+
},
|
|
824
|
+
],
|
|
825
|
+
},
|
|
826
|
+
{ type: "text", value: " " },
|
|
827
|
+
{
|
|
828
|
+
type: "bold",
|
|
829
|
+
children: [{ type: "text", value: "are" }],
|
|
830
|
+
},
|
|
831
|
+
{ type: "text", value: " you?" },
|
|
832
|
+
],
|
|
833
|
+
},
|
|
834
|
+
],
|
|
835
|
+
},
|
|
836
|
+
],
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
const expected: Root = {
|
|
840
|
+
type: "root",
|
|
841
|
+
children: [
|
|
842
|
+
{
|
|
843
|
+
type: "paragraph",
|
|
844
|
+
children: [
|
|
845
|
+
{
|
|
846
|
+
type: "underline",
|
|
847
|
+
children: [{ type: "text", value: "Hello " }],
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
type: "italic",
|
|
851
|
+
children: [{ type: "underline", children: [{ type: "text", value: "world " }] }],
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
type: "bold",
|
|
855
|
+
children: [
|
|
856
|
+
{
|
|
857
|
+
type: "italic",
|
|
858
|
+
children: [
|
|
859
|
+
{
|
|
860
|
+
type: "underline",
|
|
861
|
+
children: [{ type: "text", value: "how" }],
|
|
862
|
+
},
|
|
863
|
+
],
|
|
864
|
+
},
|
|
865
|
+
],
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
type: "underline",
|
|
869
|
+
children: [{ type: "text", value: " " }],
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
type: "bold",
|
|
873
|
+
children: [
|
|
874
|
+
{
|
|
875
|
+
type: "underline",
|
|
876
|
+
children: [{ type: "text", value: "are" }],
|
|
877
|
+
},
|
|
878
|
+
],
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
type: "underline",
|
|
882
|
+
children: [{ type: "text", value: " you?" }],
|
|
883
|
+
},
|
|
884
|
+
],
|
|
885
|
+
},
|
|
886
|
+
],
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
const result = normalizeCast(input);
|
|
890
|
+
expect(result).toEqual(expected);
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it("should handle complex overlapping formatting with multiple subtrees with best case scenario", () => {
|
|
894
|
+
// Input: *Hello 'world _how_' are you?*
|
|
895
|
+
const input: Root = {
|
|
896
|
+
type: "root",
|
|
897
|
+
children: [
|
|
898
|
+
{
|
|
899
|
+
type: "paragraph",
|
|
900
|
+
children: [
|
|
901
|
+
{
|
|
902
|
+
type: "bold",
|
|
903
|
+
children: [{ type: "text", value: "Hello " }],
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
type: "italic",
|
|
907
|
+
children: [
|
|
908
|
+
{
|
|
909
|
+
type: "bold",
|
|
910
|
+
children: [{ type: "text", value: "world " }],
|
|
911
|
+
},
|
|
912
|
+
],
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
type: "bold",
|
|
916
|
+
children: [
|
|
917
|
+
{
|
|
918
|
+
type: "italic",
|
|
919
|
+
children: [
|
|
920
|
+
{
|
|
921
|
+
type: "underline",
|
|
922
|
+
children: [{ type: "text", value: "how" }],
|
|
923
|
+
},
|
|
924
|
+
],
|
|
925
|
+
},
|
|
926
|
+
],
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
type: "bold",
|
|
930
|
+
children: [{ type: "text", value: " are you?" }],
|
|
931
|
+
},
|
|
932
|
+
],
|
|
933
|
+
},
|
|
934
|
+
],
|
|
935
|
+
};
|
|
936
|
+
// Input: *Hello 'world _how_' are you?*
|
|
937
|
+
|
|
938
|
+
const expected: Root = {
|
|
939
|
+
type: "root",
|
|
940
|
+
children: [
|
|
941
|
+
{
|
|
942
|
+
type: "paragraph",
|
|
943
|
+
children: [
|
|
944
|
+
{
|
|
945
|
+
type: "bold",
|
|
946
|
+
children: [
|
|
947
|
+
{ type: "text", value: "Hello " },
|
|
948
|
+
{
|
|
949
|
+
type: "italic",
|
|
950
|
+
children: [
|
|
951
|
+
{ type: "text", value: "world " },
|
|
952
|
+
{ type: "underline", children: [{ type: "text", value: "how" }] },
|
|
953
|
+
],
|
|
954
|
+
},
|
|
955
|
+
{ type: "text", value: " are you?" },
|
|
956
|
+
],
|
|
957
|
+
},
|
|
958
|
+
],
|
|
959
|
+
},
|
|
960
|
+
],
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
const result = normalizeCast(input);
|
|
964
|
+
expect(result).toEqual(expected);
|
|
965
|
+
});
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
describe("duplicate formatting removal", () => {
|
|
969
|
+
it("should remove simple duplicate formatting", () => {
|
|
970
|
+
const input: Root = {
|
|
971
|
+
type: "root",
|
|
972
|
+
children: [
|
|
973
|
+
{
|
|
974
|
+
type: "paragraph",
|
|
975
|
+
children: [
|
|
976
|
+
{
|
|
977
|
+
type: "bold",
|
|
978
|
+
children: [
|
|
979
|
+
{
|
|
980
|
+
type: "bold",
|
|
981
|
+
children: [{ type: "text", value: "double bold" }],
|
|
982
|
+
},
|
|
983
|
+
],
|
|
984
|
+
},
|
|
985
|
+
],
|
|
986
|
+
},
|
|
987
|
+
],
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
const expected: Root = {
|
|
991
|
+
type: "root",
|
|
992
|
+
children: [
|
|
993
|
+
{
|
|
994
|
+
type: "paragraph",
|
|
995
|
+
children: [
|
|
996
|
+
{
|
|
997
|
+
type: "bold",
|
|
998
|
+
children: [{ type: "text", value: "double bold" }],
|
|
999
|
+
},
|
|
1000
|
+
],
|
|
1001
|
+
},
|
|
1002
|
+
],
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
const result = normalizeCast(input);
|
|
1006
|
+
expect(result).toEqual(expected);
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
it("should remove multiple levels of duplicate formatting", () => {
|
|
1010
|
+
const input: Root = {
|
|
1011
|
+
type: "root",
|
|
1012
|
+
children: [
|
|
1013
|
+
{
|
|
1014
|
+
type: "paragraph",
|
|
1015
|
+
children: [
|
|
1016
|
+
{
|
|
1017
|
+
type: "bold",
|
|
1018
|
+
children: [
|
|
1019
|
+
{
|
|
1020
|
+
type: "bold",
|
|
1021
|
+
children: [
|
|
1022
|
+
{
|
|
1023
|
+
type: "bold",
|
|
1024
|
+
children: [{ type: "text", value: "triple bold" }],
|
|
1025
|
+
},
|
|
1026
|
+
],
|
|
1027
|
+
},
|
|
1028
|
+
],
|
|
1029
|
+
},
|
|
1030
|
+
],
|
|
1031
|
+
},
|
|
1032
|
+
],
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
const expected: Root = {
|
|
1036
|
+
type: "root",
|
|
1037
|
+
children: [
|
|
1038
|
+
{
|
|
1039
|
+
type: "paragraph",
|
|
1040
|
+
children: [
|
|
1041
|
+
{
|
|
1042
|
+
type: "bold",
|
|
1043
|
+
children: [{ type: "text", value: "triple bold" }],
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
},
|
|
1047
|
+
],
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
const result = normalizeCast(input);
|
|
1051
|
+
expect(result).toEqual(expected);
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
it("should remove duplicate formatting with mixed content", () => {
|
|
1055
|
+
const input: Root = {
|
|
1056
|
+
type: "root",
|
|
1057
|
+
children: [
|
|
1058
|
+
{
|
|
1059
|
+
type: "paragraph",
|
|
1060
|
+
children: [
|
|
1061
|
+
{
|
|
1062
|
+
type: "bold",
|
|
1063
|
+
children: [
|
|
1064
|
+
{ type: "text", value: "start " },
|
|
1065
|
+
{
|
|
1066
|
+
type: "bold",
|
|
1067
|
+
children: [{ type: "text", value: "double bold" }],
|
|
1068
|
+
},
|
|
1069
|
+
{ type: "text", value: " end" },
|
|
1070
|
+
],
|
|
1071
|
+
},
|
|
1072
|
+
],
|
|
1073
|
+
},
|
|
1074
|
+
],
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
const expected: Root = {
|
|
1078
|
+
type: "root",
|
|
1079
|
+
children: [
|
|
1080
|
+
{
|
|
1081
|
+
type: "paragraph",
|
|
1082
|
+
children: [
|
|
1083
|
+
{
|
|
1084
|
+
type: "bold",
|
|
1085
|
+
children: [{ type: "text", value: "start double bold end" }],
|
|
1086
|
+
},
|
|
1087
|
+
],
|
|
1088
|
+
},
|
|
1089
|
+
],
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
const result = normalizeCast(input);
|
|
1093
|
+
expect(result).toEqual(expected);
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
it("should remove duplicate formatting with nested different types", () => {
|
|
1097
|
+
const input: Root = {
|
|
1098
|
+
type: "root",
|
|
1099
|
+
children: [
|
|
1100
|
+
{
|
|
1101
|
+
type: "paragraph",
|
|
1102
|
+
children: [
|
|
1103
|
+
{
|
|
1104
|
+
type: "bold",
|
|
1105
|
+
children: [
|
|
1106
|
+
{
|
|
1107
|
+
type: "italic",
|
|
1108
|
+
children: [
|
|
1109
|
+
{
|
|
1110
|
+
type: "bold",
|
|
1111
|
+
children: [{ type: "text", value: "bold italic bold" }],
|
|
1112
|
+
},
|
|
1113
|
+
],
|
|
1114
|
+
},
|
|
1115
|
+
],
|
|
1116
|
+
},
|
|
1117
|
+
],
|
|
1118
|
+
},
|
|
1119
|
+
],
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
const expected: Root = {
|
|
1123
|
+
type: "root",
|
|
1124
|
+
children: [
|
|
1125
|
+
{
|
|
1126
|
+
type: "paragraph",
|
|
1127
|
+
children: [
|
|
1128
|
+
{
|
|
1129
|
+
type: "bold",
|
|
1130
|
+
children: [
|
|
1131
|
+
{
|
|
1132
|
+
type: "italic",
|
|
1133
|
+
children: [{ type: "text", value: "bold italic bold" }],
|
|
1134
|
+
},
|
|
1135
|
+
],
|
|
1136
|
+
},
|
|
1137
|
+
],
|
|
1138
|
+
},
|
|
1139
|
+
],
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
const result = normalizeCast(input);
|
|
1143
|
+
expect(result).toEqual(expected);
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
it("should remove multiple duplicate formatting nodes of the same type", () => {
|
|
1147
|
+
const input: Root = {
|
|
1148
|
+
type: "root",
|
|
1149
|
+
children: [
|
|
1150
|
+
{
|
|
1151
|
+
type: "paragraph",
|
|
1152
|
+
children: [
|
|
1153
|
+
{
|
|
1154
|
+
type: "bold",
|
|
1155
|
+
children: [
|
|
1156
|
+
{
|
|
1157
|
+
type: "bold",
|
|
1158
|
+
children: [{ type: "text", value: "text1" }],
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
type: "bold",
|
|
1162
|
+
children: [{ type: "text", value: "text2" }],
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
type: "bold",
|
|
1166
|
+
children: [{ type: "text", value: "text3" }],
|
|
1167
|
+
},
|
|
1168
|
+
],
|
|
1169
|
+
},
|
|
1170
|
+
],
|
|
1171
|
+
},
|
|
1172
|
+
],
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
const expected: Root = {
|
|
1176
|
+
type: "root",
|
|
1177
|
+
children: [
|
|
1178
|
+
{
|
|
1179
|
+
type: "paragraph",
|
|
1180
|
+
children: [
|
|
1181
|
+
{
|
|
1182
|
+
type: "bold",
|
|
1183
|
+
children: [{ type: "text", value: "text1text2text3" }],
|
|
1184
|
+
},
|
|
1185
|
+
],
|
|
1186
|
+
},
|
|
1187
|
+
],
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
const result = normalizeCast(input);
|
|
1191
|
+
expect(result).toEqual(expected);
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
it("should handle complex nested duplicate formatting", () => {
|
|
1195
|
+
const input: Root = {
|
|
1196
|
+
type: "root",
|
|
1197
|
+
children: [
|
|
1198
|
+
{
|
|
1199
|
+
type: "paragraph",
|
|
1200
|
+
children: [
|
|
1201
|
+
{
|
|
1202
|
+
type: "bold",
|
|
1203
|
+
children: [
|
|
1204
|
+
{
|
|
1205
|
+
type: "italic",
|
|
1206
|
+
children: [
|
|
1207
|
+
{
|
|
1208
|
+
type: "underline",
|
|
1209
|
+
children: [
|
|
1210
|
+
{
|
|
1211
|
+
type: "bold",
|
|
1212
|
+
children: [
|
|
1213
|
+
{
|
|
1214
|
+
type: "italic",
|
|
1215
|
+
children: [{ type: "text", value: "complex" }],
|
|
1216
|
+
},
|
|
1217
|
+
],
|
|
1218
|
+
},
|
|
1219
|
+
],
|
|
1220
|
+
},
|
|
1221
|
+
],
|
|
1222
|
+
},
|
|
1223
|
+
],
|
|
1224
|
+
},
|
|
1225
|
+
],
|
|
1226
|
+
},
|
|
1227
|
+
],
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
const expected: Root = {
|
|
1231
|
+
type: "root",
|
|
1232
|
+
children: [
|
|
1233
|
+
{
|
|
1234
|
+
type: "paragraph",
|
|
1235
|
+
children: [
|
|
1236
|
+
{
|
|
1237
|
+
type: "bold",
|
|
1238
|
+
children: [
|
|
1239
|
+
{
|
|
1240
|
+
type: "italic",
|
|
1241
|
+
children: [
|
|
1242
|
+
{
|
|
1243
|
+
type: "underline",
|
|
1244
|
+
children: [{ type: "text", value: "complex" }],
|
|
1245
|
+
},
|
|
1246
|
+
],
|
|
1247
|
+
},
|
|
1248
|
+
],
|
|
1249
|
+
},
|
|
1250
|
+
],
|
|
1251
|
+
},
|
|
1252
|
+
],
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1255
|
+
const result = normalizeCast(input);
|
|
1256
|
+
expect(result).toEqual(expected);
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
it("should preserve non-formatting nodes during duplicate removal", () => {
|
|
1260
|
+
const input: Root = {
|
|
1261
|
+
type: "root",
|
|
1262
|
+
children: [
|
|
1263
|
+
{
|
|
1264
|
+
type: "paragraph",
|
|
1265
|
+
children: [
|
|
1266
|
+
{
|
|
1267
|
+
type: "bold",
|
|
1268
|
+
children: [
|
|
1269
|
+
{ type: "text", value: "start " },
|
|
1270
|
+
{
|
|
1271
|
+
type: "bold",
|
|
1272
|
+
children: [{ type: "inlineCode", value: "code" }],
|
|
1273
|
+
},
|
|
1274
|
+
{ type: "text", value: " end" },
|
|
1275
|
+
],
|
|
1276
|
+
},
|
|
1277
|
+
],
|
|
1278
|
+
},
|
|
1279
|
+
],
|
|
1280
|
+
};
|
|
1281
|
+
|
|
1282
|
+
const expected: Root = {
|
|
1283
|
+
type: "root",
|
|
1284
|
+
children: [
|
|
1285
|
+
{
|
|
1286
|
+
type: "paragraph",
|
|
1287
|
+
children: [
|
|
1288
|
+
{
|
|
1289
|
+
type: "bold",
|
|
1290
|
+
children: [
|
|
1291
|
+
{ type: "text", value: "start " },
|
|
1292
|
+
{ type: "inlineCode", value: "code" },
|
|
1293
|
+
{ type: "text", value: " end" },
|
|
1294
|
+
],
|
|
1295
|
+
},
|
|
1296
|
+
],
|
|
1297
|
+
},
|
|
1298
|
+
],
|
|
1299
|
+
};
|
|
1300
|
+
|
|
1301
|
+
const result = normalizeCast(input);
|
|
1302
|
+
expect(result).toEqual(expected);
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
it("should handle already correctly formatted content", () => {
|
|
1306
|
+
const input: Root = {
|
|
1307
|
+
type: "root",
|
|
1308
|
+
children: [
|
|
1309
|
+
{
|
|
1310
|
+
type: "paragraph",
|
|
1311
|
+
children: [
|
|
1312
|
+
{
|
|
1313
|
+
type: "bold",
|
|
1314
|
+
children: [
|
|
1315
|
+
{
|
|
1316
|
+
type: "italic",
|
|
1317
|
+
children: [
|
|
1318
|
+
{
|
|
1319
|
+
type: "underline",
|
|
1320
|
+
children: [{ type: "text", value: "correctly formatted" }],
|
|
1321
|
+
},
|
|
1322
|
+
],
|
|
1323
|
+
},
|
|
1324
|
+
],
|
|
1325
|
+
},
|
|
1326
|
+
],
|
|
1327
|
+
},
|
|
1328
|
+
],
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
const result = normalizeCast(input);
|
|
1332
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
it("should remove duplicates across different formatting types", () => {
|
|
1336
|
+
const input: Root = {
|
|
1337
|
+
type: "root",
|
|
1338
|
+
children: [
|
|
1339
|
+
{
|
|
1340
|
+
type: "paragraph",
|
|
1341
|
+
children: [
|
|
1342
|
+
{
|
|
1343
|
+
type: "bold",
|
|
1344
|
+
children: [
|
|
1345
|
+
{
|
|
1346
|
+
type: "italic",
|
|
1347
|
+
children: [
|
|
1348
|
+
{
|
|
1349
|
+
type: "bold",
|
|
1350
|
+
children: [
|
|
1351
|
+
{
|
|
1352
|
+
type: "underline",
|
|
1353
|
+
children: [
|
|
1354
|
+
{
|
|
1355
|
+
type: "italic",
|
|
1356
|
+
children: [{ type: "text", value: "mixed duplicates" }],
|
|
1357
|
+
},
|
|
1358
|
+
],
|
|
1359
|
+
},
|
|
1360
|
+
],
|
|
1361
|
+
},
|
|
1362
|
+
],
|
|
1363
|
+
},
|
|
1364
|
+
],
|
|
1365
|
+
},
|
|
1366
|
+
],
|
|
1367
|
+
},
|
|
1368
|
+
],
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
const expected: Root = {
|
|
1372
|
+
type: "root",
|
|
1373
|
+
children: [
|
|
1374
|
+
{
|
|
1375
|
+
type: "paragraph",
|
|
1376
|
+
children: [
|
|
1377
|
+
{
|
|
1378
|
+
type: "bold",
|
|
1379
|
+
children: [
|
|
1380
|
+
{
|
|
1381
|
+
type: "italic",
|
|
1382
|
+
children: [
|
|
1383
|
+
{
|
|
1384
|
+
type: "underline",
|
|
1385
|
+
children: [{ type: "text", value: "mixed duplicates" }],
|
|
1386
|
+
},
|
|
1387
|
+
],
|
|
1388
|
+
},
|
|
1389
|
+
],
|
|
1390
|
+
},
|
|
1391
|
+
],
|
|
1392
|
+
},
|
|
1393
|
+
],
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
const result = normalizeCast(input);
|
|
1397
|
+
expect(result).toEqual(expected);
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
it("should handle empty formatting nodes after duplicate removal", () => {
|
|
1401
|
+
const input: Root = {
|
|
1402
|
+
type: "root",
|
|
1403
|
+
children: [
|
|
1404
|
+
{
|
|
1405
|
+
type: "paragraph",
|
|
1406
|
+
children: [
|
|
1407
|
+
{
|
|
1408
|
+
type: "bold",
|
|
1409
|
+
children: [
|
|
1410
|
+
{
|
|
1411
|
+
type: "bold",
|
|
1412
|
+
children: [],
|
|
1413
|
+
},
|
|
1414
|
+
],
|
|
1415
|
+
},
|
|
1416
|
+
],
|
|
1417
|
+
},
|
|
1418
|
+
],
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1421
|
+
const expected: Root = {
|
|
1422
|
+
type: "root",
|
|
1423
|
+
children: [
|
|
1424
|
+
{
|
|
1425
|
+
type: "paragraph",
|
|
1426
|
+
children: [],
|
|
1427
|
+
},
|
|
1428
|
+
],
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
const result = normalizeCast(input);
|
|
1432
|
+
expect(result).toEqual(expected);
|
|
1433
|
+
});
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
describe("empty node cleanup", () => {
|
|
1437
|
+
it("should remove empty formatting nodes", () => {
|
|
1438
|
+
const input: Root = {
|
|
1439
|
+
type: "root",
|
|
1440
|
+
children: [
|
|
1441
|
+
{
|
|
1442
|
+
type: "paragraph",
|
|
1443
|
+
children: [
|
|
1444
|
+
{
|
|
1445
|
+
type: "bold",
|
|
1446
|
+
children: [],
|
|
1447
|
+
},
|
|
1448
|
+
{ type: "text", value: "some text" },
|
|
1449
|
+
],
|
|
1450
|
+
},
|
|
1451
|
+
],
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
const expected: Root = {
|
|
1455
|
+
type: "root",
|
|
1456
|
+
children: [
|
|
1457
|
+
{
|
|
1458
|
+
type: "paragraph",
|
|
1459
|
+
children: [{ type: "text", value: "some text" }],
|
|
1460
|
+
},
|
|
1461
|
+
],
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
const result = normalizeCast(input);
|
|
1465
|
+
expect(result).toEqual(expected);
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
it("should preserve empty container nodes", () => {
|
|
1469
|
+
const input: Root = {
|
|
1470
|
+
type: "root",
|
|
1471
|
+
children: [
|
|
1472
|
+
{
|
|
1473
|
+
type: "paragraph",
|
|
1474
|
+
children: [],
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
type: "paragraph",
|
|
1478
|
+
children: [{ type: "text", value: "non-empty paragraph" }],
|
|
1479
|
+
},
|
|
1480
|
+
],
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
const result = normalizeCast(input);
|
|
1484
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
it("should remove nested empty formatting nodes", () => {
|
|
1488
|
+
const input: Root = {
|
|
1489
|
+
type: "root",
|
|
1490
|
+
children: [
|
|
1491
|
+
{
|
|
1492
|
+
type: "paragraph",
|
|
1493
|
+
children: [
|
|
1494
|
+
{
|
|
1495
|
+
type: "bold",
|
|
1496
|
+
children: [
|
|
1497
|
+
{
|
|
1498
|
+
type: "italic",
|
|
1499
|
+
children: [],
|
|
1500
|
+
},
|
|
1501
|
+
],
|
|
1502
|
+
},
|
|
1503
|
+
{ type: "text", value: "text after empty" },
|
|
1504
|
+
],
|
|
1505
|
+
},
|
|
1506
|
+
],
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
const expected: Root = {
|
|
1510
|
+
type: "root",
|
|
1511
|
+
children: [
|
|
1512
|
+
{
|
|
1513
|
+
type: "paragraph",
|
|
1514
|
+
children: [{ type: "text", value: "text after empty" }],
|
|
1515
|
+
},
|
|
1516
|
+
],
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
const result = normalizeCast(input);
|
|
1520
|
+
expect(result).toEqual(expected);
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
it("should preserve empty list items", () => {
|
|
1524
|
+
const input: Root = {
|
|
1525
|
+
type: "root",
|
|
1526
|
+
children: [
|
|
1527
|
+
{
|
|
1528
|
+
type: "list",
|
|
1529
|
+
ordered: false,
|
|
1530
|
+
children: [
|
|
1531
|
+
{
|
|
1532
|
+
type: "listItem",
|
|
1533
|
+
children: [],
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
type: "listItem",
|
|
1537
|
+
children: [{ type: "text", value: "non-empty item" }],
|
|
1538
|
+
},
|
|
1539
|
+
],
|
|
1540
|
+
},
|
|
1541
|
+
],
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
const result = normalizeCast(input);
|
|
1545
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
it("should preserve empty lists when all items are empty", () => {
|
|
1549
|
+
const input: Root = {
|
|
1550
|
+
type: "root",
|
|
1551
|
+
children: [
|
|
1552
|
+
{
|
|
1553
|
+
type: "list",
|
|
1554
|
+
ordered: false,
|
|
1555
|
+
children: [
|
|
1556
|
+
{
|
|
1557
|
+
type: "listItem",
|
|
1558
|
+
children: [],
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
type: "listItem",
|
|
1562
|
+
children: [],
|
|
1563
|
+
},
|
|
1564
|
+
],
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
type: "paragraph",
|
|
1568
|
+
children: [{ type: "text", value: "paragraph after empty list" }],
|
|
1569
|
+
},
|
|
1570
|
+
],
|
|
1571
|
+
};
|
|
1572
|
+
|
|
1573
|
+
const result = normalizeCast(input);
|
|
1574
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
it("should preserve non-empty formatting nodes", () => {
|
|
1578
|
+
const input: Root = {
|
|
1579
|
+
type: "root",
|
|
1580
|
+
children: [
|
|
1581
|
+
{
|
|
1582
|
+
type: "paragraph",
|
|
1583
|
+
children: [
|
|
1584
|
+
{
|
|
1585
|
+
type: "bold",
|
|
1586
|
+
children: [{ type: "text", value: "bold text" }],
|
|
1587
|
+
},
|
|
1588
|
+
],
|
|
1589
|
+
},
|
|
1590
|
+
],
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
const result = normalizeCast(input);
|
|
1594
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
it("should handle complex nested empty nodes", () => {
|
|
1598
|
+
const input: Root = {
|
|
1599
|
+
type: "root",
|
|
1600
|
+
children: [
|
|
1601
|
+
{
|
|
1602
|
+
type: "paragraph",
|
|
1603
|
+
children: [
|
|
1604
|
+
{
|
|
1605
|
+
type: "bold",
|
|
1606
|
+
children: [
|
|
1607
|
+
{
|
|
1608
|
+
type: "italic",
|
|
1609
|
+
children: [
|
|
1610
|
+
{
|
|
1611
|
+
type: "underline",
|
|
1612
|
+
children: [],
|
|
1613
|
+
},
|
|
1614
|
+
],
|
|
1615
|
+
},
|
|
1616
|
+
],
|
|
1617
|
+
},
|
|
1618
|
+
{ type: "text", value: "text" },
|
|
1619
|
+
],
|
|
1620
|
+
},
|
|
1621
|
+
],
|
|
1622
|
+
};
|
|
1623
|
+
|
|
1624
|
+
const expected: Root = {
|
|
1625
|
+
type: "root",
|
|
1626
|
+
children: [
|
|
1627
|
+
{
|
|
1628
|
+
type: "paragraph",
|
|
1629
|
+
children: [{ type: "text", value: "text" }],
|
|
1630
|
+
},
|
|
1631
|
+
],
|
|
1632
|
+
};
|
|
1633
|
+
|
|
1634
|
+
const result = normalizeCast(input);
|
|
1635
|
+
expect(result).toEqual(expected);
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
it("should preserve empty blockquote", () => {
|
|
1639
|
+
const input: Root = {
|
|
1640
|
+
type: "root",
|
|
1641
|
+
children: [
|
|
1642
|
+
{
|
|
1643
|
+
type: "blockquote",
|
|
1644
|
+
children: [],
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
type: "paragraph",
|
|
1648
|
+
children: [{ type: "text", value: "paragraph after empty blockquote" }],
|
|
1649
|
+
},
|
|
1650
|
+
],
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
const result = normalizeCast(input);
|
|
1654
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
it("should preserve empty heading", () => {
|
|
1658
|
+
const input: Root = {
|
|
1659
|
+
type: "root",
|
|
1660
|
+
children: [
|
|
1661
|
+
{
|
|
1662
|
+
type: "heading",
|
|
1663
|
+
level: 1,
|
|
1664
|
+
children: [],
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
type: "paragraph",
|
|
1668
|
+
children: [{ type: "text", value: "paragraph after empty heading" }],
|
|
1669
|
+
},
|
|
1670
|
+
],
|
|
1671
|
+
};
|
|
1672
|
+
|
|
1673
|
+
const result = normalizeCast(input);
|
|
1674
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
it("should preserve empty code block", () => {
|
|
1678
|
+
const input: Root = {
|
|
1679
|
+
type: "root",
|
|
1680
|
+
children: [
|
|
1681
|
+
{
|
|
1682
|
+
type: "code",
|
|
1683
|
+
children: [],
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
type: "paragraph",
|
|
1687
|
+
children: [{ type: "text", value: "paragraph after empty code" }],
|
|
1688
|
+
},
|
|
1689
|
+
],
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
const result = normalizeCast(input);
|
|
1693
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
it("should preserve root with only empty block children", () => {
|
|
1697
|
+
const input: Root = {
|
|
1698
|
+
type: "root",
|
|
1699
|
+
children: [
|
|
1700
|
+
{
|
|
1701
|
+
type: "paragraph",
|
|
1702
|
+
children: [],
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
type: "heading",
|
|
1706
|
+
level: 1,
|
|
1707
|
+
children: [],
|
|
1708
|
+
},
|
|
1709
|
+
],
|
|
1710
|
+
};
|
|
1711
|
+
|
|
1712
|
+
const result = normalizeCast(input);
|
|
1713
|
+
expect(result).toEqual(input); // Should remain unchanged - block nodes are preserved even when empty
|
|
1714
|
+
});
|
|
1715
|
+
|
|
1716
|
+
it("should preserve text nodes even if they're empty strings", () => {
|
|
1717
|
+
const input: Root = {
|
|
1718
|
+
type: "root",
|
|
1719
|
+
children: [
|
|
1720
|
+
{
|
|
1721
|
+
type: "paragraph",
|
|
1722
|
+
children: [{ type: "text", value: "" }],
|
|
1723
|
+
},
|
|
1724
|
+
],
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
const result = normalizeCast(input);
|
|
1728
|
+
expect(result).toEqual(input); // Should remain unchanged - empty text nodes are preserved
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
it("should preserve inlineCode nodes even if they're empty", () => {
|
|
1732
|
+
const input: Root = {
|
|
1733
|
+
type: "root",
|
|
1734
|
+
children: [
|
|
1735
|
+
{
|
|
1736
|
+
type: "paragraph",
|
|
1737
|
+
children: [
|
|
1738
|
+
{ type: "inlineCode", value: "" },
|
|
1739
|
+
{ type: "text", value: "text" },
|
|
1740
|
+
],
|
|
1741
|
+
},
|
|
1742
|
+
],
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
const result = normalizeCast(input);
|
|
1746
|
+
expect(result).toEqual(input); // Should remain unchanged - empty inlineCode nodes are preserved
|
|
1747
|
+
});
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
describe("property preservation", () => {
|
|
1751
|
+
it("should preserve list properties (ordered, start)", () => {
|
|
1752
|
+
const input: Root = {
|
|
1753
|
+
type: "root",
|
|
1754
|
+
children: [
|
|
1755
|
+
{
|
|
1756
|
+
type: "list",
|
|
1757
|
+
ordered: true,
|
|
1758
|
+
start: 5,
|
|
1759
|
+
children: [
|
|
1760
|
+
{
|
|
1761
|
+
type: "listItem",
|
|
1762
|
+
children: [{ type: "text", value: "item 1" }],
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
type: "listItem",
|
|
1766
|
+
children: [{ type: "text", value: "item 2" }],
|
|
1767
|
+
},
|
|
1768
|
+
],
|
|
1769
|
+
},
|
|
1770
|
+
],
|
|
1771
|
+
};
|
|
1772
|
+
|
|
1773
|
+
const result = normalizeCast(input);
|
|
1774
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1775
|
+
});
|
|
1776
|
+
|
|
1777
|
+
it("should preserve code block language property", () => {
|
|
1778
|
+
const input: Root = {
|
|
1779
|
+
type: "root",
|
|
1780
|
+
children: [
|
|
1781
|
+
{
|
|
1782
|
+
type: "code",
|
|
1783
|
+
lang: "typescript",
|
|
1784
|
+
children: [{ type: "text", value: "const x = 1;" }],
|
|
1785
|
+
},
|
|
1786
|
+
],
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
const result = normalizeCast(input);
|
|
1790
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
it("should preserve external link properties (url, target)", () => {
|
|
1794
|
+
const input: Root = {
|
|
1795
|
+
type: "root",
|
|
1796
|
+
children: [
|
|
1797
|
+
{
|
|
1798
|
+
type: "paragraph",
|
|
1799
|
+
children: [
|
|
1800
|
+
{
|
|
1801
|
+
type: "externalLink",
|
|
1802
|
+
url: "https://example.com",
|
|
1803
|
+
target: "blank",
|
|
1804
|
+
children: [{ type: "text", value: "external link" }],
|
|
1805
|
+
},
|
|
1806
|
+
],
|
|
1807
|
+
},
|
|
1808
|
+
],
|
|
1809
|
+
};
|
|
1810
|
+
|
|
1811
|
+
const result = normalizeCast(input);
|
|
1812
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
it("should preserve internal link properties (prn, url, target)", () => {
|
|
1816
|
+
const input: Root = {
|
|
1817
|
+
type: "root",
|
|
1818
|
+
children: [
|
|
1819
|
+
{
|
|
1820
|
+
type: "paragraph",
|
|
1821
|
+
children: [
|
|
1822
|
+
{
|
|
1823
|
+
type: "internalLink",
|
|
1824
|
+
prn: "content-value-123",
|
|
1825
|
+
url: "/some/path",
|
|
1826
|
+
target: "self",
|
|
1827
|
+
children: [{ type: "text", value: "internal link" }],
|
|
1828
|
+
},
|
|
1829
|
+
],
|
|
1830
|
+
},
|
|
1831
|
+
],
|
|
1832
|
+
};
|
|
1833
|
+
|
|
1834
|
+
const result = normalizeCast(input);
|
|
1835
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1836
|
+
});
|
|
1837
|
+
|
|
1838
|
+
it("should preserve heading levels", () => {
|
|
1839
|
+
const input: Root = {
|
|
1840
|
+
type: "root",
|
|
1841
|
+
children: [
|
|
1842
|
+
{
|
|
1843
|
+
type: "heading",
|
|
1844
|
+
level: 1,
|
|
1845
|
+
children: [{ type: "text", value: "Heading 1" }],
|
|
1846
|
+
},
|
|
1847
|
+
{
|
|
1848
|
+
type: "heading",
|
|
1849
|
+
level: 3,
|
|
1850
|
+
children: [{ type: "text", value: "Heading 3" }],
|
|
1851
|
+
},
|
|
1852
|
+
{
|
|
1853
|
+
type: "heading",
|
|
1854
|
+
level: 6,
|
|
1855
|
+
children: [{ type: "text", value: "Heading 6" }],
|
|
1856
|
+
},
|
|
1857
|
+
],
|
|
1858
|
+
};
|
|
1859
|
+
|
|
1860
|
+
const result = normalizeCast(input);
|
|
1861
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1864
|
+
it("should preserve properties while normalizing content", () => {
|
|
1865
|
+
const input: Root = {
|
|
1866
|
+
type: "root",
|
|
1867
|
+
children: [
|
|
1868
|
+
{
|
|
1869
|
+
type: "list",
|
|
1870
|
+
ordered: false,
|
|
1871
|
+
children: [
|
|
1872
|
+
{
|
|
1873
|
+
type: "listItem",
|
|
1874
|
+
children: [
|
|
1875
|
+
{ type: "text", value: "Hello" },
|
|
1876
|
+
{ type: "text", value: " " },
|
|
1877
|
+
{
|
|
1878
|
+
type: "bold",
|
|
1879
|
+
children: [
|
|
1880
|
+
{
|
|
1881
|
+
type: "italic",
|
|
1882
|
+
children: [{ type: "text", value: "world" }],
|
|
1883
|
+
},
|
|
1884
|
+
],
|
|
1885
|
+
},
|
|
1886
|
+
],
|
|
1887
|
+
},
|
|
1888
|
+
],
|
|
1889
|
+
},
|
|
1890
|
+
],
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
const expected: Root = {
|
|
1894
|
+
type: "root",
|
|
1895
|
+
children: [
|
|
1896
|
+
{
|
|
1897
|
+
type: "list",
|
|
1898
|
+
ordered: false, // Property preserved
|
|
1899
|
+
children: [
|
|
1900
|
+
{
|
|
1901
|
+
type: "listItem",
|
|
1902
|
+
children: [
|
|
1903
|
+
{ type: "text", value: "Hello " },
|
|
1904
|
+
{
|
|
1905
|
+
type: "bold", // Reordered
|
|
1906
|
+
children: [
|
|
1907
|
+
{
|
|
1908
|
+
type: "italic", // Reordered
|
|
1909
|
+
children: [{ type: "text", value: "world" }],
|
|
1910
|
+
},
|
|
1911
|
+
],
|
|
1912
|
+
},
|
|
1913
|
+
],
|
|
1914
|
+
},
|
|
1915
|
+
],
|
|
1916
|
+
},
|
|
1917
|
+
],
|
|
1918
|
+
};
|
|
1919
|
+
|
|
1920
|
+
const result = normalizeCast(input);
|
|
1921
|
+
expect(result).toEqual(expected);
|
|
1922
|
+
});
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1925
|
+
describe("contentValue node handling", () => {
|
|
1926
|
+
it("should leave contentValue nodes completely untouched", () => {
|
|
1927
|
+
const input: Root = {
|
|
1928
|
+
type: "root",
|
|
1929
|
+
children: [
|
|
1930
|
+
{
|
|
1931
|
+
type: "paragraph",
|
|
1932
|
+
children: [
|
|
1933
|
+
{
|
|
1934
|
+
type: "contentValue",
|
|
1935
|
+
prn: "content-123",
|
|
1936
|
+
value: {
|
|
1937
|
+
type: "root",
|
|
1938
|
+
children: [
|
|
1939
|
+
{
|
|
1940
|
+
type: "paragraph",
|
|
1941
|
+
children: [
|
|
1942
|
+
{ type: "text", value: "Hello" },
|
|
1943
|
+
{ type: "text", value: " " },
|
|
1944
|
+
{
|
|
1945
|
+
type: "bold",
|
|
1946
|
+
children: [
|
|
1947
|
+
{
|
|
1948
|
+
type: "bold", // Duplicate formatting
|
|
1949
|
+
children: [{ type: "text", value: "world" }],
|
|
1950
|
+
},
|
|
1951
|
+
],
|
|
1952
|
+
},
|
|
1953
|
+
],
|
|
1954
|
+
},
|
|
1955
|
+
],
|
|
1956
|
+
},
|
|
1957
|
+
},
|
|
1958
|
+
],
|
|
1959
|
+
},
|
|
1960
|
+
],
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
const result = normalizeCast(input);
|
|
1964
|
+
expect(result).toEqual(input); // Should remain completely unchanged
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1967
|
+
it("should leave contentValue nodes without nested CAST untouched", () => {
|
|
1968
|
+
const input: Root = {
|
|
1969
|
+
type: "root",
|
|
1970
|
+
children: [
|
|
1971
|
+
{
|
|
1972
|
+
type: "paragraph",
|
|
1973
|
+
children: [
|
|
1974
|
+
{
|
|
1975
|
+
type: "contentValue",
|
|
1976
|
+
prn: "content-456",
|
|
1977
|
+
// No value property
|
|
1978
|
+
},
|
|
1979
|
+
],
|
|
1980
|
+
},
|
|
1981
|
+
],
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
const result = normalizeCast(input);
|
|
1985
|
+
expect(result).toEqual(input); // Should remain unchanged
|
|
1986
|
+
});
|
|
1987
|
+
|
|
1988
|
+
it("should normalize content around contentValue nodes but leave contentValue untouched", () => {
|
|
1989
|
+
const input: Root = {
|
|
1990
|
+
type: "root",
|
|
1991
|
+
children: [
|
|
1992
|
+
{
|
|
1993
|
+
type: "paragraph",
|
|
1994
|
+
children: [
|
|
1995
|
+
{ type: "text", value: "Before" },
|
|
1996
|
+
{ type: "text", value: " " },
|
|
1997
|
+
{
|
|
1998
|
+
type: "contentValue",
|
|
1999
|
+
prn: "content-789",
|
|
2000
|
+
value: {
|
|
2001
|
+
type: "root",
|
|
2002
|
+
children: [
|
|
2003
|
+
{
|
|
2004
|
+
type: "paragraph",
|
|
2005
|
+
children: [
|
|
2006
|
+
{
|
|
2007
|
+
type: "bold",
|
|
2008
|
+
children: [
|
|
2009
|
+
{
|
|
2010
|
+
type: "bold", // This should NOT be normalized
|
|
2011
|
+
children: [{ type: "text", value: "nested content" }],
|
|
2012
|
+
},
|
|
2013
|
+
],
|
|
2014
|
+
},
|
|
2015
|
+
],
|
|
2016
|
+
},
|
|
2017
|
+
],
|
|
2018
|
+
},
|
|
2019
|
+
},
|
|
2020
|
+
{ type: "text", value: " " },
|
|
2021
|
+
{ type: "text", value: "after" },
|
|
2022
|
+
],
|
|
2023
|
+
},
|
|
2024
|
+
],
|
|
2025
|
+
};
|
|
2026
|
+
|
|
2027
|
+
const expected: Root = {
|
|
2028
|
+
type: "root",
|
|
2029
|
+
children: [
|
|
2030
|
+
{
|
|
2031
|
+
type: "paragraph",
|
|
2032
|
+
children: [
|
|
2033
|
+
{ type: "text", value: "Before " }, // Merged
|
|
2034
|
+
{
|
|
2035
|
+
type: "contentValue",
|
|
2036
|
+
prn: "content-789",
|
|
2037
|
+
value: {
|
|
2038
|
+
type: "root",
|
|
2039
|
+
children: [
|
|
2040
|
+
{
|
|
2041
|
+
type: "paragraph",
|
|
2042
|
+
children: [
|
|
2043
|
+
{
|
|
2044
|
+
type: "bold",
|
|
2045
|
+
children: [
|
|
2046
|
+
{
|
|
2047
|
+
type: "bold", // Still has duplicate formatting - untouched!
|
|
2048
|
+
children: [{ type: "text", value: "nested content" }],
|
|
2049
|
+
},
|
|
2050
|
+
],
|
|
2051
|
+
},
|
|
2052
|
+
],
|
|
2053
|
+
},
|
|
2054
|
+
],
|
|
2055
|
+
},
|
|
2056
|
+
},
|
|
2057
|
+
{ type: "text", value: " after" }, // Merged
|
|
2058
|
+
],
|
|
2059
|
+
},
|
|
2060
|
+
],
|
|
2061
|
+
};
|
|
2062
|
+
|
|
2063
|
+
const result = normalizeCast(input);
|
|
2064
|
+
expect(result).toEqual(expected);
|
|
2065
|
+
});
|
|
2066
|
+
|
|
2067
|
+
it("should handle multiple contentValue nodes", () => {
|
|
2068
|
+
const input: Root = {
|
|
2069
|
+
type: "root",
|
|
2070
|
+
children: [
|
|
2071
|
+
{
|
|
2072
|
+
type: "paragraph",
|
|
2073
|
+
children: [
|
|
2074
|
+
{
|
|
2075
|
+
type: "contentValue",
|
|
2076
|
+
prn: "content-1",
|
|
2077
|
+
value: {
|
|
2078
|
+
type: "root",
|
|
2079
|
+
children: [
|
|
2080
|
+
{
|
|
2081
|
+
type: "paragraph",
|
|
2082
|
+
children: [
|
|
2083
|
+
{ type: "text", value: "First" },
|
|
2084
|
+
{ type: "text", value: " " },
|
|
2085
|
+
{ type: "text", value: "content" },
|
|
2086
|
+
],
|
|
2087
|
+
},
|
|
2088
|
+
],
|
|
2089
|
+
},
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
type: "contentValue",
|
|
2093
|
+
prn: "content-2",
|
|
2094
|
+
value: {
|
|
2095
|
+
type: "root",
|
|
2096
|
+
children: [
|
|
2097
|
+
{
|
|
2098
|
+
type: "paragraph",
|
|
2099
|
+
children: [
|
|
2100
|
+
{
|
|
2101
|
+
type: "bold",
|
|
2102
|
+
children: [
|
|
2103
|
+
{
|
|
2104
|
+
type: "bold", // Duplicate - should remain
|
|
2105
|
+
children: [{ type: "text", value: "Second content" }],
|
|
2106
|
+
},
|
|
2107
|
+
],
|
|
2108
|
+
},
|
|
2109
|
+
],
|
|
2110
|
+
},
|
|
2111
|
+
],
|
|
2112
|
+
},
|
|
2113
|
+
},
|
|
2114
|
+
],
|
|
2115
|
+
},
|
|
2116
|
+
],
|
|
2117
|
+
};
|
|
2118
|
+
|
|
2119
|
+
const result = normalizeCast(input);
|
|
2120
|
+
expect(result).toEqual(input); // Both contentValue nodes should remain completely unchanged
|
|
2121
|
+
});
|
|
2122
|
+
});
|
|
2123
|
+
});
|