Repository: capricorn86/happy-dom (ts)

Issue:
`happy-dom` doesn't convert `&gt;` to `>` whereas `jsdom` does
**Describe the bug**

When using `happy-dom` to set and then view the contents of a DOM node via `node.textContent`, if the DOM node has a `&gt;` character, `happy-dom` will return its `textContent` as `&gt;` whereas `jsdom` and web browsers will return the `>` character. Looks like `happy-dom` isn't converting those values.

**To Reproduce**

The following code can be used to reproduce the problem:

```js
document.documentElement.innerHTML = '<div id="testnode">&gt;howdy</div>';
expect(document.getElementById('testnode').textContent).toBe('>howdy');
```

Here is a StackBlitz which runs that code in both `jsdom` and `happy-dom` -- note the difference in output: https://stackblitz.com/edit/vitest-dev-vitest-fujcfs?file=test%2Fhappydom.test.ts%3AL6,test%2Fjsdom.test.ts 

**Expected behavior**

I expect `happy-dom` to have the same behaviour as `jsdom` when setting and reading `&gt;` and `&lt;` characters that are injected into a DOM node's body.

**Screenshots**

<img width="668" alt="Screenshot 2023-03-08 at 4 51 52 PM" src="https://user-images.githubusercontent.com/226640/223886741-93600441-ce1b-4619-8eca-669bc5910c3e.png">

**Device:**

N/A



Pull request description:
#818@patch: Convert special characters to HTML entities.
fix #818 

Patch:
diff --git a/packages/happy-dom/src/nodes/character-data/CharacterDataUtility.ts b/packages/happy-dom/src/nodes/character-data/CharacterDataUtility.ts
index 9023bfaf..4976a0a1 100644
--- a/packages/happy-dom/src/nodes/character-data/CharacterDataUtility.ts
+++ b/packages/happy-dom/src/nodes/character-data/CharacterDataUtility.ts
@@ -1,5 +1,26 @@
 import ICharacterData from './ICharacterData';
 
+const HTML_ENTITIES = [
+	{ regex: /&quot;/g, value: '"' },
+	{ regex: /&#34/g, value: '"' },
+	{ regex: /&#x22/g, value: '"' },
+	{ regex: /&amp;/g, value: '&' },
+	{ regex: /&#38/g, value: '&' },
+	{ regex: /&#x26/g, value: '&' },
+	{ regex: /&apos;/g, value: "'" },
+	{ regex: /&#39/g, value: "'" },
+	{ regex: /&#x27/g, value: "'" },
+	{ regex: /&lt;/g, value: '<' },
+	{ regex: /&#60/g, value: '<' },
+	{ regex: /&#x3C/g, value: '<' },
+	{ regex: /&gt;/g, value: '>' },
+	{ regex: /&#62/g, value: '>' },
+	{ regex: /&#x3E/g, value: '>' },
+	{ regex: /&nbsp;/g, value: ' ' },
+	{ regex: /&#160/g, value: ' ' },
+	{ regex: /&#xA0/g, value: ' ' }
+];
+
 /**
  * Child node utility.
  */
@@ -69,4 +90,20 @@ export default class CharacterDataUtility {
 	): string {
 		return characterData.data.substring(offset, offset + count);
 	}
+
+	/**
+	 * Decodes unicode characters to text.
+	 *
+	 * @param html String.
+	 * @returns Decoded HTML string.
+	 */
+	public static decodeHTMLEntities(html: string): string {
+		if (!html) {
+			return '';
+		}
+		for (const entity of HTML_ENTITIES) {
+			html = html.replace(entity.regex, entity.value);
+		}
+		return html;
+	}
 }
diff --git a/packages/happy-dom/src/nodes/element/Element.ts b/packages/happy-dom/src/nodes/element/Element.ts
index 65d0159c..9457fb22 100644
--- a/packages/happy-dom/src/nodes/element/Element.ts
+++ b/packages/happy-dom/src/nodes/element/Element.ts
@@ -30,6 +30,7 @@ import INamedNodeMap from '../../named-node-map/INamedNodeMap';
 import Event from '../../event/Event';
 import ElementUtility from './ElementUtility';
 import HTMLCollection from './HTMLCollection';
+import CharacterDataUtility from '../character-data/CharacterDataUtility';
 
 /**
  * Element.
@@ -188,7 +189,7 @@ export default class Element extends Node implements IElement {
 				result += childNode.textContent;
 			}
 		}
-		return result;
+		return CharacterDataUtility.decodeHTMLEntities(result);
 	}
 
 	/**


Test patch:
diff --git a/packages/happy-dom/test/nodes/element/Element.test.ts b/packages/happy-dom/test/nodes/element/Element.test.ts
index f76ec839..12de1c12 100644
--- a/packages/happy-dom/test/nodes/element/Element.test.ts
+++ b/packages/happy-dom/test/nodes/element/Element.test.ts
@@ -129,6 +129,20 @@ describe('Element', () => {
 			div.appendChild(textNode1);
 			expect(element.textContent).toBe('text1text2');
 		});
+
+		it('Converts specifial characters to HTML entities.', () => {
+			const div = document.createElement('div');
+			div.innerHTML = '<div>&gt;</div>';
+			expect(div.textContent).toBe('>');
+			const el = document.createElement('div');
+			el.innerHTML = '<div id="testnode">&gt;howdy</div>';
+			expect(el.textContent).toBe('>howdy');
+			div.appendChild(el);
+			expect(div.textContent).toBe('>>howdy');
+			const el2 = document.createElement('div');
+			el2.innerHTML = '<div id="testnode">&gt;&lt;&amp;&quot;&apos;&nbsp;&nbsp;</div>';
+			expect(el2.textContent).toBe('><&"\'  ');
+		});
 	});
 
 	describe('set textContent()', () => {