Repository: capricorn86/happy-dom (ts)

Issue:
Element.matches producing false positives when using descendant combinators
**Describe the bug**
When using a [descendant combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator) (" ") with Element.matches, if the rightmost selector matches it always returns true regardless of the other selectors.

I ran into this bug when using vue-test-utils's [Wrapper.findAll](https://v1.test-utils.vuejs.org/api/wrapper/#findall), which under the scenes uses Element.matches.

**To Reproduce**
Given the following HTML:
```html
<div class="red">
   <div>
      <div class="active"></div>
   </div>
</div>
```
The following two statements, running on the innermost div (the one with class="active") both return true even though the second should return false:
```typescript
element.matches('div.red div.active');
element.matches('div.green div.active');
```

**Expected behavior**
Invalid matches should always return false.

**Device:**
 - OS: Windows
 - Browser: happy-dom
 - Version: latest

Pull request description:
#614@patch: Element.matches not working properly with descendant combinators.
Fixes #614

Patch:
diff --git a/packages/happy-dom/src/query-selector/QuerySelector.ts b/packages/happy-dom/src/query-selector/QuerySelector.ts
index 31bf010b..ad0958c6 100644
--- a/packages/happy-dom/src/query-selector/QuerySelector.ts
+++ b/packages/happy-dom/src/query-selector/QuerySelector.ts
@@ -106,14 +106,14 @@ export default class QuerySelector {
 		const selector = new SelectorItem(selectorParts[0]);
 		const result = selector.match(<Element>currentNode);
 
-		if (targetNode === currentNode && !result.matches) {
+		if ((targetNode === currentNode || !currentNode.parentNode) && !result.matches) {
 			return { priorityWeight: 0, matches: false };
 		}
 
 		return this.matchesSelector(
 			isDirectChild ? currentNode.parentNode : targetNode,
 			currentNode.parentNode,
-			selectorParts.slice(1),
+			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 7a6e7854..3d048987 100644
--- a/packages/happy-dom/test/nodes/element/Element.test.ts
+++ b/packages/happy-dom/test/nodes/element/Element.test.ts
@@ -694,6 +694,38 @@ describe('Element', () => {
 
 			expect(element.matches('.container, .active')).toBe(true);
 		});
+
+		it('Checks if the element matches with a descendant combinator', () => {
+			const grandparentElement = document.createElement('div');
+			grandparentElement.setAttribute('role', 'alert');
+
+			const parentElement = document.createElement('div');
+			parentElement.setAttribute('role', 'status');
+			grandparentElement.appendChild(parentElement);
+
+			const element = document.createElement('div');
+			element.className = 'active';
+			parentElement.appendChild(element);
+
+			expect(element.matches('div[role="alert"] div.active')).toBe(true);
+			expect(element.matches('div[role="article"] div.active')).toBe(false);
+		});
+
+		it('Checks if the element matches with a child combinator', () => {
+			const grandparentElement = document.createElement('div');
+			grandparentElement.setAttribute('role', 'alert');
+
+			const parentElement = document.createElement('div');
+			grandparentElement.setAttribute('role', 'status');
+			grandparentElement.appendChild(parentElement);
+
+			const element = document.createElement('div');
+			element.className = 'active';
+			parentElement.appendChild(element);
+
+			expect(element.matches('div[role="status"] > div.active')).toBe(true);
+			expect(element.matches('div[role="alert"] > div.active')).toBe(false);
+		});
 	});
 
 	describe('closest()', () => {