Repository: facelessuser/soupsieve

Issue:
When testing HTML, test all HTML parsers (namespace will be handled differently)
Rework testing to so that when we test HTML, we test all parsers, and when we test namespace specific stuff, we test HTML5 and XHTML.

Discussion:

Patch:
diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md
index 3afed4f..8392c36 100644
--- a/docs/src/markdown/about/changelog.md
+++ b/docs/src/markdown/about/changelog.md
@@ -3,6 +3,7 @@
 ## 1.7.2
 
 - **FIX**: Fix HTML detection `type` selector.
+- **FIX**: Fixes for `:enabled` and `:disabled`.
 - **FIX**: Provide a way for Beautiful Soup to parse selectors in a quirks mode to mimic some of the quirks of the old
   select method prior to Soup Sieve, but with warnings. This is to help old scripts to not break during the transitional
   period with newest Beautiful Soup. In the future, these quirks will raise an exception as Soup Sieve requires
diff --git a/soupsieve/css_parser.py b/soupsieve/css_parser.py
index 2f9bbea..4fc46fd 100644
--- a/soupsieve/css_parser.py
+++ b/soupsieve/css_parser.py
@@ -706,15 +706,15 @@ class CSSParser(object):
         relations = []
         rel_type = ":" + WS_COMBINATOR
         split_last = False
-        is_open = flags & FLG_OPEN
-        is_pseudo = flags & FLG_PSEUDO
-        is_relative = flags & FLG_RELATIVE
-        is_not = flags & FLG_NOT
-        is_html = flags & FLG_HTML
-        is_default = flags & FLG_DEFAULT
-        is_indeterminate = flags & FLG_INDETERMINATE
-        is_in_range = flags & FLG_IN_RANGE
-        is_out_of_range = flags & FLG_OUT_OF_RANGE
+        is_open = bool(flags & FLG_OPEN)
+        is_pseudo = bool(flags & FLG_PSEUDO)
+        is_relative = bool(flags & FLG_RELATIVE)
+        is_not = bool(flags & FLG_NOT)
+        is_html = bool(flags & FLG_HTML)
+        is_default = bool(flags & FLG_DEFAULT)
+        is_indeterminate = bool(flags & FLG_INDETERMINATE)
+        is_in_range = bool(flags & FLG_IN_RANGE)
+        is_out_of_range = bool(flags & FLG_OUT_OF_RANGE)
 
         if self.debug:  # pragma: no cover
             if is_pseudo:
