@@ -428,8 +428,8 @@ def xpath(css):
428428 "e[count(preceding-sibling::*) <= 0]" )
429429
430430 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) ]" )
433433 assert xpath ('e:nth-child(3n-2)' ) == (
434434 "e[count(preceding-sibling::*) mod 3 = 0]" )
435435 assert xpath ('e:nth-child(-n+6)' ) == (
@@ -442,8 +442,8 @@ def xpath(css):
442442 assert xpath ('e:nth-last-child(2n+1)' ) == (
443443 "e[count(following-sibling::*) mod 2 = 0]" )
444444 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) ]" )
447447 assert xpath ('e:nth-last-child(3n+1)' ) == (
448448 "e[count(following-sibling::*) mod 3 = 0]" )
449449 # represents the two last e elements
@@ -497,7 +497,7 @@ def xpath(css):
497497 assert xpath ('e > f' ) == (
498498 "e/f" )
499499 assert xpath ('e + f' ) == (
500- "e/following-sibling::*[name() = 'f' and (position() = 1)]" )
500+ "e/following-sibling::*[( name() = 'f') and (position() = 1)]" )
501501 assert xpath ('e ~ f' ) == (
502502 "e/following-sibling::f" )
503503 assert xpath ('e ~ f:nth-child(3)' ) == (
@@ -622,6 +622,11 @@ def xpath_attr_href_simple_pseudo_element(self, xpath):
622622 other = XPathExpr ('@href' , '' , )
623623 return xpath .join ('/' , other )
624624
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+
625630 def xpath (css ):
626631 return _unicode (CustomTranslator ().css_to_xpath (css ))
627632
@@ -633,6 +638,25 @@ def xpath(css):
633638 assert xpath ('p img::attr(src)' ) == (
634639 "descendant-or-self::p/descendant-or-self::*/img/@src" )
635640 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' ]
636660
637661 def test_series (self ):
638662 def series (css ):
@@ -935,6 +959,14 @@ def count(selector):
935959 assert count (':scope > div > div[class=dialog]' ) == 1
936960 assert count (':scope > div div' ) == 242
937961
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+
938970XMLLANG_IDS = '''
939971<test>
940972 <a id="first" xml:lang="en">a</a>
0 commit comments