@@ -415,6 +415,72 @@ describe('SelectorParser', () => {
415415 } )
416416 } )
417417
418+ describe ( 'Pseudo-class function syntax detection (has_children)' , ( ) => {
419+ it ( 'should indicate :lang() has function syntax even when empty' , ( ) => {
420+ const root = parse_selector ( ':lang()' )
421+ const pseudoClass = root . first_child ! . first_child !
422+ expect ( pseudoClass . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
423+ expect ( pseudoClass . name ) . toBe ( 'lang' )
424+ expect ( pseudoClass . has_children ) . toBe ( true ) // Function syntax, even if empty
425+ } )
426+
427+ it ( 'should indicate :lang(en) has function syntax with children' , ( ) => {
428+ const root = parse_selector ( ':lang(en)' )
429+ const pseudoClass = root . first_child ! . first_child !
430+ expect ( pseudoClass . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
431+ expect ( pseudoClass . name ) . toBe ( 'lang' )
432+ expect ( pseudoClass . has_children ) . toBe ( true ) // Function syntax with content
433+ } )
434+
435+ it ( 'should indicate :hover has no function syntax' , ( ) => {
436+ const root = parse_selector ( ':hover' )
437+ const pseudoClass = root . first_child ! . first_child !
438+ expect ( pseudoClass . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
439+ expect ( pseudoClass . name ) . toBe ( 'hover' )
440+ expect ( pseudoClass . has_children ) . toBe ( false ) // Not a function
441+ } )
442+
443+ it ( 'should indicate :is() has function syntax even when empty' , ( ) => {
444+ const root = parse_selector ( ':is()' )
445+ const pseudoClass = root . first_child ! . first_child !
446+ expect ( pseudoClass . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
447+ expect ( pseudoClass . name ) . toBe ( 'is' )
448+ expect ( pseudoClass . has_children ) . toBe ( true ) // Function syntax, even if empty
449+ } )
450+
451+ it ( 'should indicate :has() has function syntax even when empty' , ( ) => {
452+ const root = parse_selector ( ':has()' )
453+ const pseudoClass = root . first_child ! . first_child !
454+ expect ( pseudoClass . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
455+ expect ( pseudoClass . name ) . toBe ( 'has' )
456+ expect ( pseudoClass . has_children ) . toBe ( true ) // Function syntax, even if empty
457+ } )
458+
459+ it ( 'should indicate :nth-child() has function syntax even when empty' , ( ) => {
460+ const root = parse_selector ( ':nth-child()' )
461+ const pseudoClass = root . first_child ! . first_child !
462+ expect ( pseudoClass . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
463+ expect ( pseudoClass . name ) . toBe ( 'nth-child' )
464+ expect ( pseudoClass . has_children ) . toBe ( true ) // Function syntax, even if empty
465+ } )
466+
467+ it ( 'should indicate ::before has no function syntax' , ( ) => {
468+ const root = parse_selector ( '::before' )
469+ const pseudoElement = root . first_child ! . first_child !
470+ expect ( pseudoElement . type ) . toBe ( NODE_SELECTOR_PSEUDO_ELEMENT )
471+ expect ( pseudoElement . name ) . toBe ( 'before' )
472+ expect ( pseudoElement . has_children ) . toBe ( false ) // Not a function
473+ } )
474+
475+ it ( 'should indicate ::slotted() has function syntax even when empty' , ( ) => {
476+ const root = parse_selector ( '::slotted()' )
477+ const pseudoElement = root . first_child ! . first_child !
478+ expect ( pseudoElement . type ) . toBe ( NODE_SELECTOR_PSEUDO_ELEMENT )
479+ expect ( pseudoElement . name ) . toBe ( 'slotted' )
480+ expect ( pseudoElement . has_children ) . toBe ( true ) // Function syntax, even if empty
481+ } )
482+ } )
483+
418484 describe ( 'Attribute selectors' , ( ) => {
419485 it ( 'should parse simple attribute selector' , ( ) => {
420486 const { arena, rootNode, source } = parseSelectorInternal ( '[disabled]' )
@@ -943,7 +1009,7 @@ describe('SelectorParser', () => {
9431009
9441010 expect ( has . type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
9451011 expect ( has . text ) . toBe ( ':has()' )
946- expect ( has . has_children ) . toBe ( false )
1012+ expect ( has . has_children ) . toBe ( true ) // Has function syntax (parentheses )
9471013 } )
9481014
9491015 it ( 'should parse nesting with ampersand' , ( ) => {
@@ -1394,6 +1460,21 @@ describe('parse_selector()', () => {
13941460 expect ( result . has_children ) . toBe ( true )
13951461 } )
13961462
1463+ test ( 'should parse unknown pseudo-class without parens' , ( ) => {
1464+ let root = parse_selector ( ':hello' )
1465+ let pseudo = root . first_child ?. first_child
1466+ expect ( pseudo ?. type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
1467+ expect ( pseudo ?. has_children ) . toBe ( false )
1468+ } )
1469+
1470+ test ( 'should parse unknown pseudo-class with empty parens' , ( ) => {
1471+ let root = parse_selector ( ':hello()' )
1472+ let pseudo = root . first_child ?. first_child
1473+ expect ( pseudo ?. type ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
1474+ expect ( pseudo ?. has_children ) . toBe ( true )
1475+ expect ( pseudo ?. children . length ) . toBe ( 0 )
1476+ } )
1477+
13971478 test ( 'should parse attribute selector' , ( ) => {
13981479 const result = parse_selector ( '[href^="https"]' )
13991480
0 commit comments