@@ -146,6 +146,20 @@ def parse_many(first, *others):
146
146
'Negation[Element[div]:not(Class[Element[div].foo])]' ]
147
147
assert parse_many ('td ~ th' ) == [
148
148
'CombinedSelector[Element[td] ~ Element[th]]' ]
149
+ assert parse_many (':scope > foo' ) == [
150
+ 'CombinedSelector[Pseudo[Element[*]:scope] > Element[foo]]'
151
+ ]
152
+ assert parse_many (' :scope > foo' ) == [
153
+ 'CombinedSelector[Pseudo[Element[*]:scope] > Element[foo]]'
154
+ ]
155
+ assert parse_many (':scope > foo bar > div' ) == [
156
+ 'CombinedSelector[CombinedSelector[CombinedSelector[Pseudo[Element[*]:scope] > '
157
+ 'Element[foo]] <followed> Element[bar]] > Element[div]]'
158
+ ]
159
+ assert parse_many (':scope > #foo #bar' ) == [
160
+ 'CombinedSelector[CombinedSelector[Pseudo[Element[*]:scope] > '
161
+ 'Hash[Element[*]#foo]] <followed> Hash[Element[*]#bar]]'
162
+ ]
149
163
150
164
def test_pseudo_elements (self ):
151
165
def parse_pseudo (css ):
@@ -164,9 +178,16 @@ def parse_one(css):
164
178
assert len (result ) == 1
165
179
return result [0 ]
166
180
181
+ def test_pseudo_repr (css ):
182
+ result = parse (css )
183
+ assert len (result ) == 1
184
+ selector = result [0 ]
185
+ return selector .parsed_tree .__repr__ ()
186
+
167
187
assert parse_one ('foo' ) == ('Element[foo]' , None )
168
188
assert parse_one ('*' ) == ('Element[*]' , None )
169
189
assert parse_one (':empty' ) == ('Pseudo[Element[*]:empty]' , None )
190
+ assert parse_one (':scope' ) == ('Pseudo[Element[*]:scope]' , None )
170
191
171
192
# Special cases for CSS 2.1 pseudo-elements
172
193
assert parse_one (':BEfore' ) == ('Element[*]' , 'before' )
@@ -190,11 +211,14 @@ def parse_one(css):
190
211
'CombinedSelector[Hash[Element[lorem]#ipsum] ~ '
191
212
'Pseudo[Attrib[Class[Hash[Element[a]#b].c][href]]:empty]]' ,
192
213
'selection' )
193
-
194
- parse_pseudo ('foo:before, bar, baz:after' ) == [
195
- ('Element[foo]' , 'before' ),
196
- ('Element[bar]' , None ),
197
- ('Element[baz]' , 'after' )]
214
+ assert parse_pseudo (':scope > div, foo bar' ) == [
215
+ ('CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]' , None ),
216
+ ('CombinedSelector[Element[foo] <followed> Element[bar]]' , None )
217
+ ]
218
+ assert parse_pseudo ('foo:before, bar, baz:after' ) == [
219
+ ('Element[foo]' , 'before' ), ('Element[bar]' , None ),
220
+ ('Element[baz]' , 'after' )
221
+ ]
198
222
199
223
# Special cases for CSS 2.1 pseudo-elements are ignored by default
200
224
for pseudo in ('after' , 'before' , 'first-line' , 'first-letter' ):
@@ -211,6 +235,11 @@ def parse_one(css):
211
235
self .assertRaises (ExpressionError , tr .selector_to_xpath , selector ,
212
236
translate_pseudo_elements = True )
213
237
238
+ # Special test for the unicode symbols and ':scope' element if check
239
+ # Errors if use repr() instead of __repr__()
240
+ assert test_pseudo_repr (u':fİrst-child' ) == u'Pseudo[Element[*]:fİrst-child]'
241
+ assert test_pseudo_repr (':scope' ) == 'Pseudo[Element[*]:scope]'
242
+
214
243
def test_specificity (self ):
215
244
def specificity (css ):
216
245
selectors = parse (css )
@@ -310,6 +339,13 @@ def get_error(css):
310
339
"Got pseudo-element ::before inside :not() at 12" )
311
340
assert get_error (':not(:not(a))' ) == (
312
341
"Got nested :not()" )
342
+ assert get_error (':scope > div :scope header' ) == (
343
+ 'Got immediate child pseudo-element ":scope" not at the start of a selector'
344
+ )
345
+ assert get_error ('div :scope header' ) == (
346
+ 'Got immediate child pseudo-element ":scope" not at the start of a selector'
347
+ )
348
+ assert get_error ('> div p' ) == ("Expected selector, got <DELIM '>' at 0>" )
313
349
314
350
def test_translation (self ):
315
351
def xpath (css ):
@@ -483,6 +519,8 @@ def test_quoting(self):
483
519
'''descendant-or-self::*[@aval = '"']''' )
484
520
assert css_to_xpath ('*[aval=\' """\' ]' ) == (
485
521
'''descendant-or-self::*[@aval = '"""']''' )
522
+ assert css_to_xpath (':scope > div[dataimg="<testmessage>"]' ) == (
523
+ "descendant-or-self::*[1]/div[@dataimg = '<testmessage>']" )
486
524
487
525
def test_unicode_escapes (self ):
488
526
# \22 == '"' \20 == ' '
@@ -560,6 +598,7 @@ def xpath(css):
560
598
assert xpath ('::attr-href' ) == "descendant-or-self::*/@href"
561
599
assert xpath ('p img::attr(src)' ) == (
562
600
"descendant-or-self::p/descendant-or-self::*/img/@src" )
601
+ assert xpath (':scope' ) == "descendant-or-self::*[1]"
563
602
564
603
def test_series (self ):
565
604
def series (css ):
@@ -672,6 +711,11 @@ def pcss(main, *selectors, **kwargs):
672
711
assert pcss (':lang("EN")' , '*:lang(en-US)' , html_only = True ) == [
673
712
'second-li' , 'li-div' ]
674
713
assert pcss (':lang("e")' , html_only = True ) == []
714
+ assert pcss (':scope > div' ) == []
715
+ assert pcss (':scope body' ) == ['nil' ]
716
+ assert pcss (':scope body > div' ) == ['outer-div' , 'foobar-div' ]
717
+ assert pcss (':scope head' ) == ['nil' ]
718
+ assert pcss (':scope html' ) == []
675
719
676
720
# --- nth-* and nth-last-* -------------------------------------
677
721
@@ -853,6 +897,9 @@ def count(selector):
853
897
assert count ('div[class|=dialog]' ) == 50 # ? Seems right
854
898
assert count ('div[class!=madeup]' ) == 243 # ? Seems right
855
899
assert count ('div[class~=dialog]' ) == 51 # ? Seems right
900
+ assert count (':scope > div' ) == 1
901
+ assert count (':scope > div > div[class=dialog]' ) == 1
902
+ assert count (':scope > div div' ) == 242
856
903
857
904
XMLLANG_IDS = '''
858
905
<test>
0 commit comments