@@ -259,6 +259,178 @@ describe('CSS Modules', () => {
259259 } ) ;
260260 } ) ;
261261
262+ describe ( 'css-pseudo-4' , ( ) => {
263+ it ( 'should parse pseudo-4 pseudo-classes' , ( ) => {
264+ const parse = createParser ( {
265+ modules : [ 'css-pseudo-4' ]
266+ } ) ;
267+
268+ // Simple pseudo-classes
269+ expect ( parse ( ':focus-visible' ) ) . toEqual (
270+ ast . selector ( {
271+ rules : [
272+ ast . rule ( {
273+ items : [ ast . pseudoClass ( { name : 'focus-visible' } ) ]
274+ } )
275+ ]
276+ } )
277+ ) ;
278+
279+ expect ( parse ( ':blank' ) ) . toEqual (
280+ ast . selector ( {
281+ rules : [
282+ ast . rule ( {
283+ items : [ ast . pseudoClass ( { name : 'blank' } ) ]
284+ } )
285+ ]
286+ } )
287+ ) ;
288+
289+ // Functional pseudo-classes
290+ expect ( parse ( ':has(> img)' ) ) . toEqual (
291+ ast . selector ( {
292+ rules : [
293+ ast . rule ( {
294+ items : [
295+ ast . pseudoClass ( {
296+ name : 'has' ,
297+ argument : ast . selector ( {
298+ rules : [
299+ ast . rule ( {
300+ items : [ ast . tagName ( { name : 'img' } ) ] ,
301+ combinator : '>'
302+ } )
303+ ]
304+ } )
305+ } )
306+ ]
307+ } )
308+ ]
309+ } )
310+ ) ;
311+
312+ expect ( parse ( ':is(h1, h2, h3)' ) ) . toEqual (
313+ ast . selector ( {
314+ rules : [
315+ ast . rule ( {
316+ items : [
317+ ast . pseudoClass ( {
318+ name : 'is' ,
319+ argument : ast . selector ( {
320+ rules : [
321+ ast . rule ( {
322+ items : [ ast . tagName ( { name : 'h1' } ) ]
323+ } ) ,
324+ ast . rule ( {
325+ items : [ ast . tagName ( { name : 'h2' } ) ]
326+ } ) ,
327+ ast . rule ( {
328+ items : [ ast . tagName ( { name : 'h3' } ) ]
329+ } )
330+ ]
331+ } )
332+ } )
333+ ]
334+ } )
335+ ]
336+ } )
337+ ) ;
338+ } ) ;
339+
340+ it ( 'should parse pseudo-4 pseudo-elements' , ( ) => {
341+ const parse = createParser ( {
342+ modules : [ 'css-pseudo-4' ]
343+ } ) ;
344+
345+ // Simple pseudo-elements
346+ expect ( parse ( '::marker' ) ) . toEqual (
347+ ast . selector ( {
348+ rules : [
349+ ast . rule ( {
350+ items : [ ast . pseudoElement ( { name : 'marker' } ) ]
351+ } )
352+ ]
353+ } )
354+ ) ;
355+
356+ expect ( parse ( '::selection' ) ) . toEqual (
357+ ast . selector ( {
358+ rules : [
359+ ast . rule ( {
360+ items : [ ast . pseudoElement ( { name : 'selection' } ) ]
361+ } )
362+ ]
363+ } )
364+ ) ;
365+
366+ expect ( parse ( '::target-text' ) ) . toEqual (
367+ ast . selector ( {
368+ rules : [
369+ ast . rule ( {
370+ items : [ ast . pseudoElement ( { name : 'target-text' } ) ]
371+ } )
372+ ]
373+ } )
374+ ) ;
375+
376+ // String argument pseudo-elements
377+ expect ( parse ( '::highlight(example)' ) ) . toEqual (
378+ ast . selector ( {
379+ rules : [
380+ ast . rule ( {
381+ items : [
382+ ast . pseudoElement ( {
383+ name : 'highlight' ,
384+ argument : ast . string ( { value : 'example' } )
385+ } )
386+ ]
387+ } )
388+ ]
389+ } )
390+ ) ;
391+
392+ // Selector argument pseudo-elements
393+ expect ( parse ( '::part(button)' ) ) . toEqual (
394+ ast . selector ( {
395+ rules : [
396+ ast . rule ( {
397+ items : [
398+ ast . pseudoElement ( {
399+ name : 'part' ,
400+ argument : ast . selector ( {
401+ rules : [
402+ ast . rule ( {
403+ items : [ ast . tagName ( { name : 'button' } ) ]
404+ } )
405+ ]
406+ } )
407+ } )
408+ ]
409+ } )
410+ ]
411+ } )
412+ ) ;
413+ } ) ;
414+
415+ it ( 'should reject pseudo-4 selectors when module is not enabled' , ( ) => {
416+ const parse = createParser ( {
417+ syntax : {
418+ pseudoClasses : {
419+ unknown : 'reject'
420+ } ,
421+ pseudoElements : {
422+ unknown : 'reject'
423+ }
424+ }
425+ } ) ;
426+
427+ expect ( ( ) => parse ( ':focus-visible' ) ) . toThrow ( 'Unknown pseudo-class: "focus-visible".' ) ;
428+ expect ( ( ) => parse ( ':has(> img)' ) ) . toThrow ( 'Unknown pseudo-class: "has".' ) ;
429+ expect ( ( ) => parse ( '::marker' ) ) . toThrow ( 'Unknown pseudo-element "marker".' ) ;
430+ expect ( ( ) => parse ( '::highlight(example)' ) ) . toThrow ( 'Unknown pseudo-element "highlight".' ) ;
431+ } ) ;
432+ } ) ;
433+
262434 describe ( 'Multiple modules' , ( ) => {
263435 it ( 'should support multiple modules at once' , ( ) => {
264436 const parse = createParser ( {
@@ -309,5 +481,72 @@ describe('CSS Modules', () => {
309481 } )
310482 ) ;
311483 } ) ;
484+
485+ it ( 'should support combining css-position and css-pseudo modules' , ( ) => {
486+ const parse = createParser ( {
487+ modules : [ 'css-position-3' , 'css-pseudo-4' ]
488+ } ) ;
489+
490+ // Position pseudo-class
491+ expect ( parse ( ':sticky' ) ) . toEqual (
492+ ast . selector ( {
493+ rules : [
494+ ast . rule ( {
495+ items : [ ast . pseudoClass ( { name : 'sticky' } ) ]
496+ } )
497+ ]
498+ } )
499+ ) ;
500+
501+ // Pseudo-4 pseudo-class
502+ expect ( parse ( ':focus-visible' ) ) . toEqual (
503+ ast . selector ( {
504+ rules : [
505+ ast . rule ( {
506+ items : [ ast . pseudoClass ( { name : 'focus-visible' } ) ]
507+ } )
508+ ]
509+ } )
510+ ) ;
511+
512+ // Pseudo-4 pseudo-element
513+ expect ( parse ( '::marker' ) ) . toEqual (
514+ ast . selector ( {
515+ rules : [
516+ ast . rule ( {
517+ items : [ ast . pseudoElement ( { name : 'marker' } ) ]
518+ } )
519+ ]
520+ } )
521+ ) ;
522+
523+ // Complex selector using both modules
524+ expect ( parse ( 'div:sticky:has(> img::marker)' ) ) . toEqual (
525+ ast . selector ( {
526+ rules : [
527+ ast . rule ( {
528+ items : [
529+ ast . tagName ( { name : 'div' } ) ,
530+ ast . pseudoClass ( { name : 'sticky' } ) ,
531+ ast . pseudoClass ( {
532+ name : 'has' ,
533+ argument : ast . selector ( {
534+ rules : [
535+ ast . rule ( {
536+ items : [
537+ ast . tagName ( { name : 'img' } ) ,
538+ ast . pseudoElement ( { name : 'marker' } )
539+ ] ,
540+ combinator : '>'
541+ } )
542+ ]
543+ } )
544+ } )
545+ ]
546+ } )
547+ ]
548+ } )
549+ ) ;
550+ } ) ;
312551 } ) ;
313552} ) ;
0 commit comments