@tkeron/html-parser 0.1.4 → 0.1.7

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.
@@ -662,5 +662,91 @@ describe('HTML Tokenizer', () => {
662
662
  tokens.some(token => token.value === 'span');
663
663
  expect(hasValidElements).toBe(true);
664
664
  });
665
+
666
+ it('should handle empty angle brackets <>', () => {
667
+ const html = '<>text<div>content</div>';
668
+ const tokens = tokenize(html);
669
+
670
+ // Should skip the invalid <> and continue parsing
671
+ expect(tokens[tokens.length - 1]!.type).toBe(TokenType.EOF);
672
+ const divToken = tokens.find(t => t.value === 'div');
673
+ expect(divToken).toBeDefined();
674
+ });
675
+
676
+ it('should handle angle bracket with only space < >', () => {
677
+ const html = '< >text<p>paragraph</p>';
678
+ const tokens = tokenize(html);
679
+
680
+ expect(tokens[tokens.length - 1]!.type).toBe(TokenType.EOF);
681
+ const pToken = tokens.find(t => t.value === 'p');
682
+ expect(pToken).toBeDefined();
683
+ });
684
+
685
+ it('should handle tag with no valid name', () => {
686
+ const html = '<123>text</123><div>ok</div>';
687
+ const tokens = tokenize(html);
688
+
689
+ // Tags starting with numbers are invalid, should be treated as text
690
+ expect(tokens[tokens.length - 1]!.type).toBe(TokenType.EOF);
691
+ const divToken = tokens.find(t => t.value === 'div');
692
+ expect(divToken).toBeDefined();
693
+ });
694
+ });
695
+
696
+ describe('Entity Edge Cases', () => {
697
+ it('should handle entity without semicolon with valid prefix', () => {
698
+ // &nbsp followed by other text (no semicolon) should decode &nbsp
699
+ const tokens = tokenize('<div>&nbsptext</div>');
700
+
701
+ const textToken = tokens.find(t => t.type === TokenType.TEXT);
702
+ expect(textToken).toBeDefined();
703
+ // Should decode &nbsp (non-breaking space) and keep "text"
704
+ expect(textToken!.value).toContain('text');
705
+ });
706
+
707
+ it('should handle entity without semicolon - lt prefix', () => {
708
+ const tokens = tokenize('<div>&ltvalue</div>');
709
+
710
+ const textToken = tokens.find(t => t.type === TokenType.TEXT);
711
+ expect(textToken).toBeDefined();
712
+ // &lt should decode to < and "value" should follow
713
+ expect(textToken!.value).toBe('<value');
714
+ });
715
+
716
+ it('should handle entity without semicolon - gt prefix', () => {
717
+ const tokens = tokenize('<div>&gtvalue</div>');
718
+
719
+ const textToken = tokens.find(t => t.type === TokenType.TEXT);
720
+ expect(textToken).toBeDefined();
721
+ // &gt should decode to > and "value" should follow
722
+ expect(textToken!.value).toBe('>value');
723
+ });
724
+
725
+ it('should handle entity without semicolon - amp prefix', () => {
726
+ const tokens = tokenize('<div>&ampvalue</div>');
727
+
728
+ const textToken = tokens.find(t => t.type === TokenType.TEXT);
729
+ expect(textToken).toBeDefined();
730
+ // &amp should decode to & and "value" should follow
731
+ expect(textToken!.value).toBe('&value');
732
+ });
733
+
734
+ it('should handle unknown entity gracefully', () => {
735
+ const tokens = tokenize('<div>&unknownentity;</div>');
736
+
737
+ const textToken = tokens.find(t => t.type === TokenType.TEXT);
738
+ expect(textToken).toBeDefined();
739
+ // Unknown entity should be kept as-is
740
+ expect(textToken!.value).toBe('&unknownentity;');
741
+ });
742
+
743
+ it('should handle partial entity name with no matching prefix', () => {
744
+ const tokens = tokenize('<div>&xyz</div>');
745
+
746
+ const textToken = tokens.find(t => t.type === TokenType.TEXT);
747
+ expect(textToken).toBeDefined();
748
+ // No valid entity prefix, keep as-is
749
+ expect(textToken!.value).toBe('&xyz');
750
+ });
665
751
  })
666
752
  });
