441 lines
14 KiB
Python
441 lines
14 KiB
Python
import pytest
|
|
from fasthtml.components import *
|
|
|
|
from components.datagrid.DataGrid import DataGrid
|
|
from components.debugger.components.JsonViewer import JsonViewer
|
|
from helpers import *
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_structure():
|
|
"""
|
|
A pytest fixture to provide a sample tree structure for testing.
|
|
"""
|
|
return Div(
|
|
Header(cls="first-class"),
|
|
Body(
|
|
"hello world",
|
|
Div(
|
|
Span(cls="highlight"),
|
|
Span(id="inner", name="child"),
|
|
id="content"),
|
|
),
|
|
Footer(),
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("value, expected, expected_error", [
|
|
(Div(), "value",
|
|
"The types are different: <class 'fastcore.xml.FT'> != <class 'str'>\nactual=<div></div>\nexpected=value."),
|
|
(Div(), A(),
|
|
"The elements are different: 'div' != 'a'."),
|
|
(Div(Div()), Div(A()),
|
|
"Path 'div':\n\tThe elements are different: 'div' != 'a'."),
|
|
(Div(A(Span())), Div(A("element")),
|
|
"Path 'div.a':\n\tThe types are different: <class 'fastcore.xml.FT'> != <class 'str'>\nactual=<span></span>\nexpected=element."),
|
|
(Div(attr="one"), Div(attr="two"),
|
|
"Path 'div':\n\tThe values are different for 'attr' : 'one' != 'two'."),
|
|
(Div(A(attr="alpha")), Div(A(attr="beta")),
|
|
"Path 'div.a':\n\tThe values are different for 'attr' : 'alpha' != 'beta'."),
|
|
(Div(Div(), A()), Div(Div(), Span()),
|
|
"Path 'div':\n\tThe elements are different: 'a' != 'span'."),
|
|
(Div(A()), Div(A(attr="beta")),
|
|
"Path 'div.a':\n\tAttribute 'attr' is not found (with expected value: 'beta'). actual='{}'."),
|
|
(Div(id="one"), Div(id="two"),
|
|
"Path 'div#one':\n\tThe values are different for 'id' : 'one' != 'two'."),
|
|
(Div(id="same_id", attr="one"), Div(id="same_id", attr="two"),
|
|
"Path 'div#same_id':\n\tThe values are different for 'attr' : 'one' != 'two'."),
|
|
(Div(name="same_name", attr="one"), Div(name="same_name", attr="two"),
|
|
"Path 'div[name=same_name]':\n\tThe values are different for 'attr' : 'one' != 'two'."),
|
|
(Div(cls="same_class", attr="one"), Div(cls="same_class", attr="two"),
|
|
"Path 'div[class=same_class]':\n\tThe values are different for 'attr' : 'one' != 'two'."),
|
|
(Div(attr="value"), Div(Empty),
|
|
"Empty element expected, but found attributes {'attr': 'value'}."),
|
|
(Div(Div()), Div(Empty),
|
|
"Empty element expected, but found children (div((),{}),)."),
|
|
(Div(cls="a long attr"), Div(cls=StartsWith("different start")),
|
|
"Path 'div[class=a long attr]':\n\tAttribute 'class' does not start with 'different start': actual='a long attr', expected ='different start'."),
|
|
(Div(cls="a long attr"), Div(cls=Contains("not included")),
|
|
"Path 'div[class=a long attr]':\n\tAttribute 'class' does not contain 'not included': actual='a long attr', expected ='not included'."),
|
|
|
|
])
|
|
def test_matches_error_expected(value, expected, expected_error):
|
|
with pytest.raises(AssertionError) as error:
|
|
matches(value, expected)
|
|
|
|
assert error.value.args[0] == expected_error
|
|
|
|
|
|
@pytest.mark.parametrize("value, expected", [
|
|
(Div(), Div()),
|
|
(Div(A()), Div(A())),
|
|
(Div(id='do_not_validate'), Div(id='do_not_validate')),
|
|
(Div(A()), Div()), # children of actual are not selected
|
|
(Div(A(), Span(id="validate_please"), A(id="do_not_care")), Div(A(), Span(id="validate_please"))),
|
|
(Div(), Div(Empty)),
|
|
(Div(cls="a long attr"), Div(cls=StartsWith("a long"))),
|
|
(Div(cls="a long attr"), Div(cls=Contains("long"))),
|
|
])
|
|
def test_matches_success_expected(value, expected):
|
|
assert matches(value, expected)
|
|
|
|
|
|
def test_i_can_search_elements_by_name():
|
|
to_find = Table()
|
|
res = search_elements_by_name(to_find, "table")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(to_find))
|
|
res = search_elements_by_name(ft, "table")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "table")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(to_find, Span(to_find))
|
|
res = search_elements_by_name(ft, "table")
|
|
assert res == [to_find, to_find]
|
|
|
|
|
|
def test_i_can_search_not_str_by_name():
|
|
to_find = NotStr("Hello World")
|
|
|
|
res = search_elements_by_name(to_find, "NotStr")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(to_find))
|
|
res = search_elements_by_name(ft, "NotStr")
|
|
assert res == [to_find]
|
|
|
|
|
|
def test_i_can_search_elements_by_name_with_attr_and_exact_compare():
|
|
to_find = Table(attr="value")
|
|
|
|
res = search_elements_by_name(to_find, None, {"attr": "value"})
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, None, {"attr": "value"})
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "table", {"attr": "value"})
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "other", {"attr": "value"})
|
|
assert res == []
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "table", {"attr": "value2"})
|
|
assert res == []
|
|
|
|
ft = Div(Span(Table()))
|
|
res = search_elements_by_name(ft, "table", {"attr": "value"})
|
|
assert res == []
|
|
|
|
|
|
def test_i_can_search_elements_by_name_with_attr_and_contains_compare():
|
|
to_find = Table(attr="value1 value2 value3")
|
|
|
|
res = search_elements_by_name(to_find, None, {"attr": "value"}, comparison_method="contains")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, None, {"attr": "value"}, comparison_method="contains")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "table", {"attr": "value"}, comparison_method="contains")
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "other", {"attr": "value"}, comparison_method="contains")
|
|
assert res == []
|
|
|
|
ft = Div(Span(), Span(to_find))
|
|
res = search_elements_by_name(ft, "table", {"attr": "value4"}, comparison_method="contains")
|
|
assert res == []
|
|
|
|
ft = Div(Span(Table()))
|
|
res = search_elements_by_name(ft, "table", {"attr": "value"}, comparison_method="contains")
|
|
assert res == []
|
|
|
|
|
|
def test_i_can_select_path():
|
|
to_find = Table(attr="value")
|
|
|
|
res = search_elements_by_path(to_find, "table", None)
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(to_find))
|
|
res = search_elements_by_path(ft, "div.span.table", None)
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(to_find))
|
|
res = search_elements_by_path(ft, "span.table", None)
|
|
assert res == [to_find]
|
|
|
|
|
|
def test_i_can_select_path_with_attr():
|
|
to_find = Table(attr="value")
|
|
|
|
res = search_elements_by_path(to_find, "table", {"attr": "value"})
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(to_find))
|
|
res = search_elements_by_path(ft, "div.span.table", {"attr": "value"})
|
|
assert res == [to_find]
|
|
|
|
ft = Div(Span(to_find))
|
|
res = search_elements_by_path(ft, "span.table", {"attr": "value"})
|
|
assert res == [to_find]
|
|
|
|
res = search_elements_by_path(to_find, "span.table", {"attr": "value2"})
|
|
assert res == []
|
|
|
|
|
|
def test_i_can_extract_table_values_from_ft():
|
|
df = pd.DataFrame({
|
|
'Name': ['Alice', 'Bob'],
|
|
'Age': [20, 25]
|
|
})
|
|
dg = DataGrid(df, id="testing_grid_id")
|
|
element = dg.__ft__()
|
|
|
|
assert extract_table_values(element) == OrderedDict({
|
|
'Name': ['Alice', 'Bob'],
|
|
'Age': ["20", "25"]
|
|
})
|
|
|
|
assert extract_table_values(element, header=False) == [
|
|
["Alice", "20"],
|
|
["Bob", "25"]
|
|
]
|
|
|
|
|
|
def test_i_can_extract_table_values_from_html():
|
|
df = pd.DataFrame({
|
|
'Name': ['Alice', 'Bob'],
|
|
'Age': [20, 25]
|
|
})
|
|
dg = DataGrid(df, id="testing_grid_id")
|
|
html = to_xml(dg.__ft__())
|
|
element = get_from_html(html)
|
|
|
|
assert extract_table_values(element) == OrderedDict({
|
|
'Name': ['Alice', 'Bob'],
|
|
'Age': ["20", "25"]
|
|
})
|
|
|
|
assert extract_table_values(element, header=False) == [
|
|
["Alice", "20"],
|
|
["Bob", "25"]
|
|
]
|
|
|
|
|
|
def test_i_can_extract_popup_content_from_ft():
|
|
df = pd.DataFrame({
|
|
'Name': ['Alice', 'Bob', 'Charlie'],
|
|
'Age': [20, 25, 30]
|
|
})
|
|
dg = DataGrid(df, id="testing_grid_id")
|
|
element = dg.mk_filter_popup_content("name")
|
|
|
|
assert extract_popup_content(element) == OrderedDict(
|
|
{'__filter_input__': '',
|
|
'Alice': False,
|
|
'Bob': False,
|
|
'Charlie': False,
|
|
}
|
|
)
|
|
|
|
|
|
def test_i_can_extract_popup_content_from_html():
|
|
df = pd.DataFrame({
|
|
'Name': ['Alice', 'Bob', 'Charlie'],
|
|
'Age': [20, 25, 30]
|
|
})
|
|
dg = DataGrid(df, id="testing_grid_id")
|
|
html = to_xml(dg.mk_filter_popup_content("name"))
|
|
element = get_from_html(html)
|
|
|
|
assert extract_popup_content(element) == OrderedDict(
|
|
{'__filter_input__': '',
|
|
'Alice': False,
|
|
'Bob': False,
|
|
'Charlie': False,
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("path, expected_attributes", [
|
|
("div", {"tag": "div"}),
|
|
("div#my_id", {"tag": "div", "id": "my_id"}),
|
|
("div[class=my_class]", {"tag": "div", "class": "my_class"}),
|
|
("div#my_id[class=my_class]", {"tag": "div", "id": "my_id", "class": "my_class"}),
|
|
("div#my_id[a1=v1][a2=v2]", {"tag": "div", "id": "my_id", "a1": "v1", "a2": "v2"}),
|
|
("div#my_id[a1='v1']", {"tag": "div", "id": "my_id", "a1": "v1"}),
|
|
('div#my_id[a1="v1"]', {"tag": "div", "id": "my_id", "a1": "v1"}),
|
|
("div#my_id[a1='v-1']", {"tag": "div", "id": "my_id", "a1": "v-1"}),
|
|
("div#my_id[a1='v_1']", {"tag": "div", "id": "my_id", "a1": "v_1"}),
|
|
])
|
|
def test_i_can_get_path_attributes(path, expected_attributes):
|
|
assert get_path_attributes(path) == expected_attributes
|
|
|
|
|
|
def test_i_can_select_by_path():
|
|
# I can select the good one from a list
|
|
items = [Div(id='1'),
|
|
Div(id='2'),
|
|
Div(id='3')]
|
|
actual = find_first_match(items, "div#3")
|
|
assert actual == items[2]
|
|
|
|
# I can select using attribute
|
|
item = Div(Span(id="span_1"), Span(id="span_2"), id="div_1")
|
|
actual = find_first_match(item, "div.span#span_2")
|
|
assert actual == item.children[1]
|
|
|
|
# I can manage when no ft
|
|
items = [Div(Div("text")), Div(Div(Div(id="3"), id="2"), id="1")] # 'text' is not a ft, but there won't be any error
|
|
actual = find_first_match(items, "div.div.div")
|
|
assert actual.attrs["id"] == "3"
|
|
|
|
# None is returned when not found
|
|
assert find_first_match(item, "div.span#span_3") is None
|
|
|
|
|
|
@pytest.mark.parametrize("tag, attr, expected", [
|
|
("span", "class", ("span", "highlight")), # The tag and the attribute exist
|
|
("footer", "id", None), # The tag exists, but not the attribute
|
|
(None, "class", ("header", "first-class")), # First element with a given attribute
|
|
("p", "class", None), # The tag does not exist
|
|
("span", "id", ("span", "inner")), # Inner element
|
|
(None, "name", ("span", "child")), # Inner element
|
|
])
|
|
def test_i_can_search_first_with_attribute(tag, attr, expected, sample_structure):
|
|
result = search_first_with_attribute(sample_structure, tag, attr)
|
|
if expected is None:
|
|
assert result is None
|
|
else:
|
|
assert result.tag == expected[0]
|
|
assert attr in result.attrs
|
|
assert result.attrs[attr] == expected[1]
|
|
|
|
|
|
# Add tests for extract_jsonviewer_node
|
|
def test_extract_jsonviewer_node():
|
|
# Create a valid JsonViewer node element
|
|
element = Div(
|
|
span_icon("expanded"),
|
|
Span("key : "),
|
|
Span("value")
|
|
)
|
|
|
|
result = extract_jsonviewer_node(element)
|
|
|
|
assert result is not None
|
|
assert result.is_expanded is True
|
|
assert result.key == "key"
|
|
assert result.value == element.children[2]
|
|
assert result.debug_key == element.children[1]
|
|
assert result.debug_folding == element.children[0]
|
|
|
|
|
|
def test_extract_jsonviewer_node_collapsed():
|
|
# Create a collapsed JsonViewer node element
|
|
element = Div(
|
|
span_icon("collapsed"),
|
|
Span("key : "),
|
|
Span("value")
|
|
)
|
|
|
|
result = extract_jsonviewer_node(element)
|
|
|
|
assert result is not None
|
|
assert result.is_expanded is False
|
|
assert result.key == "key"
|
|
assert result.value == element.children[2]
|
|
|
|
|
|
def test_extract_jsonviewer_node_no_expansion_state():
|
|
# Create a JsonViewer node with no expansion state
|
|
element = Div(
|
|
Span(),
|
|
Span("key : "),
|
|
Span("value")
|
|
)
|
|
|
|
result = extract_jsonviewer_node(element)
|
|
|
|
assert result is not None
|
|
assert result.is_expanded is None
|
|
assert result.key == "key"
|
|
assert result.value == element.children[2]
|
|
|
|
|
|
def test_extract_jsonviewer_node_root_node():
|
|
# Create a root JsonViewer node (no key)
|
|
element = Div(
|
|
span_icon("expanded"),
|
|
None,
|
|
Span("value")
|
|
)
|
|
|
|
result = extract_jsonviewer_node(element)
|
|
|
|
assert result is not None
|
|
assert result.is_expanded is True
|
|
assert result.key is None
|
|
assert result.value == element.children[2]
|
|
|
|
|
|
def test_extract_jsonviewer_node_invalid_structure():
|
|
# Test with invalid node structure (not enough children)
|
|
element = Div(
|
|
span_icon("expanded"),
|
|
Span("key : ")
|
|
)
|
|
|
|
result = extract_jsonviewer_node(element)
|
|
|
|
assert result is None
|
|
|
|
# Test with element that has no children attribute
|
|
element = "not an element with children"
|
|
|
|
result = extract_jsonviewer_node(element)
|
|
|
|
assert result is None
|
|
|
|
|
|
def test_json_viewer_find():
|
|
value = {"a": [1, 2, 3], "b": {"x": "y", "z": True}}
|
|
jsonviewer = JsonViewer(None, None, None, None, value)
|
|
elements = jsonviewer.__ft__()
|
|
root_div = search_elements_by_name(elements, "div", attrs={"id": f"{jsonviewer.get_id()}-root"})[0]
|
|
first_level_div = root_div.children[0]
|
|
|
|
as_node = extract_jsonviewer_node(first_level_div)
|
|
child_b = as_node.find("b")
|
|
|
|
assert isinstance(child_b, JsonViewerNode)
|
|
assert child_b.key == "b"
|
|
|
|
|
|
def test_json_viewer_find_with_path():
|
|
value = {"a": {"x": None, "y": ["first", "second"], "z": True}}
|
|
jsonviewer = JsonViewer(None, None, None, None, value)
|
|
jsonviewer.set_folding_mode("expand")
|
|
elements = jsonviewer.__ft__()
|
|
root_div = search_elements_by_name(elements, "div", attrs={"id": f"{jsonviewer.get_id()}-root"})[0]
|
|
first_level_div = root_div.children[0]
|
|
|
|
as_node = extract_jsonviewer_node(first_level_div)
|
|
child = as_node.find("a.y.0")
|
|
|
|
assert isinstance(child, JsonViewerNode)
|
|
assert child.key == "0"
|