Repository: capricorn86/happy-dom
Pull request #107 (2020-10-12 13:18:07)

#105@minor: Adds support for `getElementsByTagNameNS()`.


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index fc8dd8f4..d85adf44 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -49,6 +49,7 @@ jobs:
       - name: Checks if we have changed something
         run: npm run lerna-changed
       - run: git checkout -b release/${{ needs.check-next-version.outputs.next_version }} origin/master
+      - run: git pull origin master
       - run: git push origin release/${{ needs.check-next-version.outputs.next_version }}
       - run: git config --global user.name "${GITHUB_ACTOR}"
       - run: git config --global user.email "${{ needs.check-next-version.outputs.user_email }}"
diff --git a/packages/happy-dom/src/nodes/basic/document-fragment/DocumentFragment.ts b/packages/happy-dom/src/nodes/basic/document-fragment/DocumentFragment.ts
index 8f9d1b89..86c3ebf9 100644
--- a/packages/happy-dom/src/nodes/basic/document-fragment/DocumentFragment.ts
+++ b/packages/happy-dom/src/nodes/basic/document-fragment/DocumentFragment.ts
@@ -8,15 +8,7 @@ import QuerySelector from '../../../query-selector/QuerySelector';
 export default class DocumentFragment extends Node {
 	public nodeType = Node.DOCUMENT_FRAGMENT_NODE;
 	public mode = 'open';
-
-	/**
-	 * Returns children.
-	 *
-	 * @returns Children.
-	 */
-	public get children(): Element[] {
-		return <Element[]>this.childNodes.filter(childNode => childNode instanceof Element);
-	}
+	public children: Element[] = [];
 
 	/**
 	 * Query CSS selector to find matching nodes.
@@ -58,6 +50,17 @@ export default class DocumentFragment extends Node {
 		return this.querySelectorAll(tagName);
 	}
 
+	/**
+	 * Returns an elements by tag name.
+	 *
+	 * @param namespaceURI Namespace URI.
+	 * @param tagName Tag name.
+	 * @returns Matching nodes.
+	 */
+	public getElementsByTagNameNS(namespaceURI: string, tagName: string): Element[] {
+		return this.querySelectorAll(tagName).filter(element => element._namespaceURI === namespaceURI);
+	}
+
 	/**
 	 * Returns an elements by class name.
 	 *
@@ -78,8 +81,45 @@ export default class DocumentFragment extends Node {
 	public cloneNode(deep = false): Node {
 		const clone = <DocumentFragment>super.cloneNode(deep);
 
+		if (deep) {
+			clone.children = <Element[]>(
+				clone.childNodes.filter(node => node.nodeType === Node.ELEMENT_NODE)
+			);
+		}
 		clone.mode = this.mode;
 
 		return clone;
 	}
+
+	/**
+	 * Append a child node to childNodes.
+	 *
+	 * @override
+	 * @param  node Node to append.
+	 * @return Appended node.
+	 */
+	public appendChild(node: Node): Node {
+		if (node !== this && node instanceof Element) {
+			this.children.push(node);
+		}
+
+		return super.appendChild(node);
+	}
+
+	/**
+	 * Remove Child element from childNodes array.
+	 *
+	 * @override
+	 * @param node Node to remove
+	 */
+	public removeChild(node: Node): void {
+		if (node instanceof Element) {
+			const index = this.children.indexOf(node);
+			if (index !== -1) {
+				this.children.splice(index, 1);
+			}
+		}
+
+		super.removeChild(node);
+	}
 }
diff --git a/packages/happy-dom/src/nodes/basic/document/Document.ts b/packages/happy-dom/src/nodes/basic/document/Document.ts
index 8687c1d5..6261f8e0 100644
--- a/packages/happy-dom/src/nodes/basic/document/Document.ts
+++ b/packages/happy-dom/src/nodes/basic/document/Document.ts
@@ -191,7 +191,7 @@ export default class Document extends DocumentFragment {
 				: this.defaultView.customElements.get(qualifiedName);
 		const elementClass = customElementClass
 			? customElementClass
-			: this.getElementClass(qualifiedName);
+			: HTMLElementTag[qualifiedName] || HTMLElement;
 
 		elementClass.ownerDocument = this;
 
@@ -302,14 +302,4 @@ export default class Document extends DocumentFragment {
 		clone.ownerDocument = this;
 		return clone;
 	}
-
-	/**
-	 * Returns the element class for a tag name.
-	 *
-	 * @param tagName Tag name.
-	 * @returns Element class.
-	 */
-	private getElementClass(tagName: string): typeof Element {
-		return HTMLElementTag[tagName] || HTMLElement;
-	}
 }
