Initial commit
This commit is contained in:
commit
9fba4b3d34
17 changed files with 2939 additions and 0 deletions
70
tests/hypot.py
Normal file
70
tests/hypot.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
"""Reusable hypothesis strategies for CSS selectors."""
|
||||
|
||||
from typing import cast
|
||||
|
||||
from hypothesis import strategies as st
|
||||
|
||||
# CSS identifier (used for tag, class, ID names)
|
||||
css_ident = st.from_regex(r"[_a-zA-Z][_a-zA-Z0-9-]*", fullmatch=True)
|
||||
|
||||
css_tag = css_ident
|
||||
css_class = css_ident.map(lambda x: f".{x}")
|
||||
css_class_multi = st.lists(css_class, min_size=1).map("".join)
|
||||
css_id = css_ident.map(lambda x: f"#{x}")
|
||||
|
||||
# Any amount of whitespace, including none (spaces only)
|
||||
_whitespace = st.from_regex(r" *", fullmatch=True)
|
||||
|
||||
|
||||
def simple_selector() -> st.SearchStrategy[str]:
|
||||
"""Returns a strategy for simple seletors: tag, .class, #id (all optional, at least one)."""
|
||||
|
||||
# Optional tag + (one or more classes) + optional id, at least one item present
|
||||
parts = st.tuples(
|
||||
st.none() | css_tag,
|
||||
st.none() | css_class_multi,
|
||||
st.none() | css_id,
|
||||
).filter(lambda parts: any(parts))
|
||||
|
||||
return parts.map(lambda parts: "".join(p for p in parts if p))
|
||||
|
||||
|
||||
# nth-child pseudo-classes
|
||||
nth_child_suffix = st.from_regex(r":nth-child\([1-9][0-9]*\)", fullmatch=True)
|
||||
_specific_child_pseudo_classes = st.sampled_from([":first-child", ":last-child"])
|
||||
any_nth_child_suffix = st.one_of(nth_child_suffix, _specific_child_pseudo_classes)
|
||||
|
||||
|
||||
def not_selector(inner: st.SearchStrategy[str]) -> st.SearchStrategy[str]:
|
||||
return st.tuples(st.just(":not("), _whitespace, inner, _whitespace, st.just(")")).map("".join)
|
||||
|
||||
|
||||
def pseudo_suffixes(inner: st.SearchStrategy[str]) -> st.SearchStrategy[str]:
|
||||
"""Generate a single random pseudo-class suffix."""
|
||||
return st.one_of(
|
||||
not_selector(inner),
|
||||
any_nth_child_suffix,
|
||||
)
|
||||
|
||||
|
||||
combinator = st.tuples(_whitespace, st.sampled_from([">", " ", "+", ",", "~"]), _whitespace).map("".join)
|
||||
|
||||
selector = st.recursive(
|
||||
# Base: simple selector + pseudo-classes
|
||||
base=simple_selector(),
|
||||
extend=lambda s: st.one_of(
|
||||
# Add combinator + selector sequence
|
||||
cast(
|
||||
"st.SearchStrategy[str]",
|
||||
st.builds(
|
||||
lambda left, comb, right: left + comb + right,
|
||||
s,
|
||||
combinator,
|
||||
s,
|
||||
),
|
||||
),
|
||||
# Apply pseudo-suffix
|
||||
st.tuples(s, pseudo_suffixes(s)).map("".join),
|
||||
),
|
||||
max_leaves=10,
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue