@remote-dom/polyfill 1.5.0 → 1.6.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.
Files changed (304) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +51 -3
  3. package/build/cjs/Attr.cjs +33 -4
  4. package/build/cjs/CharacterData.cjs +10 -0
  5. package/build/cjs/ChildNode.cjs +79 -13
  6. package/build/cjs/Comment.cjs +1 -1
  7. package/build/cjs/CustomElementRegistry.cjs +27 -2
  8. package/build/cjs/CustomEvent.cjs +2 -2
  9. package/build/cjs/Document.cjs +56 -32
  10. package/build/cjs/DocumentFragment.cjs +6 -2
  11. package/build/cjs/Element.cjs +68 -8
  12. package/build/cjs/Event.cjs +29 -27
  13. package/build/cjs/EventTarget.cjs +128 -58
  14. package/build/cjs/HTMLTemplateElement.cjs +1 -0
  15. package/build/cjs/MutationObserver.cjs +167 -1
  16. package/build/cjs/NamedNodeMap.cjs +82 -45
  17. package/build/cjs/Node.cjs +47 -11
  18. package/build/cjs/NodeList.cjs +9 -1
  19. package/build/cjs/ParentNode.cjs +319 -77
  20. package/build/cjs/SVGElement.cjs +1 -1
  21. package/build/cjs/Text.cjs +1 -1
  22. package/build/cjs/Window.cjs +131 -38
  23. package/build/cjs/attribute-reactions.cjs +20 -0
  24. package/build/cjs/constants.cjs +48 -24
  25. package/build/cjs/custom-element-reactions.cjs +52 -0
  26. package/build/cjs/dom-exception.cjs +15 -0
  27. package/build/cjs/hook-effects.cjs +51 -0
  28. package/build/cjs/names.cjs +61 -0
  29. package/build/cjs/selectors.cjs +213 -122
  30. package/build/cjs/serialization.cjs +168 -65
  31. package/build/cjs/shared.cjs +177 -28
  32. package/build/esm/Attr.mjs +34 -5
  33. package/build/esm/CharacterData.mjs +10 -0
  34. package/build/esm/ChildNode.mjs +76 -15
  35. package/build/esm/Comment.mjs +2 -2
  36. package/build/esm/CustomElementRegistry.mjs +27 -2
  37. package/build/esm/CustomEvent.mjs +2 -2
  38. package/build/esm/Document.mjs +60 -35
  39. package/build/esm/DocumentFragment.mjs +7 -3
  40. package/build/esm/Element.mjs +69 -9
  41. package/build/esm/Event.mjs +27 -27
  42. package/build/esm/EventTarget.mjs +130 -60
  43. package/build/esm/HTMLTemplateElement.mjs +2 -1
  44. package/build/esm/MutationObserver.mjs +166 -2
  45. package/build/esm/NamedNodeMap.mjs +83 -46
  46. package/build/esm/Node.mjs +48 -12
  47. package/build/esm/NodeList.mjs +9 -1
  48. package/build/esm/ParentNode.mjs +322 -81
  49. package/build/esm/SVGElement.mjs +2 -2
  50. package/build/esm/Text.mjs +2 -2
  51. package/build/esm/Window.mjs +132 -39
  52. package/build/esm/attribute-reactions.mjs +16 -0
  53. package/build/esm/constants.mjs +28 -23
  54. package/build/esm/custom-element-reactions.mjs +47 -0
  55. package/build/esm/dom-exception.mjs +11 -0
  56. package/build/esm/hook-effects.mjs +46 -0
  57. package/build/esm/names.mjs +54 -0
  58. package/build/esm/selectors.mjs +202 -124
  59. package/build/esm/serialization.mjs +169 -66
  60. package/build/esm/shared.mjs +173 -30
  61. package/build/esnext/Attr.esnext +45 -5
  62. package/build/esnext/CharacterData.esnext +10 -0
  63. package/build/esnext/ChildNode.esnext +84 -15
  64. package/build/esnext/Comment.esnext +2 -2
  65. package/build/esnext/CustomElementRegistry.esnext +42 -0
  66. package/build/esnext/CustomEvent.esnext +2 -2
  67. package/build/esnext/Document.esnext +69 -32
  68. package/build/esnext/DocumentFragment.esnext +7 -3
  69. package/build/esnext/Element.esnext +78 -9
  70. package/build/esnext/Event.esnext +36 -37
  71. package/build/esnext/EventTarget.esnext +133 -61
  72. package/build/esnext/HTMLTemplateElement.esnext +3 -1
  73. package/build/esnext/MutationObserver.esnext +163 -2
  74. package/build/esnext/NamedNodeMap.esnext +110 -42
  75. package/build/esnext/Node.esnext +50 -12
  76. package/build/esnext/NodeList.esnext +1 -1
  77. package/build/esnext/ParentNode.esnext +352 -79
  78. package/build/esnext/SVGElement.esnext +2 -2
  79. package/build/esnext/Text.esnext +2 -2
  80. package/build/esnext/Window.esnext +120 -16
  81. package/build/esnext/attribute-reactions.esnext +21 -0
  82. package/build/esnext/constants.esnext +34 -21
  83. package/build/esnext/custom-element-reactions.esnext +49 -0
  84. package/build/esnext/dom-exception.esnext +11 -0
  85. package/build/esnext/hook-effects.esnext +46 -0
  86. package/build/esnext/names.esnext +61 -0
  87. package/build/esnext/selectors.esnext +202 -100
  88. package/build/esnext/serialization.esnext +171 -48
  89. package/build/esnext/shared.esnext +181 -34
  90. package/build/tsconfig.tsbuildinfo +1 -1
  91. package/build/typescript/Attr.d.ts +6 -4
  92. package/build/typescript/Attr.d.ts.map +1 -1
  93. package/build/typescript/CharacterData.d.ts.map +1 -1
  94. package/build/typescript/ChildNode.d.ts +5 -0
  95. package/build/typescript/ChildNode.d.ts.map +1 -1
  96. package/build/typescript/ClipboardEvent.d.ts.map +1 -1
  97. package/build/typescript/Comment.d.ts +2 -2
  98. package/build/typescript/Comment.d.ts.map +1 -1
  99. package/build/typescript/CustomElementRegistry.d.ts +1 -0
  100. package/build/typescript/CustomElementRegistry.d.ts.map +1 -1
  101. package/build/typescript/CustomEvent.d.ts.map +1 -1
  102. package/build/typescript/Document.d.ts +10 -5
  103. package/build/typescript/Document.d.ts.map +1 -1
  104. package/build/typescript/DocumentFragment.d.ts +2 -1
  105. package/build/typescript/DocumentFragment.d.ts.map +1 -1
  106. package/build/typescript/Element.d.ts +16 -7
  107. package/build/typescript/Element.d.ts.map +1 -1
  108. package/build/typescript/ErrorEvent.d.ts.map +1 -1
  109. package/build/typescript/Event.d.ts +9 -10
  110. package/build/typescript/Event.d.ts.map +1 -1
  111. package/build/typescript/EventTarget.d.ts +20 -3
  112. package/build/typescript/EventTarget.d.ts.map +1 -1
  113. package/build/typescript/FocusEvent.d.ts.map +1 -1
  114. package/build/typescript/HTMLTemplateElement.d.ts.map +1 -1
  115. package/build/typescript/MutationObserver.d.ts +42 -0
  116. package/build/typescript/MutationObserver.d.ts.map +1 -1
  117. package/build/typescript/NamedNodeMap.d.ts +5 -3
  118. package/build/typescript/NamedNodeMap.d.ts.map +1 -1
  119. package/build/typescript/Node.d.ts +4 -3
  120. package/build/typescript/Node.d.ts.map +1 -1
  121. package/build/typescript/NodeList.d.ts +3 -2
  122. package/build/typescript/NodeList.d.ts.map +1 -1
  123. package/build/typescript/ParentNode.d.ts +29 -10
  124. package/build/typescript/ParentNode.d.ts.map +1 -1
  125. package/build/typescript/PromiseRejectionEvent.d.ts.map +1 -1
  126. package/build/typescript/SVGElement.d.ts +1 -1
  127. package/build/typescript/SVGElement.d.ts.map +1 -1
  128. package/build/typescript/Text.d.ts +1 -1
  129. package/build/typescript/Text.d.ts.map +1 -1
  130. package/build/typescript/ToggleEvent.d.ts.map +1 -1
  131. package/build/typescript/Window.d.ts +5 -1
  132. package/build/typescript/Window.d.ts.map +1 -1
  133. package/build/typescript/attribute-reactions.d.ts +4 -0
  134. package/build/typescript/attribute-reactions.d.ts.map +1 -0
  135. package/build/typescript/constants.d.ts +27 -18
  136. package/build/typescript/constants.d.ts.map +1 -1
  137. package/build/typescript/custom-element-reactions.d.ts +6 -0
  138. package/build/typescript/custom-element-reactions.d.ts.map +1 -0
  139. package/build/typescript/dom-exception.d.ts +2 -0
  140. package/build/typescript/dom-exception.d.ts.map +1 -0
  141. package/build/typescript/extensions.d.ts +4 -0
  142. package/build/typescript/extensions.d.ts.map +1 -0
  143. package/build/typescript/hook-effects.d.ts +6 -0
  144. package/build/typescript/hook-effects.d.ts.map +1 -0
  145. package/build/typescript/hooks.d.ts +3 -3
  146. package/build/typescript/hooks.d.ts.map +1 -1
  147. package/build/typescript/index.d.ts +1 -0
  148. package/build/typescript/index.d.ts.map +1 -1
  149. package/build/typescript/names.d.ts +11 -0
  150. package/build/typescript/names.d.ts.map +1 -0
  151. package/build/typescript/selectors.d.ts +21 -22
  152. package/build/typescript/selectors.d.ts.map +1 -1
  153. package/build/typescript/serialization.d.ts.map +1 -1
  154. package/build/typescript/shared.d.ts +10 -0
  155. package/build/typescript/shared.d.ts.map +1 -1
  156. package/build/typescript/tests/ChildNode.test.d.ts +2 -0
  157. package/build/typescript/tests/ChildNode.test.d.ts.map +1 -0
  158. package/build/typescript/tests/MutationObserver.test.d.ts +2 -0
  159. package/build/typescript/tests/MutationObserver.test.d.ts.map +1 -0
  160. package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts +2 -0
  161. package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts.map +1 -0
  162. package/build/typescript/tests/attr-mutation.test.d.ts +2 -0
  163. package/build/typescript/tests/attr-mutation.test.d.ts.map +1 -0
  164. package/build/typescript/tests/child-node-before-after.test.d.ts +2 -0
  165. package/build/typescript/tests/child-node-before-after.test.d.ts.map +1 -0
  166. package/build/typescript/tests/child-node-replace-with.test.d.ts +2 -0
  167. package/build/typescript/tests/child-node-replace-with.test.d.ts.map +1 -0
  168. package/build/typescript/tests/clone-import-namespaces.test.d.ts +2 -0
  169. package/build/typescript/tests/clone-import-namespaces.test.d.ts.map +1 -0
  170. package/build/typescript/tests/constants.test.d.ts +2 -0
  171. package/build/typescript/tests/constants.test.d.ts.map +1 -0
  172. package/build/typescript/tests/create-element-namespaces.test.d.ts +2 -0
  173. package/build/typescript/tests/create-element-namespaces.test.d.ts.map +1 -0
  174. package/build/typescript/tests/create-text-node.test.d.ts +2 -0
  175. package/build/typescript/tests/create-text-node.test.d.ts.map +1 -0
  176. package/build/typescript/tests/custom-element-reactions.test.d.ts +2 -0
  177. package/build/typescript/tests/custom-element-reactions.test.d.ts.map +1 -0
  178. package/build/typescript/tests/custom-element-registry.test.d.ts +2 -0
  179. package/build/typescript/tests/custom-element-registry.test.d.ts.map +1 -0
  180. package/build/typescript/tests/custom-event.test.d.ts +2 -0
  181. package/build/typescript/tests/custom-event.test.d.ts.map +1 -0
  182. package/build/typescript/tests/document-clone-import-contract.test.d.ts +2 -0
  183. package/build/typescript/tests/document-clone-import-contract.test.d.ts.map +1 -0
  184. package/build/typescript/tests/document.test.d.ts +2 -0
  185. package/build/typescript/tests/document.test.d.ts.map +1 -0
  186. package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts +2 -0
  187. package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts.map +1 -0
  188. package/build/typescript/tests/event-dispatch.test.d.ts +2 -0
  189. package/build/typescript/tests/event-dispatch.test.d.ts.map +1 -0
  190. package/build/typescript/tests/event-target.test.d.ts +2 -0
  191. package/build/typescript/tests/event-target.test.d.ts.map +1 -0
  192. package/build/typescript/tests/event.test.d.ts +2 -0
  193. package/build/typescript/tests/event.test.d.ts.map +1 -0
  194. package/build/typescript/tests/extensions.test.d.ts +2 -0
  195. package/build/typescript/tests/extensions.test.d.ts.map +1 -0
  196. package/build/typescript/tests/get-elements-by-tag-name.test.d.ts +2 -0
  197. package/build/typescript/tests/get-elements-by-tag-name.test.d.ts.map +1 -0
  198. package/build/typescript/tests/getElementsByClassName.test.d.ts +2 -0
  199. package/build/typescript/tests/getElementsByClassName.test.d.ts.map +1 -0
  200. package/build/typescript/tests/hooks.test.d.ts +2 -0
  201. package/build/typescript/tests/hooks.test.d.ts.map +1 -0
  202. package/build/typescript/tests/inner-html-parsing.test.d.ts +2 -0
  203. package/build/typescript/tests/inner-html-parsing.test.d.ts.map +1 -0
  204. package/build/typescript/tests/is-default-namespace.test.d.ts +2 -0
  205. package/build/typescript/tests/is-default-namespace.test.d.ts.map +1 -0
  206. package/build/typescript/tests/name-and-namespace-normalization.test.d.ts +2 -0
  207. package/build/typescript/tests/name-and-namespace-normalization.test.d.ts.map +1 -0
  208. package/build/typescript/tests/named-node-map.test.d.ts +2 -0
  209. package/build/typescript/tests/named-node-map.test.d.ts.map +1 -0
  210. package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts +2 -0
  211. package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts.map +1 -0
  212. package/build/typescript/tests/node.test.d.ts +2 -0
  213. package/build/typescript/tests/node.test.d.ts.map +1 -0
  214. package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts +2 -0
  215. package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts.map +1 -0
  216. package/build/typescript/tests/parent-node-mutation-validation.test.d.ts +2 -0
  217. package/build/typescript/tests/parent-node-mutation-validation.test.d.ts.map +1 -0
  218. package/build/typescript/tests/parent-node.test.d.ts +2 -0
  219. package/build/typescript/tests/parent-node.test.d.ts.map +1 -0
  220. package/build/typescript/tests/serialization-template.test.d.ts +2 -0
  221. package/build/typescript/tests/serialization-template.test.d.ts.map +1 -0
  222. package/build/typescript/tests/slot-attribute.test.d.ts +2 -0
  223. package/build/typescript/tests/slot-attribute.test.d.ts.map +1 -0
  224. package/build/typescript/tests/text-content.test.d.ts +2 -0
  225. package/build/typescript/tests/text-content.test.d.ts.map +1 -0
  226. package/build/typescript/tests/toggle-attribute.test.d.ts +2 -0
  227. package/build/typescript/tests/toggle-attribute.test.d.ts.map +1 -0
  228. package/build/typescript/tests/wide-tree-traversal.test.d.ts +2 -0
  229. package/build/typescript/tests/wide-tree-traversal.test.d.ts.map +1 -0
  230. package/build/typescript/tests/window-set-global.test.d.ts +2 -0
  231. package/build/typescript/tests/window-set-global.test.d.ts.map +1 -0
  232. package/package.json +1 -1
  233. package/source/Attr.ts +60 -8
  234. package/source/CharacterData.ts +13 -0
  235. package/source/ChildNode.ts +107 -14
  236. package/source/Comment.ts +2 -2
  237. package/source/CustomElementRegistry.ts +56 -0
  238. package/source/CustomEvent.ts +2 -2
  239. package/source/Document.ts +104 -38
  240. package/source/DocumentFragment.ts +15 -3
  241. package/source/Element.ts +129 -14
  242. package/source/Event.ts +34 -32
  243. package/source/EventTarget.ts +203 -79
  244. package/source/HTMLTemplateElement.ts +2 -1
  245. package/source/MutationObserver.ts +279 -1
  246. package/source/NamedNodeMap.ts +141 -60
  247. package/source/Node.ts +70 -14
  248. package/source/NodeList.ts +13 -3
  249. package/source/ParentNode.ts +500 -88
  250. package/source/SVGElement.ts +2 -2
  251. package/source/Text.ts +2 -2
  252. package/source/Window.ts +156 -17
  253. package/source/attribute-reactions.ts +32 -0
  254. package/source/constants.ts +40 -17
  255. package/source/custom-element-reactions.ts +64 -0
  256. package/source/dom-exception.ts +14 -0
  257. package/source/extensions.ts +4 -0
  258. package/source/hook-effects.ts +56 -0
  259. package/source/hooks.ts +11 -3
  260. package/source/index.ts +1 -0
  261. package/source/names.ts +94 -0
  262. package/source/selectors.ts +301 -138
  263. package/source/serialization.ts +224 -66
  264. package/source/shared.ts +269 -40
  265. package/source/tests/ChildNode.test.ts +178 -0
  266. package/source/tests/MutationObserver.test.ts +303 -0
  267. package/source/tests/adopt-inserted-subtrees.test.ts +584 -0
  268. package/source/tests/attr-mutation.test.ts +112 -0
  269. package/source/tests/child-node-before-after.test.ts +421 -0
  270. package/source/tests/child-node-replace-with.test.ts +251 -0
  271. package/source/tests/clone-import-namespaces.test.ts +365 -0
  272. package/source/tests/constants.test.ts +46 -0
  273. package/source/tests/create-element-namespaces.test.ts +142 -0
  274. package/source/tests/create-text-node.test.ts +38 -0
  275. package/source/tests/custom-element-reactions.test.ts +600 -0
  276. package/source/tests/custom-element-registry.test.ts +137 -0
  277. package/source/tests/custom-event.test.ts +26 -0
  278. package/source/tests/document-clone-import-contract.test.ts +77 -0
  279. package/source/tests/document.test.ts +122 -0
  280. package/source/tests/dom-mutation-selector-errors.test.ts +484 -0
  281. package/source/tests/event-dispatch.test.ts +270 -0
  282. package/source/tests/event-target.test.ts +208 -0
  283. package/source/tests/event.test.ts +98 -0
  284. package/source/tests/extensions.test.ts +159 -0
  285. package/source/tests/get-elements-by-tag-name.test.ts +79 -0
  286. package/source/tests/getElementsByClassName.test.ts +109 -0
  287. package/source/tests/hooks.test.ts +43 -0
  288. package/source/tests/inner-html-parsing.test.ts +461 -0
  289. package/source/tests/is-default-namespace.test.ts +111 -0
  290. package/source/tests/name-and-namespace-normalization.test.ts +319 -0
  291. package/source/tests/named-node-map.test.ts +122 -0
  292. package/source/tests/namespaced-attribute-reactions.test.ts +141 -0
  293. package/source/tests/node.test.ts +56 -0
  294. package/source/tests/parent-node-mutation-return-values.test.ts +63 -0
  295. package/source/tests/parent-node-mutation-validation.test.ts +362 -0
  296. package/source/tests/parent-node.test.ts +169 -0
  297. package/source/tests/selectors.test.ts +533 -22
  298. package/source/tests/serialization-template.test.ts +232 -0
  299. package/source/tests/slot-attribute.test.ts +133 -0
  300. package/source/tests/text-content.test.ts +440 -0
  301. package/source/tests/toggle-attribute.test.ts +113 -0
  302. package/source/tests/wide-tree-traversal.test.ts +699 -0
  303. package/source/tests/window-set-global.test.ts +321 -0
  304. package/tsconfig.json +1 -1
