Initial commit
This commit is contained in:
commit
9fba4b3d34
17 changed files with 2939 additions and 0 deletions
443
tests/test_matching.py
Normal file
443
tests/test_matching.py
Normal file
|
@ -0,0 +1,443 @@
|
|||
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
|
Loading…
Add table
Add a link
Reference in a new issue