443 lines
14 KiB
Python
443 lines
14 KiB
Python
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:
|
|
# <div id="topDiv">
|
|
# <div class="container colour-primary" id="innerDiv">
|
|
# <h1>This is a heading!</h1>
|
|
# <p class="colour-secondary" id="innerContent">
|
|
# I have some content within this container also!
|
|
# </p>
|
|
# <p class="colour-secondary" id="two">
|
|
# This is another paragraph.
|
|
# </p>
|
|
# <p class="colour-secondary important">
|
|
# This is a third paragraph.
|
|
# </p>
|
|
# <a class="colour-primary button" id="home-link">
|
|
# This is a button link.
|
|
# </a>
|
|
# </div>
|
|
# <div class="container colour-secondary">
|
|
# <p class="colour-primary">This is a paragraph in a secondary container.</p>
|
|
# </div>
|
|
# </div>
|
|
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
|