@@ -0,0 +1,365 @@
1
+ import {beforeEach, describe, expect, it} from 'vitest';
2
+
3
+ import {Attr} from '../Attr.ts';
4
+ import {CONTENT, HOOKS, HTML_NAMESPACE, SVG_NAMESPACE} from '../constants.ts';
5
+ import {Window} from '../index.ts';
6
+ import type {HTMLTemplateElement} from '../HTMLTemplateElement.ts';
7
+
8
+ let window: Window;
9
+ let document: Window['document'];
10
+
11
+ beforeEach(() => {
12
+ window = new Window();
13
+ Window.setGlobalThis(window);
14
+ document = window.document;
15
+ });
16
+
17
+ const DEEP_TEMPLATE_LEVELS = 6_000;
18
+
19
+ function createDeepTemplateTree(ownerDocument: Window['document']) {
20
+ const root = ownerDocument.createElement('template') as HTMLTemplateElement;
21
+ let current = root;
22
+
23
+ for (let i = 0; i < DEEP_TEMPLATE_LEVELS; i++) {
24
+ const child = ownerDocument.createElement(
25
+ 'template',
26
+ ) as HTMLTemplateElement;
27
+ current.content.appendChild(child);
28
+ current = child;
29
+ }
30
+
31
+ current.content.appendChild(ownerDocument.createTextNode('leaf'));
32
+ return root;
33
+ }
34
+
35
+ function expectDeepTemplateCopy(
36
+ source: HTMLTemplateElement,
37
+ copy: HTMLTemplateElement,
38
+ ownerDocument: Window['document'],
39
+ ) {
40
+ let sourceTemplate = source;
41
+ let copiedTemplate = copy;
42
+
43
+ // Avoid recursively formatting the 6,000-level documents on assertion failure.
44
+ for (let i = 0; i < DEEP_TEMPLATE_LEVELS; i++) {
45
+ expect(copiedTemplate === sourceTemplate).toBe(false);
46
+ expect(copiedTemplate.ownerDocument === ownerDocument).toBe(true);
47
+ expect(copiedTemplate.content.ownerDocument === ownerDocument).toBe(true);
48
+
49
+ sourceTemplate = sourceTemplate.content.firstChild as HTMLTemplateElement;
50
+ copiedTemplate = copiedTemplate.content.firstChild as HTMLTemplateElement;
51
+ expect(copiedTemplate.localName).toBe('template');
52
+ }
53
+
54
+ expect(copiedTemplate === sourceTemplate).toBe(false);
55
+ expect(copiedTemplate.ownerDocument === ownerDocument).toBe(true);
56
+ expect(copiedTemplate.content.ownerDocument === ownerDocument).toBe(true);
57
+ const sourceLeaf = sourceTemplate.content.firstChild;
58
+ const copiedLeaf = copiedTemplate.content.firstChild;
59
+ expect(copiedLeaf === sourceLeaf).toBe(false);
60
+ expect(copiedLeaf?.nodeValue).toBe('leaf');
61
+ expect(copiedLeaf?.ownerDocument === ownerDocument).toBe(true);
62
+ }
63
+
64
+ describe('Node.cloneNode()', () => {
65
+ it('preserves SVG element shape, qualified names, and attributes', () => {
66
+ const gradient = document.createElementNS(
67
+ SVG_NAMESPACE,
68
+ 'svg:linearGradient',
69
+ );
70
+ gradient.setAttribute('viewBox', '0 0 10 10');
71
+ gradient.setAttributeNS('urn:paint', 'paint:Color', 'red');
72
+ gradient.appendChild(document.createElementNS(SVG_NAMESPACE, 'stop'));
73
+
74
+ const shallow = gradient.cloneNode() as typeof gradient;
75
+ const deep = gradient.cloneNode(true) as typeof gradient;
76
+
77
+ for (const clone of [shallow, deep]) {
78
+ expect(clone).toBeInstanceOf(window.SVGElement);
79
+ expect(clone.namespaceURI).toBe(SVG_NAMESPACE);
80
+ expect(clone.prefix).toBe('svg');
81
+ expect(clone.localName).toBe('linearGradient');
82
+ expect(clone.nodeName).toBe('svg:linearGradient');
83
+ expect(clone.getAttributeNames()).toEqual(['viewBox', 'paint:Color']);
84
+ expect(clone.getAttribute('viewBox')).toBe('0 0 10 10');
85
+ expect(clone.getAttributeNS('urn:paint', 'Color')).toBe('red');
86
+ }
87
+
88
+ expect(shallow.childNodes).toHaveLength(0);
89
+ expect(deep.childNodes).toHaveLength(1);
90
+ expect(deep.firstChild).not.toBe(gradient.firstChild);
91
+ expect(deep.firstChild).toBeInstanceOf(window.SVGElement);
92
+ expect((deep.firstChild as typeof gradient).namespaceURI).toBe(
93
+ SVG_NAMESPACE,
94
+ );
95
+ });
96
+
97
+ it('preserves deep-clone hook ordering', () => {
98
+ const source = document.createElement('div');
99
+ const child = document.createElement('section');
100
+ child.appendChild(document.createElement('span'));
101
+ source.appendChild(child);
102
+ const calls: string[] = [];
103
+ window[HOOKS].createElement = (element) => {
104
+ calls.push(`create:${element.localName}`);
105
+ };
106
+ window[HOOKS].insertChild = (parent, node) => {
107
+ calls.push(`insert:${parent.localName}:${node.nodeName.toLowerCase()}`);
108
+ };
109
+
110
+ source.cloneNode(true);
111
+
112
+ expect(calls).toEqual([
113
+ 'create:div',
114
+ 'create:section',
115
+ 'create:span',
116
+ 'insert:section:span',
117
+ 'insert:div:section',
118
+ ]);
119
+ });
120
+
121
+ it.each(['has:colon', ':leading', 'trailing:', 'xml:thing', 'xmlns:thing'])(
122
+ 'preserves the established literal-colon element name %s',
123
+ (name) => {
124
+ const source = document.createElement(name);
125
+ source.append('child');
126
+ const destination = new Window().document;
127
+
128
+ const clone = source.cloneNode(true) as typeof source;
129
+ const imported = destination.importNode(source, true) as typeof source;
130
+
131
+ for (const copy of [clone, imported]) {
132
+ expect(copy.namespaceURI).toBe(HTML_NAMESPACE);
133
+ expect(copy.prefix).toBeNull();
134
+ expect(copy.localName).toBe(source.localName);
135
+ expect(copy.nodeName).toBe(source.nodeName);
136
+ expect(copy.textContent).toBe('child');
137
+ }
138
+ expect(clone.ownerDocument).toBe(document);
139
+ expect(imported.ownerDocument).toBe(destination);
140
+ expect(imported.firstChild?.ownerDocument).toBe(destination);
141
+ },
142
+ );
143
+
144
+ it('preserves unnamespaced colon attributes without reparsing them', () => {
145
+ const html = document.createElement('div');
146
+ html.setAttribute('Data:State', 'html');
147
+ const svg = document.createElementNS(SVG_NAMESPACE, 'svg');
148
+ svg.setAttribute('Data:State', 'svg');
149
+ const destination = new Window().document;
150
+
151
+ for (const [source, expectedName] of [
152
+ [html, 'data:state'],
153
+ [svg, 'Data:State'],
154
+ ] as const) {
155
+ const clone = source.cloneNode() as typeof source;
156
+ const imported = destination.importNode(source) as typeof source;
157
+
158
+ for (const copy of [clone, imported]) {
159
+ const attribute = copy.attributes.item(0)!;
160
+ expect(attribute.name).toBe(expectedName);
161
+ expect(attribute.localName).toBe(expectedName);
162
+ expect(attribute.prefix).toBeNull();
163
+ expect(attribute.namespaceURI).toBeNull();
164
+ expect(attribute.value).toBe(source.getAttribute(expectedName));
165
+ }
166
+ expect(imported.attributes.item(0)?.ownerDocument).toBe(destination);
167
+ }
168
+ });
169
+
170
+ it('keeps shallow templates empty and deeply clones direct and content children', () => {
171
+ const template = document.createElement('template');
172
+ const direct = document.createElement('aside');
173
+ direct.append('direct');
174
+ const section = document.createElement('section');
175
+ const nested = document.createElement('template');
176
+ const nestedDirect = document.createElement('em');
177
+ nestedDirect.append('nested direct');
178
+ const nestedContent = document.createElement('strong');
179
+ nestedContent.append('nested content');
180
+ nested.append(nestedDirect);
181
+ nested.content.append(nestedContent);
182
+ section.append('before', nested, 'after');
183
+ template.append(direct);
184
+ template.content.append(section);
185
+
186
+ const shallow = template.cloneNode() as typeof template;
187
+
188
+ expect(shallow).toBeInstanceOf(window.HTMLTemplateElement);
189
+ expect(shallow.childNodes).toHaveLength(0);
190
+ expect((shallow as any)[CONTENT]).toBeUndefined();
191
+ expect(shallow.content.childNodes).toHaveLength(0);
192
+ expect(template.firstChild).toBe(direct);
193
+ expect(template.content.firstChild).toBe(section);
194
+
195
+ const deep = template.cloneNode(true) as typeof template;
196
+ const clonedDirect = deep.firstChild as typeof direct;
197
+ const clonedSection = deep.content.firstChild as typeof section;
198
+ const clonedNested = deep.content.querySelector(
199
+ 'template',
200
+ ) as typeof nested;
201
+
202
+ expect(deep).toBeInstanceOf(window.HTMLTemplateElement);
203
+ expect(clonedDirect).not.toBe(direct);
204
+ expect(clonedDirect.textContent).toBe('direct');
205
+ expect(clonedSection).not.toBe(section);
206
+ expect(clonedSection.ownerDocument).toBe(document);
207
+ expect(clonedNested).not.toBe(nested);
208
+ expect(clonedNested).toBeInstanceOf(window.HTMLTemplateElement);
209
+ expect(clonedNested.firstChild).not.toBe(nestedDirect);
210
+ expect(clonedNested.firstChild?.textContent).toBe('nested direct');
211
+ expect(clonedNested.content.textContent).toBe('nested content');
212
+ expect(clonedNested.content.firstChild).not.toBe(nestedContent);
213
+ expect(clonedNested.content.firstChild?.ownerDocument).toBe(document);
214
+ });
215
+
216
+ it('clones a 6,000-level template content tree without overflowing', () => {
217
+ const source = createDeepTemplateTree(document);
218
+
219
+ const clone = source.cloneNode(true) as HTMLTemplateElement;
220
+
221
+ expectDeepTemplateCopy(source, clone, document);
222
+ }, 15_000);
223
+
224
+ it('rejects cyclic or repeated malformed node graphs', () => {
225
+ const cyclic = document.createElement('template');
226
+ cyclic.content.childNodes.push(cyclic);
227
+ const repeated = document.createElement('div');
228
+ const child = document.createElement('span');
229
+ repeated.childNodes.push(child, child);
230
+
231
+ for (const source of [cyclic, repeated]) {
232
+ expect(() => source.cloneNode(true)).toThrowError(
233
+ 'Cannot clone a cyclic or repeated node graph',
234
+ );
235
+ }
236
+ });
237
+
238
+ it('clones attributes without retaining their owner element', () => {
239
+ const element = document.createElementNS(SVG_NAMESPACE, 'svg');
240
+ element.setAttributeNS('urn:example', 'ex:State', 'ready');
241
+ const attribute = element.attributes.item(0)!;
242
+
243
+ const clone = attribute.cloneNode() as Attr;
244
+
245
+ expect(clone).toBeInstanceOf(Attr);
246
+ expect(clone).not.toBe(attribute);
247
+ expect(clone.name).toBe('ex:State');
248
+ expect(clone.localName).toBe('State');
249
+ expect(clone.prefix).toBe('ex');
250
+ expect(clone.namespaceURI).toBe('urn:example');
251
+ expect(clone.value).toBe('ready');
252
+ expect(clone.ownerElement).toBeNull();
253
+ expect(clone.ownerDocument).toBe(document);
254
+ });
255
+ });
256
+
257
+ describe('Document.importNode()', () => {
258
+ it('preserves SVG namespaces and qualified names across documents', () => {
259
+ const svg = document.createElementNS(SVG_NAMESPACE, 'svg:svg');
260
+ const gradient = document.createElementNS(
261
+ SVG_NAMESPACE,
262
+ 'svg:linearGradient',
263
+ );
264
+ gradient.setAttribute('viewBox', '0 0 10 10');
265
+ svg.appendChild(gradient);
266
+ const destination = new Window().document;
267
+
268
+ const imported = destination.importNode(svg, true) as typeof svg;
269
+ const importedGradient = imported.firstChild as typeof gradient;
270
+
271
+ expect(imported).toBeInstanceOf(window.SVGElement);
272
+ expect(imported.ownerDocument).toBe(destination);
273
+ expect(imported.nodeName).toBe('svg:svg');
274
+ expect(importedGradient).toBeInstanceOf(window.SVGElement);
275
+ expect(importedGradient.ownerDocument).toBe(destination);
276
+ expect(importedGradient.namespaceURI).toBe(SVG_NAMESPACE);
277
+ expect(importedGradient.prefix).toBe('svg');
278
+ expect(importedGradient.localName).toBe('linearGradient');
279
+ expect(importedGradient.nodeName).toBe('svg:linearGradient');
280
+ expect(importedGradient.getAttributeNames()).toEqual(['viewBox']);
281
+ });
282
+
283
+ it('preserves custom namespaces and prefixed names across documents', () => {
284
+ const source = document.createElementNS('urn:example', 'ex:Widget');
285
+ source.setAttributeNS('urn:metadata', 'meta:State', 'ready');
286
+ source.appendChild(document.createElementNS('urn:child', 'child:Part'));
287
+ const destination = new Window().document;
288
+
289
+ const imported = destination.importNode(source, true) as typeof source;
290
+ const importedChild = imported.firstChild as typeof source;
291
+
292
+ expect(imported.ownerDocument).toBe(destination);
293
+ expect(imported.namespaceURI).toBe('urn:example');
294
+ expect(imported.prefix).toBe('ex');
295
+ expect(imported.localName).toBe('Widget');
296
+ expect(imported.nodeName).toBe('ex:Widget');
297
+ expect(imported.getAttributeNS('urn:metadata', 'State')).toBe('ready');
298
+ expect(imported.attributes.item(0)?.name).toBe('meta:State');
299
+ expect(importedChild.ownerDocument).toBe(destination);
300
+ expect(importedChild.namespaceURI).toBe('urn:child');
301
+ expect(importedChild.nodeName).toBe('child:Part');
302
+ });
303
+
304
+ it('imports a 6,000-level template content tree without overflowing', () => {
305
+ const source = createDeepTemplateTree(document);
306
+ const destination = new Window().document;
307
+
308
+ const imported = destination.importNode(
309
+ source,
310
+ true,
311
+ ) as HTMLTemplateElement;
312
+
313
+ expectDeepTemplateCopy(source, imported, destination);
314
+ }, 15_000);
315
+
316
+ it('imports attributes and mixed template trees into the destination document', () => {
317
+ const element = document.createElement('div');
318
+ element.setAttributeNS('urn:example', 'ex:State', 'ready');
319
+ const attribute = element.attributes.item(0)!;
320
+ const template = document.createElement('template');
321
+ const direct = document.createElement('aside');
322
+ direct.append('direct');
323
+ const contentChild = document.createElement('section');
324
+ const nested = document.createElement('template');
325
+ const nestedDirect = document.createElement('em');
326
+ const nestedContent = document.createElement('strong');
327
+ nested.append(nestedDirect);
328
+ nested.content.append(nestedContent);
329
+ contentChild.append(nested);
330
+ template.append(direct);
331
+ template.content.append(contentChild);
332
+ const destinationWindow = new Window();
333
+ const destination = destinationWindow.document;
334
+
335
+ const importedAttribute = destination.importNode(attribute) as Attr;
336
+ const importedTemplate = destination.importNode(
337
+ template,
338
+ true,
339
+ ) as typeof template;
340
+ const importedDirect = importedTemplate.firstChild as typeof direct;
341
+ const importedContentChild = importedTemplate.content
342
+ .firstChild as typeof contentChild;
343
+ const importedNested = importedContentChild.firstChild as typeof nested;
344
+
345
+ expect(importedAttribute.name).toBe('ex:State');
346
+ expect(importedAttribute.value).toBe('ready');
347
+ expect(importedAttribute.namespaceURI).toBe('urn:example');
348
+ expect(importedAttribute.ownerElement).toBeNull();
349
+ expect(importedAttribute.ownerDocument).toBe(destination);
350
+ expect(importedTemplate).toBeInstanceOf(
351
+ destinationWindow.HTMLTemplateElement,
352
+ );
353
+ expect(importedTemplate.ownerDocument).toBe(destination);
354
+ expect(importedDirect).not.toBe(direct);
355
+ expect(importedDirect.ownerDocument).toBe(destination);
356
+ expect(importedTemplate.content.ownerDocument).toBe(destination);
357
+ expect(importedContentChild).not.toBe(contentChild);
358
+ expect(importedContentChild.ownerDocument).toBe(destination);
359
+ expect(importedNested).not.toBe(nested);
360
+ expect(importedNested.ownerDocument).toBe(destination);
361
+ expect(importedNested.firstChild?.ownerDocument).toBe(destination);
362
+ expect(importedNested.content.ownerDocument).toBe(destination);
363
+ expect(importedNested.content.firstChild?.ownerDocument).toBe(destination);
364
+ });
365
+ });
@@ -0,0 +1,46 @@
1
+ import {describe, expect, it} from 'vitest';
2
+
3
+ import {Window} from '../index.ts';
4
+
5
+ describe('DOM constants', () => {
6
+ it('exposes standard event phase constants', () => {
7
+ const {Event} = new Window();
8
+
9
+ expect(Event.NONE).toBe(0);
10
+ expect(Event.CAPTURING_PHASE).toBe(1);
11
+ expect(Event.AT_TARGET).toBe(2);
12
+ expect(Event.BUBBLING_PHASE).toBe(3);
13
+ });
14
+
15
+ it('initializes the event type', () => {
16
+ const {Event} = new Window();
17
+ const event = new Event('click');
18
+
19
+ expect(event.type).toBe('click');
20
+ });
21
+
22
+ it('uses standard node type values', () => {
23
+ const {document} = new Window();
24
+ const element = document.createElement('div');
25
+ element.setAttribute('example', 'value');
26
+
27
+ expect(element.nodeType).toBe(1);
28
+ expect(element.attributes.item(0)?.nodeType).toBe(2);
29
+ expect(document.createTextNode('').nodeType).toBe(3);
30
+ expect(document.createComment('').nodeType).toBe(8);
31
+ expect(document.nodeType).toBe(9);
32
+ expect(document.createDocumentFragment().nodeType).toBe(11);
33
+ });
34
+
35
+ it('uses standard namespace values', () => {
36
+ const {document} = new Window();
37
+
38
+ expect(document.createElement('div').namespaceURI).toBe(
39
+ 'http://www.w3.org/1999/xhtml',
40
+ );
41
+ expect(
42
+ document.createElementNS('http://www.w3.org/2000/svg', 'svg')
43
+ .namespaceURI,
44
+ ).toBe('http://www.w3.org/2000/svg');
45
+ });
46
+ });
@@ -0,0 +1,142 @@
1
+ import {beforeEach, describe, expect, it} from 'vitest';
2
+
3
+ import {
4
+ HTML_NAMESPACE,
5
+ SVG_NAMESPACE,
6
+ XML_NAMESPACE,
7
+ XMLNS_NAMESPACE,
8
+ } from '../constants.ts';
9
+ import {Window} from '../index.ts';
10
+
11
+ let window: Window;
12
+ let document: Window['document'];
13
+
14
+ beforeEach(() => {
15
+ window = new Window();
16
+ Window.setGlobalThis(window);
17
+ document = window.document;
18
+ });
19
+
20
+ describe('Document element namespace handling', () => {
21
+ it('validates and ASCII-normalizes createElement names', () => {
22
+ expect(document.createElement('AÇ').localName).toBe('aÇ');
23
+ expect(document.createElement(null as any).localName).toBe('null');
24
+ expect(() => document.createElement('1element')).toThrowError(
25
+ expect.objectContaining({name: 'InvalidCharacterError'}),
26
+ );
27
+ });
28
+
29
+ it('coerces and represents createElementNS arguments', () => {
30
+ const namespace = {toString: () => 'urn:example'};
31
+ const qualifiedName = {toString: () => 'prefix:Local'};
32
+ const element = document.createElementNS(
33
+ namespace as any,
34
+ qualifiedName as any,
35
+ );
36
+
37
+ expect(element.namespaceURI).toBe('urn:example');
38
+ expect(element.prefix).toBe('prefix');
39
+ expect(element.localName).toBe('Local');
40
+ expect(element.nodeName).toBe('prefix:Local');
41
+ expect(element.tagName).toBe('prefix:Local');
42
+ });
43
+
44
+ it.each([null, '', undefined])(
45
+ 'keeps the %s namespace distinct from the HTML namespace',
46
+ (namespace) => {
47
+ const element = document.createElementNS(namespace as any, 'template');
48
+
49
+ expect(element.namespaceURI).toBeNull();
50
+ expect(element.prefix).toBeNull();
51
+ expect(element.localName).toBe('template');
52
+ expect(element).not.toBeInstanceOf(window.HTMLTemplateElement);
53
+ },
54
+ );
55
+
56
+ it('specializes template, custom, and SVG elements only in their namespaces', () => {
57
+ class CustomElement extends window.HTMLElement {}
58
+ window.customElements.define(
59
+ 'x-example',
60
+ CustomElement as unknown as CustomElementConstructor,
61
+ );
62
+
63
+ const template = document.createElementNS(HTML_NAMESPACE, 'html:template');
64
+ const custom = document.createElementNS(HTML_NAMESPACE, 'html:x-example');
65
+ const uppercaseCustom = document.createElementNS(
66
+ HTML_NAMESPACE,
67
+ 'html:X-EXAMPLE',
68
+ );
69
+ const foreignCustom = document.createElementNS('urn:example', 'x-example');
70
+ const svg = document.createElementNS(SVG_NAMESPACE, 'svg');
71
+
72
+ expect(template).toBeInstanceOf(window.HTMLTemplateElement);
73
+ expect(template.prefix).toBe('html');
74
+ expect(template.localName).toBe('template');
75
+ expect(template.tagName).toBe('HTML:TEMPLATE');
76
+ expect(custom).toBeInstanceOf(CustomElement);
77
+ expect(custom.prefix).toBe('html');
78
+ expect(uppercaseCustom).not.toBeInstanceOf(CustomElement);
79
+ expect(foreignCustom).not.toBeInstanceOf(CustomElement);
80
+ expect(svg).toBeInstanceOf(window.SVGElement);
81
+ });
82
+
83
+ it.each([
84
+ {
85
+ namespace: 'urn:example',
86
+ name: '1element',
87
+ error: 'InvalidCharacterError',
88
+ },
89
+ {
90
+ namespace: 'urn:example',
91
+ name: ':element',
92
+ error: 'InvalidCharacterError',
93
+ },
94
+ {namespace: 'urn:example', name: 'prefix:', error: 'InvalidCharacterError'},
95
+ {namespace: null, name: 'prefix:element', error: 'NamespaceError'},
96
+ {namespace: 'urn:example', name: 'xml:element', error: 'NamespaceError'},
97
+ {namespace: 'urn:example', name: 'xmlns', error: 'NamespaceError'},
98
+ {namespace: XMLNS_NAMESPACE, name: 'element', error: 'NamespaceError'},
99
+ ])('rejects $name in $namespace with $error', ({namespace, name, error}) => {
100
+ expect(() => document.createElementNS(namespace, name)).toThrowError(
101
+ expect.objectContaining({name: error}),
102
+ );
103
+ });
104
+
105
+ it.each([
106
+ {
107
+ namespace: 'urn:example',
108
+ name: '0:element',
109
+ prefix: '0',
110
+ local: 'element',
111
+ },
112
+ {
113
+ namespace: 'urn:example',
114
+ name: 'prefix:element:more',
115
+ prefix: 'prefix',
116
+ local: 'element:more',
117
+ },
118
+ {
119
+ namespace: XML_NAMESPACE,
120
+ name: 'xml:element',
121
+ prefix: 'xml',
122
+ local: 'element',
123
+ },
124
+ {namespace: XMLNS_NAMESPACE, name: 'xmlns', prefix: null, local: 'xmlns'},
125
+ {
126
+ namespace: XMLNS_NAMESPACE,
127
+ name: 'xmlns:element',
128
+ prefix: 'xmlns',
129
+ local: 'element',
130
+ },
131
+ ])(
132
+ 'accepts the qualified name $name in its namespace',
133
+ ({namespace, name, prefix, local}) => {
134
+ const element = document.createElementNS(namespace, name);
135
+
136
+ expect(element.namespaceURI).toBe(namespace);
137
+ expect(element.prefix).toBe(prefix);
138
+ expect(element.localName).toBe(local);
139
+ expect(element.nodeName).toBe(name);
140
+ },
141
+ );
142
+ });
@@ -0,0 +1,38 @@
1
+ import {HOOKS, Window} from '../index.ts';
2
+
3
+ import {expect, it} from 'vitest';
4
+
5
+ function expectCreateTextNodeHookData(data: any, expected: string) {
6
+ const window = new Window();
7
+ Window.setGlobalThis(window);
8
+
9
+ let hookText: unknown;
10
+ let hookData: string | undefined;
11
+
12
+ window[HOOKS].createText = (text, data) => {
13
+ hookText = text;
14
+ hookData = data;
15
+ };
16
+
17
+ const text = document.createTextNode(data);
18
+
19
+ expect(text.data).toBe(expected);
20
+ expect(hookText).toBe(text);
21
+ expect(hookData).toBe(text.data);
22
+ }
23
+
24
+ it('normalizes null createTextNode hook data', () => {
25
+ expectCreateTextNodeHookData(null, '');
26
+ });
27
+
28
+ it('normalizes undefined createTextNode hook data', () => {
29
+ expectCreateTextNodeHookData(undefined, '');
30
+ });
31
+
32
+ it('preserves empty createTextNode hook data', () => {
33
+ expectCreateTextNodeHookData('', '');
34
+ });
35
+
36
+ it('preserves ordinary createTextNode hook data', () => {
37
+ expectCreateTextNodeHookData('Hello, world!', 'Hello, world!');
38
+ });