@@ -937,22 +937,16 @@ CSS_INDETERMINATE = CSSParser(
 # CSS pattern for `:disabled`
 CSS_DISABLED = CSSParser(
     '''
-    :is(input[type!=hidden], button, select, textarea, fieldset, optgroup, option)[disabled],
+    :is(input[type!=hidden], button, select, textarea, fieldset, optgroup, option, fieldset)[disabled],
     optgroup[disabled] > option,
-    fieldset[disabled] > :not(legend) :is(input[type!=hidden], button, select, textarea),
-    fieldset[disabled] > :is(input[type!=hidden], button, select, textarea)
+    fieldset[disabled] > :is(input[type!=hidden], button, select, textarea, fieldset),
+    fieldset[disabled] > :not(legend:nth-of-type(1)) :is(input[type!=hidden], button, select, textarea, fieldset)
     '''
 ).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
 # CSS pattern for `:enabled`
 CSS_ENABLED = CSSParser(
     '''
-    :is(a, area, link)[href],
-    :is(fieldset, optgroup):not([disabled]),
-    option:not(optgroup[disabled] *):not([disabled]),
-    :is(input[type!=hidden], button, select, textarea):not(
-        fieldset[disabled] > :not(legend) *,
-        fieldset[disabled] > *
-    ):not([disabled])
+    :is(input[type!=hidden], button, select, textarea, fieldset, optgroup, option, fieldset):not(:disabled)
     '''
 ).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
 # CSS pattern for `:required`


Test patch:
diff --git a/tests/test_level1.py b/tests/test_level1.py
index 8653e4a..2186477 100644
--- a/tests/test_level1.py
+++ b/tests/test_level1.py
@@ -31,7 +31,7 @@ class TestLevel1(util.TestCase):
             """,
             "span",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -40,7 +40,7 @@ class TestLevel1(util.TestCase):
             """,
             "prefix\\:p",
             ["0"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_tags(self):
@@ -56,7 +56,7 @@ class TestLevel1(util.TestCase):
             """,
             "span, a",
             ["1", "2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_child(self):
@@ -72,7 +72,7 @@ class TestLevel1(util.TestCase):
             """,
             "div span",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_id(self):
@@ -90,14 +90,14 @@ class TestLevel1(util.TestCase):
             markup,
             "#\\31",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "a#\\32",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_class(self):
@@ -115,14 +115,14 @@ class TestLevel1(util.TestCase):
             markup,
             ".foo",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "a.bar",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -149,7 +149,7 @@ class TestLevel1(util.TestCase):
             markup,
             "a.foo.bar",
             ["4"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_escapes(self):
@@ -167,7 +167,7 @@ class TestLevel1(util.TestCase):
             markup,
             ".foo\\:bar\\3a foobar",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_link(self):
@@ -186,7 +186,7 @@ class TestLevel1(util.TestCase):
             markup,
             ":link",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -212,7 +212,7 @@ class TestLevel1(util.TestCase):
             markup,
             "a:active",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_visited(self):
@@ -231,7 +231,7 @@ class TestLevel1(util.TestCase):
             markup,
             "a:visited",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
 
diff --git a/tests/test_level2.py b/tests/test_level2.py
index f8a5a6a..5b8ac36 100644
--- a/tests/test_level2.py
+++ b/tests/test_level2.py
@@ -55,7 +55,7 @@ class TestLevel2(util.TestCase):
             markup,
             "div > span",
             ["3"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # No spaces
@@ -63,7 +63,7 @@ class TestLevel2(util.TestCase):
             markup,
             "div>span",
             ["3"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_direct_sibling(self):
@@ -87,7 +87,7 @@ class TestLevel2(util.TestCase):
             markup,
             "span + span",
             ["5", "6"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # No spaces
@@ -95,7 +95,7 @@ class TestLevel2(util.TestCase):
             markup,
             "span+span",
             ["5", "6"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Complex
@@ -103,7 +103,7 @@ class TestLevel2(util.TestCase):
             markup,
             "span#\\34 + span#\\35",
             ["5"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_wild_tag(self):
@@ -111,6 +111,7 @@ class TestLevel2(util.TestCase):
 
         self.assert_selector(
             """
+            <body>
             <div id="div">
             <p id="0">Some text <span id="1"> in a paragraph</span>.</p>
             <a id="2" href="http://google.com">Link</a>
@@ -121,10 +122,11 @@ class TestLevel2(util.TestCase):
             <span id="6">Child 3</span>
             </pre>
             </div>
+            </body>
             """,
             "body *",
             ["0", "1", "2", "3", "4", "5", "6", "div", "pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     @util.skip_no_quirks
@@ -148,7 +150,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[href={}?/]",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     @util.skip_no_quirks
@@ -201,7 +203,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[href]",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # With spaces
@@ -209,7 +211,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[   href   ]",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_multi_attribute(self):
@@ -231,7 +233,7 @@ class TestLevel2(util.TestCase):
             """,
             "span[id].test[data-test=test]",
             ["5"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_attribute_equal(self):
@@ -255,7 +257,7 @@ class TestLevel2(util.TestCase):
             markup,
             '[id=\\35]',
             ["5"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Single quoted
@@ -263,7 +265,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[id='5']",
             ["5"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Double quoted
@@ -271,24 +273,10 @@ class TestLevel2(util.TestCase):
             markup,
             '[id="5"]',
             ["5"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # With spaces
-        self.assert_selector(
-            markup,
-            '[  id  =  "5"  ]',
-            ["5"],
-            flags=util.HTML5
-        )
-
-        self.assert_selector(
-            markup,
-            '[ID="5"]',
-            ["5"],
-            flags=util.HTML5
-        )
-
         self.assert_selector(
             markup,
             '[  id  =  "5"  ]',
@@ -330,13 +318,6 @@ class TestLevel2(util.TestCase):
         </html>
         """
 
-        self.assert_selector(
-            markup,
-            '[type="test"]',
-            ["0", '2'],
-            flags=util.HTML5
-        )
-
         self.assert_selector(
             markup,
             '[type="test"]',
@@ -344,13 +325,6 @@ class TestLevel2(util.TestCase):
             flags=util.HTML
         )
 
-        self.assert_selector(
-            markup,
-            '[type="test"]',
-            ["0", '2'],
-            flags=util.PYHTML
-        )
-
         self.assert_selector(
             markup,
             '[type="test"]',
@@ -405,7 +379,7 @@ class TestLevel2(util.TestCase):
             """,
             "[lang|=en]",
             ["0"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_attribute_contains_space(self):
@@ -429,7 +403,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[class~=test2]",
             ["0"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Shouldn't match anything
@@ -437,19 +411,19 @@ class TestLevel2(util.TestCase):
             markup,
             '[class~="test1 test2"]',
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
         self.assert_selector(
             markup,
             '[class~=""]',
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
         self.assert_selector(
             markup,
             '[class~="test1\\ test2"]',
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Start of list
@@ -457,7 +431,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[class~=test-a]",
             ["pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # End of list
@@ -465,7 +439,7 @@ class TestLevel2(util.TestCase):
             markup,
             "[class~=test-b]",
             ["pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_first_child(self):
@@ -486,7 +460,7 @@ class TestLevel2(util.TestCase):
             """,
             "span:first-child",
             ["1", "4"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_hover(self):
@@ -505,7 +479,7 @@ class TestLevel2(util.TestCase):
             markup,
             "a:hover",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_focus(self):
@@ -544,14 +518,14 @@ class TestLevel2(util.TestCase):
             markup,
             "input:focus",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "input:not(:focus)",
             ["1", "2", "3", "4", "5", "6"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_lang(self):
@@ -580,7 +554,7 @@ class TestLevel2(util.TestCase):
             markup,
             "p:lang(de)",
             ['1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_pseudo_element(self):
@@ -622,7 +596,7 @@ class TestLevel2(util.TestCase):
             /* End comment */
             """,
             ['3'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -634,7 +608,7 @@ class TestLevel2(util.TestCase):
             )
             """,
             ['1', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
 
diff --git a/tests/test_level3.py b/tests/test_level3.py
index cfad53c..08755b1 100644
--- a/tests/test_level3.py
+++ b/tests/test_level3.py
@@ -52,7 +52,7 @@ class TestLevel3(util.TestCase):
             """,
             "p ~ span",
             ["3"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_not(self):
@@ -75,21 +75,21 @@ class TestLevel3(util.TestCase):
             markup,
             'div :not([id="1"])',
             ["0", "2", "3", "4", "5", "6", "pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             'div :NOT([id="1"])',
             ["0", "2", "3", "4", "5", "6", "pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             'span:not([id="1"])',
             ["3", "4", "5", "6"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_attribute_begins(self):
@@ -110,7 +110,7 @@ class TestLevel3(util.TestCase):
             """,
             "[class^=here]",
             ["0"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_attribute_end(self):
@@ -131,7 +131,7 @@ class TestLevel3(util.TestCase):
             """,
             "[class$=words]",
             ["0"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_attribute_contains(self):
@@ -154,7 +154,7 @@ class TestLevel3(util.TestCase):
             markup,
             "[class*=words]",
             ["0", "3", "pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_root(self):
@@ -184,20 +184,21 @@ class TestLevel3(util.TestCase):
             markup,
             ":root",
             ["root"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":root > body > div",
             ["div"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_empty(self):
         """Test empty."""
 
         markup = """
+        <body>
         <div id="div">
         <p id="0" class="somewordshere">Some text <span id="1"> in a paragraph</span>.</p>
         <a id="2" href="http://google.com">Link</a>
@@ -209,13 +210,14 @@ class TestLevel3(util.TestCase):
         <span id="7"><span id="8"></span></span>
         </pre>
         </div>
+        </body>
         """
 
         self.assert_selector(
             markup,
             "body :empty",
             ["4", "5", "6", "8"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_last_child(self):
@@ -238,14 +240,14 @@ class TestLevel3(util.TestCase):
             markup,
             "span:last-child",
             ["1", "6"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "span:LAST-CHILD",
             ["1", "6"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_only_child(self):
@@ -266,7 +268,7 @@ class TestLevel3(util.TestCase):
             """,
             "span:only-child",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_namespace(self):
@@ -675,6 +677,7 @@ class TestLevel3(util.TestCase):
         """Test first of type."""
 
         markup = """
+        <body>
         <p id="0"></p>
         <p id="1"></p>
         <span id="2"></span>
@@ -687,33 +690,35 @@ class TestLevel3(util.TestCase):
         <p id="9"></p>
         <p id="10"></p>
         <span id="11"></span>
+        </body>
         """
 
         self.assert_selector(
             markup,
             "p:first-of-type",
             ['0'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "span:first-of-type",
             ['2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "body :first-of-type",
             ['0', '2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_last_of_type(self):
         """Test last of type."""
 
         markup = """
+        <body>
         <p id="0"></p>
         <p id="1"></p>
         <span id="2"></span>
@@ -726,27 +731,28 @@ class TestLevel3(util.TestCase):
         <p id="9"></p>
         <p id="10"></p>
         <span id="11"></span>
+        </body>
         """
 
         self.assert_selector(
             markup,
             "p:last-of-type",
             ['10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "span:last-of-type",
             ['11'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "body :last-of-type",
             ['10', '11'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_only_of_type(self):
@@ -771,13 +777,14 @@ class TestLevel3(util.TestCase):
             markup,
             "p:only-of-type",
             ['1', '4'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_nth_child(self):
         """Test `nth` child."""
 
         markup = """
+        <body>
         <p id="0"></p>
         <p id="1"></p>
         <span id="2"></span>
@@ -790,106 +797,113 @@ class TestLevel3(util.TestCase):
         <p id="9"></p>
         <p id="10"></p>
         <span id="11"></span>
+        </body>
         """
 
         self.assert_selector(
             markup,
             "p:nth-child(2n-5)",
-            ['0', '8', '10']
+            ['0', '8', '10'],
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:NTH-CHILD(2n-5)",
-            ['0', '8', '10']
+            ['0', '8', '10'],
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(-2n+20)",
-            ['1', '7', '9']
+            ['1', '7', '9'],
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(50n-20)",
-            []
+            [],
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(-2n-2)",
-            []
+            [],
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(-2)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(2)",
             ['1'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(9n - 1)",
             ['7'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(2n + 1)",
             ['0', '8', '10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(-n+3)",
             ['0', '1'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "span:nth-child(-n+3)",
             ['2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "body *:nth-child(-n+3)",
             ['0', '1', '2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(odd)",
             ['0', '8', '10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-child(even)",
             ['1', '7', '9'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
-        # Paragraph is the root. There is no document.
-        markup = """<p id="1">text</p>"""
-        soup = bs4.BeautifulSoup(markup, 'html5lib')
-        fragment = soup.p.extract()
-        self.assertTrue(sv.match("p:nth-child(1)", fragment, flags=sv.DEBUG))
+        for parser in ('html.parser', 'lxml', 'html5lib'):
+            # Paragraph is the root. There is no document.
+            markup = """<p id="1">text</p>"""
+            soup = bs4.BeautifulSoup(markup, parser)
+            fragment = soup.p.extract()
+            self.assertTrue(sv.match("p:nth-child(1)", fragment, flags=sv.DEBUG))
 
     def test_nth_child_issue_78(self):
         """Test issue for https://github.com/facelessuser/soupsieve/issues/78."""
@@ -946,6 +960,7 @@ class TestLevel3(util.TestCase):
         """Test `nth` last child."""
 
         markup = """
+        <body>
         <p id="0"></p>
         <p id="1"></p>
         <span id="2"></span>
@@ -958,26 +973,28 @@ class TestLevel3(util.TestCase):
         <p id="9"></p>
         <p id="10"></p>
         <span id="11"></span>
+        </body>
         """
 
         self.assert_selector(
             markup,
             "p:nth-last-child(2)",
             ['10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-last-child(2n + 1)",
             ['1', '7', '9'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_nth_of_type(self):
         """Test `nth` of type."""
 
         markup = """
+        <body>
         <p id="0"></p>
         <p id="1"></p>
         <span id="2"></span>
@@ -990,34 +1007,35 @@ class TestLevel3(util.TestCase):
         <p id="9"></p>
         <p id="10"></p>
         <span id="11"></span>
+        </body>
         """
 
         self.assert_selector(
             markup,
             "p:nth-of-type(3)",
             ['7'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-of-type(2n + 1)",
             ['0', '7', '9'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "span:nth-of-type(2n + 1)",
             ['2', '4', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "body :nth-of-type(2n + 1)",
             ['0', '2', '4', '6', '7', '9'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_nth_last_of_type(self):
@@ -1042,20 +1060,21 @@ class TestLevel3(util.TestCase):
             markup,
             "p:nth-last-of-type(3)",
             ['8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:nth-last-of-type(2n + 1)",
             ['1', '8', '10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_checked(self):
         """Test checked."""
 
         markup = """
+        <body>
         <div>
           <input type="radio" name="my-input" id="yes" checked>
           <label for="yes">Yes</label>
@@ -1074,13 +1093,14 @@ class TestLevel3(util.TestCase):
           <option id="2" value="opt2" selected>Grapes</option>
           <option id="3" value="opt3">Pears</option>
         </select>
+        </body>
         """
 
         self.assert_selector(
             markup,
             ":checked",
             ['yes', 'opt-in', '2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_disabled(self):
@@ -1093,6 +1113,7 @@ class TestLevel3(util.TestCase):
         """
 
         markup = """
+        <body>
         <form action="#">
           <fieldset id='a' disabled>
             <legend>
@@ -1118,9 +1139,11 @@ class TestLevel3(util.TestCase):
             <option id="9">option</option>
           </optgroup>
         </form>
+        </body>
         """
 
         markup2 = """
+        <body>
         <form action="#">
           <fieldset id='a' disabled>
             <legend>
@@ -1146,6 +1169,7 @@ class TestLevel3(util.TestCase):
             <option id="9">option</option>
           </optgroup>
         </form>
+        </body>
         """
 
         self.assert_selector(
@@ -1155,6 +1179,20 @@ class TestLevel3(util.TestCase):
             flags=util.HTML5
         )
 
+        self.assert_selector(
+            markup,
+            ":disabled",
+            ['2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c'],
+            flags=util.LXML_HTML
+        )
+
+        self.assert_selector(
+            markup,
+            ":disabled",
+            ['3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c'],
+            flags=util.PYHTML
+        )
+
         self.assert_selector(
             markup2,
             ":disabled",
@@ -1162,6 +1200,20 @@ class TestLevel3(util.TestCase):
             flags=util.HTML5
         )
 
+        self.assert_selector(
+            markup2,
+            ":disabled",
+            ['2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c'],
+            flags=util.LXML_HTML
+        )
+
+        self.assert_selector(
+            markup2,
+            ":disabled",
+            ['4', '5', '6', '7', '8', '9', 'a', 'c'],
+            flags=util.PYHTML
+        )
+
     def test_enable(self):
         """
         Test enable.
@@ -1172,6 +1224,7 @@ class TestLevel3(util.TestCase):
         """
 
         markup = """
+        <body>
         <form action="#">
           <fieldset id='a' disabled>
             <legend>
@@ -1198,9 +1251,11 @@ class TestLevel3(util.TestCase):
           </optgroup>
           <a href="" id="link">text</a>
         </form>
+        </body>
         """
 
         markup2 = """
+        <body>
         <form action="#">
           <fieldset id='a' disabled>
             <legend>
@@ -1227,22 +1282,51 @@ class TestLevel3(util.TestCase):
           </optgroup>
           <a href="" id="link">text</a>
         </form>
+        </body>
         """
 
         self.assert_selector(
             markup,
             ":enabled",
-            ['1', '2', 'opt-enable', 'link'],
+            ['1', '2', 'opt-enable'],
             flags=util.HTML5
         )
 
+        self.assert_selector(
+            markup,
+            ":enabled",
+            ['1', 'opt-enable'],
+            flags=util.LXML_HTML
+        )
+
+        self.assert_selector(
+            markup,
+            ":enabled",
+            ['1', '2', 'opt-enable'],
+            flags=util.PYHTML
+        )
+
         self.assert_selector(
             markup2,
             ":enabled",
-            ['1', '2', 'opt-enable', 'b', '3', 'link'],
+            ['1', '2', 'opt-enable', 'b', '3'],
             flags=util.HTML5
         )
 
+        self.assert_selector(
+            markup2,
+            ":enabled",
+            ['1', 'opt-enable'],
+            flags=util.LXML_HTML
+        )
+
+        self.assert_selector(
+            markup2,
+            ":enabled",
+            ['1', '2', 'opt-enable', 'b', '3'],
+            flags=util.PYHTML
+        )
+
     def test_target(self):
         """Test target."""
 
@@ -1259,14 +1343,14 @@ class TestLevel3(util.TestCase):
             markup,
             "#head-2:target",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "#head-2:not(:target)",
             ["head-2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
 
diff --git a/tests/test_level4.py b/tests/test_level4.py
index 65d6b39..efdf28e 100644
--- a/tests/test_level4.py
+++ b/tests/test_level4.py
@@ -78,14 +78,14 @@ class TestLevel4(util.TestCase):
             markup,
             "[class*=WORDS]",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "[class*=WORDS i]",
             ["0", "3", "pre"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_raises('[id i]', SyntaxError)
@@ -114,7 +114,7 @@ class TestLevel4(util.TestCase):
             markup,
             '[type="test" s]',
             ['2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -139,21 +139,21 @@ class TestLevel4(util.TestCase):
             markup,
             ":is(span, a)",
             ["1", "2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":is(span, a:matches(#\\32))",
             ["1", "2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":where(span, a:matches(#\\32))",
             ["1", "2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Each pseudo class is evaluated separately
@@ -162,7 +162,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":is(span):not(span)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Each pseudo class is evaluated separately
@@ -171,7 +171,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":is(span):is(div)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Each pseudo class is evaluated separately
@@ -180,7 +180,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":is(a):is(#\\32)",
             ['2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_multi_nested_not(self):
@@ -203,7 +203,7 @@ class TestLevel4(util.TestCase):
             markup,
             'div :not(p, :not([id=\\35]))',
             ['5'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_has(self):
@@ -252,56 +252,56 @@ class TestLevel4(util.TestCase):
             markup,
             'div:not(.aaaa):has(.kkkk > p.llll)',
             ['4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             'div:NOT(.aaaa):HAS(.kkkk > p.llll)',
             ['4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             'p:has(+ .dddd:has(+ div .jjjj))',
             ['2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             'p:has(~ .jjjj)',
             ['7', '8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup2,
             'div:has(> .bbbb, .ffff, .jjjj)',
             ['0', '4', '8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup2,
             'div:has(.ffff, > .bbbb, .jjjj)',
             ['0', '4', '8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup2,
             'div:has(> :not(.bbbb, .ffff, .jjjj))',
             ['2', '6', '8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup2,
             'div:not(:has(> .bbbb, .ffff, .jjjj))',
             ['2', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_nth_child_of_s(self):
@@ -326,14 +326,14 @@ class TestLevel4(util.TestCase):
             markup,
             ":nth-child(2n + 1 of :is(p, span).test)",
             ['2', '6', '10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":nth-child(-n+3 of p)",
             ['0', '1', '7'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_anylink(self):
@@ -352,7 +352,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":any-link",
             ["2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -382,14 +382,14 @@ class TestLevel4(util.TestCase):
             markup,
             "form:focus-within",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "form:not(:focus-within)",
             ["form"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_focus_visible(self):
@@ -405,14 +405,14 @@ class TestLevel4(util.TestCase):
             markup,
             "form:focus-visible",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "form:not(:focus-visible)",
             ["form"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_target_within(self):
@@ -432,82 +432,84 @@ class TestLevel4(util.TestCase):
             markup,
             "article:target-within",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "article:not(:target-within)",
             ["article"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_current_past_future(self):
         """Test current, past, future."""
 
         markup = """
+        <body>
         <div id="div">
         <p id="0">Some text <span id="1" class="foo:bar:foobar"> in a paragraph</span>.
         <a id="2" class="bar" href="http://google.com">Link</a>
         <a id="3">Placeholder text.</a>
         </p>
         </div>
+        </body>
         """
 
         self.assert_selector(
             markup,
             ":current(p, div, a)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":current(p, :not(div), a)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "body :not(:current(p, div, a))",
             ["div", "0", "1", "2", "3"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "body :not(:current(p, :not(div), a))",
             ["div", "0", "1", "2", "3"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:current",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:not(:current)",
             ["0"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:past",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "p:future",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_required(self):
@@ -518,8 +520,8 @@ class TestLevel4(util.TestCase):
         <input id="1" type="name" required>
         <input id="2" type="checkbox" required>
         <input id="3" type="email">
-        <textarea id="4" name="name" id="message" cols="30" rows="10" required></textarea>
-        <select id="5" name="nm" id="sel" required>
+        <textarea id="4" name="name" cols="30" rows="10" required></textarea>
+        <select id="5" name="nm" required>
             <!-- options -->
         </select>
         </form>
@@ -529,14 +531,14 @@ class TestLevel4(util.TestCase):
             markup,
             ":required",
             ['1', '2', '4', '5'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "input:required",
             ['1', '2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_optional(self):
@@ -547,8 +549,8 @@ class TestLevel4(util.TestCase):
         <input id="1" type="name" required>
         <input id="2" type="checkbox" required>
         <input id="3" type="email">
-        <textarea id="4" name="name" id="message" cols="30" rows="10"></textarea>
-        <select id="5" name="nm" id="sel">
+        <textarea id="4" name="name" cols="30" rows="10"></textarea>
+        <select id="5" name="nm">
             <!-- options -->
         </select>
         </form>
@@ -558,14 +560,14 @@ class TestLevel4(util.TestCase):
             markup,
             ":optional",
             ['3', '4', '5'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "input:optional",
             ['3'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_default(self):
@@ -636,7 +638,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":default",
             ['summer', 'd1', 'd3', 'hamster', 'enable'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # This is technically invalid use of forms, but browsers will generally evaluated the nested form
@@ -659,7 +661,7 @@ class TestLevel4(util.TestCase):
             markup2,
             ":default",
             ['d1'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # For the sake of coverage, we will do this impractical select
@@ -668,7 +670,7 @@ class TestLevel4(util.TestCase):
             markup2,
             ":default:default",
             ['d1'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # You shouldn't nest forms, but if you do,
@@ -692,7 +694,7 @@ class TestLevel4(util.TestCase):
             markup3,
             ":default",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_indeterminate(self):
@@ -732,7 +734,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":indeterminate",
             ['checkbox', 'radio1', 'radio6', 'radio4', 'radio5', 'radio-no-name1'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_placeholder(self):
@@ -776,7 +778,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":placeholder-shown",
             ['0', '1', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_lang(self):
@@ -806,7 +808,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang(de-DE)",
             ['1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Explicit wild
@@ -814,7 +816,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang(de-\\*-DE)",
             ['1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # First wild has meaning (escaped)
@@ -822,7 +824,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang(\\*-DE)",
             ['1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # First wild quoted
@@ -830,7 +832,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang('*-DE')",
             ['1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Normal quoted
@@ -846,7 +848,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p[lang]:lang(de-DE)",
             ['6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Undetermined language
@@ -860,7 +862,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang(en)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Find language in header
@@ -884,7 +886,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang('*-US')",
             ['1', '2'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # XML style language when out of HTML namespace
@@ -1033,7 +1035,7 @@ class TestLevel4(util.TestCase):
             markup,
             "p:lang(de-DE, '*-US')",
             ['1', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_scope(self):
@@ -1063,46 +1065,47 @@ class TestLevel4(util.TestCase):
             markup,
             ":scope",
             ["root"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":scope > body > div",
             ["div"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
-        soup = bs4.BeautifulSoup(textwrap.dedent(markup.replace('\r\n', '\n')), 'html5lib')
-        el = soup.html
+        for parser in ('html.parser', 'lxml', 'html5lib', 'xml'):
+            soup = bs4.BeautifulSoup(textwrap.dedent(markup.replace('\r\n', '\n')), parser)
+            el = soup.html
 
-        # Scope is the element we are applying the select to, and that element is never returned
-        self.assertTrue(len(sv.select(':scope', el, flags=sv.DEBUG)) == 0)
+            # Scope is the element we are applying the select to, and that element is never returned
+            self.assertTrue(len(sv.select(':scope', el, flags=sv.DEBUG)) == 0)
 
-        # Scope here means the current element under select
-        ids = []
-        for el in sv.select(':scope div', el, flags=sv.DEBUG):
-            ids.append(el.attrs['id'])
-        self.assertEqual(sorted(ids), sorted(['div']))
+            # Scope here means the current element under select
+            ids = []
+            for el in sv.select(':scope div', el, flags=sv.DEBUG):
+                ids.append(el.attrs['id'])
+            self.assertEqual(sorted(ids), sorted(['div']))
 
-        el = soup.body
-        ids = []
-        for el in sv.select(':scope div', el, flags=sv.DEBUG):
-            ids.append(el.attrs['id'])
-        self.assertEqual(sorted(ids), sorted(['div']))
+            el = soup.body
+            ids = []
+            for el in sv.select(':scope div', el, flags=sv.DEBUG):
+                ids.append(el.attrs['id'])
+            self.assertEqual(sorted(ids), sorted(['div']))
 
-        # `div` is the current element under select, and it has no `div` elements.
-        el = soup.div
-        ids = []
-        for el in sv.select(':scope div', el, flags=sv.DEBUG):
-            ids.append(el.attrs['id'])
-        self.assertEqual(sorted(ids), sorted([]))
+            # `div` is the current element under select, and it has no `div` elements.
+            el = soup.div
+            ids = []
+            for el in sv.select(':scope div', el, flags=sv.DEBUG):
+                ids.append(el.attrs['id'])
+            self.assertEqual(sorted(ids), sorted([]))
 
-        # `div` does have an element with the class `.wordshere`
-        ids = []
-        for el in sv.select(':scope .wordshere', el, flags=sv.DEBUG):
-            ids.append(el.attrs['id'])
-        self.assertEqual(sorted(ids), sorted(['pre']))
+            # `div` does have an element with the class `.wordshere`
+            ids = []
+            for el in sv.select(':scope .wordshere', el, flags=sv.DEBUG):
+                ids.append(el.attrs['id'])
+            self.assertEqual(sorted(ids), sorted(['pre']))
 
     def test_user_invalid(self):
         """Test user invalid (matches nothing)."""
@@ -1117,14 +1120,14 @@ class TestLevel4(util.TestCase):
             markup,
             "input:user-invalid",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "input:not(:user-invalid)",
             ["1"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_playing(self):
@@ -1150,14 +1153,14 @@ class TestLevel4(util.TestCase):
             markup,
             "video:playing",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "video:not(:playing)",
             ["vid"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_paused(self):
@@ -1183,14 +1186,14 @@ class TestLevel4(util.TestCase):
             markup,
             "video:paused",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "video:not(:paused)",
             ["vid"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_local_link(self):
@@ -1205,14 +1208,14 @@ class TestLevel4(util.TestCase):
             markup,
             "a:local-link",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "a:not(:local-link)",
             ["1", "2"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_read_write(self):
@@ -1267,13 +1270,14 @@ class TestLevel4(util.TestCase):
                 '0', '1', '2', '4', '5', '6', '7', '8', '9', '10', '11',
                 '12', '16', '17', '21', '26', '27', '28', '29', '30'
             ],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_read_only(self):
         """Test read only."""
 
         markup = """
+        <body>
         <input id="0">
         <textarea id="1"></textarea>
 
@@ -1313,6 +1317,7 @@ class TestLevel4(util.TestCase):
         <p id="32">Text</p>
 
         <input id="33" type="number" readonly>
+        </body>
         """
 
         self.assert_selector(
@@ -1322,7 +1327,7 @@ class TestLevel4(util.TestCase):
                 '3', '13', '14', '15', '18', '19', '20', '22',
                 '23', '24', '25', '31', '32', '33'
             ],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_host(self):
@@ -1334,14 +1339,14 @@ class TestLevel4(util.TestCase):
             markup,
             ":host",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             ":host(h1)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_host_context(self):
@@ -1353,7 +1358,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":host-context(h1, h2)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_dir(self):
@@ -1396,14 +1401,14 @@ class TestLevel4(util.TestCase):
             markup,
             "div:dir(ltr)",
             ["3"],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "div:dir(ltr):dir(rtl)",
             [],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -1417,14 +1422,14 @@ class TestLevel4(util.TestCase):
             markup,
             "span:dir(rtl)",
             ['2', '5', '7'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
             markup,
             "span:dir(ltr)",
             ['8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         self.assert_selector(
@@ -1438,7 +1443,7 @@ class TestLevel4(util.TestCase):
             markup,
             "html:dir(ltr)",
             ['0'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1453,14 +1458,15 @@ class TestLevel4(util.TestCase):
             markup,
             "html:dir(ltr)",
             ['0'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         # Input is root
-        markup = """<input id="1" type="text" dir="auto">"""
-        soup = bs4.BeautifulSoup(markup, 'html5lib')
-        fragment = soup.input.extract()
-        self.assertTrue(sv.match(":root:dir(ltr)", fragment, flags=sv.DEBUG))
+        for parser in ('html.parser', 'lxml', 'html5lib'):
+            markup = """<input id="1" type="text" dir="auto">"""
+            soup = bs4.BeautifulSoup(markup, parser)
+            fragment = soup.input.extract()
+            self.assertTrue(sv.match(":root:dir(ltr)", fragment, flags=sv.DEBUG))
 
     def test_in_range(self):
         """Test in range."""
@@ -1491,7 +1497,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6', '7', '8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1519,7 +1525,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6', '7', '8'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1547,7 +1553,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1576,7 +1582,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6', '7'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1606,7 +1612,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1640,7 +1646,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1672,7 +1678,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":in-range",
             ['0', '1', '2', '3', '4', '5', '6', '7'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
     def test_out_of_range(self):
@@ -1704,7 +1710,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['9', '10', '11'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1732,7 +1738,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['9', '10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1760,7 +1766,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['7', '8', '9', '10'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1789,7 +1795,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['8', '9', '10', '11'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1819,7 +1825,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['7', '8', '9', '10', '11', '12'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1853,7 +1859,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['7', '8', '9', '10', '11', '12', '13', '14', '15', '16'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
         markup = """
@@ -1885,7 +1891,7 @@ class TestLevel4(util.TestCase):
             markup,
             ":out-of-range",
             ['8', '9', '10', '11', '12', '13', '14'],
-            flags=util.HTML5
+            flags=util.HTML
         )
 
 
diff --git a/tests/util.py b/tests/util.py
index 958873a..003d5cd 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -8,11 +8,12 @@ import sys
 
 PY3 = sys.version_info >= (3, 0)
 
-HTML5 = 1
-HTML = 2
-XHTML = 4
-XML = 8
-PYHTML = 10
+HTML5 = 0x1
+HTML = 0x2
+XHTML = 0x4
+XML = 0x8
+PYHTML = 0x10
+LXML_HTML = 0x20
 
 
 def skip_quirks(func):
@@ -66,23 +67,28 @@ class TestCase(unittest.TestCase):
     def assert_selector(self, markup, selectors, expected_ids, namespaces={}, flags=0):
         """Assert selector."""
 
-        mode = flags & 0x0F
-        if mode == PYHTML:
-            bs_mode = 'html.parser'
-        elif mode == HTML:
-            bs_mode = 'lxml'
+        mode = flags & 0x2F
+        if mode == HTML:
+            bs_mode = ('html5lib', 'lxml', 'html.parser')
+        elif mode == PYHTML:
+            bs_mode = ('html.parser',)
+        elif mode == LXML_HTML:
+            bs_mode = ('lxml',)
         elif mode in (HTML5, 0):
-            bs_mode = 'html5lib'
+            bs_mode = ('html5lib',)
         elif mode in (XHTML, XML):
-            bs_mode = 'xml'
-        soup = bs4.BeautifulSoup(textwrap.dedent(markup.replace('\r\n', '\n')), bs_mode)
-
-        flags = sv.DEBUG
-        if self.quirks:
-            flags = sv._QUIRKS
-
-        ids = []
-        for el in sv.select(selectors, soup, namespaces=namespaces, flags=flags):
-            print('TAG: ', el.name)
-            ids.append(el.attrs['id'])
-        self.assertEqual(sorted(ids), sorted(expected_ids))
+            bs_mode = ('xml',)
+
+        for parser in bs_mode:
+            print('PARSER: ', parser)
+            soup = bs4.BeautifulSoup(textwrap.dedent(markup.replace('\r\n', '\n')), parser)
+            # print(soup)
+            flags |= sv.DEBUG
+            if self.quirks:
+                flags |= sv._QUIRKS
+
+            ids = []
+            for el in sv.select(selectors, soup, namespaces=namespaces, flags=flags):
+                print('TAG: ', el.name)
+                ids.append(el.attrs['id'])
+            self.assertEqual(sorted(ids), sorted(expected_ids))