diff --git a/packages/happy-dom/src/nodes/basic/element/Element.ts b/packages/happy-dom/src/nodes/basic/element/Element.ts
index ec9bf3c2..4bfeb23b 100644
--- a/packages/happy-dom/src/nodes/basic/element/Element.ts
+++ b/packages/happy-dom/src/nodes/basic/element/Element.ts
@@ -19,9 +19,10 @@ export default class Element extends Node {
 	public tagName: string = null;
 	public nodeType = Node.ELEMENT_NODE;
 	public shadowRoot: ShadowRoot = null;
-	public classList = new ClassList(this);
+	public readonly classList = new ClassList(this);
 	public scrollTop = 0;
 	public scrollLeft = 0;
+	public children: Element[] = [];
 	public _attributes: { [k: string]: Attr } = {};
 	public _namespaceURI: string = null;
 
@@ -72,15 +73,6 @@ export default class Element extends Node {
 		this.setAttribute('class', className);
 	}
 
-	/**
-	 * Returns children.
-	 *
-	 * @returns Children.
-	 */
-	public get children(): Element[] {
-		return <Element[]>this.childNodes.filter(childNode => childNode instanceof Element);
-	}
-
 	/**
 	 * Node name.
 	 *
@@ -194,7 +186,12 @@ export default class Element extends Node {
 			clone._attributes[key] = Object.assign(new Attr(), this._attributes[key]);
 		}
 
-		clone.classList = new ClassList(clone);
+		if (deep) {
+			clone.children = <Element[]>(
+				clone.childNodes.filter(node => node.nodeType === Node.ELEMENT_NODE)
+			);
+		}
+
 		clone.tagName = this.tagName;
 		clone.scrollLeft = this.scrollLeft;
 		clone.scrollTop = this.scrollTop;
@@ -203,6 +200,38 @@ export default class Element extends Node {
 		return clone;
 	}
 
+	/**
+	 * Append a child node to childNodes.
+	 *
+	 * @override
+	 * @param  node Node to append.
+	 * @return Appended node.
+	 */
+	public appendChild(node: Node): Node {
+		if (node !== this && node instanceof Element) {
+			this.children.push(node);
+		}
+
+		return super.appendChild(node);
+	}
+
+	/**
+	 * Remove Child element from childNodes array.
+	 *
+	 * @override
+	 * @param node Node to remove
+	 */
+	public removeChild(node: Node): void {
+		if (node instanceof Element) {
+			const index = this.children.indexOf(node);
+			if (index !== -1) {
+				this.children.splice(index, 1);
+			}
+		}
+
+		super.removeChild(node);
+	}
+
 	/**
 	 * Sets an attribute.
 	 *
@@ -400,6 +429,17 @@ export default class Element extends Node {
 		return this.querySelectorAll(tagName);
 	}
 
+	/**
+	 * Returns an elements by tag name.
+	 *
+	 * @param namespaceURI Namespace URI.
+	 * @param tagName Tag name.
+	 * @returns Matching nodes.
+	 */
+	public getElementsByTagNameNS(namespaceURI: string, tagName: string): Element[] {
+		return this.querySelectorAll(tagName).filter(element => element._namespaceURI === namespaceURI);
+	}
+
 	/**
 	 * Returns an elements by class name.
 	 *
diff --git a/packages/happy-dom/src/nodes/basic/node/Node.ts b/packages/happy-dom/src/nodes/basic/node/Node.ts
index 731e2f46..27f836bb 100644
--- a/packages/happy-dom/src/nodes/basic/node/Node.ts
+++ b/packages/happy-dom/src/nodes/basic/node/Node.ts
@@ -11,12 +11,12 @@ import HTMLParser from '../../../html-parser/HTMLParser';
  */
 export default class Node extends EventTarget {
 	// Public properties
-	public static ELEMENT_NODE = 1;
-	public static TEXT_NODE = 3;
-	public static COMMENT_NODE = 8;
-	public static DOCUMENT_NODE = 9;
-	public static DOCUMENT_TYPE_NODE = 10;
-	public static DOCUMENT_FRAGMENT_NODE = 11;
+	public static readonly ELEMENT_NODE = 1;
+	public static readonly TEXT_NODE = 3;
+	public static readonly COMMENT_NODE = 8;
+	public static readonly DOCUMENT_NODE = 9;
+	public static readonly DOCUMENT_TYPE_NODE = 10;
+	public static readonly DOCUMENT_FRAGMENT_NODE = 11;
 	public static ownerDocument: Document = null;
 	public ownerDocument: Document = null;
 	public parentNode: Node = null;
@@ -177,12 +177,7 @@ export default class Node extends EventTarget {
 	 * @return {Node} Node.
 	 */
 	public get firstElementChild(): Node {
-		for (const node of this.childNodes) {
-			if (node.nodeType === Node.ELEMENT_NODE) {
-				return node;
-			}
-		}
-		return null;
+		return this['children'] ? this['children'][0] || null : null;
 	}
 
 	/**
@@ -191,12 +186,7 @@ export default class Node extends EventTarget {
 	 * @return {Node} Node.
 	 */
 	public get lastElementChild(): Node {
-		for (let i = this.childNodes.length - 1; i >= 0; i--) {
-			if (this.childNodes[i].nodeType === Node.ELEMENT_NODE) {
-				return this.childNodes[i];
-			}
-		}
-		return null;
+		return this['children'] ? this['children'][this['children'].length - 1] || null : null;
 	}
 
 	/**
@@ -226,6 +216,8 @@ export default class Node extends EventTarget {
 			}
 		}
 
+		clone.ownerDocument = this.ownerDocument;
+
 		return clone;
 	}
 
diff --git a/packages/happy-dom/src/nodes/elements/text-area/HTMLTextAreaElement.ts b/packages/happy-dom/src/nodes/elements/text-area/HTMLTextAreaElement.ts
index 08dc14e0..81f6cf4e 100644
--- a/packages/happy-dom/src/nodes/elements/text-area/HTMLTextAreaElement.ts
+++ b/packages/happy-dom/src/nodes/elements/text-area/HTMLTextAreaElement.ts
@@ -175,11 +175,9 @@ export default class HTMLTextAreaElement extends HTMLElement {
 				this.selectionEnd = !!attribute.value ? Number(attribute.value) : 0;
 				break;
 			case 'minlength': // number
-				debugger;
 				this.minLength = !!attribute.value ? Number(attribute.value) : -1;
 				break;
 			case 'maxlength': // number
-				debugger;
 				this.maxLength = !!attribute.value ? Number(attribute.value) : -1;
 				break;
 			case 'name': // string
diff --git a/packages/happy-dom/src/query-selector/QuerySelector.ts b/packages/happy-dom/src/query-selector/QuerySelector.ts
index d562b526..1d9e769c 100644
--- a/packages/happy-dom/src/query-selector/QuerySelector.ts
+++ b/packages/happy-dom/src/query-selector/QuerySelector.ts
@@ -1,5 +1,4 @@
 import Element from '../nodes/basic/element/Element';
-import Node from '../nodes/basic/node/Node';
 import DocumentFragment from '../nodes/basic/document-fragment/DocumentFragment';
 import SelectorItem from './SelectorItem';
 
@@ -21,7 +20,7 @@ export default class QuerySelector {
 	 * @param selector Selector.
 	 * @returns HTML elements.
 	 */
-	public static querySelectorAll(node: Node, selector: string): Element[] {
+	public static querySelectorAll(node: Element | DocumentFragment, selector: string): Element[] {
 		const matched = [];
 
 		for (const parts of this.getSelectorParts(selector)) {
@@ -42,7 +41,7 @@ export default class QuerySelector {
 	 * @param selector Selector.
 	 * @return HTML element.
 	 */
-	public static querySelector(node: Node, selector: string): Element {
+	public static querySelector(node: Element | DocumentFragment, selector: string): Element {
 		for (const parts of this.getSelectorParts(selector)) {
 			const match = this.findFirst(node, [node], parts);
 
@@ -64,8 +63,8 @@ export default class QuerySelector {
 	 * @returns HTML elements.
 	 */
 	private static findAll(
-		rootNode: Node,
-		nodes: Node[],
+		rootNode: Element | DocumentFragment,
+		nodes: (Element | DocumentFragment)[],
 		selectorParts: string[],
 		selectorItem?: SelectorItem
 	): Element[] {
@@ -77,26 +76,22 @@ export default class QuerySelector {
 		let matched = [];
 
 		for (const node of nodes) {
-			if (node instanceof Element || node instanceof DocumentFragment) {
-				if (node instanceof Element) {
-					if (selector.match(node)) {
-						if (selectorParts.length === 1) {
-							if (rootNode !== node) {
-								matched.push(node);
-							}
-						} else {
-							matched = matched.concat(
-								this.findAll(rootNode, node.childNodes, selectorParts.slice(1), null)
-							);
+			if (node instanceof Element) {
+				if (selector.match(node)) {
+					if (selectorParts.length === 1) {
+						if (rootNode !== node) {
+							matched.push(node);
 						}
+					} else {
+						matched = matched.concat(
+							this.findAll(rootNode, node.children, selectorParts.slice(1), null)
+						);
 					}
 				}
+			}
 
-				if (!isDirectChild) {
-					matched = matched.concat(
-						this.findAll(rootNode, node.childNodes, selectorParts, selector)
-					);
-				}
+			if (!isDirectChild) {
+				matched = matched.concat(this.findAll(rootNode, node.children, selectorParts, selector));
 			}
 		}
 
@@ -112,8 +107,8 @@ export default class QuerySelector {
 	 * @return HTML element.
 	 */
 	private static findFirst(
-		rootNode: Node,
-		nodes: Node[],
+		rootNode: Element | DocumentFragment,
+		nodes: (Element | DocumentFragment)[],
 		selectorParts: string[],
 		selectorItem?: SelectorItem
 	): Element {
@@ -124,33 +119,31 @@ export default class QuerySelector {
 		const selector = selectorItem || new SelectorItem(selectorParts[0]);
 
 		for (const node of nodes) {
-			if (node instanceof Element || node instanceof DocumentFragment) {
-				if (node instanceof Element && selector.match(node)) {
-					if (selectorParts.length === 1) {
-						if (rootNode !== node) {
-							return node;
-						}
-					} else {
-						const childSelector = this.findFirst(
-							rootNode,
-							node.childNodes,
-							selectorParts.slice(1),
-							null
-						);
-						if (childSelector) {
-							return childSelector;
-						}
+			if (node instanceof Element && selector.match(node)) {
+				if (selectorParts.length === 1) {
+					if (rootNode !== node) {
+						return node;
 					}
-				}
-
-				if (!isDirectChild) {
-					const childSelector = this.findFirst(rootNode, node.childNodes, selectorParts, selector);
-
+				} else {
+					const childSelector = this.findFirst(
+						rootNode,
+						node.children,
+						selectorParts.slice(1),
+						null
+					);
 					if (childSelector) {
 						return childSelector;
 					}
 				}
 			}
+
+			if (!isDirectChild) {
+				const childSelector = this.findFirst(rootNode, node.children, selectorParts, selector);
+
+				if (childSelector) {
+					return childSelector;
+				}
+			}
 		}
 
 		return null;
diff --git a/packages/happy-dom/src/query-selector/SelectorItem.ts b/packages/happy-dom/src/query-selector/SelectorItem.ts
index 86227900..213964a2 100644
--- a/packages/happy-dom/src/query-selector/SelectorItem.ts
+++ b/packages/happy-dom/src/query-selector/SelectorItem.ts
@@ -183,24 +183,24 @@ export default class SelectorItem {
 				const onlyChildChildren = parent.children;
 				return onlyChildChildren.length === 1 && onlyChildChildren[0] === element;
 			case 'first-of-type':
-				for (const child of parent.childNodes) {
-					if (child instanceof Element && child.tagName === element.tagName) {
+				for (const child of parent.children) {
+					if (child.tagName === element.tagName) {
 						return child === element;
 					}
 				}
 				return false;
 			case 'last-of-type':
-				for (let i = parent.childNodes.length - 1; i >= 0; i--) {
-					const child = parent.childNodes[i];
-					if (child instanceof Element && child.tagName === element.tagName) {
+				for (let i = parent.children.length - 1; i >= 0; i--) {
+					const child = parent.children[i];
+					if (child.tagName === element.tagName) {
 						return child === element;
 					}
 				}
 				return false;
 			case 'only-of-type':
 				let isFound = false;
-				for (const child of parent.childNodes) {
-					if (child instanceof Element && child.tagName === element.tagName) {
+				for (const child of parent.children) {
+					if (child.tagName === element.tagName) {
 						if (isFound || child !== element) {
 							return false;
 						}


Test patch:
diff --git a/packages/happy-dom/test/nodes/basic/document-fragment/DocumentFragment.test.ts b/packages/happy-dom/test/nodes/basic/document-fragment/DocumentFragment.test.ts
index b862c777..53709632 100644
--- a/packages/happy-dom/test/nodes/basic/document-fragment/DocumentFragment.test.ts
+++ b/packages/happy-dom/test/nodes/basic/document-fragment/DocumentFragment.test.ts
@@ -1,4 +1,6 @@
+import NamespaceURI from '../../../../src/html-config/NamespaceURI';
 import Window from '../../../../src/window/Window';
+import Node from '../../../../src/nodes/basic/node/Node';
 
 describe('DocumentFragment', () => {
 	let window, document, documentFragment;
@@ -64,14 +66,40 @@ describe('DocumentFragment', () => {
 	});
 
 	describe('getElementsByTagName()', () => {
-		test('Returns elements by tag name.', () => {
-			const div = document.createElement('div');
+		test('Returns elements matching a tag name.', () => {
+			const div1 = document.createElement('div');
 			const div2 = document.createElement('div');
+			const div3 = document.createElement('div');
+			const textNode = document.createTextNode('text');
 
-			div.appendChild(div2);
-			documentFragment.appendChild(div);
+			documentFragment.appendChild(div1);
 
-			expect(documentFragment.getElementsByTagName('div')).toEqual([div, div2]);
+			div1.appendChild(div2);
+			div2.appendChild(div3);
+			div3.appendChild(textNode);
+
+			expect(documentFragment.getElementsByTagName('div')).toEqual([div1, div2, div3]);
+		});
+	});
+
+	describe('getElementsByTagNameNS()', () => {
+		test('Returns elements matching a tag name and namespace.', () => {
+			const div1 = document.createElementNS(NamespaceURI.svg, 'div');
+			const div2 = document.createElement('div');
+			const div3 = document.createElementNS(NamespaceURI.svg, 'div');
+			const textNode = document.createTextNode('text');
+
+			documentFragment.appendChild(div1);
+
+			div1.appendChild(div2);
+			div2.appendChild(div3);
+			div3.appendChild(textNode);
+
+			expect(documentFragment.getElementsByTagNameNS(NamespaceURI.svg, 'div')).toEqual([
+				div1,
+				div3
+			]);
+			expect(documentFragment.getElementsByTagNameNS(NamespaceURI.svg, '*')).toEqual([div1, div3]);
 		});
 	});
 
@@ -88,4 +116,39 @@ describe('DocumentFragment', () => {
 			expect(documentFragment.getElementsByClassName('div')).toEqual([div, div2]);
 		});
 	});
+
+	describe('cloneNode', () => {
+		test('Makes a shallow clone of a node (default behaviour).', () => {
+			const text = document.createTextNode('test');
+			const div = document.createElement('div');
+			const comment = document.createComment('test');
+
+			documentFragment.appendChild(text);
+			documentFragment.appendChild(div);
+			documentFragment.appendChild(comment);
+
+			const clone = documentFragment.cloneNode(false);
+
+			expect(clone).toEqual({ ...documentFragment, childNodes: [], children: [] });
+			expect(documentFragment !== clone).toBe(true);
+		});
+
+		test('Makes a deep clone of the document fragment.', () => {
+			const text = document.createTextNode('test');
+			const div = document.createElement('div');
+			const comment = document.createComment('test');
+
+			documentFragment.appendChild(text);
+			documentFragment.appendChild(div);
+			documentFragment.appendChild(comment);
+
+			const clone = documentFragment.cloneNode(true);
+
+			expect(clone).toEqual(documentFragment);
+			expect(clone.childNodes.length).toBe(3);
+			expect(clone.children).toEqual(
+				clone.childNodes.filter(node => node.nodeType === Node.ELEMENT_NODE)
+			);
+		});
+	});
 });
diff --git a/packages/happy-dom/test/nodes/basic/element/Element.test.ts b/packages/happy-dom/test/nodes/basic/element/Element.test.ts
index 16629289..1b739ba3 100644
--- a/packages/happy-dom/test/nodes/basic/element/Element.test.ts
+++ b/packages/happy-dom/test/nodes/basic/element/Element.test.ts
@@ -29,6 +29,18 @@ describe('Element', () => {
 		jest.restoreAllMocks();
 	});
 
+	describe('children', () => {
+		test('Returns nodes of type Element.', () => {
+			const div1 = document.createElement('div');
+			const div2 = document.createElement('div');
+			const textNode = document.createTextNode('text');
+			element.appendChild(div1);
+			element.appendChild(textNode);
+			element.appendChild(div2);
+			expect(element.children).toEqual([div1, div2]);
+		});
+	});
+
 	describe('get id()', () => {
 		test('Returns the element "id" attribute.', () => {
 			element.setAttribute('id', 'id');
@@ -57,18 +69,6 @@ describe('Element', () => {
 		});
 	});
 
-	describe('get children()', () => {
-		test('Returns nodes of type Element.', () => {
-			const div1 = document.createElement('div');
-			const div2 = document.createElement('div');
-			const textNode = document.createTextNode('text');
-			element.appendChild(div1);
-			element.appendChild(textNode);
-			element.appendChild(div2);
-			expect(element.children).toEqual([div1, div2]);
-		});
-	});
-
 	describe('get namespaceURI()', () => {
 		test('Returns the "namespaceURI" property of the element.', () => {
 			expect(element.namespaceURI).toEqual(NamespaceURI.html);
@@ -368,16 +368,36 @@ describe('Element', () => {
 
 	describe('getElementsByTagName()', () => {
 		test('Returns elements matching a tag name.', () => {
-			const tagName = 'div';
-			const result = [];
-			jest
-				.spyOn(QuerySelector, 'querySelectorAll')
-				.mockImplementation((targetElement, targetSelector) => {
-					expect(targetElement).toBe(element);
-					expect(targetSelector).toBe(tagName);
-					return result;
-				});
-			expect(element.getElementsByTagName(tagName)).toBe(result);
+			const div1 = document.createElement('div');
+			const div2 = document.createElement('div');
+			const div3 = document.createElement('div');
+			const textNode = document.createTextNode('text');
+
+			element.appendChild(div1);
+
+			div1.appendChild(div2);
+			div2.appendChild(div3);
+			div3.appendChild(textNode);
+
+			expect(element.getElementsByTagName('div')).toEqual([div1, div2, div3]);
+		});
+	});
+
+	describe('getElementsByTagNameNS()', () => {
+		test('Returns elements matching a tag name and namespace.', () => {
+			const div1 = document.createElementNS(NamespaceURI.svg, 'div');
+			const div2 = document.createElement('div');
+			const div3 = document.createElementNS(NamespaceURI.svg, 'div');
+			const textNode = document.createTextNode('text');
+
+			element.appendChild(div1);
+
+			div1.appendChild(div2);
+			div2.appendChild(div3);
+			div3.appendChild(textNode);
+
+			expect(element.getElementsByTagNameNS(NamespaceURI.svg, 'div')).toEqual([div1, div3]);
+			expect(element.getElementsByTagNameNS(NamespaceURI.svg, '*')).toEqual([div1, div3]);
 		});
 	});
 
diff --git a/packages/happy-dom/test/nodes/basic/node/Node.test.ts b/packages/happy-dom/test/nodes/basic/node/Node.test.ts
index fbe60cf1..95648e5a 100644
--- a/packages/happy-dom/test/nodes/basic/node/Node.test.ts
+++ b/packages/happy-dom/test/nodes/basic/node/Node.test.ts
@@ -233,13 +233,19 @@ describe('Node', () => {
 			const span = document.createElement('span');
 			const text = document.createTextNode('text');
 			const comment = document.createComment('comment');
+
 			div.appendChild(span);
 			span.appendChild(text);
 			span.appendChild(comment);
+
 			document.body.appendChild(div);
+
 			const clone = div.cloneNode();
+
 			document.body.removeChild(div);
+
 			div.removeChild(span);
+
 			expect(div).toEqual(clone);
 			expect(div !== clone).toBe(true);
 		});
@@ -249,14 +255,23 @@ describe('Node', () => {
 			const span = document.createElement('span');
 			const text = document.createTextNode('text');
 			const comment = document.createComment('comment');
+
 			div.appendChild(span);
 			span.appendChild(text);
 			span.appendChild(comment);
+
 			document.body.appendChild(div);
+
 			const clone = div.cloneNode(true);
+
 			document.body.removeChild(div);
+
 			expect(div).toEqual(clone);
 			expect(div !== clone).toBe(true);
+
+			expect(clone.children).toEqual(
+				clone.childNodes.filter(node => node.nodeType === Node.ELEMENT_NODE)
+			);
 		});
 	});