@tkeron/html-parser 1.1.2 → 1.3.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/.github/workflows/npm_deploy.yml +14 -4
- package/README.md +6 -6
- package/bun.lock +6 -8
- package/check-versions.ts +147 -0
- package/index.ts +4 -8
- package/package.json +5 -6
- package/src/dom-simulator/append-child.ts +130 -0
- package/src/dom-simulator/append.ts +18 -0
- package/src/dom-simulator/attributes.ts +23 -0
- package/src/dom-simulator/clone-node.ts +51 -0
- package/src/dom-simulator/convert-ast-node-to-dom.ts +37 -0
- package/src/dom-simulator/create-cdata.ts +18 -0
- package/src/dom-simulator/create-comment.ts +23 -0
- package/src/dom-simulator/create-doctype.ts +24 -0
- package/src/dom-simulator/create-document.ts +81 -0
- package/src/dom-simulator/create-element.ts +195 -0
- package/src/dom-simulator/create-processing-instruction.ts +19 -0
- package/src/dom-simulator/create-temp-parent.ts +9 -0
- package/src/dom-simulator/create-text-node.ts +23 -0
- package/src/dom-simulator/escape-text-content.ts +6 -0
- package/src/dom-simulator/find-special-elements.ts +14 -0
- package/src/dom-simulator/get-text-content.ts +18 -0
- package/src/dom-simulator/index.ts +36 -0
- package/src/dom-simulator/inner-outer-html.ts +182 -0
- package/src/dom-simulator/insert-after.ts +20 -0
- package/src/dom-simulator/insert-before.ts +108 -0
- package/src/dom-simulator/matches.ts +26 -0
- package/src/dom-simulator/node-types.ts +26 -0
- package/src/dom-simulator/prepend.ts +24 -0
- package/src/dom-simulator/remove-child.ts +68 -0
- package/src/dom-simulator/remove.ts +7 -0
- package/src/dom-simulator/replace-child.ts +152 -0
- package/src/dom-simulator/set-text-content.ts +33 -0
- package/src/dom-simulator/update-element-content.ts +56 -0
- package/src/dom-simulator.ts +12 -1126
- package/src/encoding/constants.ts +8 -0
- package/src/encoding/detect-encoding.ts +21 -0
- package/src/encoding/index.ts +1 -0
- package/src/encoding/normalize-encoding.ts +6 -0
- package/src/html-entities.ts +2127 -0
- package/src/index.ts +5 -5
- package/src/parser/adoption-agency-helpers.ts +145 -0
- package/src/parser/constants.ts +137 -0
- package/src/parser/dom-to-ast.ts +79 -0
- package/src/parser/index.ts +9 -0
- package/src/parser/parse.ts +772 -0
- package/src/parser/types.ts +56 -0
- package/src/selectors/find-elements-descendant.ts +47 -0
- package/src/selectors/index.ts +2 -0
- package/src/selectors/matches-selector.ts +12 -0
- package/src/selectors/matches-token.ts +27 -0
- package/src/selectors/parse-selector.ts +48 -0
- package/src/selectors/query-selector-all.ts +43 -0
- package/src/selectors/query-selector.ts +6 -0
- package/src/selectors/types.ts +10 -0
- package/src/serializer/attributes.ts +74 -0
- package/src/serializer/escape.ts +13 -0
- package/src/serializer/index.ts +1 -0
- package/src/serializer/serialize-tokens.ts +511 -0
- package/src/tokenizer/calculate-position.ts +10 -0
- package/src/tokenizer/constants.ts +11 -0
- package/src/tokenizer/decode-entities.ts +64 -0
- package/src/tokenizer/index.ts +2 -0
- package/src/tokenizer/parse-attributes.ts +74 -0
- package/src/tokenizer/tokenize.ts +165 -0
- package/src/tokenizer/types.ts +25 -0
- package/tests/adoption-agency-helpers.test.ts +304 -0
- package/tests/advanced.test.ts +242 -221
- package/tests/cloneNode.test.ts +19 -66
- package/tests/custom-elements-head.test.ts +54 -55
- package/tests/dom-extended.test.ts +77 -64
- package/tests/dom-manipulation.test.ts +51 -24
- package/tests/dom.test.ts +15 -13
- package/tests/encoding/detect-encoding.test.ts +33 -0
- package/tests/google-dom.test.ts +2 -2
- package/tests/helpers/tokenizer-adapter.test.ts +29 -43
- package/tests/helpers/tokenizer-adapter.ts +36 -33
- package/tests/helpers/tree-adapter.test.ts +20 -20
- package/tests/helpers/tree-adapter.ts +34 -24
- package/tests/html-entities-text.test.ts +6 -2
- package/tests/innerhtml-void-elements.test.ts +52 -36
- package/tests/outerHTML-replacement.test.ts +37 -65
- package/tests/parser/dom-to-ast.test.ts +109 -0
- package/tests/parser/parse.test.ts +139 -0
- package/tests/parser.test.ts +281 -217
- package/tests/selectors/query-selector-all.test.ts +39 -0
- package/tests/selectors/query-selector.test.ts +42 -0
- package/tests/serializer/attributes.test.ts +132 -0
- package/tests/serializer/escape.test.ts +51 -0
- package/tests/serializer/serialize-tokens.test.ts +80 -0
- package/tests/serializer-core.test.ts +6 -6
- package/tests/serializer-injectmeta.test.ts +6 -6
- package/tests/serializer-optionaltags.test.ts +9 -6
- package/tests/serializer-options.test.ts +6 -6
- package/tests/serializer-whitespace.test.ts +6 -6
- package/tests/tokenizer/calculate-position.test.ts +34 -0
- package/tests/tokenizer/decode-entities.test.ts +31 -0
- package/tests/tokenizer/parse-attributes.test.ts +44 -0
- package/tests/tokenizer/tokenize.test.ts +757 -0
- package/tests/tokenizer-namedEntities.test.ts +10 -7
- package/tests/tokenizer-pendingSpecChanges.test.ts +10 -7
- package/tests/tokenizer.test.ts +268 -256
- package/tests/tree-construction-adoption01.test.ts +25 -16
- package/tests/tree-construction-adoption02.test.ts +30 -19
- package/tests/tree-construction-domjs-unsafe.test.ts +6 -4
- package/tests/tree-construction-entities02.test.ts +18 -16
- package/tests/tree-construction-html5test-com.test.ts +16 -10
- package/tests/tree-construction-math.test.ts +11 -9
- package/tests/tree-construction-namespace-sensitivity.test.ts +11 -9
- package/tests/tree-construction-noscript01.test.ts +11 -9
- package/tests/tree-construction-ruby.test.ts +6 -4
- package/tests/tree-construction-scriptdata01.test.ts +6 -4
- package/tests/tree-construction-svg.test.ts +6 -4
- package/tests/tree-construction-template.test.ts +6 -4
- package/tests/tree-construction-tests10.test.ts +6 -4
- package/tests/tree-construction-tests11.test.ts +6 -4
- package/tests/tree-construction-tests20.test.ts +7 -4
- package/tests/tree-construction-tests21.test.ts +7 -4
- package/tests/tree-construction-tests23.test.ts +7 -4
- package/tests/tree-construction-tests24.test.ts +7 -4
- package/tests/tree-construction-tests5.test.ts +6 -5
- package/tests/tree-construction-tests6.test.ts +6 -5
- package/tests/tree-construction-tests_innerHTML_1.test.ts +6 -5
- package/tests/void-elements.test.ts +85 -40
- package/tsconfig.json +1 -1
- package/src/css-selector.ts +0 -185
- package/src/encoding.ts +0 -39
- package/src/parser.ts +0 -682
- package/src/serializer.ts +0 -450
- package/src/tokenizer.ts +0 -325
- package/tests/selectors.test.ts +0 -128
package/tests/cloneNode.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { parseHTML } from "../index.js";
|
|
3
|
-
import { NodeType } from "../src/dom-simulator.js";
|
|
3
|
+
import { NodeType } from "../src/dom-simulator/index.js";
|
|
4
4
|
|
|
5
5
|
describe("cloneNode functionality", () => {
|
|
6
6
|
describe("cloneNode(true) - deep cloning", () => {
|
|
@@ -11,17 +11,13 @@ describe("cloneNode functionality", () => {
|
|
|
11
11
|
|
|
12
12
|
const cloned = original.cloneNode(true);
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
expect(cloned).toBeTruthy();
|
|
16
15
|
expect(cloned.nodeName).toBe("DIV");
|
|
17
16
|
|
|
18
|
-
|
|
19
17
|
expect(cloned.getAttribute("id")).toBe("original");
|
|
20
18
|
|
|
21
|
-
|
|
22
19
|
expect(cloned.textContent).toBe("Hello World");
|
|
23
20
|
|
|
24
|
-
|
|
25
21
|
expect(cloned.childNodes.length).toBeGreaterThan(0);
|
|
26
22
|
});
|
|
27
23
|
|
|
@@ -38,20 +34,16 @@ describe("cloneNode functionality", () => {
|
|
|
38
34
|
|
|
39
35
|
const cloned = parent.cloneNode(true);
|
|
40
36
|
|
|
41
|
-
|
|
42
37
|
expect(cloned.nodeName).toBe("DIV");
|
|
43
38
|
expect(cloned.getAttribute("id")).toBe("parent");
|
|
44
39
|
|
|
45
|
-
|
|
46
40
|
expect(cloned.childNodes.length).toBeGreaterThan(0);
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
|
|
49
42
|
const childDiv = cloned.querySelector(".child");
|
|
50
43
|
expect(childDiv).toBeTruthy();
|
|
51
44
|
expect(childDiv?.nodeName).toBe("DIV");
|
|
52
45
|
expect(childDiv?.getAttribute("class")).toBe("child");
|
|
53
46
|
|
|
54
|
-
|
|
55
47
|
const span = cloned.querySelector("span");
|
|
56
48
|
expect(span).toBeTruthy();
|
|
57
49
|
expect(span?.textContent).toBe("Nested Text");
|
|
@@ -70,7 +62,6 @@ describe("cloneNode functionality", () => {
|
|
|
70
62
|
|
|
71
63
|
const cloned = list.cloneNode(true);
|
|
72
64
|
|
|
73
|
-
|
|
74
65
|
const items = cloned.querySelectorAll("li");
|
|
75
66
|
expect(items.length).toBe(3);
|
|
76
67
|
expect(items[0]?.textContent).toBe("Item 1");
|
|
@@ -89,18 +80,15 @@ describe("cloneNode functionality", () => {
|
|
|
89
80
|
const doc = parseHTML(html);
|
|
90
81
|
const container = doc.querySelector("#container")!;
|
|
91
82
|
|
|
92
|
-
|
|
93
83
|
const originalInnerHTML = container.innerHTML;
|
|
94
84
|
expect(originalInnerHTML).toBeTruthy();
|
|
95
85
|
expect(originalInnerHTML.length).toBeGreaterThan(0);
|
|
96
86
|
|
|
97
87
|
const cloned = container.cloneNode(true);
|
|
98
88
|
|
|
99
|
-
|
|
100
89
|
expect(cloned.innerHTML).toBeTruthy();
|
|
101
90
|
expect(cloned.innerHTML.length).toBeGreaterThan(0);
|
|
102
91
|
|
|
103
|
-
|
|
104
92
|
expect(cloned.innerHTML).toContain("<h1>Title</h1>");
|
|
105
93
|
expect(cloned.innerHTML).toContain("<p>Paragraph 1</p>");
|
|
106
94
|
expect(cloned.innerHTML).toContain("<p>Paragraph 2</p>");
|
|
@@ -111,19 +99,15 @@ describe("cloneNode functionality", () => {
|
|
|
111
99
|
const doc = parseHTML(html);
|
|
112
100
|
const mixed = doc.querySelector("#mixed")!;
|
|
113
101
|
|
|
114
|
-
|
|
115
102
|
const originalChildCount = mixed.childNodes.length;
|
|
116
103
|
expect(originalChildCount).toBeGreaterThan(0);
|
|
117
104
|
|
|
118
105
|
const cloned = mixed.cloneNode(true);
|
|
119
106
|
|
|
120
|
-
|
|
121
107
|
expect(cloned.childNodes.length).toBe(originalChildCount);
|
|
122
108
|
|
|
123
|
-
|
|
124
109
|
expect(cloned.textContent).toBe("Text beforebold textText after");
|
|
125
110
|
|
|
126
|
-
|
|
127
111
|
const strong = cloned.querySelector("strong");
|
|
128
112
|
expect(strong).toBeTruthy();
|
|
129
113
|
expect(strong?.textContent).toBe("bold text");
|
|
@@ -136,7 +120,6 @@ describe("cloneNode functionality", () => {
|
|
|
136
120
|
|
|
137
121
|
const cloned = element.cloneNode(true);
|
|
138
122
|
|
|
139
|
-
|
|
140
123
|
expect(cloned.getAttribute("id")).toBe("attrs");
|
|
141
124
|
expect(cloned.getAttribute("class")).toBe("test");
|
|
142
125
|
expect(cloned.getAttribute("data-value")).toBe("123");
|
|
@@ -161,26 +144,22 @@ describe("cloneNode functionality", () => {
|
|
|
161
144
|
|
|
162
145
|
const cloned = article.cloneNode(true);
|
|
163
146
|
|
|
164
|
-
|
|
165
147
|
expect(cloned.nodeName).toBe("ARTICLE");
|
|
166
148
|
expect(cloned.getAttribute("id")).toBe("article");
|
|
167
149
|
|
|
168
|
-
|
|
169
150
|
expect(cloned.querySelector("h2")?.textContent).toBe("Article Title");
|
|
170
|
-
|
|
151
|
+
|
|
171
152
|
const paragraphs = cloned.querySelectorAll("p");
|
|
172
153
|
expect(paragraphs.length).toBe(2);
|
|
173
154
|
expect(paragraphs[0]?.textContent).toBe("First paragraph");
|
|
174
155
|
expect(paragraphs[1]?.textContent).toBe("Last paragraph");
|
|
175
156
|
|
|
176
|
-
|
|
177
157
|
const highlight = cloned.querySelector(".highlight");
|
|
178
158
|
expect(highlight).toBeTruthy();
|
|
179
159
|
expect(highlight?.querySelector("span")?.textContent).toBe("Highlighted");
|
|
180
160
|
|
|
181
|
-
|
|
182
161
|
const hasComment = Array.from(cloned.childNodes).some(
|
|
183
|
-
(node: any) => node.nodeType === NodeType.COMMENT_NODE
|
|
162
|
+
(node: any) => node.nodeType === NodeType.COMMENT_NODE,
|
|
184
163
|
);
|
|
185
164
|
expect(hasComment).toBe(true);
|
|
186
165
|
});
|
|
@@ -203,10 +182,9 @@ describe("cloneNode functionality", () => {
|
|
|
203
182
|
|
|
204
183
|
const cloned = section.cloneNode(true);
|
|
205
184
|
|
|
206
|
-
|
|
207
185
|
expect(cloned.outerHTML).toBeTruthy();
|
|
208
186
|
expect(cloned.outerHTML).toContain("section");
|
|
209
|
-
expect(cloned.outerHTML).toContain(
|
|
187
|
+
expect(cloned.outerHTML).toContain('class="main"');
|
|
210
188
|
expect(cloned.outerHTML).toContain("<h1>Title</h1>");
|
|
211
189
|
expect(cloned.outerHTML).toContain("<p>Text</p>");
|
|
212
190
|
});
|
|
@@ -220,7 +198,6 @@ describe("cloneNode functionality", () => {
|
|
|
220
198
|
|
|
221
199
|
const cloned = parent.cloneNode(false);
|
|
222
200
|
|
|
223
|
-
|
|
224
201
|
expect(cloned.nodeName).toBe("DIV");
|
|
225
202
|
expect(cloned.getAttribute("id")).toBe("parent");
|
|
226
203
|
expect(cloned.childNodes.length).toBe(0);
|
|
@@ -234,12 +211,10 @@ describe("cloneNode functionality", () => {
|
|
|
234
211
|
|
|
235
212
|
const cloned = element.cloneNode(false);
|
|
236
213
|
|
|
237
|
-
|
|
238
214
|
expect(cloned.getAttribute("id")).toBe("test");
|
|
239
215
|
expect(cloned.getAttribute("class")).toBe("container");
|
|
240
216
|
expect(cloned.getAttribute("data-value")).toBe("123");
|
|
241
217
|
|
|
242
|
-
|
|
243
218
|
expect(cloned.childNodes.length).toBe(0);
|
|
244
219
|
expect(cloned.innerHTML).toBe("");
|
|
245
220
|
});
|
|
@@ -253,7 +228,6 @@ describe("cloneNode functionality", () => {
|
|
|
253
228
|
|
|
254
229
|
const cloned = original.cloneNode(true);
|
|
255
230
|
|
|
256
|
-
|
|
257
231
|
cloned.setAttribute("id", "cloned");
|
|
258
232
|
cloned.setAttribute("data-modified", "true");
|
|
259
233
|
|
|
@@ -270,13 +244,11 @@ describe("cloneNode functionality", () => {
|
|
|
270
244
|
|
|
271
245
|
const cloned = parent.cloneNode(true);
|
|
272
246
|
const clonedChild = cloned.querySelector("#child");
|
|
273
|
-
|
|
247
|
+
|
|
274
248
|
expect(clonedChild).toBeTruthy();
|
|
275
|
-
|
|
276
|
-
|
|
249
|
+
|
|
277
250
|
clonedChild?.setAttribute("data-cloned", "yes");
|
|
278
251
|
|
|
279
|
-
|
|
280
252
|
const originalChild = parent.querySelector("#child");
|
|
281
253
|
expect(originalChild?.hasAttribute("data-cloned")).toBe(false);
|
|
282
254
|
});
|
|
@@ -324,7 +296,6 @@ describe("cloneNode functionality", () => {
|
|
|
324
296
|
|
|
325
297
|
const cloned = level1.cloneNode(true);
|
|
326
298
|
|
|
327
|
-
|
|
328
299
|
expect(cloned.querySelector("#level2")).toBeTruthy();
|
|
329
300
|
expect(cloned.querySelector("#level3")).toBeTruthy();
|
|
330
301
|
expect(cloned.querySelector("#level4")).toBeTruthy();
|
|
@@ -340,22 +311,18 @@ describe("cloneNode functionality", () => {
|
|
|
340
311
|
const doc = parseHTML(html);
|
|
341
312
|
const container = doc.querySelector("#container")!;
|
|
342
313
|
|
|
343
|
-
|
|
344
314
|
const originalInnerHTML = container.innerHTML;
|
|
345
315
|
expect(originalInnerHTML).toBeTruthy();
|
|
346
316
|
|
|
347
317
|
const cloned = container.cloneNode(true);
|
|
348
318
|
|
|
349
|
-
|
|
350
319
|
const clonedInnerHTML = cloned.innerHTML;
|
|
351
320
|
expect(clonedInnerHTML).toBeTruthy();
|
|
352
321
|
expect(clonedInnerHTML.length).toBeGreaterThan(0);
|
|
353
322
|
|
|
354
|
-
|
|
355
323
|
expect(clonedInnerHTML).toContain("<p>Paragraph 1</p>");
|
|
356
324
|
expect(clonedInnerHTML).toContain("<p>Paragraph 2</p>");
|
|
357
325
|
|
|
358
|
-
|
|
359
326
|
expect(typeof cloned.innerHTML).toBe("string");
|
|
360
327
|
});
|
|
361
328
|
|
|
@@ -369,10 +336,8 @@ describe("cloneNode functionality", () => {
|
|
|
369
336
|
|
|
370
337
|
const cloned = parent.cloneNode(true);
|
|
371
338
|
|
|
372
|
-
|
|
373
339
|
expect(cloned.childNodes.length).toBe(originalChildCount);
|
|
374
|
-
|
|
375
|
-
|
|
340
|
+
|
|
376
341
|
for (let i = 0; i < cloned.childNodes.length; i++) {
|
|
377
342
|
expect(cloned.childNodes[i]).toBeTruthy();
|
|
378
343
|
expect(cloned.childNodes[i].nodeType).toBeDefined();
|
|
@@ -386,12 +351,10 @@ describe("cloneNode functionality", () => {
|
|
|
386
351
|
|
|
387
352
|
const cloned = container.cloneNode(true);
|
|
388
353
|
|
|
389
|
-
|
|
390
354
|
expect(cloned.children).toBeTruthy();
|
|
391
355
|
expect(Array.isArray(cloned.children)).toBe(true);
|
|
392
356
|
expect(cloned.children.length).toBe(3);
|
|
393
357
|
|
|
394
|
-
|
|
395
358
|
for (const child of cloned.children) {
|
|
396
359
|
expect(child.nodeType).toBe(NodeType.ELEMENT_NODE);
|
|
397
360
|
}
|
|
@@ -404,18 +367,15 @@ describe("cloneNode functionality", () => {
|
|
|
404
367
|
|
|
405
368
|
const cloned = list.cloneNode(true);
|
|
406
369
|
|
|
407
|
-
|
|
408
370
|
expect(cloned.firstChild).toBeTruthy();
|
|
409
371
|
expect(cloned.lastChild).toBeTruthy();
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
372
|
+
|
|
413
373
|
expect(cloned.firstElementChild).toBeTruthy();
|
|
414
374
|
expect(cloned.lastElementChild).toBeTruthy();
|
|
415
|
-
|
|
375
|
+
|
|
416
376
|
const firstLi = cloned.firstElementChild;
|
|
417
377
|
const lastLi = cloned.lastElementChild;
|
|
418
|
-
|
|
378
|
+
|
|
419
379
|
expect(firstLi?.textContent).toContain("First");
|
|
420
380
|
expect(lastLi?.textContent).toContain("Last");
|
|
421
381
|
});
|
|
@@ -427,12 +387,10 @@ describe("cloneNode functionality", () => {
|
|
|
427
387
|
const doc = parseHTML(html);
|
|
428
388
|
const dynamic = doc.querySelector("#dynamic")!;
|
|
429
389
|
|
|
430
|
-
|
|
431
390
|
dynamic.innerHTML = "<p>Dynamic content</p><span>More content</span>";
|
|
432
391
|
|
|
433
392
|
const cloned = dynamic.cloneNode(true);
|
|
434
393
|
|
|
435
|
-
|
|
436
394
|
expect(cloned.querySelector("p")).toBeTruthy();
|
|
437
395
|
expect(cloned.querySelector("p")?.textContent).toBe("Dynamic content");
|
|
438
396
|
expect(cloned.querySelector("span")).toBeTruthy();
|
|
@@ -446,17 +404,13 @@ describe("cloneNode functionality", () => {
|
|
|
446
404
|
|
|
447
405
|
const cloned = original.cloneNode(true);
|
|
448
406
|
|
|
449
|
-
|
|
450
407
|
expect(cloned.querySelector("p")?.textContent).toBe("Original");
|
|
451
408
|
|
|
452
|
-
|
|
453
409
|
cloned.innerHTML = "<span>Modified</span>";
|
|
454
410
|
|
|
455
|
-
|
|
456
411
|
expect(original.querySelector("p")?.textContent).toBe("Original");
|
|
457
412
|
expect(original.querySelector("span")).toBeNull();
|
|
458
413
|
|
|
459
|
-
|
|
460
414
|
expect(cloned.querySelector("span")?.textContent).toBe("Modified");
|
|
461
415
|
expect(cloned.querySelector("p")).toBeNull();
|
|
462
416
|
});
|
|
@@ -488,21 +442,21 @@ describe("cloneNode functionality", () => {
|
|
|
488
442
|
|
|
489
443
|
const cloned = card.cloneNode(true);
|
|
490
444
|
|
|
491
|
-
|
|
492
445
|
expect(cloned.getAttribute("data-id")).toBe("123");
|
|
493
446
|
expect(cloned.querySelector(".card-header")).toBeTruthy();
|
|
494
447
|
expect(cloned.querySelector(".card-body")).toBeTruthy();
|
|
495
448
|
expect(cloned.querySelector(".card-footer")).toBeTruthy();
|
|
496
449
|
|
|
497
|
-
|
|
498
|
-
|
|
450
|
+
expect(cloned.querySelector(".card-title")?.textContent).toBe(
|
|
451
|
+
"Card Title",
|
|
452
|
+
);
|
|
499
453
|
expect(cloned.querySelector("strong")?.textContent).toBe("bold");
|
|
500
|
-
|
|
454
|
+
|
|
501
455
|
const items = cloned.querySelectorAll("li");
|
|
502
456
|
expect(items.length).toBe(2);
|
|
503
|
-
|
|
457
|
+
|
|
504
458
|
const buttons = cloned.querySelectorAll("button");
|
|
505
|
-
expect(buttons.length).toBe(3);
|
|
459
|
+
expect(buttons.length).toBe(3);
|
|
506
460
|
});
|
|
507
461
|
|
|
508
462
|
it("should clone a form with various input types", () => {
|
|
@@ -522,7 +476,6 @@ describe("cloneNode functionality", () => {
|
|
|
522
476
|
|
|
523
477
|
const cloned = form.cloneNode(true);
|
|
524
478
|
|
|
525
|
-
|
|
526
479
|
const textInput = cloned.querySelector('[name="username"]');
|
|
527
480
|
expect(textInput).toBeTruthy();
|
|
528
481
|
expect(textInput?.getAttribute("value")).toBe("john");
|
|
@@ -569,7 +522,7 @@ describe("cloneNode functionality", () => {
|
|
|
569
522
|
|
|
570
523
|
expect(cloned.querySelector("thead")).toBeTruthy();
|
|
571
524
|
expect(cloned.querySelector("tbody")).toBeTruthy();
|
|
572
|
-
|
|
525
|
+
|
|
573
526
|
const headers = cloned.querySelectorAll("th");
|
|
574
527
|
expect(headers.length).toBe(2);
|
|
575
528
|
expect(headers[0]?.textContent).toBe("Name");
|
|
@@ -577,7 +530,7 @@ describe("cloneNode functionality", () => {
|
|
|
577
530
|
|
|
578
531
|
const rows = cloned.querySelectorAll("tbody tr");
|
|
579
532
|
expect(rows.length).toBe(2);
|
|
580
|
-
|
|
533
|
+
|
|
581
534
|
const firstRowCells = rows[0]?.querySelectorAll("td");
|
|
582
535
|
expect(firstRowCells?.length).toBe(2);
|
|
583
536
|
expect(firstRowCells?.[0]?.textContent).toBe("John");
|
|
@@ -1,105 +1,104 @@
|
|
|
1
|
-
import { describe, it, expect } from
|
|
2
|
-
import { parseHTML } from
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { parseHTML } from "../index";
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
|
|
6
|
-
it('should keep <meta-tags> custom element in head', () => {
|
|
4
|
+
describe("Custom Elements in <head>", () => {
|
|
5
|
+
it("should keep <meta-tags> custom element in head", () => {
|
|
7
6
|
const doc = parseHTML(
|
|
8
|
-
|
|
7
|
+
"<!DOCTYPE html><html><head><meta-tags></meta-tags></head><body></body></html>",
|
|
9
8
|
);
|
|
10
|
-
|
|
11
|
-
const metaTags = doc.head?.querySelector(
|
|
9
|
+
|
|
10
|
+
const metaTags = doc.head?.querySelector("meta-tags");
|
|
12
11
|
expect(metaTags).toBeTruthy();
|
|
13
|
-
expect(metaTags?.parentElement?.tagName).toBe(
|
|
12
|
+
expect(metaTags?.parentElement?.tagName).toBe("HEAD");
|
|
14
13
|
});
|
|
15
14
|
|
|
16
|
-
it(
|
|
15
|
+
it("should keep <social-meta> custom element in head", () => {
|
|
17
16
|
const doc = parseHTML(
|
|
18
|
-
|
|
17
|
+
"<!DOCTYPE html><html><head><social-meta></social-meta></head><body></body></html>",
|
|
19
18
|
);
|
|
20
|
-
|
|
21
|
-
const socialMeta = doc.head?.querySelector(
|
|
19
|
+
|
|
20
|
+
const socialMeta = doc.head?.querySelector("social-meta");
|
|
22
21
|
expect(socialMeta).toBeTruthy();
|
|
23
|
-
expect(socialMeta?.parentElement?.tagName).toBe(
|
|
22
|
+
expect(socialMeta?.parentElement?.tagName).toBe("HEAD");
|
|
24
23
|
});
|
|
25
24
|
|
|
26
|
-
it(
|
|
25
|
+
it("should keep any <custom-element> with hyphen in head", () => {
|
|
27
26
|
const doc = parseHTML(
|
|
28
|
-
|
|
27
|
+
"<!DOCTYPE html><html><head><my-component></my-component></head><body></body></html>",
|
|
29
28
|
);
|
|
30
|
-
|
|
31
|
-
const myComponent = doc.head?.querySelector(
|
|
29
|
+
|
|
30
|
+
const myComponent = doc.head?.querySelector("my-component");
|
|
32
31
|
expect(myComponent).toBeTruthy();
|
|
33
|
-
expect(myComponent?.parentElement?.tagName).toBe(
|
|
32
|
+
expect(myComponent?.parentElement?.tagName).toBe("HEAD");
|
|
34
33
|
});
|
|
35
34
|
|
|
36
|
-
it(
|
|
35
|
+
it("should still eject non-custom elements like <div> to body", () => {
|
|
37
36
|
const doc = parseHTML(
|
|
38
|
-
|
|
37
|
+
"<!DOCTYPE html><html><head><div>test</div></head><body></body></html>",
|
|
39
38
|
);
|
|
40
|
-
|
|
41
|
-
const divInHead = doc.head?.querySelector(
|
|
42
|
-
const divInBody = doc.body?.querySelector(
|
|
39
|
+
|
|
40
|
+
const divInHead = doc.head?.querySelector("div");
|
|
41
|
+
const divInBody = doc.body?.querySelector("div");
|
|
43
42
|
expect(divInHead).toBeFalsy();
|
|
44
43
|
expect(divInBody).toBeTruthy();
|
|
45
44
|
});
|
|
46
45
|
|
|
47
|
-
it(
|
|
46
|
+
it("should handle nested custom elements in head", () => {
|
|
48
47
|
const doc = parseHTML(
|
|
49
|
-
|
|
48
|
+
"<!DOCTYPE html><html><head><my-wrapper><inner-comp></inner-comp></my-wrapper></head><body></body></html>",
|
|
50
49
|
);
|
|
51
|
-
|
|
52
|
-
const myWrapper = doc.head?.querySelector(
|
|
50
|
+
|
|
51
|
+
const myWrapper = doc.head?.querySelector("my-wrapper");
|
|
53
52
|
expect(myWrapper).toBeTruthy();
|
|
54
|
-
expect(myWrapper?.parentElement?.tagName).toBe(
|
|
55
|
-
|
|
56
|
-
const innerComp = myWrapper?.querySelector(
|
|
53
|
+
expect(myWrapper?.parentElement?.tagName).toBe("HEAD");
|
|
54
|
+
|
|
55
|
+
const innerComp = myWrapper?.querySelector("inner-comp");
|
|
57
56
|
expect(innerComp).toBeTruthy();
|
|
58
57
|
});
|
|
59
58
|
|
|
60
|
-
it(
|
|
59
|
+
it("should keep custom elements with attributes in head", () => {
|
|
61
60
|
const doc = parseHTML(
|
|
62
|
-
'<!DOCTYPE html><html><head><seo-meta property="og:title" content="Test"></seo-meta></head><body></body></html>'
|
|
61
|
+
'<!DOCTYPE html><html><head><seo-meta property="og:title" content="Test"></seo-meta></head><body></body></html>',
|
|
63
62
|
);
|
|
64
|
-
|
|
65
|
-
const seoMeta = doc.head?.querySelector(
|
|
63
|
+
|
|
64
|
+
const seoMeta = doc.head?.querySelector("seo-meta");
|
|
66
65
|
expect(seoMeta).toBeTruthy();
|
|
67
|
-
expect(seoMeta?.getAttribute(
|
|
68
|
-
expect(seoMeta?.getAttribute(
|
|
69
|
-
expect(seoMeta?.parentElement?.tagName).toBe(
|
|
66
|
+
expect(seoMeta?.getAttribute("property")).toBe("og:title");
|
|
67
|
+
expect(seoMeta?.getAttribute("content")).toBe("Test");
|
|
68
|
+
expect(seoMeta?.parentElement?.tagName).toBe("HEAD");
|
|
70
69
|
});
|
|
71
70
|
|
|
72
|
-
it(
|
|
71
|
+
it("should keep self-closing custom elements in head", () => {
|
|
73
72
|
const doc = parseHTML(
|
|
74
|
-
|
|
73
|
+
"<!DOCTYPE html><html><head><custom-void /></head><body></body></html>",
|
|
75
74
|
);
|
|
76
|
-
|
|
77
|
-
const customVoid = doc.head?.querySelector(
|
|
75
|
+
|
|
76
|
+
const customVoid = doc.head?.querySelector("custom-void");
|
|
78
77
|
expect(customVoid).toBeTruthy();
|
|
79
|
-
expect(customVoid?.parentElement?.tagName).toBe(
|
|
78
|
+
expect(customVoid?.parentElement?.tagName).toBe("HEAD");
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
it(
|
|
81
|
+
it("should handle custom elements mixed with standard head elements", () => {
|
|
83
82
|
const doc = parseHTML(
|
|
84
|
-
'<!DOCTYPE html><html><head><title>Test</title><meta-tags></meta-tags><link rel="stylesheet" href="style.css"></head><body></body></html>'
|
|
83
|
+
'<!DOCTYPE html><html><head><title>Test</title><meta-tags></meta-tags><link rel="stylesheet" href="style.css"></head><body></body></html>',
|
|
85
84
|
);
|
|
86
|
-
|
|
87
|
-
const title = doc.head?.querySelector(
|
|
88
|
-
const metaTags = doc.head?.querySelector(
|
|
89
|
-
const link = doc.head?.querySelector(
|
|
90
|
-
|
|
85
|
+
|
|
86
|
+
const title = doc.head?.querySelector("title");
|
|
87
|
+
const metaTags = doc.head?.querySelector("meta-tags");
|
|
88
|
+
const link = doc.head?.querySelector("link");
|
|
89
|
+
|
|
91
90
|
expect(title).toBeTruthy();
|
|
92
91
|
expect(metaTags).toBeTruthy();
|
|
93
92
|
expect(link).toBeTruthy();
|
|
94
93
|
});
|
|
95
94
|
|
|
96
|
-
it(
|
|
95
|
+
it("should handle custom element containing text in head", () => {
|
|
97
96
|
const doc = parseHTML(
|
|
98
|
-
'<!DOCTYPE html><html><head><inline-script>console.log("test")</inline-script></head><body></body></html>'
|
|
97
|
+
'<!DOCTYPE html><html><head><inline-script>console.log("test")</inline-script></head><body></body></html>',
|
|
99
98
|
);
|
|
100
|
-
|
|
101
|
-
const inlineScript = doc.head?.querySelector(
|
|
99
|
+
|
|
100
|
+
const inlineScript = doc.head?.querySelector("inline-script");
|
|
102
101
|
expect(inlineScript).toBeTruthy();
|
|
103
|
-
expect(inlineScript?.parentElement?.tagName).toBe(
|
|
102
|
+
expect(inlineScript?.parentElement?.tagName).toBe("HEAD");
|
|
104
103
|
});
|
|
105
104
|
});
|