Repository: capricorn86/happy-dom (ts)

Issue:
Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.
**Describe the bug**
When I load an ant-design-vue InputNumber component using Vitest and Vue Test Utils, I report Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present. mistake

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
![image](https://user-images.githubusercontent.com/11454918/225186923-70b45e3c-f524-4fe2-9738-8acb1d5aa911.png)

**Device:**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version 8.9.0
**Additional context**
```ts
import Antd from "ant-design-vue";

import c from 'example.ts';

const wrapper = mount(c, {
     attachTo: document.body,
     props: mockProps,
     global: {
         plugins: [ Antd ]
      }
 });
```
my custom component:

example.ts

```vue
<template>
  <div>
    <a-input-number
      v-model:value="textValue"
      :size="inputConfig.inputSize"
      :placeholder="inputConfig.placeholder"
      :clearable="inputConfig.clearable"
      :disabled="inputConfig.disabled"
      :max="inputConfig.max"
      :min="inputConfig.min"
      :step="inputConfig.step"
      :controls="inputConfig.controls"
      :style="styleObj"
      @change="change"
    ></a-input-number>
  </div>
</template>
```
  


Pull request:
#827@patch: Distinguish between referenceNode values that are undefin…
…ed and not passed.

fix #827 

Patch:
diff --git a/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts b/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
index 83f65a3f..0bea54e8 100644
--- a/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
+++ b/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
@@ -171,6 +171,12 @@ export default class DocumentFragment extends Node implements IDocumentFragment
 	 * @override
 	 */
 	public override insertBefore(newNode: INode, referenceNode: INode | null): INode {
+		if (arguments.length < 2) {
+			throw new TypeError(
+				`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
+			);
+		}
+
 		// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
 		return ElementUtility.insertBefore(this, newNode, referenceNode);
 	}
diff --git a/packages/happy-dom/src/nodes/document/Document.ts b/packages/happy-dom/src/nodes/document/Document.ts
index 38fd0e9e..9ce6205f 100644
--- a/packages/happy-dom/src/nodes/document/Document.ts
+++ b/packages/happy-dom/src/nodes/document/Document.ts
@@ -637,6 +637,12 @@ export default class Document extends Node implements IDocument {
 	 * @override
 	 */
 	public override insertBefore(newNode: INode, referenceNode: INode | null): INode {
+		if (arguments.length < 2) {
+			throw new TypeError(
+				`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
+			);
+		}
+
 		// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
 		return ElementUtility.insertBefore(this, newNode, referenceNode);
 	}
diff --git a/packages/happy-dom/src/nodes/element/Element.ts b/packages/happy-dom/src/nodes/element/Element.ts
index eba3f463..8a34c7ea 100644
--- a/packages/happy-dom/src/nodes/element/Element.ts
+++ b/packages/happy-dom/src/nodes/element/Element.ts
@@ -386,6 +386,12 @@ export default class Element extends Node implements IElement {
 	 * @override
 	 */
 	public override insertBefore(newNode: INode, referenceNode: INode | null): INode {
+		if (arguments.length < 2) {
+			throw new TypeError(
+				`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
+			);
+		}
+
 		// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
 		return ElementUtility.insertBefore(this, newNode, referenceNode);
 	}
diff --git a/packages/happy-dom/src/nodes/element/ElementUtility.ts b/packages/happy-dom/src/nodes/element/ElementUtility.ts
index e023b34e..5294c8ab 100644
--- a/packages/happy-dom/src/nodes/element/ElementUtility.ts
+++ b/packages/happy-dom/src/nodes/element/ElementUtility.ts
@@ -123,7 +123,8 @@ export default class ElementUtility {
 		referenceNode: INode | null,
 		options?: { disableAncestorValidation?: boolean }
 	): INode {
-		if (newNode.nodeType === NodeTypeEnum.elementNode) {
+		// NodeUtility.insertBefore() will call appendChild() for the scenario where "referenceNode" is "null" or "undefined"
+		if (newNode.nodeType === NodeTypeEnum.elementNode && referenceNode) {
 			if (
 				!options?.disableAncestorValidation &&
 				NodeUtility.isInclusiveAncestor(newNode, ancestorNode)
@@ -152,36 +153,30 @@ export default class ElementUtility {
 				}
 			}
 
-			// Node.ts will call appendChild() for the scenario where "referenceNode" is "null"
+			if (referenceNode.nodeType === NodeTypeEnum.elementNode) {
+				const index = ancestorNode.children.indexOf(<IElement>referenceNode);
+				if (index !== -1) {
+					ancestorNode.children.splice(index, 0, <IElement>newNode);
+				}
+			} else {
+				ancestorNode.children.length = 0;
 
-			if (referenceNode) {
-				if (referenceNode.nodeType === NodeTypeEnum.elementNode) {
-					const index = ancestorNode.children.indexOf(<IElement>referenceNode);
-					if (index !== -1) {
-						ancestorNode.children.splice(index, 0, <IElement>newNode);
+				for (const node of ancestorNode.childNodes) {
+					if (node === referenceNode) {
+						ancestorNode.children.push(<IElement>newNode);
 					}
-				} else {
-					ancestorNode.children.length = 0;
-
-					for (const node of ancestorNode.childNodes) {
-						if (node === referenceNode) {
-							ancestorNode.children.push(<IElement>newNode);
-						}
-						if (node.nodeType === NodeTypeEnum.elementNode) {
-							ancestorNode.children.push(<IElement>node);
-						}
+					if (node.nodeType === NodeTypeEnum.elementNode) {
+						ancestorNode.children.push(<IElement>node);
 					}
 				}
 			}
 
-			if (referenceNode || referenceNode === null) {
-				for (const attribute of NAMED_ITEM_ATTRIBUTES) {
-					if ((<Element>newNode)._attributes[attribute]) {
-						(<HTMLCollection<IHTMLElement>>ancestorNode.children)._appendNamedItem(
-							<IHTMLElement>newNode,
-							(<Element>newNode)._attributes[attribute].value
-						);
-					}
+			for (const attribute of NAMED_ITEM_ATTRIBUTES) {
+				if ((<Element>newNode)._attributes[attribute]) {
+					(<HTMLCollection<IHTMLElement>>ancestorNode.children)._appendNamedItem(
+						<IHTMLElement>newNode,
+						(<Element>newNode)._attributes[attribute].value
+					);
 				}
 			}
 
diff --git a/packages/happy-dom/src/nodes/node/Node.ts b/packages/happy-dom/src/nodes/node/Node.ts
index 39b56813..b4594f59 100644
--- a/packages/happy-dom/src/nodes/node/Node.ts
+++ b/packages/happy-dom/src/nodes/node/Node.ts
@@ -322,6 +322,11 @@ export default class Node extends EventTarget implements INode {
 	 * @returns Inserted node.
 	 */
 	public insertBefore(newNode: INode, referenceNode: INode | null): INode {
+		if (arguments.length < 2) {
+			throw new TypeError(
+				`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
+			);
+		}
 		return NodeUtility.insertBefore(this, newNode, referenceNode);
 	}
 
diff --git a/packages/happy-dom/src/nodes/node/NodeUtility.ts b/packages/happy-dom/src/nodes/node/NodeUtility.ts
index 3e8a97ba..6af2daf5 100644
--- a/packages/happy-dom/src/nodes/node/NodeUtility.ts
+++ b/packages/happy-dom/src/nodes/node/NodeUtility.ts
@@ -164,18 +164,13 @@ export default class NodeUtility {
 			return newNode;
 		}
 
-		if (referenceNode === null) {
+		// If the referenceNode is null or undefined, then the newNode should be appended to the ancestorNode.
+		// According to spec only null is valid, but browsers support undefined as well.
+		if (!referenceNode) {
 			ancestorNode.appendChild(newNode);
 			return newNode;
 		}
 
-		if (!referenceNode) {
-			throw new DOMException(
-				"Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.",
-				'TypeError'
-			);
-		}
-
 		if (ancestorNode.childNodes.indexOf(referenceNode) === -1) {
 			throw new DOMException(
 				"Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node."


Test patch:
diff --git a/packages/happy-dom/test/nodes/node/Node.test.ts b/packages/happy-dom/test/nodes/node/Node.test.ts
index 8f503255..66ddc98b 100644
--- a/packages/happy-dom/test/nodes/node/Node.test.ts
+++ b/packages/happy-dom/test/nodes/node/Node.test.ts
@@ -547,6 +547,24 @@ describe('Node', () => {
 			);
 		});
 
+		it('If reference node is null or undefined, the newNode should be inserted at the end of the peer node.', () => {
+			const child1 = document.createElement('span');
+			const child2 = document.createElement('span');
+			const newNode = document.createElement('span');
+			const newNode1 = document.createElement('span');
+			const parent = document.createElement('div');
+
+			parent.appendChild(child1);
+			parent.appendChild(child2);
+			parent.insertBefore(newNode, null);
+			parent.insertBefore(newNode1, undefined);
+
+			expect(parent.childNodes[0]).toBe(child1);
+			expect(parent.childNodes[1]).toBe(child2);
+			expect(parent.childNodes[2]).toBe(newNode);
+			expect(parent.childNodes[3]).toBe(newNode1);
+		});
+
 		it('Throws an exception if reference node is not child of parent node.', () => {
 			const referenceNode = document.createElement('span');
 			const newNode = document.createElement('span');