Repository: capricorn86/happy-dom
Pull request #377 (2022-02-22 16:26:46)

#374@patch: HTMLElement.textContent should not return the content of …
…script and style elements.


Patch:
diff --git a/packages/happy-dom/src/nodes/html-element/HTMLElement.ts b/packages/happy-dom/src/nodes/html-element/HTMLElement.ts
index 1ed1a9a9..a483558e 100644
--- a/packages/happy-dom/src/nodes/html-element/HTMLElement.ts
+++ b/packages/happy-dom/src/nodes/html-element/HTMLElement.ts
@@ -4,6 +4,7 @@ import CSSStyleDeclaration from '../../css/CSSStyleDeclaration';
 import Attr from '../../attribute/Attr';
 import FocusEvent from '../../event/events/FocusEvent';
 import PointerEvent from '../../event/events/PointerEvent';
+import Node from '../node/Node';
 
 /**
  * HTML Element.
@@ -49,21 +50,34 @@ export default class HTMLElement extends Element implements IHTMLElement {
 	}
 
 	/**
-	 * Returns inner text.
+	 * Returns inner text, which is the rendered appearance of text.
 	 *
-	 * @returns Text.
+	 * @returns Inner text.
 	 */
 	public get innerText(): string {
-		return this.textContent;
+		let result = '';
+		for (const childNode of this.childNodes) {
+			if (childNode instanceof HTMLElement) {
+				if (childNode.tagName !== 'SCRIPT' && childNode.tagName !== 'STYLE') {
+					result += childNode.innerText;
+				}
+			} else if (
+				childNode.nodeType === Node.ELEMENT_NODE ||
+				childNode.nodeType === Node.TEXT_NODE
+			) {
+				result += childNode.textContent;
+			}
+		}
+		return result;
 	}
 
 	/**
-	 * Sets inner text.
+	 * Sets the inner text, which is the rendered appearance of text.
 	 *
-	 * @param text Text.
+	 * @param innerText Inner text.
 	 */