@@ -0,0 +1,471 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { parseHTML } from "../index";
3
+
4
+ /**
5
+ * Test suite for HTML void elements serialization
6
+ *
7
+ * Void elements should NOT have closing tags according to HTML spec:
8
+ * https://html.spec.whatwg.org/multipage/syntax.html#void-elements
9
+ *
10
+ * List: area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr
11
+ */
12
+
13
+ const VOID_ELEMENTS = [
14
+ "area",
15
+ "base",
16
+ "br",
17
+ "col",
18
+ "embed",
19
+ "hr",
20
+ "img",
21
+ "input",
22
+ "link",
23
+ "meta",
24
+ "source",
25
+ "track",
26
+ "wbr",
27
+ ];
28
+
29
+ describe("Void Elements - outerHTML serialization", () => {
30
+ describe("Individual void elements without attributes", () => {
31
+ it("should serialize <br> without closing tag", () => {
32
+ const doc = parseHTML("<html><body><br></body></html>");
33
+ const br = doc.querySelector("br");
34
+ expect(br).not.toBeNull();
35
+ expect(br!.outerHTML).toBe("<br>");
36
+ });
37
+
38
+ it("should serialize <hr> without closing tag", () => {
39
+ const doc = parseHTML("<html><body><hr></body></html>");
40
+ const hr = doc.querySelector("hr");
41
+ expect(hr).not.toBeNull();
42
+ expect(hr!.outerHTML).toBe("<hr>");
43
+ });
44
+
45
+ it("should serialize <wbr> without closing tag", () => {
46
+ const doc = parseHTML("<html><body><wbr></body></html>");
47
+ const wbr = doc.querySelector("wbr");
48
+ expect(wbr).not.toBeNull();
49
+ expect(wbr!.outerHTML).toBe("<wbr>");
50
+ });
51
+ });
52
+
53
+ describe("Individual void elements with attributes", () => {
54
+ it("should serialize <img> with attributes without closing tag", () => {
55
+ const doc = parseHTML('<html><body><img src="test.jpg" alt="test image"></body></html>');
56
+ const img = doc.querySelector("img");
57
+ expect(img).not.toBeNull();
58
+ expect(img!.outerHTML).toBe('<img src="test.jpg" alt="test image">');
59
+ });
60
+
61
+ it("should serialize <input> with type attribute without closing tag", () => {
62
+ const doc = parseHTML('<html><body><input type="text" name="username"></body></html>');
63
+ const input = doc.querySelector("input");
64
+ expect(input).not.toBeNull();
65
+ expect(input!.outerHTML).toBe('<input type="text" name="username">');
66
+ });
67
+
68
+ it("should serialize <meta> with attributes without closing tag", () => {
69
+ const doc = parseHTML('<html><head><meta charset="utf-8"></head><body></body></html>');
70
+ const meta = doc.querySelector("meta");
71
+ expect(meta).not.toBeNull();
72
+ expect(meta!.outerHTML).toBe('<meta charset="utf-8">');
73
+ });
74
+
75
+ it("should serialize <link> with attributes without closing tag", () => {
76
+ const doc = parseHTML('<html><head><link rel="stylesheet" href="style.css"></head><body></body></html>');
77
+ const link = doc.querySelector("link");
78
+ expect(link).not.toBeNull();
79
+ expect(link!.outerHTML).toBe('<link rel="stylesheet" href="style.css">');
80
+ });
81
+
82
+ it("should serialize <base> with href without closing tag", () => {
83
+ const doc = parseHTML('<html><head><base href="https://example.com/"></head><body></body></html>');
84
+ const base = doc.querySelector("base");
85
+ expect(base).not.toBeNull();
86
+ expect(base!.outerHTML).toBe('<base href="https://example.com/">');
87
+ });
88
+
89
+ it("should serialize <col> with attributes without closing tag", () => {
90
+ const doc = parseHTML('<html><body><table><colgroup><col span="2" style="background:red"></colgroup></table></body></html>');
91
+ const col = doc.querySelector("col");
92
+ expect(col).not.toBeNull();
93
+ expect(col!.outerHTML).toBe('<col span="2" style="background:red">');
94
+ });
95
+
96
+ it("should serialize <embed> with attributes without closing tag", () => {
97
+ const doc = parseHTML('<html><body><embed src="video.swf" type="application/x-shockwave-flash"></body></html>');
98
+ const embed = doc.querySelector("embed");
99
+ expect(embed).not.toBeNull();
100
+ expect(embed!.outerHTML).toBe('<embed src="video.swf" type="application/x-shockwave-flash">');
101
+ });
102
+
103
+ it("should serialize <source> with attributes without closing tag", () => {
104
+ const doc = parseHTML('<html><body><video><source src="video.mp4" type="video/mp4"></video></body></html>');
105
+ const source = doc.querySelector("source");
106
+ expect(source).not.toBeNull();
107
+ expect(source!.outerHTML).toBe('<source src="video.mp4" type="video/mp4">');
108
+ });
109
+
110
+ it("should serialize <track> with attributes without closing tag", () => {
111
+ const doc = parseHTML('<html><body><video><track kind="subtitles" src="subs.vtt" srclang="en"></video></body></html>');
112
+ const track = doc.querySelector("track");
113
+ expect(track).not.toBeNull();
114
+ expect(track!.outerHTML).toBe('<track kind="subtitles" src="subs.vtt" srclang="en">');
115
+ });
116
+
117
+ it("should serialize <area> with attributes without closing tag", () => {
118
+ const doc = parseHTML('<html><body><map name="test"><area shape="rect" coords="0,0,100,100" href="link.html"></map></body></html>');
119
+ const area = doc.querySelector("area");
120
+ expect(area).not.toBeNull();
121
+ expect(area!.outerHTML).toBe('<area shape="rect" coords="0,0,100,100" href="link.html">');
122
+ });
123
+ });
124
+
125
+ describe("All void elements - comprehensive test", () => {
126
+ VOID_ELEMENTS.forEach((tagName) => {
127
+ it(`should serialize <${tagName}> without closing tag`, () => {
128
+ const doc = parseHTML(`<html><body><${tagName}></body></html>`);
129
+ const element = doc.querySelector(tagName);
130
+ expect(element).not.toBeNull();
131
+ expect(element!.outerHTML).toBe(`<${tagName}>`);
132
+ expect(element!.outerHTML).not.toContain(`</${tagName}>`);
133
+ });
134
+ });
135
+ });
136
+
137
+ describe("Multiple void elements in same document", () => {
138
+ it("should serialize multiple void elements correctly", () => {
139
+ const doc = parseHTML('<html><body><img src="test.jpg"><br><input type="text"></body></html>');
140
+
141
+ const img = doc.querySelector("img");
142
+ const br = doc.querySelector("br");
143
+ const input = doc.querySelector("input");
144
+
145
+ expect(img!.outerHTML).toBe('<img src="test.jpg">');
146
+ expect(br!.outerHTML).toBe("<br>");
147
+ expect(input!.outerHTML).toBe('<input type="text">');
148
+ });
149
+
150
+ it("should serialize document with multiple void elements without closing tags", () => {
151
+ const html = '<html><body><img src="test.jpg"><br><input type="text"></body></html>';
152
+ const doc = parseHTML(html);
153
+ const outerHTML = doc.documentElement.outerHTML;
154
+
155
+ expect(outerHTML).not.toContain("</img>");
156
+ expect(outerHTML).not.toContain("</br>");
157
+ expect(outerHTML).not.toContain("</input>");
158
+ });
159
+ });
160
+
161
+ describe("Void elements in head section", () => {
162
+ it("should serialize head void elements without closing tags", () => {
163
+ const html = `<html>
164
+ <head>
165
+ <meta charset="utf-8">
166
+ <meta name="viewport" content="width=device-width">
167
+ <link rel="stylesheet" href="style.css">
168
+ <base href="https://example.com/">
169
+ </head>
170
+ <body></body>
171
+ </html>`;
172
+ const doc = parseHTML(html);
173
+
174
+ const metas = doc.querySelectorAll("meta");
175
+ const link = doc.querySelector("link");
176
+ const base = doc.querySelector("base");
177
+
178
+ metas.forEach((meta: any) => {
179
+ expect(meta.outerHTML).not.toContain("</meta>");
180
+ });
181
+ expect(link!.outerHTML).not.toContain("</link>");
182
+ expect(base!.outerHTML).not.toContain("</base>");
183
+ });
184
+ });
185
+
186
+ describe("Void elements created with createElement", () => {
187
+ it("should serialize dynamically created <img> without closing tag", () => {
188
+ const doc = parseHTML("<html><body></body></html>");
189
+ const img = doc.createElement("img");
190
+ img.setAttribute("src", "dynamic.jpg");
191
+ expect(img.outerHTML).toBe('<img src="dynamic.jpg">');
192
+ });
193
+
194
+ it("should serialize dynamically created <br> without closing tag", () => {
195
+ const doc = parseHTML("<html><body></body></html>");
196
+ const br = doc.createElement("br");
197
+ expect(br.outerHTML).toBe("<br>");
198
+ });
199
+
200
+ it("should serialize dynamically created <input> without closing tag", () => {
201
+ const doc = parseHTML("<html><body></body></html>");
202
+ const input = doc.createElement("input");
203
+ input.setAttribute("type", "password");
204
+ input.setAttribute("name", "secret");
205
+ expect(input.outerHTML).toBe('<input type="password" name="secret">');
206
+ });
207
+
208
+ it("should serialize dynamically created <meta> without closing tag", () => {
209
+ const doc = parseHTML("<html><body></body></html>");
210
+ const meta = doc.createElement("meta");
211
+ meta.setAttribute("name", "description");
212
+ meta.setAttribute("content", "Test page");
213
+ expect(meta.outerHTML).toBe('<meta name="description" content="Test page">');
214
+ });
215
+
216
+ it("should serialize dynamically created <hr> without closing tag", () => {
217
+ const doc = parseHTML("<html><body></body></html>");
218
+ const hr = doc.createElement("hr");
219
+ expect(hr.outerHTML).toBe("<hr>");
220
+ });
221
+
222
+ VOID_ELEMENTS.forEach((tagName) => {
223
+ it(`should serialize dynamically created <${tagName}> without closing tag`, () => {
224
+ const doc = parseHTML("<html><body></body></html>");
225
+ const element = doc.createElement(tagName);
226
+ expect(element.outerHTML).toBe(`<${tagName}>`);
227
+ expect(element.outerHTML).not.toContain(`</${tagName}>`);
228
+ });
229
+ });
230
+ });
231
+
232
+ describe("Void elements with XHTML-style syntax", () => {
233
+ it("should handle <br /> and serialize without closing tag", () => {
234
+ const doc = parseHTML("<html><body><br /></body></html>");
235
+ const br = doc.querySelector("br");
236
+ expect(br).not.toBeNull();
237
+ expect(br!.outerHTML).toBe("<br>");
238
+ expect(br!.outerHTML).not.toContain("</br>");
239
+ });
240
+
241
+ it("should handle <img /> and serialize without closing tag", () => {
242
+ const doc = parseHTML('<html><body><img src="test.jpg" /></body></html>');
243
+ const img = doc.querySelector("img");
244
+ expect(img).not.toBeNull();
245
+ expect(img!.outerHTML).toBe('<img src="test.jpg">');
246
+ expect(img!.outerHTML).not.toContain("</img>");
247
+ });
248
+
249
+ it("should handle <input /> and serialize without closing tag", () => {
250
+ const doc = parseHTML('<html><body><input type="text" /></body></html>');
251
+ const input = doc.querySelector("input");
252
+ expect(input).not.toBeNull();
253
+ expect(input!.outerHTML).toBe('<input type="text">');
254
+ expect(input!.outerHTML).not.toContain("</input>");
255
+ });
256
+ });
257
+
258
+ describe("Non-void elements should have closing tags", () => {
259
+ it("should serialize <div> with closing tag", () => {
260
+ const doc = parseHTML("<html><body><div></div></body></html>");
261
+ const div = doc.querySelector("div");
262
+ expect(div).not.toBeNull();
263
+ expect(div!.outerHTML).toBe("<div></div>");
264
+ });
265
+
266
+ it("should serialize <span> with closing tag", () => {
267
+ const doc = parseHTML("<html><body><span></span></body></html>");
268
+ const span = doc.querySelector("span");
269
+ expect(span).not.toBeNull();
270
+ expect(span!.outerHTML).toBe("<span></span>");
271
+ });
272
+
273
+ it("should serialize <p> with closing tag", () => {
274
+ const doc = parseHTML("<html><body><p></p></body></html>");
275
+ const p = doc.querySelector("p");
276
+ expect(p).not.toBeNull();
277
+ expect(p!.outerHTML).toBe("<p></p>");
278
+ });
279
+
280
+ it("should serialize <script> with closing tag", () => {
281
+ const doc = parseHTML("<html><body><script></script></body></html>");
282
+ const script = doc.querySelector("script");
283
+ expect(script).not.toBeNull();
284
+ expect(script!.outerHTML).toBe("<script></script>");
285
+ });
286
+
287
+ it("should serialize <style> with closing tag", () => {
288
+ const doc = parseHTML("<html><head><style></style></head><body></body></html>");
289
+ const style = doc.querySelector("style");
290
+ expect(style).not.toBeNull();
291
+ expect(style!.outerHTML).toBe("<style></style>");
292
+ });
293
+
294
+ it("should serialize <iframe> with closing tag", () => {
295
+ const doc = parseHTML('<html><body><iframe src="page.html"></iframe></body></html>');
296
+ const iframe = doc.querySelector("iframe");
297
+ expect(iframe).not.toBeNull();
298
+ expect(iframe!.outerHTML).toBe('<iframe src="page.html"></iframe>');
299
+ });
300
+
301
+ it("should serialize <textarea> with closing tag", () => {
302
+ const doc = parseHTML("<html><body><textarea></textarea></body></html>");
303
+ const textarea = doc.querySelector("textarea");
304
+ expect(textarea).not.toBeNull();
305
+ expect(textarea!.outerHTML).toBe("<textarea></textarea>");
306
+ });
307
+
308
+ it("should serialize <video> with closing tag", () => {
309
+ const doc = parseHTML("<html><body><video></video></body></html>");
310
+ const video = doc.querySelector("video");
311
+ expect(video).not.toBeNull();
312
+ expect(video!.outerHTML).toBe("<video></video>");
313
+ });
314
+
315
+ it("should serialize <audio> with closing tag", () => {
316
+ const doc = parseHTML("<html><body><audio></audio></body></html>");
317
+ const audio = doc.querySelector("audio");
318
+ expect(audio).not.toBeNull();
319
+ expect(audio!.outerHTML).toBe("<audio></audio>");
320
+ });
321
+
322
+ it("should serialize <canvas> with closing tag", () => {
323
+ const doc = parseHTML("<html><body><canvas></canvas></body></html>");
324
+ const canvas = doc.querySelector("canvas");
325
+ expect(canvas).not.toBeNull();
326
+ expect(canvas!.outerHTML).toBe("<canvas></canvas>");
327
+ });
328
+ });
329
+
330
+ describe("Void elements with content (should be ignored)", () => {
331
+ it("should not include text content in void element", () => {
332
+ const doc = parseHTML("<html><body><br>text</body></html>");
333
+ const br = doc.querySelector("br");
334
+ expect(br).not.toBeNull();
335
+ expect(br!.outerHTML).toBe("<br>");
336
+ });
337
+
338
+ it("should not include innerHTML content in void element", () => {
339
+ const doc = parseHTML("<html><body><img src=\"test.jpg\"></body></html>");
340
+ const img = doc.querySelector("img");
341
+ expect(img).not.toBeNull();
342
+ expect(img!.innerHTML).toBe("");
343
+ expect(img!.outerHTML).toBe('<img src="test.jpg">');
344
+ });
345
+ });
346
+
347
+ describe("Void elements in nested structures", () => {
348
+ it("should serialize void elements inside multiple nested elements", () => {
349
+ const html = `<html><body>
350
+ <div class="container">
351
+ <form>
352
+ <div class="form-group">
353
+ <input type="text" name="field1">
354
+ <br>
355
+ <input type="password" name="field2">
356
+ </div>
357
+ </form>
358
+ </div>
359
+ </body></html>`;
360
+
361
+ const doc = parseHTML(html);
362
+ const inputs = doc.querySelectorAll("input");
363
+ const br = doc.querySelector("br");
364
+
365
+ expect(inputs.length).toBe(2);
366
+ inputs.forEach((input: any) => {
367
+ expect(input.outerHTML).not.toContain("</input>");
368
+ });
369
+ expect(br!.outerHTML).toBe("<br>");
370
+ });
371
+
372
+ it("should serialize void elements inside tables correctly", () => {
373
+ const html = `<html><body>
374
+ <table>
375
+ <colgroup>
376
+ <col span="1" class="col1">
377
+ <col span="2" class="col2">
378
+ </colgroup>
379
+ <tr><td><img src="icon.png"></td></tr>
380
+ </table>
381
+ </body></html>`;
382
+
383
+ const doc = parseHTML(html);
384
+ const cols = doc.querySelectorAll("col");
385
+ const img = doc.querySelector("img");
386
+
387
+ expect(cols.length).toBe(2);
388
+ cols.forEach((col: any) => {
389
+ expect(col.outerHTML).not.toContain("</col>");
390
+ });
391
+ expect(img!.outerHTML).not.toContain("</img>");
392
+ });
393
+ });
394
+
395
+ describe("Edge cases", () => {
396
+ it("should handle void element with boolean attributes", () => {
397
+ const doc = parseHTML('<html><body><input type="checkbox" checked disabled></body></html>');
398
+ const input = doc.querySelector("input");
399
+ expect(input).not.toBeNull();
400
+ expect(input!.outerHTML).not.toContain("</input>");
401
+ });
402
+
403
+ it("should handle void element with empty attribute value", () => {
404
+ const doc = parseHTML('<html><body><input type="text" value=""></body></html>');
405
+ const input = doc.querySelector("input");
406
+ expect(input).not.toBeNull();
407
+ expect(input!.outerHTML).not.toContain("</input>");
408
+ });
409
+
410
+ it("should handle uppercase void element tag names", () => {
411
+ const doc = parseHTML("<html><body><BR><IMG SRC=\"test.jpg\"></body></html>");
412
+ const br = doc.querySelector("br");
413
+ const img = doc.querySelector("img");
414
+
415
+ expect(br).not.toBeNull();
416
+ expect(img).not.toBeNull();
417
+ expect(br!.outerHTML).not.toContain("</br>");
418
+ expect(br!.outerHTML).not.toContain("</BR>");
419
+ expect(img!.outerHTML).not.toContain("</img>");
420
+ expect(img!.outerHTML).not.toContain("</IMG>");
421
+ });
422
+
423
+ it("should handle mixed case void element tag names", () => {
424
+ const doc = parseHTML("<html><body><Br><ImG src=\"test.jpg\"></body></html>");
425
+ const br = doc.querySelector("br");
426
+ const img = doc.querySelector("img");
427
+
428
+ expect(br).not.toBeNull();
429
+ expect(img).not.toBeNull();
430
+ expect(br!.outerHTML.toLowerCase()).not.toContain("</br>");
431
+ expect(img!.outerHTML.toLowerCase()).not.toContain("</img>");
432
+ });
433
+ });
434
+
435
+ describe("Full document serialization", () => {
436
+ it("should serialize complete document without closing tags on void elements", () => {
437
+ const html = `<html>
438
+ <head>
439
+ <meta charset="utf-8">
440
+ <link rel="stylesheet" href="style.css">
441
+ </head>
442
+ <body>
443
+ <img src="logo.png" alt="Logo">
444
+ <hr>
445
+ <form>
446
+ <input type="text" name="username">
447
+ <br>
448
+ <input type="password" name="password">
449
+ </form>
450
+ </body>
451
+ </html>`;
452
+
453
+ const doc = parseHTML(html);
454
+ const fullHTML = doc.documentElement.outerHTML;
455
+
456
+ // Check no void elements have closing tags
457
+ expect(fullHTML).not.toContain("</meta>");
458
+ expect(fullHTML).not.toContain("</link>");
459
+ expect(fullHTML).not.toContain("</img>");
460
+ expect(fullHTML).not.toContain("</hr>");
461
+ expect(fullHTML).not.toContain("</input>");
462
+ expect(fullHTML).not.toContain("</br>");
463
+
464
+ // Check non-void elements still have closing tags
465
+ expect(fullHTML).toContain("</head>");
466
+ expect(fullHTML).toContain("</body>");
467
+ expect(fullHTML).toContain("</form>");
468
+ expect(fullHTML).toContain("</html>");
469
+ });
470
+ });
471
+ });
@@ -1,114 +0,0 @@
1
- import { describe, it, expect } from 'bun:test';
2
- import { parseHTML } from '../index';
3
-
4
- describe('API Integration Tests - Real DOM Usage', () => {
5
- it('should work like real DOM API - querySelector and setAttribute integrated', () => {
6
- const doc = parseHTML(`
7
- <html>
8
- <head>
9
- <title>Test Document</title>
10
- </head>
11
- <body>
12
- <div id="container" class="main">
13
- <h1>Welcome</h1>
14
- <p class="intro">This is a paragraph.</p>
15
- <ul>
16
- <li>Item 1</li>
17
- <li>Item 2</li>
18
- </ul>
19
- </div>
20
- </body>
21
- </html>
22
- `);
23
-
24
- expect(typeof doc.querySelector).toBe('function');
25
- expect(typeof doc.querySelectorAll).toBe('function');
26
- expect(typeof doc.createElement).toBe('function');
27
- expect(typeof doc.createTextNode).toBe('function');
28
-
29
- const container = doc.querySelector('#container');
30
- expect(container).toBeTruthy();
31
- expect(container?.tagName).toBe('DIV');
32
-
33
- expect(typeof container?.querySelector).toBe('function');
34
- expect(typeof container?.setAttribute).toBe('function');
35
- expect(typeof container?.getAttribute).toBe('function');
36
-
37
- const h1 = container?.querySelector('h1');
38
- expect(h1).toBeTruthy();
39
- expect(h1?.tagName).toBe('H1');
40
- expect(h1?.textContent).toBe('Welcome');
41
-
42
- h1?.setAttribute('class', 'title');
43
- expect(h1?.getAttribute('class')).toBe('title');
44
- expect(h1?.hasAttribute('class')).toBe(true);
45
-
46
- const allLi = doc.querySelectorAll('li');
47
- expect(allLi.length).toBe(2);
48
- expect(allLi[0]?.tagName).toBe('LI');
49
- expect(allLi[1]?.tagName).toBe('LI');
50
-
51
- const newElement = doc.createElement('span');
52
- newElement.setAttribute('id', 'new-span');
53
- container?.appendChild(newElement);
54
-
55
- const spanElement = container?.querySelector('#new-span');
56
- expect(spanElement).toBeTruthy();
57
- expect(spanElement?.tagName).toBe('SPAN');
58
- expect(spanElement?.getAttribute('id')).toBe('new-span');
59
- });
60
-
61
- it('should work for complex DOM manipulation scenarios', () => {
62
- const doc = parseHTML('<div><p>Hello</p></div>');
63
-
64
- const div = doc.querySelector('div');
65
- expect(div).toBeTruthy();
66
-
67
- const p = div?.querySelector('p');
68
- expect(p).toBeTruthy();
69
- expect(p?.textContent).toBe('Hello');
70
-
71
- p?.setAttribute('class', 'greeting');
72
- expect(p?.getAttribute('class')).toBe('greeting');
73
-
74
- const span = doc.createElement('span');
75
- span.setAttribute('id', 'dynamic');
76
-
77
- div?.appendChild(span);
78
-
79
- const foundSpan = div?.querySelector('#dynamic');
80
- expect(foundSpan).toBeTruthy();
81
- expect(foundSpan?.getAttribute('id')).toBe('dynamic');
82
- });
83
-
84
- it('should handle removeChild correctly', () => {
85
- const doc = parseHTML('<div><p>First</p><span>Second</span></div>');
86
- const div = doc.querySelector('div');
87
- const p = div?.querySelector('p');
88
-
89
- expect(div?.childNodes.length).toBe(2);
90
- expect(p?.textContent).toBe('First');
91
-
92
- if (p) {
93
- div?.removeChild(p);
94
- }
95
-
96
- expect(div?.childNodes.length).toBe(1);
97
- expect(div?.querySelector('p')).toBe(null);
98
- expect(div?.querySelector('span')).toBeTruthy();
99
- });
100
-
101
- it('should demonstrate clean API without helper functions', () => {
102
- const doc = parseHTML('<html><body><div id="test">Content</div></body></html>');
103
-
104
- const testDiv = doc.querySelector('#test');
105
- testDiv?.setAttribute('class', 'active');
106
- const className = testDiv?.getAttribute('class');
107
- const bodyDiv = doc.body?.querySelector('#test');
108
-
109
- expect(testDiv).toBeTruthy();
110
- expect(className).toBe('active');
111
- expect(bodyDiv).toBeTruthy();
112
- expect(testDiv === bodyDiv).toBe(true);
113
- });
114
- });