Repository: capricorn86/happy-dom
Pull request #326 (2022-01-12 23:32:32)

#325@patch: Fixes issue related to warning when unmounting a node usi…
…ng ReactDOM.unmountComponentAtNode().


Patch:
diff --git a/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts b/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
index 28e82b62..97a03eaa 100644
--- a/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
+++ b/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
@@ -66,7 +66,9 @@ export default class DocumentFragment extends Node implements IDocumentFragment
 		for (const child of this.childNodes.slice()) {
 			this.removeChild(child);
 		}
-		this.appendChild(this.ownerDocument.createTextNode(textContent));
+		if (textContent) {
+			this.appendChild(this.ownerDocument.createTextNode(textContent));
+		}
 	}
 
 	/**
diff --git a/packages/happy-dom/src/nodes/element/Element.ts b/packages/happy-dom/src/nodes/element/Element.ts
index 000a67e7..7c0d6f15 100644
--- a/packages/happy-dom/src/nodes/element/Element.ts
+++ b/packages/happy-dom/src/nodes/element/Element.ts
@@ -139,7 +139,9 @@ export default class Element extends Node implements IElement {
 		for (const child of this.childNodes.slice()) {
 			this.removeChild(child);
 		}
-		this.appendChild(this.ownerDocument.createTextNode(textContent));
+		if (textContent) {
+			this.appendChild(this.ownerDocument.createTextNode(textContent));
+		}
 	}
 
 	/**
@@ -454,6 +456,9 @@ export default class Element extends Node implements IElement {
 	 * @param text String to insert.
 	 */
 	public insertAdjacentText(position: TInsertAdjacentPositions, text: string): void {
+		if (!text) {
+			return;
+		}
 		const textNode = <IText>this.ownerDocument.createTextNode(text);
 		this.insertAdjacentElement(position, textNode);
 	}
diff --git a/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts b/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts
index 012344fb..27a19485 100644
--- a/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts
+++ b/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts
@@ -11,5 +11,5 @@ export default interface ISVGElement extends IElement {
 	readonly viewportElement: ISVGElement;
 	readonly ownerSVGElement: ISVGSVGElement;
 	readonly dataset: { [key: string]: string };
-	style: CSSStyleDeclaration;
+	readonly style: CSSStyleDeclaration;
 }
diff --git a/packages/happy-dom/src/nodes/svg-element/SVGElement.ts b/packages/happy-dom/src/nodes/svg-element/SVGElement.ts
index b75a44cf..483beb64 100644
--- a/packages/happy-dom/src/nodes/svg-element/SVGElement.ts
+++ b/packages/happy-dom/src/nodes/svg-element/SVGElement.ts
@@ -94,5 +94,4 @@ export default class SVGElement extends Element implements ISVGElement {
 			this._style.cssText = '';
 		}
 	}
-
 }
