@tkeron/html-parser 0.1.2 → 0.1.4
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/bun.lock +4 -4
- package/package.json +1 -1
- package/src/dom-simulator.ts +396 -51
- package/src/dom-types.ts +3 -0
- package/tests/cloneNode-bug-reproduction.test.ts +325 -0
- package/tests/cloneNode-interactive.ts +235 -0
- package/tests/cloneNode.test.ts +587 -0
- package/tests/dom-adoption.test.ts +363 -0
- package/tests/dom-manipulation.test.ts +688 -0
- package/tests/dom-synchronization.test.ts +675 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseHTML } from "../index.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests específicos para reproducir el problema reportado:
|
|
6
|
+
* "Cuando se clona un nodo con cloneNode(true), el nodo clonado
|
|
7
|
+
* pierde su contenido interno (_internalInnerHTML y estructura de hijos)."
|
|
8
|
+
*
|
|
9
|
+
* Estos tests intentan reproducir el problema exacto descrito.
|
|
10
|
+
*/
|
|
11
|
+
describe("cloneNode - Bug Reproduction Tests", () => {
|
|
12
|
+
|
|
13
|
+
it("REPRODUCCIÓN: debe copiar _internalInnerHTML correctamente", () => {
|
|
14
|
+
const html = `
|
|
15
|
+
<div id="container">
|
|
16
|
+
<h1>Título Principal</h1>
|
|
17
|
+
<p>Este es un párrafo con <strong>texto en negrita</strong>.</p>
|
|
18
|
+
<ul>
|
|
19
|
+
<li>Item 1</li>
|
|
20
|
+
<li>Item 2</li>
|
|
21
|
+
<li>Item 3</li>
|
|
22
|
+
</ul>
|
|
23
|
+
</div>
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const doc = parseHTML(html);
|
|
27
|
+
const container = doc.querySelector("#container")!;
|
|
28
|
+
|
|
29
|
+
// Verificar estado ANTES del clonado
|
|
30
|
+
console.log("=== ANTES DEL CLONADO ===");
|
|
31
|
+
console.log("Original innerHTML:", container.innerHTML);
|
|
32
|
+
console.log("Original _internalInnerHTML:", (container as any)._internalInnerHTML);
|
|
33
|
+
console.log("Original childNodes.length:", container.childNodes.length);
|
|
34
|
+
console.log("Original children.length:", container.children.length);
|
|
35
|
+
|
|
36
|
+
// Realizar el clonado
|
|
37
|
+
const cloned = container.cloneNode(true);
|
|
38
|
+
|
|
39
|
+
// Verificar estado DESPUÉS del clonado
|
|
40
|
+
console.log("\n=== DESPUÉS DEL CLONADO ===");
|
|
41
|
+
console.log("Clonado innerHTML:", cloned.innerHTML);
|
|
42
|
+
console.log("Clonado _internalInnerHTML:", (cloned as any)._internalInnerHTML);
|
|
43
|
+
console.log("Clonado childNodes.length:", cloned.childNodes.length);
|
|
44
|
+
console.log("Clonado children.length:", cloned.children.length);
|
|
45
|
+
|
|
46
|
+
// VERIFICACIONES CRÍTICAS
|
|
47
|
+
|
|
48
|
+
// 1. innerHTML no debe estar vacío
|
|
49
|
+
expect(cloned.innerHTML).toBeTruthy();
|
|
50
|
+
expect(cloned.innerHTML.length).toBeGreaterThan(0);
|
|
51
|
+
|
|
52
|
+
// 2. Debe contener los elementos esperados
|
|
53
|
+
expect(cloned.innerHTML).toContain("Título Principal");
|
|
54
|
+
expect(cloned.innerHTML).toContain("<strong>texto en negrita</strong>");
|
|
55
|
+
expect(cloned.innerHTML).toContain("<li>Item 1</li>");
|
|
56
|
+
|
|
57
|
+
// 3. childNodes debe tener contenido
|
|
58
|
+
expect(cloned.childNodes.length).toBeGreaterThan(0);
|
|
59
|
+
expect(cloned.childNodes.length).toBe(container.childNodes.length);
|
|
60
|
+
|
|
61
|
+
// 4. children debe tener los elementos
|
|
62
|
+
expect(cloned.children.length).toBe(3); // h1, p, ul
|
|
63
|
+
expect(cloned.children.length).toBe(container.children.length);
|
|
64
|
+
|
|
65
|
+
// 5. Los elementos deben ser accesibles mediante querySelector
|
|
66
|
+
expect(cloned.querySelector("h1")).toBeTruthy();
|
|
67
|
+
expect(cloned.querySelector("p")).toBeTruthy();
|
|
68
|
+
expect(cloned.querySelector("ul")).toBeTruthy();
|
|
69
|
+
expect(cloned.querySelectorAll("li").length).toBe(3);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("REPRODUCCIÓN: debe mantener la estructura completa de hijos", () => {
|
|
73
|
+
const html = `
|
|
74
|
+
<div id="parent">
|
|
75
|
+
<div class="level-1">
|
|
76
|
+
<div class="level-2">
|
|
77
|
+
<div class="level-3">
|
|
78
|
+
Contenido profundo
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
const doc = parseHTML(html);
|
|
86
|
+
const parent = doc.querySelector("#parent")!;
|
|
87
|
+
|
|
88
|
+
console.log("\n=== ESTRUCTURA DE HIJOS ANTES ===");
|
|
89
|
+
console.log("Parent childNodes:", parent.childNodes.length);
|
|
90
|
+
console.log("Level-1 existe:", !!parent.querySelector(".level-1"));
|
|
91
|
+
console.log("Level-2 existe:", !!parent.querySelector(".level-2"));
|
|
92
|
+
console.log("Level-3 existe:", !!parent.querySelector(".level-3"));
|
|
93
|
+
|
|
94
|
+
const cloned = parent.cloneNode(true);
|
|
95
|
+
|
|
96
|
+
console.log("\n=== ESTRUCTURA DE HIJOS DESPUÉS ===");
|
|
97
|
+
console.log("Clonado childNodes:", cloned.childNodes.length);
|
|
98
|
+
console.log("Level-1 existe:", !!cloned.querySelector(".level-1"));
|
|
99
|
+
console.log("Level-2 existe:", !!cloned.querySelector(".level-2"));
|
|
100
|
+
console.log("Level-3 existe:", !!cloned.querySelector(".level-3"));
|
|
101
|
+
|
|
102
|
+
// VERIFICAR que la estructura se mantiene
|
|
103
|
+
expect(cloned.childNodes.length).toBe(parent.childNodes.length);
|
|
104
|
+
expect(cloned.querySelector(".level-1")).toBeTruthy();
|
|
105
|
+
expect(cloned.querySelector(".level-2")).toBeTruthy();
|
|
106
|
+
expect(cloned.querySelector(".level-3")).toBeTruthy();
|
|
107
|
+
expect(cloned.querySelector(".level-3")?.textContent).toContain("Contenido profundo");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("REPRODUCCIÓN: debe copiar atributos Y contenido simultáneamente", () => {
|
|
111
|
+
const html = `
|
|
112
|
+
<div
|
|
113
|
+
id="complex"
|
|
114
|
+
class="container main"
|
|
115
|
+
data-id="123"
|
|
116
|
+
data-type="test"
|
|
117
|
+
aria-label="Complex element"
|
|
118
|
+
>
|
|
119
|
+
<header>
|
|
120
|
+
<h1>Header Title</h1>
|
|
121
|
+
</header>
|
|
122
|
+
<main>
|
|
123
|
+
<p>Main content</p>
|
|
124
|
+
</main>
|
|
125
|
+
<footer>
|
|
126
|
+
<p>Footer content</p>
|
|
127
|
+
</footer>
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const doc = parseHTML(html);
|
|
132
|
+
const complex = doc.querySelector("#complex")!;
|
|
133
|
+
|
|
134
|
+
console.log("\n=== ANTES: ATRIBUTOS Y CONTENIDO ===");
|
|
135
|
+
console.log("Atributos:", JSON.stringify(complex.attributes, null, 2));
|
|
136
|
+
console.log("innerHTML length:", complex.innerHTML.length);
|
|
137
|
+
console.log("Hijos:", complex.children.length);
|
|
138
|
+
|
|
139
|
+
const cloned = complex.cloneNode(true);
|
|
140
|
+
|
|
141
|
+
console.log("\n=== DESPUÉS: ATRIBUTOS Y CONTENIDO ===");
|
|
142
|
+
console.log("Atributos clonados:", JSON.stringify(cloned.attributes, null, 2));
|
|
143
|
+
console.log("innerHTML length clonado:", cloned.innerHTML.length);
|
|
144
|
+
console.log("Hijos clonados:", cloned.children.length);
|
|
145
|
+
|
|
146
|
+
// VERIFICAR atributos
|
|
147
|
+
expect(cloned.getAttribute("id")).toBe("complex");
|
|
148
|
+
expect(cloned.getAttribute("class")).toBe("container main");
|
|
149
|
+
expect(cloned.getAttribute("data-id")).toBe("123");
|
|
150
|
+
expect(cloned.getAttribute("data-type")).toBe("test");
|
|
151
|
+
expect(cloned.getAttribute("aria-label")).toBe("Complex element");
|
|
152
|
+
|
|
153
|
+
// VERIFICAR contenido
|
|
154
|
+
expect(cloned.innerHTML.length).toBeGreaterThan(0);
|
|
155
|
+
expect(cloned.children.length).toBe(3); // header, main, footer
|
|
156
|
+
expect(cloned.querySelector("header h1")?.textContent).toBe("Header Title");
|
|
157
|
+
expect(cloned.querySelector("main p")?.textContent).toBe("Main content");
|
|
158
|
+
expect(cloned.querySelector("footer p")?.textContent).toBe("Footer content");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("REPRODUCCIÓN: comparar original vs clonado lado a lado", () => {
|
|
162
|
+
const html = `
|
|
163
|
+
<article id="article">
|
|
164
|
+
<h1>Artículo de Prueba</h1>
|
|
165
|
+
<p class="intro">Introducción del artículo.</p>
|
|
166
|
+
<section>
|
|
167
|
+
<h2>Sección 1</h2>
|
|
168
|
+
<p>Contenido de la sección 1.</p>
|
|
169
|
+
</section>
|
|
170
|
+
<section>
|
|
171
|
+
<h2>Sección 2</h2>
|
|
172
|
+
<p>Contenido de la sección 2.</p>
|
|
173
|
+
</section>
|
|
174
|
+
</article>
|
|
175
|
+
`;
|
|
176
|
+
|
|
177
|
+
const doc = parseHTML(html);
|
|
178
|
+
const original = doc.querySelector("#article")!;
|
|
179
|
+
const cloned = original.cloneNode(true);
|
|
180
|
+
|
|
181
|
+
console.log("\n=== COMPARACIÓN ORIGINAL VS CLONADO ===");
|
|
182
|
+
|
|
183
|
+
const comparison = {
|
|
184
|
+
nodeName: {
|
|
185
|
+
original: original.nodeName,
|
|
186
|
+
cloned: cloned.nodeName,
|
|
187
|
+
match: original.nodeName === cloned.nodeName
|
|
188
|
+
},
|
|
189
|
+
id: {
|
|
190
|
+
original: original.getAttribute("id"),
|
|
191
|
+
cloned: cloned.getAttribute("id"),
|
|
192
|
+
match: original.getAttribute("id") === cloned.getAttribute("id")
|
|
193
|
+
},
|
|
194
|
+
childNodesLength: {
|
|
195
|
+
original: original.childNodes.length,
|
|
196
|
+
cloned: cloned.childNodes.length,
|
|
197
|
+
match: original.childNodes.length === cloned.childNodes.length
|
|
198
|
+
},
|
|
199
|
+
childrenLength: {
|
|
200
|
+
original: original.children.length,
|
|
201
|
+
cloned: cloned.children.length,
|
|
202
|
+
match: original.children.length === cloned.children.length
|
|
203
|
+
},
|
|
204
|
+
innerHTMLLength: {
|
|
205
|
+
original: original.innerHTML.length,
|
|
206
|
+
cloned: cloned.innerHTML.length,
|
|
207
|
+
match: original.innerHTML.length === cloned.innerHTML.length
|
|
208
|
+
},
|
|
209
|
+
textContentLength: {
|
|
210
|
+
original: original.textContent.length,
|
|
211
|
+
cloned: cloned.textContent.length,
|
|
212
|
+
match: original.textContent.length === cloned.textContent.length
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
console.table(comparison);
|
|
217
|
+
|
|
218
|
+
// VERIFICAR que todos coinciden
|
|
219
|
+
expect(comparison.nodeName.match).toBe(true);
|
|
220
|
+
expect(comparison.id.match).toBe(true);
|
|
221
|
+
expect(comparison.childNodesLength.match).toBe(true);
|
|
222
|
+
expect(comparison.childrenLength.match).toBe(true);
|
|
223
|
+
expect(comparison.innerHTMLLength.match).toBe(true);
|
|
224
|
+
expect(comparison.textContentLength.match).toBe(true);
|
|
225
|
+
|
|
226
|
+
// Verificar contenido específico
|
|
227
|
+
expect(cloned.querySelector("h1")?.textContent).toBe("Artículo de Prueba");
|
|
228
|
+
expect(cloned.querySelector(".intro")?.textContent).toBe("Introducción del artículo.");
|
|
229
|
+
expect(cloned.querySelectorAll("section").length).toBe(2);
|
|
230
|
+
expect(cloned.querySelectorAll("h2").length).toBe(2);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("REPRODUCCIÓN: verificar que innerHTML NO está vacío después de clonar", () => {
|
|
234
|
+
const testCases = [
|
|
235
|
+
{
|
|
236
|
+
name: "Elemento simple con texto",
|
|
237
|
+
html: `<div>Texto simple</div>`
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "Elemento con un hijo",
|
|
241
|
+
html: `<div><span>Un hijo</span></div>`
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: "Elemento con múltiples hijos",
|
|
245
|
+
html: `<div><p>P1</p><p>P2</p><p>P3</p></div>`
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "Elemento con anidación profunda",
|
|
249
|
+
html: `<div><div><div><div>Profundo</div></div></div></div>`
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "Lista compleja",
|
|
253
|
+
html: `<ul><li>A</li><li>B<ul><li>B1</li><li>B2</li></ul></li><li>C</li></ul>`
|
|
254
|
+
}
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
console.log("\n=== VERIFICANDO innerHTML EN MÚLTIPLES CASOS ===");
|
|
258
|
+
|
|
259
|
+
testCases.forEach(({ name, html }) => {
|
|
260
|
+
const doc = parseHTML(html);
|
|
261
|
+
const root = doc.body || doc;
|
|
262
|
+
const element = root.querySelector("div") || root.querySelector("ul")!;
|
|
263
|
+
const cloned = element.cloneNode(true);
|
|
264
|
+
|
|
265
|
+
const result = {
|
|
266
|
+
caso: name,
|
|
267
|
+
originalInnerHTML_vacio: !element.innerHTML || element.innerHTML.length === 0,
|
|
268
|
+
clonedInnerHTML_vacio: !cloned.innerHTML || cloned.innerHTML.length === 0,
|
|
269
|
+
originalLength: element.innerHTML.length,
|
|
270
|
+
clonedLength: cloned.innerHTML.length,
|
|
271
|
+
match: element.innerHTML.length === cloned.innerHTML.length
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
console.log(JSON.stringify(result, null, 2));
|
|
275
|
+
|
|
276
|
+
// VERIFICAR que ningún innerHTML está vacío (excepto elementos sin hijos)
|
|
277
|
+
if (element.children.length > 0) {
|
|
278
|
+
expect(cloned.innerHTML).toBeTruthy();
|
|
279
|
+
expect(cloned.innerHTML.length).toBeGreaterThan(0);
|
|
280
|
+
expect(cloned.innerHTML.length).toBe(element.innerHTML.length);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("REPRODUCCIÓN: acceder a childNodes inmediatamente después de clonar", () => {
|
|
286
|
+
const html = `
|
|
287
|
+
<div id="test">
|
|
288
|
+
Texto inicial
|
|
289
|
+
<span>Span 1</span>
|
|
290
|
+
Texto intermedio
|
|
291
|
+
<span>Span 2</span>
|
|
292
|
+
Texto final
|
|
293
|
+
</div>
|
|
294
|
+
`;
|
|
295
|
+
|
|
296
|
+
const doc = parseHTML(html);
|
|
297
|
+
const test = doc.querySelector("#test")!;
|
|
298
|
+
|
|
299
|
+
console.log("\n=== ACCESO A childNodes ===");
|
|
300
|
+
console.log("Original childNodes.length:", test.childNodes.length);
|
|
301
|
+
|
|
302
|
+
const cloned = test.cloneNode(true);
|
|
303
|
+
|
|
304
|
+
console.log("Clonado childNodes.length INMEDIATAMENTE:", cloned.childNodes.length);
|
|
305
|
+
|
|
306
|
+
// Verificar inmediatamente después del clonado
|
|
307
|
+
expect(cloned.childNodes).toBeTruthy();
|
|
308
|
+
expect(cloned.childNodes.length).toBeGreaterThan(0);
|
|
309
|
+
expect(cloned.childNodes.length).toBe(test.childNodes.length);
|
|
310
|
+
|
|
311
|
+
// Verificar que podemos iterar los childNodes
|
|
312
|
+
console.log("\nIterando childNodes del clon:");
|
|
313
|
+
for (let i = 0; i < cloned.childNodes.length; i++) {
|
|
314
|
+
const node = cloned.childNodes[i];
|
|
315
|
+
console.log(` ${i}: nodeType=${node.nodeType}, nodeName=${node.nodeName}, content="${node.textContent?.substring(0, 20)}"`);
|
|
316
|
+
expect(node).toBeTruthy();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Verificar que los spans son accesibles
|
|
320
|
+
const spans = cloned.querySelectorAll("span");
|
|
321
|
+
expect(spans.length).toBe(2);
|
|
322
|
+
expect(spans[0]?.textContent).toBe("Span 1");
|
|
323
|
+
expect(spans[1]?.textContent).toBe("Span 2");
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Script interactivo para probar cloneNode manualmente
|
|
5
|
+
*
|
|
6
|
+
* Uso:
|
|
7
|
+
* bun run tests/cloneNode-interactive.ts
|
|
8
|
+
*
|
|
9
|
+
* Este script permite probar cloneNode con diferentes HTML inputs
|
|
10
|
+
* y ver en detalle qué sucede durante el clonado.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { parseHTML } from "../index.js";
|
|
14
|
+
|
|
15
|
+
// Colores para la consola
|
|
16
|
+
const colors = {
|
|
17
|
+
reset: "\x1b[0m",
|
|
18
|
+
bright: "\x1b[1m",
|
|
19
|
+
green: "\x1b[32m",
|
|
20
|
+
blue: "\x1b[34m",
|
|
21
|
+
yellow: "\x1b[33m",
|
|
22
|
+
red: "\x1b[31m",
|
|
23
|
+
cyan: "\x1b[36m",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function log(color: string, ...args: any[]) {
|
|
27
|
+
console.log(color + args.join(" ") + colors.reset);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function separator() {
|
|
31
|
+
console.log("\n" + "=".repeat(70) + "\n");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function testCloneNode(html: string, selector: string, testName: string) {
|
|
35
|
+
separator();
|
|
36
|
+
log(colors.bright + colors.blue, `🧪 TEST: ${testName}`);
|
|
37
|
+
console.log();
|
|
38
|
+
|
|
39
|
+
log(colors.cyan, "📝 HTML Input:");
|
|
40
|
+
console.log(html);
|
|
41
|
+
console.log();
|
|
42
|
+
|
|
43
|
+
// Parse HTML
|
|
44
|
+
const doc = parseHTML(html);
|
|
45
|
+
const element = doc.querySelector(selector);
|
|
46
|
+
|
|
47
|
+
if (!element) {
|
|
48
|
+
log(colors.red, "❌ ERROR: No se encontró elemento con selector:", selector);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
log(colors.yellow, "🔍 Elemento original encontrado:");
|
|
53
|
+
console.log(" - nodeName:", element.nodeName);
|
|
54
|
+
console.log(" - id:", element.getAttribute("id"));
|
|
55
|
+
console.log(" - childNodes.length:", element.childNodes.length);
|
|
56
|
+
console.log(" - children.length:", element.children.length);
|
|
57
|
+
console.log(" - innerHTML.length:", element.innerHTML.length);
|
|
58
|
+
console.log(" - textContent.length:", element.textContent.length);
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
log(colors.bright, "⚙️ Clonando con cloneNode(true)...");
|
|
62
|
+
const cloned = element.cloneNode(true);
|
|
63
|
+
console.log();
|
|
64
|
+
|
|
65
|
+
log(colors.yellow, "🔍 Elemento clonado:");
|
|
66
|
+
console.log(" - nodeName:", cloned.nodeName);
|
|
67
|
+
console.log(" - id:", cloned.getAttribute("id"));
|
|
68
|
+
console.log(" - childNodes.length:", cloned.childNodes.length);
|
|
69
|
+
console.log(" - children.length:", cloned.children.length);
|
|
70
|
+
console.log(" - innerHTML.length:", cloned.innerHTML.length);
|
|
71
|
+
console.log(" - textContent.length:", cloned.textContent.length);
|
|
72
|
+
console.log();
|
|
73
|
+
|
|
74
|
+
// Comparación
|
|
75
|
+
log(colors.bright + colors.cyan, "📊 Comparación:");
|
|
76
|
+
const checks = {
|
|
77
|
+
"nodeName coincide":
|
|
78
|
+
element.nodeName === cloned.nodeName ? "✅" : "❌",
|
|
79
|
+
"childNodes.length coincide":
|
|
80
|
+
element.childNodes.length === cloned.childNodes.length ? "✅" : "❌",
|
|
81
|
+
"children.length coincide":
|
|
82
|
+
element.children.length === cloned.children.length ? "✅" : "❌",
|
|
83
|
+
"innerHTML.length coincide":
|
|
84
|
+
element.innerHTML.length === cloned.innerHTML.length ? "✅" : "❌",
|
|
85
|
+
"innerHTML no vacío":
|
|
86
|
+
cloned.innerHTML.length > 0 ? "✅" : "❌",
|
|
87
|
+
"childNodes no vacío":
|
|
88
|
+
cloned.childNodes.length > 0 ? "✅" : "❌",
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const [check, result] of Object.entries(checks)) {
|
|
92
|
+
console.log(` ${result} ${check}`);
|
|
93
|
+
}
|
|
94
|
+
console.log();
|
|
95
|
+
|
|
96
|
+
// Mostrar innerHTML
|
|
97
|
+
log(colors.cyan, "📄 Original innerHTML:");
|
|
98
|
+
console.log(element.innerHTML);
|
|
99
|
+
console.log();
|
|
100
|
+
|
|
101
|
+
log(colors.cyan, "📄 Clonado innerHTML:");
|
|
102
|
+
console.log(cloned.innerHTML);
|
|
103
|
+
console.log();
|
|
104
|
+
|
|
105
|
+
// Verificar querySelector en el clon
|
|
106
|
+
log(colors.bright + colors.cyan, "🔍 Pruebas de querySelector en el clon:");
|
|
107
|
+
const queries = [
|
|
108
|
+
"div",
|
|
109
|
+
"p",
|
|
110
|
+
"span",
|
|
111
|
+
"h1, h2, h3",
|
|
112
|
+
"ul",
|
|
113
|
+
"li",
|
|
114
|
+
"[id]",
|
|
115
|
+
"[class]",
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
for (const query of queries) {
|
|
119
|
+
const result = cloned.querySelector(query);
|
|
120
|
+
console.log(
|
|
121
|
+
` ${result ? "✅" : "⚪"} querySelector("${query}"): ${
|
|
122
|
+
result ? result.nodeName : "no encontrado"
|
|
123
|
+
}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const allDone = Object.values(checks).every((v) => v === "✅");
|
|
128
|
+
console.log();
|
|
129
|
+
log(
|
|
130
|
+
allDone ? colors.green : colors.red,
|
|
131
|
+
allDone ? "✅ TEST PASÓ" : "❌ TEST FALLÓ"
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// TESTS
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
console.clear();
|
|
140
|
+
log(
|
|
141
|
+
colors.bright + colors.green,
|
|
142
|
+
"\n🧬 cloneNode Interactive Test Suite\n"
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Test 1: Simple element
|
|
146
|
+
testCloneNode(
|
|
147
|
+
`<div id="simple">Hello World</div>`,
|
|
148
|
+
"#simple",
|
|
149
|
+
"Elemento simple con texto"
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Test 2: Nested elements
|
|
153
|
+
testCloneNode(
|
|
154
|
+
`
|
|
155
|
+
<div id="nested">
|
|
156
|
+
<h1>Title</h1>
|
|
157
|
+
<p>Paragraph with <strong>bold</strong> text</p>
|
|
158
|
+
<ul>
|
|
159
|
+
<li>Item 1</li>
|
|
160
|
+
<li>Item 2</li>
|
|
161
|
+
</ul>
|
|
162
|
+
</div>
|
|
163
|
+
`,
|
|
164
|
+
"#nested",
|
|
165
|
+
"Elementos anidados múltiples niveles"
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Test 3: Complex structure
|
|
169
|
+
testCloneNode(
|
|
170
|
+
`
|
|
171
|
+
<article id="article">
|
|
172
|
+
<header>
|
|
173
|
+
<h1>Article Title</h1>
|
|
174
|
+
<p class="meta">By Author Name</p>
|
|
175
|
+
</header>
|
|
176
|
+
<section class="content">
|
|
177
|
+
<p>First paragraph</p>
|
|
178
|
+
<p>Second paragraph</p>
|
|
179
|
+
<div class="callout">
|
|
180
|
+
<strong>Important:</strong> This is a callout
|
|
181
|
+
</div>
|
|
182
|
+
</section>
|
|
183
|
+
<footer>
|
|
184
|
+
<a href="#">Read more</a>
|
|
185
|
+
</footer>
|
|
186
|
+
</article>
|
|
187
|
+
`,
|
|
188
|
+
"#article",
|
|
189
|
+
"Estructura compleja tipo artículo"
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// Test 4: Form elements
|
|
193
|
+
testCloneNode(
|
|
194
|
+
`
|
|
195
|
+
<form id="form">
|
|
196
|
+
<input type="text" name="username" value="john" />
|
|
197
|
+
<input type="email" name="email" value="test@example.com" />
|
|
198
|
+
<textarea name="bio">User bio</textarea>
|
|
199
|
+
<button type="submit">Submit</button>
|
|
200
|
+
</form>
|
|
201
|
+
`,
|
|
202
|
+
"#form",
|
|
203
|
+
"Formulario con inputs"
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Test 5: Table
|
|
207
|
+
testCloneNode(
|
|
208
|
+
`
|
|
209
|
+
<table id="table">
|
|
210
|
+
<thead>
|
|
211
|
+
<tr>
|
|
212
|
+
<th>Name</th>
|
|
213
|
+
<th>Age</th>
|
|
214
|
+
</tr>
|
|
215
|
+
</thead>
|
|
216
|
+
<tbody>
|
|
217
|
+
<tr>
|
|
218
|
+
<td>John</td>
|
|
219
|
+
<td>30</td>
|
|
220
|
+
</tr>
|
|
221
|
+
<tr>
|
|
222
|
+
<td>Jane</td>
|
|
223
|
+
<td>25</td>
|
|
224
|
+
</tr>
|
|
225
|
+
</tbody>
|
|
226
|
+
</table>
|
|
227
|
+
`,
|
|
228
|
+
"#table",
|
|
229
|
+
"Tabla HTML"
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
separator();
|
|
233
|
+
log(colors.bright + colors.green, "✅ Todos los tests interactivos completados");
|
|
234
|
+
log(colors.cyan, "\n💡 TIP: Modifica este archivo para agregar tus propios tests");
|
|
235
|
+
console.log();
|