@@ -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