@@ -428,8 +428,8 @@ def xpath(css):
428
428
"e[count(preceding-sibling::*) <= 0]" )
429
429
430
430
assert xpath ('e:nth-child(3n+2)' ) == (
431
- "e[count(preceding-sibling::*) >= 1 and "
432
- "(count(preceding-sibling::*) +2) mod 3 = 0]" )
431
+ "e[( count(preceding-sibling::*) >= 1) and "
432
+ "(( count(preceding-sibling::*) +2) mod 3 = 0) ]" )
433
433
assert xpath ('e:nth-child(3n-2)' ) == (
434
434
"e[count(preceding-sibling::*) mod 3 = 0]" )
435
435
assert xpath ('e:nth-child(-n+6)' ) == (
@@ -442,8 +442,8 @@ def xpath(css):
442
442
assert xpath ('e:nth-last-child(2n+1)' ) == (
443
443
"e[count(following-sibling::*) mod 2 = 0]" )
444
444
assert xpath ('e:nth-last-child(2n+2)' ) == (
445
- "e[count(following-sibling::*) >= 1 and "
446
- "(count(following-sibling::*) +1) mod 2 = 0]" )
445
+ "e[( count(following-sibling::*) >= 1) and "
446
+ "(( count(following-sibling::*) +1) mod 2 = 0) ]" )
447
447
assert xpath ('e:nth-last-child(3n+1)' ) == (
448
448
"e[count(following-sibling::*) mod 3 = 0]" )
449
449
# represents the two last e elements
@@ -497,7 +497,7 @@ def xpath(css):
497
497
assert xpath ('e > f' ) == (
498
498
"e/f" )
499
499
assert xpath ('e + f' ) == (
500
- "e/following-sibling::*[name() = 'f' and (position() = 1)]" )
500
+ "e/following-sibling::*[( name() = 'f') and (position() = 1)]" )
501
501
assert xpath ('e ~ f' ) == (
502
502
"e/following-sibling::f" )
503
503
assert xpath ('e ~ f:nth-child(3)' ) == (
@@ -622,6 +622,11 @@ def xpath_attr_href_simple_pseudo_element(self, xpath):
622
622
other = XPathExpr ('@href' , '' , )
623
623
return xpath .join ('/' , other )
624
624
625
+ # pseudo-element:
626
+ # used to demonstrate operator precedence
627
+ def xpath_first_or_second_pseudo (self , xpath ):
628
+ return xpath .add_condition ("@id = 'first' or @id = 'second'" )
629
+
625
630
def xpath (css ):
626
631
return _unicode (CustomTranslator ().css_to_xpath (css ))
627
632
@@ -633,6 +638,25 @@ def xpath(css):
633
638
assert xpath ('p img::attr(src)' ) == (
634
639
"descendant-or-self::p/descendant-or-self::*/img/@src" )
635
640
assert xpath (':scope' ) == "descendant-or-self::*[1]"
641
+ assert xpath (':first-or-second[href]' ) == (
642
+ "descendant-or-self::*[(@id = 'first' or @id = 'second') "
643
+ "and (@href)]" )
644
+
645
+ assert str (XPathExpr ('' , '' , condition = '@href' )) == "[@href]"
646
+
647
+ document = etree .fromstring (OPERATOR_PRECEDENCE_IDS )
648
+ sort_key = dict (
649
+ (el , count ) for count , el in enumerate (document .getiterator ())
650
+ ).__getitem__
651
+ def operator_id (selector ):
652
+ xpath = CustomTranslator ().css_to_xpath (selector )
653
+ items = document .xpath (xpath )
654
+ items .sort (key = sort_key )
655
+ return [element .get ('id' , 'nil' ) for element in items ]
656
+
657
+ assert operator_id (':first-or-second' ) == ['first' , 'second' ]
658
+ assert operator_id (':first-or-second[href]' ) == ['second' ]
659
+ assert operator_id ('[href]:first-or-second' ) == ['second' ]
636
660
637
661
def test_series (self ):
638
662
def series (css ):
@@ -935,6 +959,14 @@ def count(selector):
935
959
assert count (':scope > div > div[class=dialog]' ) == 1
936
960
assert count (':scope > div div' ) == 242
937
961
962
+ OPERATOR_PRECEDENCE_IDS = '''
963
+ <html>
964
+ <a id="first"></a>
965
+ <a id="second" href="#"></a>
966
+ <a id="third" href="#"></a>
967
+ </html>
968
+ '''
969
+
938
970
XMLLANG_IDS = '''
939
971
<test>
940
972
<a id="first" xml:lang="en">a</a>
0 commit comments