Repository: capricorn86/happy-dom (ts)

Issue:
Element.matches fails when using non-matching descendant selector on element attached to document
```ts
const element = document.createElement('div');
document.appendChild(element);
expect(element.matches('.nonexistent-class div')).toBe(false);
```

```
TypeError: Cannot read properties of undefined (reading 'split')

  296 |     ): { priorityWeight: number; matches: boolean } {
  297 |             const regexp = new RegExp(CLASS_REGEXP, 'g');
> 298 |             const classList = element.className.split(' ');
	  |                                                 ^
  299 |             const classSelector = selector.split(':')[0];
  300 |             let priorityWeight = 0;
  301 |             let match: RegExpMatchArray;

  at SelectorItem.matchesClass (src/query-selector/SelectorItem.ts:298:39)
  at SelectorItem.match (src/query-selector/SelectorItem.ts:91:24)
  at Function.matchesSelector (src/query-selector/QuerySelector.ts:107:27)
  at Function.matchesSelector (src/query-selector/QuerySelector.ts:113:15)
  at Function.matchesSelector (src/query-selector/QuerySelector.ts:113:15)
  at Function.matchesSelector (src/query-selector/QuerySelector.ts:113:15)
  at Function.match (src/query-selector/QuerySelector.ts:66:24)
  at HTMLElement.matches (src/nodes/element/Element.ts:734:24)
```

Pull request:
#622@patch: Fix Element.matches failing when using non-matching desce…
…ndant selector on element attached to document.

Fixes #622. Was calling selector.match on the Document, which isn't an Element.

Patch:
diff --git a/packages/happy-dom/src/query-selector/QuerySelector.ts b/packages/happy-dom/src/query-selector/QuerySelector.ts
index ad0958c6..b0c70202 100644
--- a/packages/happy-dom/src/query-selector/QuerySelector.ts
+++ b/packages/happy-dom/src/query-selector/QuerySelector.ts
@@ -104,15 +104,15 @@ export default class QuerySelector {
 		}
 
 		const selector = new SelectorItem(selectorParts[0]);
-		const result = selector.match(<Element>currentNode);
+		const result = selector.match(<IElement>currentNode);
 
-		if ((targetNode === currentNode || !currentNode.parentNode) && !result.matches) {
+		if ((targetNode === currentNode || !currentNode.parentElement) && !result.matches) {
 			return { priorityWeight: 0, matches: false };
 		}
 
 		return this.matchesSelector(
-			isDirectChild ? currentNode.parentNode : targetNode,
-			currentNode.parentNode,
+			isDirectChild ? currentNode.parentElement : targetNode,
+			currentNode.parentElement,
 			result.matches ? selectorParts.slice(1) : selectorParts,
 			priorityWeight + result.priorityWeight
 		);


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 3d048987..0d6b551a 100644
--- a/packages/happy-dom/test/nodes/element/Element.test.ts
+++ b/packages/happy-dom/test/nodes/element/Element.test.ts
@@ -698,6 +698,7 @@ describe('Element', () => {
 		it('Checks if the element matches with a descendant combinator', () => {
 			const grandparentElement = document.createElement('div');
 			grandparentElement.setAttribute('role', 'alert');
+			document.appendChild(grandparentElement);
 
 			const parentElement = document.createElement('div');
 			parentElement.setAttribute('role', 'status');
@@ -709,6 +710,7 @@ describe('Element', () => {
 
 			expect(element.matches('div[role="alert"] div.active')).toBe(true);
 			expect(element.matches('div[role="article"] div.active')).toBe(false);
+			expect(element.matches('.nonexistent-class div.active')).toBe(false);
 		});
 
 		it('Checks if the element matches with a child combinator', () => {
diff --git a/packages/happy-dom/test/nodes/node/Node.test.ts b/packages/happy-dom/test/nodes/node/Node.test.ts
index 53ed11f1..57c7a517 100644
--- a/packages/happy-dom/test/nodes/node/Node.test.ts
+++ b/packages/happy-dom/test/nodes/node/Node.test.ts
@@ -196,6 +196,14 @@ describe('Node', () => {
 
 			expect(text.parentElement).toBe(null);
 		});
+
+		it('Returns null if parent node is not an element.', () => {
+			const htmlElement = document.createElement('html');
+			document.appendChild(htmlElement);
+
+			expect(htmlElement.parentNode).toBe(document);
+			expect(htmlElement.parentElement).toBe(null);
+		});
 	});
 
 	describe('get baseURI()', () => {