From 17634e659866a5524512d7d8867442f16dba7f24 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Wed, 1 Jan 2025 20:22:42 +0100 Subject: [PATCH] feat: [#1642] Adds support for child combinator to :has pseudo selector (#1660) --- .../happy-dom/src/query-selector/SelectorItem.ts | 10 ++++++++++ .../src/query-selector/SelectorParser.ts | 15 +++++++++++---- .../test/query-selector/QuerySelector.test.ts | 5 ++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/happy-dom/src/query-selector/SelectorItem.ts b/packages/happy-dom/src/query-selector/SelectorItem.ts index 3e822890..d27063ff 100644 --- a/packages/happy-dom/src/query-selector/SelectorItem.ts +++ b/packages/happy-dom/src/query-selector/SelectorItem.ts @@ -340,6 +340,16 @@ export default class SelectorItem { priorityWeightForHas = match.priorityWeight; } } + } else if (pseudo.arguments[0] === '>') { + for (const selectorItem of pseudo.selectorItems) { + for (const child of element[PropertySymbol.elementArray]) { + const match = selectorItem.match(child); + if (match && priorityWeightForHas < match.priorityWeight) { + priorityWeightForHas = match.priorityWeight; + break; + } + } + } } else { for (const selectorItem of pseudo.selectorItems) { const match = this.matchChildOfElement(selectorItem, element); diff --git a/packages/happy-dom/src/query-selector/SelectorParser.ts b/packages/happy-dom/src/query-selector/SelectorParser.ts index 88590a45..c04ca4c8 100644 --- a/packages/happy-dom/src/query-selector/SelectorParser.ts +++ b/packages/happy-dom/src/query-selector/SelectorParser.ts @@ -277,6 +277,10 @@ export default class SelectorParser { ): ISelectorPseudo { const lowerName = name.toLowerCase(); + if (args) { + args = args.trim(); + } + if (!args) { return { name: lowerName, arguments: null, selectorItems: null, nthFunction: null }; } @@ -328,10 +332,13 @@ export default class SelectorParser { // The ":has()" pseudo selector doesn't allow for it to be nested inside another ":has()" pseudo selector, as it can lead to cyclic querying. if (!args.includes(':has(')) { - for (const group of this.getSelectorGroups( - args[0] === '+' ? args.replace('+', '') : args, - options - )) { + let newArgs = args; + if (args[0] === '+') { + newArgs = args.replace('+', ''); + } else if (args[0] === '>') { + newArgs = args.replace('>', ''); + } + for (const group of this.getSelectorGroups(newArgs, options)) { hasSelectorItems.push(group[0]); } } diff --git a/packages/happy-dom/test/query-selector/QuerySelector.test.ts b/packages/happy-dom/test/query-selector/QuerySelector.test.ts index 51ee3024..a72324b5 100644 --- a/packages/happy-dom/test/query-selector/QuerySelector.test.ts +++ b/packages/happy-dom/test/query-selector/QuerySelector.test.ts @@ -1205,9 +1205,12 @@ describe('QuerySelector', () => { expect(Array.from(container.querySelectorAll('span:has(+video)'))).toEqual([ container.children[1] ]); - expect(Array.from(container.querySelectorAll('h1:has(+h2)'))).toEqual([ + expect(Array.from(container.querySelectorAll('h1:has( +h2)'))).toEqual([ container.children[3] ]); + expect(Array.from(container.querySelectorAll('span:has(> video)'))).toEqual([ + container.children[0] + ]); }); });