-	public set innerText(text: string) {
-		this.textContent = text;
+	public set innerText(innerText: string) {
+		this.textContent = innerText;
 	}
 
 	/**
diff --git a/packages/happy-dom/src/xml-parser/XMLParser.ts b/packages/happy-dom/src/xml-parser/XMLParser.ts
index 9b278bd2..aeebf8db 100755
--- a/packages/happy-dom/src/xml-parser/XMLParser.ts
+++ b/packages/happy-dom/src/xml-parser/XMLParser.ts
@@ -37,7 +37,6 @@ export default class XMLParser {
 		let parentUnnestableTagName = null;
 		let lastTextIndex = 0;
 		let match: RegExpExecArray;
-		let childLessIndex = 0;
 
 		while ((match = markupRegexp.exec(data))) {
 			const tagName = match[2].toLowerCase();
@@ -88,15 +87,18 @@ export default class XMLParser {
 				} else {
 					parent.appendChild(newElement);
 				}
-				lastTextIndex = childLessIndex = markupRegexp.lastIndex;
+				lastTextIndex = markupRegexp.lastIndex;
 
 				// Tags which contain non-parsed content
 				// For example: <script> JavaScript should not be parsed
 				if (ChildLessElements.includes(tagName)) {
-					while (markupRegexp.exec(data)[2].toLowerCase() != tagName) {
-						childLessIndex = markupRegexp.lastIndex;
+					let childLessMatch = null;
+					while ((childLessMatch = markupRegexp.exec(data))) {
+						if (childLessMatch[2] === match[2] && childLessMatch[1]) {
+							markupRegexp.lastIndex -= childLessMatch[0].length;
+							break;
+						}
 					}
-					markupRegexp.lastIndex = childLessIndex;
 				}
 			} else {
 				stack.pop();


Test patch:
diff --git a/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts b/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
index 757a44f5..1931b0c3 100644
--- a/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
@@ -83,13 +83,16 @@ describe('HTMLElement', () => {
 	describe('get innerText()', () => {
 		it('Returns the as the textContent property.', () => {
 			const div = document.createElement('div');
-			const textNode1 = document.createTextNode('text1');
-			const textNode2 = document.createTextNode('text2');
+			const script = document.createElement('script');
+			const style = document.createElement('script');
 			element.appendChild(div);
-			element.appendChild(textNode2);
-			div.appendChild(textNode1);
+			element.appendChild(script);
+			element.appendChild(style);
+			element.appendChild(document.createTextNode('text2'));
+			div.appendChild(document.createTextNode('text1'));
+			script.appendChild(document.createTextNode('var key = "value";'));
+			style.appendChild(document.createTextNode('button { background: red; }'));
 			expect(element.innerText).toBe('text1text2');
-			expect(element.innerText).toBe(element.textContent);
 		});
 	});
 
diff --git a/packages/happy-dom/test/xml-parser/XMLParser.test.ts b/packages/happy-dom/test/xml-parser/XMLParser.test.ts
index 0eb52499..d2761307 100644
--- a/packages/happy-dom/test/xml-parser/XMLParser.test.ts
+++ b/packages/happy-dom/test/xml-parser/XMLParser.test.ts
@@ -1,7 +1,8 @@
 import XMLParser from '../../src/xml-parser/XMLParser';
 import Window from '../../src/window/Window';
 import Document from '../../src/nodes/document/Document';
-import HTMLElement from '../../src/nodes/html-element/HTMLElement';
+import IHTMLElement from '../../src/nodes/html-element/IHTMLElement';
+import IHTMLTemplateElement from '../../src/nodes/html-template-element/IHTMLTemplateElement';
 import XMLParserHTML from './data/XMLParserHTML';
 import NamespaceURI from '../../src/config/NamespaceURI';
 import DocumentType from '../../src/nodes/document-type/DocumentType';
@@ -28,7 +29,7 @@ describe('XMLParser', () => {
 			const root = XMLParser.parse(window.document, '<div></div>');
 			expect(root.childNodes.length).toBe(1);
 			expect(root.childNodes[0].childNodes.length).toBe(0);
-			expect((<HTMLElement>root.childNodes[0]).tagName).toBe('DIV');
+			expect((<IHTMLElement>root.childNodes[0]).tagName).toBe('DIV');
 		});
 
 		it('Parses HTML with a single <div> with attributes.', () => {
@@ -38,10 +39,10 @@ describe('XMLParser', () => {
 			);
 			expect(root.childNodes.length).toBe(1);
 			expect(root.childNodes[0].childNodes.length).toBe(0);
-			expect((<HTMLElement>root.childNodes[0]).tagName).toBe('DIV');
-			expect((<HTMLElement>root.childNodes[0]).id).toBe('id');
-			expect((<HTMLElement>root.childNodes[0]).className).toBe('class1 class2');
-			expect((<HTMLElement>root.childNodes[0]).attributes).toEqual({
+			expect((<IHTMLElement>root.childNodes[0]).tagName).toBe('DIV');
+			expect((<IHTMLElement>root.childNodes[0]).id).toBe('id');
+			expect((<IHTMLElement>root.childNodes[0]).className).toBe('class1 class2');
+			expect((<IHTMLElement>root.childNodes[0]).attributes).toEqual({
 				'0': {
 					name: 'class',
 					value: 'class1 class2',
@@ -154,6 +155,37 @@ describe('XMLParser', () => {
 			);
 		});
 
+		it('Does not parse the content of script, style and template elements.', () => {
+			const root = XMLParser.parse(
+				window.document,
+				`<div>
+					<script>if(1<Math['random']()){else if(Math['random']()>1){console.log("1")}</script>
+					<script><b></b></script>
+					<style><b></b></style>
+					<template><b></b></template>
+				</div>`
+			);
+
+			expect((<IHTMLElement>root.children[0].children[0]).innerText).toBe(
+				`if(1<Math['random']()){else if(Math['random']()>1){console.log("1")}`
+			);
+
+			expect((<IHTMLElement>root.children[0].children[1]).innerText).toBe('<b></b>');
+			expect((<IHTMLElement>root.children[0].children[2]).innerText).toBe('<b></b>');
+			expect((<IHTMLTemplateElement>root.children[0].children[3]).content.textContent).toBe(
+				'<b></b>'
+			);
+
+			expect(root.innerHTML.replace(/[\s]/gm, '')).toBe(
+				`<div>
+					<script>if(1<Math['random']()){else if(Math['random']()>1){console.log("1")}</script>
+					<script><b></b></script>
+					<style><b></b></style>
+					<template></template>
+				</div>`.replace(/[\s]/gm, '')
+			);
+		});
+
 		it('Parses an SVG with "xmlns" set to HTML.', () => {
 			const root = XMLParser.parse(
 				window.document,