import pytest from src.node import Node from src.node_helpers import ParentAwareNode from src.parser import ( DescendantSelector, MultiSelector, NonMultiSelector, NotPseudoClassSelector, NthChildPseudoClassSelector, SiblingSelector, SimpleSelector, ) # This test tree matches the one from python discord's original test suite # # Structure: #
#
#

This is a heading!

#

# I have some content within this container also! #

#

# This is another paragraph. #

#

# This is a third paragraph. #

# # This is a button link. # #
#
#

This is a paragraph in a secondary container.

#
#
TEST_TREE = ParentAwareNode.from_node( Node( tag="div", attributes={"id": "topDiv"}, children=[ Node( tag="div", attributes={"id": "innerDiv", "class": "container colour-primary"}, children=[ Node(tag="h1", text="This is a heading!"), Node( tag="p", attributes={"class": "colour-secondary", "id": "innerContent"}, text="I have some content within this container also!", ), Node( tag="p", attributes={"class": "colour-secondary", "id": "two"}, text="This is another paragraph.", ), Node( tag="p", attributes={"class": "colour-secondary important"}, text="This is a third paragraph.", ), Node( tag="a", attributes={"id": "home-link", "class": "colour-primary button"}, text="This is a button link.", ), ], ), Node( tag="div", attributes={"class": "container colour-secondary"}, children=[ Node( tag="p", attributes={"class": "colour-primary"}, text="This is a paragraph in a secondary container.", ), ], ), ], ) ) # Get references to important nodes TOP_DIV = TEST_TREE INNER_DIV = TOP_DIV.children[0] H1 = INNER_DIV.children[0] P1 = INNER_DIV.children[1] P2 = INNER_DIV.children[2] P3 = INNER_DIV.children[3] A_LINK = INNER_DIV.children[4] SECOND_DIV = TEST_TREE.children[1] SECOND_P = SECOND_DIV.children[0] @pytest.mark.parametrize( ("selector", "node", "expected"), [ (SimpleSelector(tag="div"), TOP_DIV, True), (SimpleSelector(tag="div"), H1, False), (SimpleSelector(classes=["colour-primary"]), P1, False), (SimpleSelector(classes=["colour-secondary"]), P1, True), (SimpleSelector(ids=["home-link"]), A_LINK, True), (SimpleSelector(tag="a", classes=["button"], ids=["home-link"]), A_LINK, True), (SimpleSelector(tag="p", classes=["important"]), P3, True), (SimpleSelector(tag="p", classes=["nonexistent"]), P3, False), (SimpleSelector(tag="p", ids=["innerContent"]), P1, True), (SimpleSelector(tag="h1", ids=["innerContent"]), P1, False), ], ) def test_simple_selector(selector: SimpleSelector, node: ParentAwareNode, expected: bool) -> None: assert selector.match_node(node) is expected @pytest.mark.parametrize( ("selector", "node", "expected"), [ pytest.param( DescendantSelector( parent=SimpleSelector(tag="div", classes=["container"]), child=SimpleSelector(tag="h1"), direct=True, ), H1, True, id="div.container > h1 (on h1)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div"), child=DescendantSelector( parent=SimpleSelector(tag="div"), child=SimpleSelector(tag="p", classes=["important"]), direct=True, ), direct=True, ), P3, True, id="div > div > p.important (on p3)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div", classes=["container"]), child=SimpleSelector(tag="p", classes=["colour-secondary"]), direct=False, ), P1, True, id="div.container p.colour-secondary (on p1)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div", ids=["topDiv"]), child=SimpleSelector(tag="p", classes=["colour-secondary"]), direct=False, ), P1, True, id="div#topDiv p.colour-secondary (on p1)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div", classes=["container"]), child=SimpleSelector(tag="a", classes=["colour-secondary"]), direct=False, ), P1, False, id="div.container a.colour-secondary (on p1)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div", classes=["nonexistent"]), child=SimpleSelector(tag="p", classes=["colour-secondary"]), direct=False, ), P1, False, id="div.nonexistent p.colour-secondary (on p1)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div", ids=["topDiv"]), child=SimpleSelector(tag="div", ids=["topDiv"]), direct=False, ), TOP_DIV, False, id="div#topDiv div#topDiv (on top_div)", ), pytest.param( DescendantSelector( parent=SimpleSelector(tag="div"), child=SimpleSelector(tag="div", classes=["container"]), direct=False, ), A_LINK, True, id="div#topDiv > div.container > a (on a_link)", ), ], ) def test_descendant_selector_matching(selector: DescendantSelector, node: ParentAwareNode, expected: bool) -> None: assert selector.match_node(node) is expected @pytest.mark.parametrize( ("selector", "node", "expected"), [ pytest.param( SiblingSelector( sibling_selector=SimpleSelector(tag="p", classes=["important"]), selector=SimpleSelector(tag="a", classes=["button"]), is_adjacent=True, ), A_LINK, True, id="p.colour-secondary + a.button (on a_link)", ), pytest.param( SiblingSelector( sibling_selector=SimpleSelector(tag="p", classes=["colour-secondary"]), selector=SimpleSelector(tag="a", classes=["button"]), is_adjacent=False, ), A_LINK, True, id="p.colour-secondary ~ a.button (on a_link)", ), pytest.param( SiblingSelector( sibling_selector=SimpleSelector(tag="p", ids=["innerContent"]), selector=SimpleSelector(tag="p", ids=["two"]), is_adjacent=True, ), P2, True, id="p + p (on p2)", ), pytest.param( SiblingSelector( sibling_selector=SimpleSelector(tag="h1"), selector=SimpleSelector(tag="p", ids=["innerContent"]), is_adjacent=True, ), P1, True, id="h1 + p (on p1)", ), pytest.param( SiblingSelector( sibling_selector=SimpleSelector(tag="h1"), selector=SimpleSelector(tag="p", ids=["innerContent"]), is_adjacent=True, ), P2, False, id="h1 + p (on p2)", ), pytest.param( SiblingSelector( sibling_selector=SimpleSelector(tag="div"), selector=SimpleSelector(tag="h1"), is_adjacent=True, ), H1, False, id="div + h1 (on h1)", ), ], ) def test_sibling_selector_matching(selector: SiblingSelector, node: ParentAwareNode, expected: bool) -> None: assert selector.match_node(node) is expected @pytest.mark.parametrize( ("n", "node", "expected"), [ (1, H1, True), # first-child (2, P1, True), (3, P2, True), (4, P3, True), (5, A_LINK, True), (6, A_LINK, False), (-1, A_LINK, True), # last-child (-1, P3, False), (1, TOP_DIV, False), (1, SECOND_P, True), (-1, SECOND_P, True), (2, SECOND_P, False), ], ) def test_nth_child_selector(n: int, node: ParentAwareNode, expected: bool) -> None: selector = NthChildPseudoClassSelector(selector=None, n=n) assert selector.match_node(node) is expected @pytest.mark.parametrize( ("selector", "node", "expected"), [ pytest.param( NotPseudoClassSelector( selector=SimpleSelector("p"), not_selector=SimpleSelector(classes=["important"]), ), P1, True, id="p:not(.important) (on p1)", ), pytest.param( NotPseudoClassSelector( selector=SimpleSelector("p"), not_selector=SimpleSelector(classes=["important"]), ), P3, False, id="p:not(.important) (on p3)", ), pytest.param( NotPseudoClassSelector( selector=SimpleSelector("div"), not_selector=SimpleSelector(classes=["important"]), ), P1, False, id="div:not(.important) (on p1)", ), pytest.param( NotPseudoClassSelector( selector=None, not_selector=SimpleSelector(classes=["important"]), ), P1, True, id=":not(.important) (on p1)", ), pytest.param( NotPseudoClassSelector( selector=SimpleSelector("div"), not_selector=SimpleSelector(ids=["innerDiv"]), ), SECOND_DIV, True, id="div:not(#innerDiv) (on second_div)", ), pytest.param( NotPseudoClassSelector( selector=SimpleSelector("div"), not_selector=SimpleSelector(ids=["innerDiv"]), ), INNER_DIV, False, id="div:not(#innerDiv) (on inner_div)", ), pytest.param( NotPseudoClassSelector( selector=SimpleSelector("p"), not_selector=DescendantSelector( SimpleSelector(tag="div", classes=["colour-secondary"]), SimpleSelector(tag="p"), direct=False ), ), P2, True, id="p:not(div.colour-secondary p) (on p2)", ), pytest.param( NotPseudoClassSelector( selector=SimpleSelector("p"), not_selector=DescendantSelector( SimpleSelector(tag="div", classes=["container"]), SimpleSelector(tag="p"), direct=False, ), ), SECOND_P, False, id="p:not(div.container p) (on second_p)", ), ], ) def test_not_selector(selector: NotPseudoClassSelector, node: ParentAwareNode, expected: bool) -> None: assert selector.match_node(node) is expected @pytest.mark.parametrize( ("selectors", "node", "expected"), [ pytest.param( [ SimpleSelector(tag="a"), SimpleSelector(tag="p", ids=["innerContent"]), ], A_LINK, True, id="a, p#innerContent (on a_link)", ), pytest.param( [ SimpleSelector(tag="a"), SimpleSelector(tag="p", ids=["innerContent"]), ], P1, True, id="a, p#innerContent (on p1)", ), pytest.param( [ SimpleSelector(tag="a"), SimpleSelector(tag="p", ids=["innerContent"]), ], P2, False, id="a, p#innerContent (on p2)", ), pytest.param( [ SimpleSelector(tag="h1"), SimpleSelector(classes=["colour-primary"]), SimpleSelector(ids=["home-link"]), ], TOP_DIV, False, id="h1, .colour-primary, #home-link (on top_div)", ), pytest.param( [ NotPseudoClassSelector( selector=None, not_selector=SimpleSelector(tag="h1"), ), SimpleSelector(tag="p"), ], TOP_DIV, True, id=":not(h1), p", ), ], ) def test_multi_selector_matching(selectors: list[NonMultiSelector], node: ParentAwareNode, expected: bool) -> None: selector = MultiSelector(selectors=selectors) assert selector.match_node(node) is expected