@tkeron/html-parser 1.5.2 → 1.5.3

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.
@@ -0,0 +1,336 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { parseHTML } from "../index";
3
+
4
+ describe("DOM Attributes - getAttribute, setAttribute, removeAttribute, hasAttribute", () => {
5
+ describe("Basic attribute operations", () => {
6
+ it("should set and get attribute", () => {
7
+ const doc = parseHTML("<div></div>");
8
+ const div = doc.querySelector("div");
9
+
10
+ div!.setAttribute("data-test", "value");
11
+ expect(div!.getAttribute("data-test")).toBe("value");
12
+ });
13
+
14
+ it("should return null for non-existent attribute", () => {
15
+ const doc = parseHTML("<div></div>");
16
+ const div = doc.querySelector("div");
17
+
18
+ expect(div!.getAttribute("nonexistent")).toBeNull();
19
+ });
20
+
21
+ it("should check attribute existence with hasAttribute", () => {
22
+ const doc = parseHTML("<div data-test='value'></div>");
23
+ const div = doc.querySelector("div");
24
+
25
+ expect(div!.hasAttribute("data-test")).toBe(true);
26
+ expect(div!.hasAttribute("nonexistent")).toBe(false);
27
+ });
28
+
29
+ it("should remove attribute", () => {
30
+ const doc = parseHTML("<div data-test='value'></div>");
31
+ const div = doc.querySelector("div");
32
+
33
+ div!.removeAttribute("data-test");
34
+ expect(div!.hasAttribute("data-test")).toBe(false);
35
+ expect(div!.getAttribute("data-test")).toBeNull();
36
+ });
37
+
38
+ it("should handle case insensitive attribute names", () => {
39
+ const doc = parseHTML("<div DATA-TEST='value'></div>");
40
+ const div = doc.querySelector("div");
41
+
42
+ expect(div!.getAttribute("data-test")).toBe("value");
43
+ expect(div!.hasAttribute("data-test")).toBe(true);
44
+ });
45
+
46
+ it("should overwrite existing attribute", () => {
47
+ const doc = parseHTML("<div data-test='old'></div>");
48
+ const div = doc.querySelector("div");
49
+
50
+ div!.setAttribute("data-test", "new");
51
+ expect(div!.getAttribute("data-test")).toBe("new");
52
+ });
53
+
54
+ it("should set multiple attributes", () => {
55
+ const doc = parseHTML("<div></div>");
56
+ const div = doc.querySelector("div");
57
+
58
+ div!.setAttribute("id", "myId");
59
+ div!.setAttribute("class", "myClass");
60
+ div!.setAttribute("data-value", "123");
61
+
62
+ expect(div!.getAttribute("id")).toBe("myId");
63
+ expect(div!.getAttribute("class")).toBe("myClass");
64
+ expect(div!.getAttribute("data-value")).toBe("123");
65
+ });
66
+ });
67
+
68
+ describe("Attribute values", () => {
69
+ it("should handle empty string value", () => {
70
+ const doc = parseHTML("<div></div>");
71
+ const div = doc.querySelector("div");
72
+
73
+ div!.setAttribute("data-empty", "");
74
+ expect(div!.hasAttribute("data-empty")).toBe(true);
75
+ });
76
+
77
+ it("should handle whitespace-only value", () => {
78
+ const doc = parseHTML("<div></div>");
79
+ const div = doc.querySelector("div");
80
+
81
+ div!.setAttribute("data-ws", " ");
82
+ expect(div!.getAttribute("data-ws")).toBe(" ");
83
+ });
84
+
85
+ it("should handle numeric values as strings", () => {
86
+ const doc = parseHTML("<div></div>");
87
+ const div = doc.querySelector("div");
88
+
89
+ div!.setAttribute("data-num", "42");
90
+ expect(div!.getAttribute("data-num")).toBe("42");
91
+ });
92
+
93
+ it("should handle boolean-like values", () => {
94
+ const doc = parseHTML("<div></div>");
95
+ const div = doc.querySelector("div");
96
+
97
+ div!.setAttribute("data-bool", "true");
98
+ expect(div!.getAttribute("data-bool")).toBe("true");
99
+
100
+ div!.setAttribute("data-bool", "false");
101
+ expect(div!.getAttribute("data-bool")).toBe("false");
102
+ });
103
+
104
+ it("should handle JSON-like values", () => {
105
+ const doc = parseHTML("<div></div>");
106
+ const div = doc.querySelector("div");
107
+ const json = '{"key":"value","num":123}';
108
+
109
+ div!.setAttribute("data-json", json);
110
+ expect(div!.getAttribute("data-json")).toBe(json);
111
+ });
112
+ });
113
+
114
+ describe("Special characters", () => {
115
+ it("should handle special characters in values", () => {
116
+ const doc = parseHTML("<div></div>");
117
+ const div = doc.querySelector("div");
118
+
119
+ div!.setAttribute("data-special", 'value with "quotes" & <tags>');
120
+ expect(div!.getAttribute("data-special")).toBe(
121
+ 'value with "quotes" & <tags>',
122
+ );
123
+ });
124
+
125
+ it("should handle special characters in names", () => {
126
+ const doc = parseHTML("<div></div>");
127
+ const div = doc.querySelector("div");
128
+
129
+ div!.setAttribute("data_special-name", "value");
130
+ expect(div!.getAttribute("data_special-name")).toBe("value");
131
+ });
132
+
133
+ it("should handle attribute names with colons (namespaces)", () => {
134
+ const doc = parseHTML("<div></div>");
135
+ const div = doc.querySelector("div");
136
+
137
+ div!.setAttribute("xmlns:test", "value");
138
+ expect(div!.getAttribute("xmlns:test")).toBe("value");
139
+ });
140
+
141
+ it("should handle newlines in values", () => {
142
+ const doc = parseHTML("<div></div>");
143
+ const div = doc.querySelector("div");
144
+
145
+ div!.setAttribute("data-multiline", "line1\nline2\nline3");
146
+ expect(div!.getAttribute("data-multiline")).toBe("line1\nline2\nline3");
147
+ });
148
+
149
+ it("should handle tabs in values", () => {
150
+ const doc = parseHTML("<div></div>");
151
+ const div = doc.querySelector("div");
152
+
153
+ div!.setAttribute("data-tabs", "col1\tcol2\tcol3");
154
+ expect(div!.getAttribute("data-tabs")).toBe("col1\tcol2\tcol3");
155
+ });
156
+
157
+ it("should handle unicode characters", () => {
158
+ const doc = parseHTML("<div></div>");
159
+ const div = doc.querySelector("div");
160
+
161
+ div!.setAttribute("data-unicode", "日本語 🎉 émoji");
162
+ expect(div!.getAttribute("data-unicode")).toBe("日本語 🎉 émoji");
163
+ });
164
+
165
+ it("should handle HTML entities in values", () => {
166
+ const doc = parseHTML("<div></div>");
167
+ const div = doc.querySelector("div");
168
+
169
+ div!.setAttribute("data-entities", "&lt;div&gt;");
170
+ expect(div!.getAttribute("data-entities")).toBe("&lt;div&gt;");
171
+ });
172
+
173
+ it("should handle single quotes in values", () => {
174
+ const doc = parseHTML("<div></div>");
175
+ const div = doc.querySelector("div");
176
+
177
+ div!.setAttribute("data-quotes", "it's a test");
178
+ expect(div!.getAttribute("data-quotes")).toBe("it's a test");
179
+ });
180
+ });
181
+
182
+ describe("Long values", () => {
183
+ it("should handle long attribute values", () => {
184
+ const doc = parseHTML("<div></div>");
185
+ const div = doc.querySelector("div");
186
+ const longValue = "a".repeat(10000);
187
+
188
+ div!.setAttribute("data-long", longValue);
189
+ expect(div!.getAttribute("data-long")).toBe(longValue);
190
+ });
191
+
192
+ it("should handle very long attribute names", () => {
193
+ const doc = parseHTML("<div></div>");
194
+ const div = doc.querySelector("div");
195
+ const longName = "data-" + "x".repeat(1000);
196
+
197
+ div!.setAttribute(longName, "value");
198
+ expect(div!.getAttribute(longName)).toBe("value");
199
+ });
200
+ });
201
+
202
+ describe("removeAttribute edge cases", () => {
203
+ it("should handle removing non-existent attribute gracefully", () => {
204
+ const doc = parseHTML("<div></div>");
205
+ const div = doc.querySelector("div");
206
+
207
+ expect(() => div!.removeAttribute("nonexistent")).not.toThrow();
208
+ expect(div!.hasAttribute("nonexistent")).toBe(false);
209
+ });
210
+
211
+ it("should handle removing same attribute twice", () => {
212
+ const doc = parseHTML("<div data-test='value'></div>");
213
+ const div = doc.querySelector("div");
214
+
215
+ div!.removeAttribute("data-test");
216
+ div!.removeAttribute("data-test");
217
+
218
+ expect(div!.hasAttribute("data-test")).toBe(false);
219
+ });
220
+
221
+ it("should allow re-setting after remove", () => {
222
+ const doc = parseHTML("<div data-test='old'></div>");
223
+ const div = doc.querySelector("div");
224
+
225
+ div!.removeAttribute("data-test");
226
+ div!.setAttribute("data-test", "new");
227
+
228
+ expect(div!.getAttribute("data-test")).toBe("new");
229
+ });
230
+ });
231
+
232
+ describe("Standard HTML attributes", () => {
233
+ it("should handle id attribute", () => {
234
+ const doc = parseHTML("<div></div>");
235
+ const div = doc.querySelector("div");
236
+
237
+ div!.setAttribute("id", "myId");
238
+ expect(div!.getAttribute("id")).toBe("myId");
239
+ expect(div!.id).toBe("myId");
240
+ });
241
+
242
+ it("should handle class attribute", () => {
243
+ const doc = parseHTML("<div></div>");
244
+ const div = doc.querySelector("div");
245
+
246
+ div!.setAttribute("class", "class1 class2");
247
+ expect(div!.getAttribute("class")).toBe("class1 class2");
248
+ expect(div!.className).toBe("class1 class2");
249
+ });
250
+
251
+ it("should handle style attribute", () => {
252
+ const doc = parseHTML("<div></div>");
253
+ const div = doc.querySelector("div");
254
+
255
+ div!.setAttribute("style", "color: red; font-size: 12px;");
256
+ expect(div!.getAttribute("style")).toBe("color: red; font-size: 12px;");
257
+ });
258
+
259
+ it("should handle href attribute", () => {
260
+ const doc = parseHTML("<a></a>");
261
+ const a = doc.querySelector("a");
262
+
263
+ a!.setAttribute("href", "https://example.com/path?query=1&other=2");
264
+ expect(a!.getAttribute("href")).toBe(
265
+ "https://example.com/path?query=1&other=2",
266
+ );
267
+ });
268
+
269
+ it("should handle src attribute", () => {
270
+ const doc = parseHTML("<img>");
271
+ const img = doc.querySelector("img");
272
+
273
+ img!.setAttribute("src", "/images/photo.jpg");
274
+ expect(img!.getAttribute("src")).toBe("/images/photo.jpg");
275
+ });
276
+ });
277
+
278
+ describe("Parsed attributes", () => {
279
+ it("should get attribute from parsed HTML", () => {
280
+ const doc = parseHTML(
281
+ '<div id="test" class="myClass" data-value="123"></div>',
282
+ );
283
+ const div = doc.querySelector("div");
284
+
285
+ expect(div!.getAttribute("id")).toBe("test");
286
+ expect(div!.getAttribute("class")).toBe("myClass");
287
+ expect(div!.getAttribute("data-value")).toBe("123");
288
+ });
289
+
290
+ it("should get boolean attribute from parsed HTML", () => {
291
+ const doc = parseHTML("<input disabled>");
292
+ const input = doc.querySelector("input");
293
+
294
+ expect(input!.hasAttribute("disabled")).toBe(true);
295
+ });
296
+
297
+ it("should get attribute with empty value from parsed HTML", () => {
298
+ const doc = parseHTML('<div data-empty=""></div>');
299
+ const div = doc.querySelector("div");
300
+
301
+ expect(div!.hasAttribute("data-empty")).toBe(true);
302
+ });
303
+
304
+ it("should get attribute with special chars from parsed HTML", () => {
305
+ const doc = parseHTML('<div data-special="&lt;tag&gt;"></div>');
306
+ const div = doc.querySelector("div");
307
+
308
+ expect(div!.getAttribute("data-special")).toBe("<tag>");
309
+ });
310
+ });
311
+
312
+ describe("Multiple elements", () => {
313
+ it("should set attributes independently on different elements", () => {
314
+ const doc = parseHTML("<div></div><span></span>");
315
+ const div = doc.querySelector("div");
316
+ const span = doc.querySelector("span");
317
+
318
+ div!.setAttribute("data-test", "div-value");
319
+ span!.setAttribute("data-test", "span-value");
320
+
321
+ expect(div!.getAttribute("data-test")).toBe("div-value");
322
+ expect(span!.getAttribute("data-test")).toBe("span-value");
323
+ });
324
+
325
+ it("should not affect parent when setting child attribute", () => {
326
+ const doc = parseHTML("<div><span></span></div>");
327
+ const div = doc.querySelector("div");
328
+ const span = doc.querySelector("span");
329
+
330
+ span!.setAttribute("data-test", "value");
331
+
332
+ expect(span!.getAttribute("data-test")).toBe("value");
333
+ expect(div!.getAttribute("data-test")).toBeNull();
334
+ });
335
+ });
336
+ });