@tkeron/html-parser 0.1.3 → 0.1.5
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/README.md +5 -5
- package/bun.lock +4 -4
- package/index.ts +0 -5
- package/package.json +1 -1
- package/src/css-selector.ts +0 -5
- package/src/dom-simulator.ts +122 -45
- package/src/tokenizer.ts +0 -20
- package/tests/advanced.test.ts +2 -2
- package/tests/cloneNode.test.ts +587 -0
- package/tests/custom-elements.test.ts +8 -8
- package/tests/official/acid/acid-tests.test.ts +6 -6
- package/tests/official/final-output/final-output.test.ts +15 -15
- package/tests/official/html5lib/tokenizer-utils.ts +19 -31
- package/tests/official/html5lib/tokenizer.test.ts +4 -4
- package/tests/official/html5lib/tree-construction-utils.ts +20 -34
- package/tests/official/html5lib/tree-construction.test.ts +5 -5
- package/tests/official/validator/validator-tests.test.ts +11 -11
- package/tests/official/wpt/wpt-tests.test.ts +5 -5
- package/tests/outerHTML-replacement.test.ts +208 -0
- package/tests/parser.test.ts +1 -1
- package/tests/test-page-0.txt +12 -355
- package/tests/api-integration.test.ts +0 -114
- package/tests/dom-adoption.test.ts +0 -363
- package/tests/dom-synchronization.test.ts +0 -675
- package/tests/setAttribute-outerHTML.test.ts +0 -102
|
@@ -1,675 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { parseHTML } from "../index";
|
|
3
|
-
import { NodeType } from "../src/dom-simulator";
|
|
4
|
-
|
|
5
|
-
describe("DOM Manipulation - appendChild Synchronization", () => {
|
|
6
|
-
describe("appendChild updates innerHTML correctly", () => {
|
|
7
|
-
it("should update innerHTML when appending element", () => {
|
|
8
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
9
|
-
const div = doc.querySelector("div");
|
|
10
|
-
|
|
11
|
-
const newSpan = doc.createElement("span");
|
|
12
|
-
newSpan.textContent = "Second";
|
|
13
|
-
|
|
14
|
-
div.appendChild(newSpan);
|
|
15
|
-
|
|
16
|
-
expect(div.innerHTML).toBe("<span>First</span><span>Second</span>");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("should update innerHTML when appending text node", () => {
|
|
20
|
-
const doc = parseHTML("<div><span>Element</span></div>");
|
|
21
|
-
const div = doc.querySelector("div");
|
|
22
|
-
|
|
23
|
-
const textNode = doc.createTextNode(" and text");
|
|
24
|
-
div.appendChild(textNode);
|
|
25
|
-
|
|
26
|
-
expect(div.innerHTML).toBe("<span>Element</span> and text");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("should update innerHTML when appending to empty element", () => {
|
|
30
|
-
const doc = parseHTML("<div></div>");
|
|
31
|
-
const div = doc.querySelector("div");
|
|
32
|
-
|
|
33
|
-
const span = doc.createElement("span");
|
|
34
|
-
span.textContent = "Content";
|
|
35
|
-
|
|
36
|
-
div.appendChild(span);
|
|
37
|
-
|
|
38
|
-
expect(div.innerHTML).toBe("<span>Content</span>");
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("appendChild updates outerHTML correctly", () => {
|
|
43
|
-
it("should update outerHTML of parent element", () => {
|
|
44
|
-
const doc = parseHTML("<div></div>");
|
|
45
|
-
const div = doc.querySelector("div");
|
|
46
|
-
|
|
47
|
-
const span = doc.createElement("span");
|
|
48
|
-
span.textContent = "Child";
|
|
49
|
-
|
|
50
|
-
div.appendChild(span);
|
|
51
|
-
|
|
52
|
-
expect(div.outerHTML).toBe("<div><span>Child</span></div>");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should update outerHTML with attributes intact", () => {
|
|
56
|
-
const doc = parseHTML('<div class="container"></div>');
|
|
57
|
-
const div = doc.querySelector("div");
|
|
58
|
-
|
|
59
|
-
const p = doc.createElement("p");
|
|
60
|
-
p.textContent = "Paragraph";
|
|
61
|
-
|
|
62
|
-
div.appendChild(p);
|
|
63
|
-
|
|
64
|
-
expect(div.outerHTML).toBe(
|
|
65
|
-
'<div class="container"><p>Paragraph</p></div>'
|
|
66
|
-
);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe("appendChild updates children array correctly", () => {
|
|
71
|
-
it("should add element to children array", () => {
|
|
72
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
73
|
-
const div = doc.querySelector("div");
|
|
74
|
-
|
|
75
|
-
const newSpan = doc.createElement("span");
|
|
76
|
-
newSpan.textContent = "Second";
|
|
77
|
-
|
|
78
|
-
div.appendChild(newSpan);
|
|
79
|
-
|
|
80
|
-
expect(div.children.length).toBe(2);
|
|
81
|
-
expect(div.children[0].textContent).toBe("First");
|
|
82
|
-
expect(div.children[1]).toBe(newSpan);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("should not add text node to children array", () => {
|
|
86
|
-
const doc = parseHTML("<div><span>Element</span></div>");
|
|
87
|
-
const div = doc.querySelector("div");
|
|
88
|
-
|
|
89
|
-
const textNode = doc.createTextNode("Text");
|
|
90
|
-
div.appendChild(textNode);
|
|
91
|
-
|
|
92
|
-
expect(div.children.length).toBe(1);
|
|
93
|
-
expect(div.childNodes.length).toBe(2);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("appendChild updates childNodes correctly", () => {
|
|
98
|
-
it("should add node to childNodes array", () => {
|
|
99
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
100
|
-
const div = doc.querySelector("div");
|
|
101
|
-
const initialLength = div.childNodes.length;
|
|
102
|
-
|
|
103
|
-
const newSpan = doc.createElement("span");
|
|
104
|
-
div.appendChild(newSpan);
|
|
105
|
-
|
|
106
|
-
expect(div.childNodes.length).toBe(initialLength + 1);
|
|
107
|
-
expect(div.childNodes[div.childNodes.length - 1]).toBe(newSpan);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("should add both elements and text nodes to childNodes", () => {
|
|
111
|
-
const doc = parseHTML("<div></div>");
|
|
112
|
-
const div = doc.querySelector("div");
|
|
113
|
-
|
|
114
|
-
const span = doc.createElement("span");
|
|
115
|
-
const text = doc.createTextNode("Text");
|
|
116
|
-
|
|
117
|
-
div.appendChild(span);
|
|
118
|
-
div.appendChild(text);
|
|
119
|
-
|
|
120
|
-
expect(div.childNodes.length).toBe(2);
|
|
121
|
-
expect(div.childNodes[0]).toBe(span);
|
|
122
|
-
expect(div.childNodes[1]).toBe(text);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe("appendChild updates navigation properties", () => {
|
|
127
|
-
it("should update firstChild when appending to empty element", () => {
|
|
128
|
-
const doc = parseHTML("<div></div>");
|
|
129
|
-
const div = doc.querySelector("div");
|
|
130
|
-
|
|
131
|
-
const span = doc.createElement("span");
|
|
132
|
-
div.appendChild(span);
|
|
133
|
-
|
|
134
|
-
expect(div.firstChild).toBe(span);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("should update lastChild when appending", () => {
|
|
138
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
139
|
-
const div = doc.querySelector("div");
|
|
140
|
-
|
|
141
|
-
const newSpan = doc.createElement("span");
|
|
142
|
-
div.appendChild(newSpan);
|
|
143
|
-
|
|
144
|
-
expect(div.lastChild).toBe(newSpan);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it("should update firstElementChild when appending first element", () => {
|
|
148
|
-
const doc = parseHTML("<div></div>");
|
|
149
|
-
const div = doc.querySelector("div");
|
|
150
|
-
|
|
151
|
-
const span = doc.createElement("span");
|
|
152
|
-
div.appendChild(span);
|
|
153
|
-
|
|
154
|
-
expect(div.firstElementChild).toBe(span);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("should update lastElementChild when appending element", () => {
|
|
158
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
159
|
-
const div = doc.querySelector("div");
|
|
160
|
-
|
|
161
|
-
const newSpan = doc.createElement("span");
|
|
162
|
-
div.appendChild(newSpan);
|
|
163
|
-
|
|
164
|
-
expect(div.lastElementChild).toBe(newSpan);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("should update sibling relationships", () => {
|
|
168
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
169
|
-
const div = doc.querySelector("div");
|
|
170
|
-
const firstSpan = div.childNodes[0];
|
|
171
|
-
|
|
172
|
-
const secondSpan = doc.createElement("span");
|
|
173
|
-
div.appendChild(secondSpan);
|
|
174
|
-
|
|
175
|
-
expect(firstSpan.nextSibling).toBe(secondSpan);
|
|
176
|
-
expect(secondSpan.previousSibling).toBe(firstSpan);
|
|
177
|
-
expect(secondSpan.nextSibling).toBeNull();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("should update element sibling relationships", () => {
|
|
181
|
-
const doc = parseHTML("<div><span>First</span></div>");
|
|
182
|
-
const div = doc.querySelector("div");
|
|
183
|
-
const firstSpan = div.children[0];
|
|
184
|
-
|
|
185
|
-
const secondSpan = doc.createElement("span");
|
|
186
|
-
div.appendChild(secondSpan);
|
|
187
|
-
|
|
188
|
-
expect(firstSpan.nextElementSibling).toBe(secondSpan);
|
|
189
|
-
expect(secondSpan.previousElementSibling).toBe(firstSpan);
|
|
190
|
-
expect(secondSpan.nextElementSibling).toBeNull();
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe("appendChild updates textContent correctly", () => {
|
|
195
|
-
it("should update textContent when appending text node", () => {
|
|
196
|
-
const doc = parseHTML("<div>Hello</div>");
|
|
197
|
-
const div = doc.querySelector("div");
|
|
198
|
-
|
|
199
|
-
const textNode = doc.createTextNode(" World");
|
|
200
|
-
div.appendChild(textNode);
|
|
201
|
-
|
|
202
|
-
expect(div.textContent).toBe("Hello World");
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("should update textContent when appending element with text", () => {
|
|
206
|
-
const doc = parseHTML("<div>Start</div>");
|
|
207
|
-
const div = doc.querySelector("div");
|
|
208
|
-
|
|
209
|
-
const span = doc.createElement("span");
|
|
210
|
-
span.textContent = " End";
|
|
211
|
-
|
|
212
|
-
div.appendChild(span);
|
|
213
|
-
|
|
214
|
-
expect(div.textContent).toBe("Start End");
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe("appendChild moves node from previous parent", () => {
|
|
219
|
-
it("should remove node from previous parent when appending", () => {
|
|
220
|
-
const doc = parseHTML(
|
|
221
|
-
"<div id='p1'><span>Child</span></div><div id='p2'></div>"
|
|
222
|
-
);
|
|
223
|
-
const parent1 = doc.querySelector("#p1");
|
|
224
|
-
const parent2 = doc.querySelector("#p2");
|
|
225
|
-
const child = parent1.querySelector("span");
|
|
226
|
-
|
|
227
|
-
parent2.appendChild(child);
|
|
228
|
-
|
|
229
|
-
expect(parent1.childNodes.length).toBe(0);
|
|
230
|
-
expect(parent1.children.length).toBe(0);
|
|
231
|
-
expect(parent2.childNodes.length).toBe(1);
|
|
232
|
-
expect(child.parentNode).toBe(parent2);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("should update innerHTML of both parents when moving node", () => {
|
|
236
|
-
const doc = parseHTML(
|
|
237
|
-
"<div id='p1'><span>Moving</span><span>Staying</span></div><div id='p2'><span>Existing</span></div>"
|
|
238
|
-
);
|
|
239
|
-
const parent1 = doc.querySelector("#p1");
|
|
240
|
-
const parent2 = doc.querySelector("#p2");
|
|
241
|
-
const movingSpan = parent1.children[0];
|
|
242
|
-
|
|
243
|
-
parent2.appendChild(movingSpan);
|
|
244
|
-
|
|
245
|
-
expect(parent1.innerHTML).toBe("<span>Staying</span>");
|
|
246
|
-
expect(parent2.innerHTML).toBe(
|
|
247
|
-
"<span>Existing</span><span>Moving</span>"
|
|
248
|
-
);
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
describe("DOM Manipulation - removeChild Synchronization", () => {
|
|
254
|
-
describe("removeChild updates innerHTML correctly", () => {
|
|
255
|
-
it("should update innerHTML when removing element", () => {
|
|
256
|
-
const doc = parseHTML(
|
|
257
|
-
"<div><span>First</span><span>Second</span></div>"
|
|
258
|
-
);
|
|
259
|
-
const div = doc.querySelector("div");
|
|
260
|
-
const secondSpan = div.children[1];
|
|
261
|
-
|
|
262
|
-
div.removeChild(secondSpan);
|
|
263
|
-
|
|
264
|
-
expect(div.innerHTML).toBe("<span>First</span>");
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it("should update innerHTML when removing text node", () => {
|
|
268
|
-
const doc = parseHTML("<div><span>Element</span> and text</div>");
|
|
269
|
-
const div = doc.querySelector("div");
|
|
270
|
-
const textNode = div.childNodes[1];
|
|
271
|
-
|
|
272
|
-
div.removeChild(textNode);
|
|
273
|
-
|
|
274
|
-
expect(div.innerHTML).toBe("<span>Element</span>");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("should have empty innerHTML when removing all children", () => {
|
|
278
|
-
const doc = parseHTML("<div><span>Only child</span></div>");
|
|
279
|
-
const div = doc.querySelector("div");
|
|
280
|
-
const child = div.childNodes[0];
|
|
281
|
-
|
|
282
|
-
div.removeChild(child);
|
|
283
|
-
|
|
284
|
-
expect(div.innerHTML).toBe("");
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
describe("removeChild updates outerHTML correctly", () => {
|
|
289
|
-
it("should update outerHTML of parent", () => {
|
|
290
|
-
const doc = parseHTML(
|
|
291
|
-
"<div><span>Keep</span><span>Remove</span></div>"
|
|
292
|
-
);
|
|
293
|
-
const div = doc.querySelector("div");
|
|
294
|
-
const removeSpan = div.children[1];
|
|
295
|
-
|
|
296
|
-
div.removeChild(removeSpan);
|
|
297
|
-
|
|
298
|
-
expect(div.outerHTML).toBe("<div><span>Keep</span></div>");
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe("removeChild updates children array correctly", () => {
|
|
303
|
-
it("should remove element from children array", () => {
|
|
304
|
-
const doc = parseHTML(
|
|
305
|
-
"<div><span>First</span><span>Second</span><span>Third</span></div>"
|
|
306
|
-
);
|
|
307
|
-
const div = doc.querySelector("div");
|
|
308
|
-
const secondSpan = div.children[1];
|
|
309
|
-
|
|
310
|
-
div.removeChild(secondSpan);
|
|
311
|
-
|
|
312
|
-
expect(div.children.length).toBe(2);
|
|
313
|
-
expect(div.children[0].textContent).toBe("First");
|
|
314
|
-
expect(div.children[1].textContent).toBe("Third");
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it("should not affect children array when removing text node", () => {
|
|
318
|
-
const doc = parseHTML("<div><span>Element</span>Text</div>");
|
|
319
|
-
const div = doc.querySelector("div");
|
|
320
|
-
const textNode = div.childNodes[1];
|
|
321
|
-
|
|
322
|
-
div.removeChild(textNode);
|
|
323
|
-
|
|
324
|
-
expect(div.children.length).toBe(1);
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
describe("removeChild updates childNodes correctly", () => {
|
|
329
|
-
it("should remove node from childNodes array", () => {
|
|
330
|
-
const doc = parseHTML(
|
|
331
|
-
"<div><span>First</span><span>Second</span></div>"
|
|
332
|
-
);
|
|
333
|
-
const div = doc.querySelector("div");
|
|
334
|
-
const initialLength = div.childNodes.length;
|
|
335
|
-
const secondSpan = div.childNodes[1];
|
|
336
|
-
|
|
337
|
-
div.removeChild(secondSpan);
|
|
338
|
-
|
|
339
|
-
expect(div.childNodes.length).toBe(initialLength - 1);
|
|
340
|
-
expect(div.childNodes.indexOf(secondSpan)).toBe(-1);
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
describe("removeChild updates navigation properties", () => {
|
|
345
|
-
it("should update firstChild when removing first child", () => {
|
|
346
|
-
const doc = parseHTML(
|
|
347
|
-
"<div><span>First</span><span>Second</span></div>"
|
|
348
|
-
);
|
|
349
|
-
const div = doc.querySelector("div");
|
|
350
|
-
const firstSpan = div.childNodes[0];
|
|
351
|
-
const secondSpan = div.childNodes[1];
|
|
352
|
-
|
|
353
|
-
div.removeChild(firstSpan);
|
|
354
|
-
|
|
355
|
-
expect(div.firstChild).toBe(secondSpan);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("should update lastChild when removing last child", () => {
|
|
359
|
-
const doc = parseHTML(
|
|
360
|
-
"<div><span>First</span><span>Second</span></div>"
|
|
361
|
-
);
|
|
362
|
-
const div = doc.querySelector("div");
|
|
363
|
-
const firstSpan = div.childNodes[0];
|
|
364
|
-
const secondSpan = div.childNodes[1];
|
|
365
|
-
|
|
366
|
-
div.removeChild(secondSpan);
|
|
367
|
-
|
|
368
|
-
expect(div.lastChild).toBe(firstSpan);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("should set firstChild and lastChild to null when removing only child", () => {
|
|
372
|
-
const doc = parseHTML("<div><span>Only</span></div>");
|
|
373
|
-
const div = doc.querySelector("div");
|
|
374
|
-
const child = div.childNodes[0];
|
|
375
|
-
|
|
376
|
-
div.removeChild(child);
|
|
377
|
-
|
|
378
|
-
expect(div.firstChild).toBeNull();
|
|
379
|
-
expect(div.lastChild).toBeNull();
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it("should update firstElementChild when removing first element", () => {
|
|
383
|
-
const doc = parseHTML(
|
|
384
|
-
"<div><span>First</span><span>Second</span></div>"
|
|
385
|
-
);
|
|
386
|
-
const div = doc.querySelector("div");
|
|
387
|
-
const firstSpan = div.children[0];
|
|
388
|
-
const secondSpan = div.children[1];
|
|
389
|
-
|
|
390
|
-
div.removeChild(firstSpan);
|
|
391
|
-
|
|
392
|
-
expect(div.firstElementChild).toBe(secondSpan);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it("should update lastElementChild when removing last element", () => {
|
|
396
|
-
const doc = parseHTML(
|
|
397
|
-
"<div><span>First</span><span>Second</span></div>"
|
|
398
|
-
);
|
|
399
|
-
const div = doc.querySelector("div");
|
|
400
|
-
const firstSpan = div.children[0];
|
|
401
|
-
const secondSpan = div.children[1];
|
|
402
|
-
|
|
403
|
-
div.removeChild(secondSpan);
|
|
404
|
-
|
|
405
|
-
expect(div.lastElementChild).toBe(firstSpan);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it("should update sibling relationships when removing middle child", () => {
|
|
409
|
-
const doc = parseHTML(
|
|
410
|
-
"<div><span>A</span><span>B</span><span>C</span></div>"
|
|
411
|
-
);
|
|
412
|
-
const div = doc.querySelector("div");
|
|
413
|
-
const spanA = div.childNodes[0];
|
|
414
|
-
const spanB = div.childNodes[1];
|
|
415
|
-
const spanC = div.childNodes[2];
|
|
416
|
-
|
|
417
|
-
div.removeChild(spanB);
|
|
418
|
-
|
|
419
|
-
expect(spanA.nextSibling).toBe(spanC);
|
|
420
|
-
expect(spanC.previousSibling).toBe(spanA);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it("should update element sibling relationships", () => {
|
|
424
|
-
const doc = parseHTML(
|
|
425
|
-
"<div><span>A</span><span>B</span><span>C</span></div>"
|
|
426
|
-
);
|
|
427
|
-
const div = doc.querySelector("div");
|
|
428
|
-
const spanA = div.children[0];
|
|
429
|
-
const spanB = div.children[1];
|
|
430
|
-
const spanC = div.children[2];
|
|
431
|
-
|
|
432
|
-
div.removeChild(spanB);
|
|
433
|
-
|
|
434
|
-
expect(spanA.nextElementSibling).toBe(spanC);
|
|
435
|
-
expect(spanC.previousElementSibling).toBe(spanA);
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
describe("removeChild clears removed node properties", () => {
|
|
440
|
-
it("should clear parentNode of removed child", () => {
|
|
441
|
-
const doc = parseHTML("<div><span>Child</span></div>");
|
|
442
|
-
const div = doc.querySelector("div");
|
|
443
|
-
const child = div.childNodes[0];
|
|
444
|
-
|
|
445
|
-
div.removeChild(child);
|
|
446
|
-
|
|
447
|
-
expect(child.parentNode).toBeNull();
|
|
448
|
-
expect(child.parentElement).toBeNull();
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
it("should clear sibling references of removed child", () => {
|
|
452
|
-
const doc = parseHTML(
|
|
453
|
-
"<div><span>A</span><span>B</span><span>C</span></div>"
|
|
454
|
-
);
|
|
455
|
-
const div = doc.querySelector("div");
|
|
456
|
-
const spanB = div.childNodes[1];
|
|
457
|
-
|
|
458
|
-
div.removeChild(spanB);
|
|
459
|
-
|
|
460
|
-
expect(spanB.previousSibling).toBeNull();
|
|
461
|
-
expect(spanB.nextSibling).toBeNull();
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
it("should clear element sibling references of removed element", () => {
|
|
465
|
-
const doc = parseHTML(
|
|
466
|
-
"<div><span>A</span><span>B</span><span>C</span></div>"
|
|
467
|
-
);
|
|
468
|
-
const div = doc.querySelector("div");
|
|
469
|
-
const spanB = div.children[1];
|
|
470
|
-
|
|
471
|
-
div.removeChild(spanB);
|
|
472
|
-
|
|
473
|
-
expect(spanB.previousElementSibling).toBeNull();
|
|
474
|
-
expect(spanB.nextElementSibling).toBeNull();
|
|
475
|
-
});
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
describe("removeChild updates textContent correctly", () => {
|
|
479
|
-
it("should update textContent when removing text node", () => {
|
|
480
|
-
const doc = parseHTML("<div>Hello World</div>");
|
|
481
|
-
const div = doc.querySelector("div");
|
|
482
|
-
const textNode = div.childNodes[0];
|
|
483
|
-
|
|
484
|
-
div.removeChild(textNode);
|
|
485
|
-
|
|
486
|
-
expect(div.textContent).toBe("");
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
it("should update textContent when removing element with text", () => {
|
|
490
|
-
const doc = parseHTML("<div><span>Remove this</span> Keep this</div>");
|
|
491
|
-
const div = doc.querySelector("div");
|
|
492
|
-
const span = div.childNodes[0];
|
|
493
|
-
|
|
494
|
-
div.removeChild(span);
|
|
495
|
-
|
|
496
|
-
expect(div.textContent).toBe(" Keep this");
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
describe("removeChild error cases", () => {
|
|
501
|
-
it("should throw error when child is not found", () => {
|
|
502
|
-
const doc = parseHTML("<div><span>Child</span></div>");
|
|
503
|
-
const div = doc.querySelector("div");
|
|
504
|
-
const notAChild = doc.createElement("span");
|
|
505
|
-
|
|
506
|
-
expect(() => {
|
|
507
|
-
div.removeChild(notAChild);
|
|
508
|
-
}).toThrow("Child not found");
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
describe("DOM Manipulation - Navigation Properties Update", () => {
|
|
514
|
-
describe("Navigation properties after complex manipulations", () => {
|
|
515
|
-
it("should maintain correct relationships after multiple appendChild calls", () => {
|
|
516
|
-
const doc = parseHTML("<div></div>");
|
|
517
|
-
const div = doc.querySelector("div");
|
|
518
|
-
|
|
519
|
-
const span1 = doc.createElement("span");
|
|
520
|
-
span1.textContent = "1";
|
|
521
|
-
const span2 = doc.createElement("span");
|
|
522
|
-
span2.textContent = "2";
|
|
523
|
-
const span3 = doc.createElement("span");
|
|
524
|
-
span3.textContent = "3";
|
|
525
|
-
|
|
526
|
-
div.appendChild(span1);
|
|
527
|
-
div.appendChild(span2);
|
|
528
|
-
div.appendChild(span3);
|
|
529
|
-
|
|
530
|
-
expect(div.firstChild).toBe(span1);
|
|
531
|
-
expect(div.lastChild).toBe(span3);
|
|
532
|
-
expect(span1.nextSibling).toBe(span2);
|
|
533
|
-
expect(span2.previousSibling).toBe(span1);
|
|
534
|
-
expect(span2.nextSibling).toBe(span3);
|
|
535
|
-
expect(span3.previousSibling).toBe(span2);
|
|
536
|
-
expect(span1.previousSibling).toBeNull();
|
|
537
|
-
expect(span3.nextSibling).toBeNull();
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
it("should maintain correct relationships after insertBefore and appendChild", () => {
|
|
541
|
-
const doc = parseHTML("<div><span>B</span></div>");
|
|
542
|
-
const div = doc.querySelector("div");
|
|
543
|
-
const spanB = div.childNodes[0];
|
|
544
|
-
|
|
545
|
-
const spanA = doc.createElement("span");
|
|
546
|
-
spanA.textContent = "A";
|
|
547
|
-
const spanC = doc.createElement("span");
|
|
548
|
-
spanC.textContent = "C";
|
|
549
|
-
|
|
550
|
-
div.insertBefore(spanA, spanB);
|
|
551
|
-
div.appendChild(spanC);
|
|
552
|
-
|
|
553
|
-
expect(div.firstChild).toBe(spanA);
|
|
554
|
-
expect(div.lastChild).toBe(spanC);
|
|
555
|
-
expect(spanA.nextSibling).toBe(spanB);
|
|
556
|
-
expect(spanB.previousSibling).toBe(spanA);
|
|
557
|
-
expect(spanB.nextSibling).toBe(spanC);
|
|
558
|
-
expect(spanC.previousSibling).toBe(spanB);
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
it("should maintain correct relationships after removeChild and appendChild", () => {
|
|
562
|
-
const doc = parseHTML(
|
|
563
|
-
"<div><span>A</span><span>B</span><span>C</span></div>"
|
|
564
|
-
);
|
|
565
|
-
const div = doc.querySelector("div");
|
|
566
|
-
const spanB = div.children[1];
|
|
567
|
-
|
|
568
|
-
div.removeChild(spanB);
|
|
569
|
-
div.appendChild(spanB);
|
|
570
|
-
|
|
571
|
-
const spanA = div.children[0];
|
|
572
|
-
const spanC = div.children[1];
|
|
573
|
-
|
|
574
|
-
expect(div.children.length).toBe(3);
|
|
575
|
-
expect(spanA.nextElementSibling).toBe(spanC);
|
|
576
|
-
expect(spanC.previousElementSibling).toBe(spanA);
|
|
577
|
-
expect(spanC.nextElementSibling).toBe(spanB);
|
|
578
|
-
expect(spanB.previousElementSibling).toBe(spanC);
|
|
579
|
-
expect(div.lastElementChild).toBe(spanB);
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
it("should maintain correct relationships after replaceChild", () => {
|
|
583
|
-
const doc = parseHTML(
|
|
584
|
-
"<div><span>A</span><span>Old</span><span>C</span></div>"
|
|
585
|
-
);
|
|
586
|
-
const div = doc.querySelector("div");
|
|
587
|
-
const spanA = div.children[0];
|
|
588
|
-
const oldSpan = div.children[1];
|
|
589
|
-
const spanC = div.children[2];
|
|
590
|
-
|
|
591
|
-
const newSpan = doc.createElement("span");
|
|
592
|
-
newSpan.textContent = "B";
|
|
593
|
-
|
|
594
|
-
div.replaceChild(newSpan, oldSpan);
|
|
595
|
-
|
|
596
|
-
expect(spanA.nextSibling).toBe(newSpan);
|
|
597
|
-
expect(spanA.nextElementSibling).toBe(newSpan);
|
|
598
|
-
expect(newSpan.previousSibling).toBe(spanA);
|
|
599
|
-
expect(newSpan.previousElementSibling).toBe(spanA);
|
|
600
|
-
expect(newSpan.nextSibling).toBe(spanC);
|
|
601
|
-
expect(newSpan.nextElementSibling).toBe(spanC);
|
|
602
|
-
expect(spanC.previousSibling).toBe(newSpan);
|
|
603
|
-
expect(spanC.previousElementSibling).toBe(newSpan);
|
|
604
|
-
});
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
describe("Element-specific navigation with mixed node types", () => {
|
|
608
|
-
it("should skip text nodes for element sibling navigation", () => {
|
|
609
|
-
const doc = parseHTML("<div><span>A</span>Text<span>B</span></div>");
|
|
610
|
-
const div = doc.querySelector("div");
|
|
611
|
-
const spanA = div.children[0];
|
|
612
|
-
const spanB = div.children[1];
|
|
613
|
-
|
|
614
|
-
expect(spanA.nextElementSibling).toBe(spanB);
|
|
615
|
-
expect(spanB.previousElementSibling).toBe(spanA);
|
|
616
|
-
expect(div.children.length).toBe(2);
|
|
617
|
-
expect(div.childNodes.length).toBe(3);
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
it("should maintain element navigation after inserting text nodes", () => {
|
|
621
|
-
const doc = parseHTML("<div><span>A</span><span>B</span></div>");
|
|
622
|
-
const div = doc.querySelector("div");
|
|
623
|
-
const spanA = div.children[0];
|
|
624
|
-
const spanB = div.children[1];
|
|
625
|
-
|
|
626
|
-
const textNode = doc.createTextNode("Text");
|
|
627
|
-
div.insertBefore(textNode, spanB);
|
|
628
|
-
|
|
629
|
-
expect(spanA.nextElementSibling).toBe(spanB);
|
|
630
|
-
expect(spanB.previousElementSibling).toBe(spanA);
|
|
631
|
-
});
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
describe("firstChild/lastChild vs firstElementChild/lastElementChild", () => {
|
|
635
|
-
it("should differentiate between first child and first element child", () => {
|
|
636
|
-
const doc = parseHTML("<div>Text<span>Element</span></div>");
|
|
637
|
-
const div = doc.querySelector("div");
|
|
638
|
-
const textNode = div.childNodes[0];
|
|
639
|
-
const span = div.children[0];
|
|
640
|
-
|
|
641
|
-
expect(div.firstChild).toBe(textNode);
|
|
642
|
-
expect(div.firstElementChild).toBe(span);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
it("should differentiate between last child and last element child", () => {
|
|
646
|
-
const doc = parseHTML("<div><span>Element</span>Text</div>");
|
|
647
|
-
const div = doc.querySelector("div");
|
|
648
|
-
const span = div.children[0];
|
|
649
|
-
const textNode = div.childNodes[1];
|
|
650
|
-
|
|
651
|
-
expect(div.lastChild).toBe(textNode);
|
|
652
|
-
expect(div.lastElementChild).toBe(span);
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it("should update both when adding/removing only element", () => {
|
|
656
|
-
const doc = parseHTML("<div></div>");
|
|
657
|
-
const div = doc.querySelector("div");
|
|
658
|
-
|
|
659
|
-
const span = doc.createElement("span");
|
|
660
|
-
div.appendChild(span);
|
|
661
|
-
|
|
662
|
-
expect(div.firstChild).toBe(span);
|
|
663
|
-
expect(div.lastChild).toBe(span);
|
|
664
|
-
expect(div.firstElementChild).toBe(span);
|
|
665
|
-
expect(div.lastElementChild).toBe(span);
|
|
666
|
-
|
|
667
|
-
div.removeChild(span);
|
|
668
|
-
|
|
669
|
-
expect(div.firstChild).toBeNull();
|
|
670
|
-
expect(div.lastChild).toBeNull();
|
|
671
|
-
expect(div.firstElementChild).toBeNull();
|
|
672
|
-
expect(div.lastElementChild).toBeNull();
|
|
673
|
-
});
|
|
674
|
-
});
|
|
675
|
-
});
|