@@ -1070,6 +1070,166 @@ describe('SelectorParser', () => {
10701070 } )
10711071 } )
10721072
1073+ describe ( 'Relaxed nesting (CSS Nesting Module Level 1)' , ( ) => {
1074+ it ( 'should parse selector starting with child combinator' , ( ) => {
1075+ const { arena, rootNode, source } = parseSelectorInternal ( '> a' )
1076+
1077+ expect ( rootNode ) . not . toBeNull ( )
1078+ if ( ! rootNode ) return
1079+
1080+ // Should have one selector
1081+ const selectorWrappers = getChildren ( arena , source , rootNode )
1082+ expect ( selectorWrappers ) . toHaveLength ( 1 )
1083+
1084+ // The selector should have 2 children: combinator (>) and type selector (a)
1085+ const selectorWrapper = selectorWrappers [ 0 ]
1086+ const children = getChildren ( arena , source , selectorWrapper )
1087+ expect ( children ) . toHaveLength ( 2 )
1088+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1089+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '>' )
1090+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1091+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'a' )
1092+ } )
1093+
1094+ it ( 'should parse selector starting with next-sibling combinator' , ( ) => {
1095+ const { arena, rootNode, source } = parseSelectorInternal ( '+ div' )
1096+
1097+ expect ( rootNode ) . not . toBeNull ( )
1098+ if ( ! rootNode ) return
1099+
1100+ const selectorWrappers = getChildren ( arena , source , rootNode )
1101+ expect ( selectorWrappers ) . toHaveLength ( 1 )
1102+
1103+ const selectorWrapper = selectorWrappers [ 0 ]
1104+ const children = getChildren ( arena , source , selectorWrapper )
1105+ expect ( children ) . toHaveLength ( 2 )
1106+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1107+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '+' )
1108+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1109+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'div' )
1110+ } )
1111+
1112+ it ( 'should parse selector starting with subsequent-sibling combinator' , ( ) => {
1113+ const { arena, rootNode, source } = parseSelectorInternal ( '~ span' )
1114+
1115+ expect ( rootNode ) . not . toBeNull ( )
1116+ if ( ! rootNode ) return
1117+
1118+ const selectorWrappers = getChildren ( arena , source , rootNode )
1119+ expect ( selectorWrappers ) . toHaveLength ( 1 )
1120+
1121+ const selectorWrapper = selectorWrappers [ 0 ]
1122+ const children = getChildren ( arena , source , selectorWrapper )
1123+ expect ( children ) . toHaveLength ( 2 )
1124+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1125+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '~' )
1126+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1127+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'span' )
1128+ } )
1129+
1130+ it ( 'should parse complex selector after leading combinator' , ( ) => {
1131+ const { arena, rootNode, source } = parseSelectorInternal ( '> a.link#nav[href]:hover' )
1132+
1133+ expect ( rootNode ) . not . toBeNull ( )
1134+ if ( ! rootNode ) return
1135+
1136+ const selectorWrappers = getChildren ( arena , source , rootNode )
1137+ expect ( selectorWrappers ) . toHaveLength ( 1 )
1138+
1139+ const selectorWrapper = selectorWrappers [ 0 ]
1140+ const children = getChildren ( arena , source , selectorWrapper )
1141+
1142+ // Should have: combinator (>), type (a), class (.link), id (#nav), attribute ([href]), pseudo-class (:hover)
1143+ expect ( children . length ) . toBeGreaterThanOrEqual ( 6 )
1144+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1145+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '>' )
1146+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1147+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'a' )
1148+ expect ( arena . get_type ( children [ 2 ] ) ) . toBe ( NODE_SELECTOR_CLASS )
1149+ expect ( getNodeText ( arena , source , children [ 2 ] ) ) . toBe ( '.link' )
1150+ expect ( arena . get_type ( children [ 3 ] ) ) . toBe ( NODE_SELECTOR_ID )
1151+ expect ( getNodeText ( arena , source , children [ 3 ] ) ) . toBe ( '#nav' )
1152+ expect ( arena . get_type ( children [ 4 ] ) ) . toBe ( NODE_SELECTOR_ATTRIBUTE )
1153+ expect ( arena . get_type ( children [ 5 ] ) ) . toBe ( NODE_SELECTOR_PSEUDO_CLASS )
1154+ } )
1155+
1156+ it ( 'should parse multiple selectors with leading combinators' , ( ) => {
1157+ const { arena, rootNode, source } = parseSelectorInternal ( '> a, ~ span, + div' )
1158+
1159+ expect ( rootNode ) . not . toBeNull ( )
1160+ if ( ! rootNode ) return
1161+
1162+ // Should have three selectors
1163+ const selectorWrappers = getChildren ( arena , source , rootNode )
1164+ expect ( selectorWrappers ) . toHaveLength ( 3 )
1165+
1166+ // First selector: > a
1167+ let children = getChildren ( arena , source , selectorWrappers [ 0 ] )
1168+ expect ( children ) . toHaveLength ( 2 )
1169+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1170+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '>' )
1171+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1172+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'a' )
1173+
1174+ // Second selector: ~ span
1175+ children = getChildren ( arena , source , selectorWrappers [ 1 ] )
1176+ expect ( children ) . toHaveLength ( 2 )
1177+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1178+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '~' )
1179+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1180+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'span' )
1181+
1182+ // Third selector: + div
1183+ children = getChildren ( arena , source , selectorWrappers [ 2 ] )
1184+ expect ( children ) . toHaveLength ( 2 )
1185+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1186+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '+' )
1187+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1188+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'div' )
1189+ } )
1190+
1191+ it ( 'should parse leading combinator with whitespace' , ( ) => {
1192+ const { arena, rootNode, source } = parseSelectorInternal ( '> a' )
1193+
1194+ expect ( rootNode ) . not . toBeNull ( )
1195+ if ( ! rootNode ) return
1196+
1197+ const selectorWrappers = getChildren ( arena , source , rootNode )
1198+ expect ( selectorWrappers ) . toHaveLength ( 1 )
1199+
1200+ const selectorWrapper = selectorWrappers [ 0 ]
1201+ const children = getChildren ( arena , source , selectorWrapper )
1202+ expect ( children ) . toHaveLength ( 2 )
1203+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1204+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '>' )
1205+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1206+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'a' )
1207+ } )
1208+
1209+ it ( 'should parse selector with both leading and middle combinators' , ( ) => {
1210+ const { arena, rootNode, source } = parseSelectorInternal ( '> div span' )
1211+
1212+ expect ( rootNode ) . not . toBeNull ( )
1213+ if ( ! rootNode ) return
1214+
1215+ const selectorWrappers = getChildren ( arena , source , rootNode )
1216+ expect ( selectorWrappers ) . toHaveLength ( 1 )
1217+
1218+ const selectorWrapper = selectorWrappers [ 0 ]
1219+ const children = getChildren ( arena , source , selectorWrapper )
1220+
1221+ // Should have: combinator (>), type (div), combinator (descendant), type (span)
1222+ expect ( children ) . toHaveLength ( 4 )
1223+ expect ( arena . get_type ( children [ 0 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1224+ expect ( getNodeText ( arena , source , children [ 0 ] ) . trim ( ) ) . toBe ( '>' )
1225+ expect ( arena . get_type ( children [ 1 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1226+ expect ( getNodeText ( arena , source , children [ 1 ] ) ) . toBe ( 'div' )
1227+ expect ( arena . get_type ( children [ 2 ] ) ) . toBe ( NODE_SELECTOR_COMBINATOR )
1228+ expect ( arena . get_type ( children [ 3 ] ) ) . toBe ( NODE_SELECTOR_TYPE )
1229+ expect ( getNodeText ( arena , source , children [ 3 ] ) ) . toBe ( 'span' )
1230+ } )
1231+ } )
1232+
10731233 describe ( 'Edge cases' , ( ) => {
10741234 it ( 'should parse selector with multiple spaces' , ( ) => {
10751235 const { arena, rootNode, source } = parseSelectorInternal ( 'div p' )
0 commit comments