diff --git a/packages/happy-dom/src/xml-parser/XMLParser.ts b/packages/happy-dom/src/xml-parser/XMLParser.ts
index 5cff5648..f0ea4394 100755
--- a/packages/happy-dom/src/xml-parser/XMLParser.ts
+++ b/packages/happy-dom/src/xml-parser/XMLParser.ts
@@ -145,7 +145,7 @@ export default class XMLParser {
 		let match;
 
 		while ((match = commentRegExp.exec(text))) {
-			if (match.index > 0) {
+			if (match.index > 0 && lastIndex !== match.index) {
 				const textNode = document.createTextNode(text.substring(lastIndex, match.index));
 				nodes.push(textNode);
 			}


Test patch:
diff --git a/packages/happy-dom/test/nodes/document-fragment/DocumentFragment.test.ts b/packages/happy-dom/test/nodes/document-fragment/DocumentFragment.test.ts
index a115ab36..cde91271 100644
--- a/packages/happy-dom/test/nodes/document-fragment/DocumentFragment.test.ts
+++ b/packages/happy-dom/test/nodes/document-fragment/DocumentFragment.test.ts
@@ -98,6 +98,20 @@ describe('DocumentFragment', () => {
 			expect(documentFragment.childNodes.length).toBe(1);
 			expect((<Text>documentFragment.childNodes[0]).textContent).toBe('new_text');
 		});
+
+		it('Removes all child nodes if textContent is set to empty string.', () => {
+			const div = document.createElement('div');
+			const textNode1 = document.createTextNode('text1');
+			const textNode2 = document.createTextNode('text2');
+
+			documentFragment.appendChild(div);
+			documentFragment.appendChild(textNode1);
+			documentFragment.appendChild(textNode2);
+
+			documentFragment.textContent = '';
+
+			expect(documentFragment.childNodes.length).toBe(0);
+		});
 	});
 
 	describe('append()', () => {
diff --git a/packages/happy-dom/test/nodes/document/Document.test.ts b/packages/happy-dom/test/nodes/document/Document.test.ts
index 028606f6..515d5275 100644
--- a/packages/happy-dom/test/nodes/document/Document.test.ts
+++ b/packages/happy-dom/test/nodes/document/Document.test.ts
@@ -25,6 +25,7 @@ import IResponse from '../../../src/window/IResponse';
 import ResourceFetcher from '../../../src/fetch/ResourceFetcher';
 import IHTMLScriptElement from '../../../src/nodes/html-script-element/IHTMLScriptElement';
 import DocumentReadyStateEnum from '../../../src/nodes/document/DocumentReadyStateEnum';
+import { ISVGElement } from '../../../src';
 
 describe('Document', () => {
 	let window: Window;
@@ -673,9 +674,9 @@ describe('Document', () => {
 		});
 
 		it('Creates a custom element with namespace set to SVG and can set the style.', () => {
-			const svg = document.createElementNS(NamespaceURI.svg, 'svg');
+			const svg = <ISVGElement>document.createElementNS(NamespaceURI.svg, 'svg');
 			svg.style.cssText = 'user-select:none;';
-			expect(svg.style.cssText).toBe('user-select:none;');
+			expect(svg.style.cssText).toBe('user-select: none;');
 		});
 	});
 
diff --git a/packages/happy-dom/test/nodes/element/Element.test.ts b/packages/happy-dom/test/nodes/element/Element.test.ts
index bfe9cdcf..a5c23cd4 100644
--- a/packages/happy-dom/test/nodes/element/Element.test.ts
+++ b/packages/happy-dom/test/nodes/element/Element.test.ts
@@ -123,6 +123,20 @@ describe('Element', () => {
 			expect(element.childNodes.length).toBe(1);
 			expect((<Text>element.childNodes[0]).textContent).toBe('new_text');
 		});
+
+		it('Removes all child nodes if textContent is set to empty string.', () => {
+			const div = document.createElement('div');
+			const textNode1 = document.createTextNode('text1');
+			const textNode2 = document.createTextNode('text2');
+
+			element.appendChild(div);
+			element.appendChild(textNode1);
+			element.appendChild(textNode2);
+
+			element.textContent = '';
+
+			expect(element.childNodes.length).toBe(0);
+		});
 	});
 
 	describe('get innerHTML()', () => {
@@ -580,6 +594,19 @@ describe('Element', () => {
 			expect(document.body.childNodes[1].textContent).toEqual(text);
 			expect(document.body.childNodes[2]).toBe(sibling);
 		});
+
+		it('Does nothing is an emptry string is sent.', () => {
+			const parent = document.createElement('div');
+			const sibling = document.createElement('div');
+
+			document.body.appendChild(parent);
+			document.body.appendChild(sibling);
+			parent.insertAdjacentText('afterend', '');
+
+			expect(parent.childNodes).toEqual([]);
+			expect(document.body.childNodes[0]).toBe(parent);
+			expect(document.body.childNodes[1]).toBe(sibling);
+		});
 	});
 
 	describe('replaceChildren()', () => {
diff --git a/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts b/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
index 4adceafc..b6cc0e7b 100644
--- a/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
+++ b/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
@@ -192,7 +192,7 @@ describe('SVGSVGElement', () => {
 
 	describe('get style()', () => {
 		it('Returns styles.', () => {
-            element.setAttribute('style', 'border-radius: 2px; padding: 2px;');
+			element.setAttribute('style', 'border-radius: 2px; padding: 2px;');
 			expect(element.style.length).toEqual(2);
 			expect(element.style[0]).toEqual('border-radius');
 			expect(element.style[1]).toEqual('padding');