import pytest from fastcore.basics import NotStr from fasthtml.components import * from myfasthtml.core.matcher import matches, StartsWith, Contains, DoesNotContain, Empty, DoNotCheck, ErrorOutput from myfasthtml.core.testclient import MyFT @pytest.mark.parametrize('actual, expected', [ (None, None), (123, 123), (Div(), Div()), ([Div(), Span()], [Div(), Span()]), (Div(attr1="value"), Div(attr1="value")), (Div(attr1="value", attr2="value"), Div(attr1="value")), (Div(attr1="valueXXX", attr2="value"), Div(attr1=StartsWith("value"))), (Div(attr1="before value after", attr2="value"), Div(attr1=Contains("value"))), (Div(attr1="before after", attr2="value"), Div(attr1=DoesNotContain("value"))), (None, DoNotCheck()), (123, DoNotCheck()), (Div(), DoNotCheck()), ([Div(), Span()], DoNotCheck()), (NotStr("123456"), NotStr("123")), # for NotStr, only the beginning is checked (Div(), Div(Empty())), (Div(123), Div(123)), (Div(Span(123)), Div(Span(123))), (Div(Span(123)), Div(DoNotCheck())), ]) def test_i_can_match(actual, expected): assert matches(actual, expected) @pytest.mark.parametrize('actual, expected, error_message', [ (None, Div(), "Actual is None"), (Div(), None, "Actual is not None"), (123, Div(), "The types are different:"), (123, 124, "The values are different:"), ([Div(), Span()], [], "Actual is bigger than expected:"), ([], [Div(), Span()], "Actual is smaller than expected:"), ("not a list", [Div(), Span()], "The types are different:"), ([Div(), Span()], [Div(), 123], "The types are different:"), (Div(), Span(), "The elements are different:"), ([Div(), Span()], [Div(), Div()], "The elements are different:"), (Div(), Div(attr1="value"), "'attr1' is not found in Actual:"), (Div(attr2="value"), Div(attr1="value"), "'attr1' is not found in Actual:"), (Div(attr1="value1"), Div(attr1="value2"), "The values are different for 'attr1':"), (Div(attr1="value1"), Div(attr1=StartsWith("value2")), "The condition 'StartsWith(value2)' is not satisfied:"), (Div(attr1="value1"), Div(attr1=Contains("value2")), "The condition 'Contains(value2)' is not satisfied:"), (Div(attr1="value1 value2"), Div(attr1=DoesNotContain("value2")), "The condition 'DoesNotContain(value2)'"), (NotStr("456"), NotStr("123"), "Notstr values are different:"), (Div(attr="value"), Div(Empty()), "Actual is not empty:"), (Div(120), Div(Empty()), "Actual is not empty:"), (Div(Span()), Div(Empty()), "Actual is not empty:"), (Div(), Div(Span()), "Actual is lesser than expected:"), (Div(), Div(123), "Actual is lesser than expected:"), (Div(Span()), Div(Div()), "The elements are different:"), (Div(123), Div(Div()), "The types are different:"), (Div(123), Div(456), "The values are different:"), (Div(Span(), Span()), Div(Span(), Div()), "The elements are different:"), (Div(Span(Div())), Div(Span(Span())), "The elements are different:"), ]) def test_i_can_detect_errors(actual, expected, error_message): with pytest.raises(AssertionError) as exc_info: matches(actual, expected) assert error_message in str(exc_info.value) @pytest.mark.parametrize('element, expected_path', [ (Div(), "Path: 'div"), (Div(Span()), "Path: 'div.span"), (Div(Span(Div())), "Path: 'div.span.div"), (Div(id="div_id"), "Path: 'div#div_id"), (Div(cls="div_class"), "Path: 'div[class=div_class]"), (Div(name="div_class"), "Path: 'div[name=div_class]"), (Div(attr="value"), "Path: 'div"), (Div(Span(Div(), cls="span_class"), id="div_id"), "Path: 'div#div_id.span[class=span_class].div"), ]) def test_i_can_properly_show_path(element, expected_path): def _construct_test_element(source, tail): res = MyFT(source.tag, source.attrs) if source.children: res.children = [_construct_test_element(child, tail) for child in source.children] else: res.children = [tail] return res with pytest.raises(AssertionError) as exc_info: actual = _construct_test_element(element, "Actual") expected = _construct_test_element(element, "Expected") matches(actual, expected) assert expected_path in str(exc_info.value) def test_i_can_output_error_path(): elt = Div() expected = Div() path = "div#div_id.div.span[class=span_class].p[name=p_name].div" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "id"="div_id" ...', ' (div ...', ' (span "class"="span_class" ...', ' (p "name"="p_name" ...', ' (div )'] def test_i_can_output_error_attribute(): elt = Div(attr1="value1", attr2="value2") expected = elt path = "" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "attr1"="value1" "attr2"="value2")'] def test_i_can_output_error_attribute_missing_1(): elt = Div(attr2="value2") expected = Div(attr1="value1", attr2="value2") path = "" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "attr1"="** MISSING **" "attr2"="value2")', ' ^^^^^^^^^^^^^^^^^^^^^^^ '] def test_i_can_output_error_attribute_missing_2(): elt = Div(attr1="value1") expected = Div(attr1="value1", attr2="value2") path = "" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "attr1"="value1" "attr2"="** MISSING **")', ' ^^^^^^^^^^^^^^^^^^^^^^^'] def test_i_can_output_error_attribute_wrong_value(): elt = Div(attr1="value3", attr2="value2") expected = Div(attr1="value1", attr2="value2") path = "" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "attr1"="value3" "attr2"="value2")', ' ^^^^^^^^^^^^^^^^ '] def test_i_can_output_error_child_element(): elt = Div(P(id="p_id"), Div(id="child_1"), Div(id="child_2"), attr1="value1") expected = elt path = "" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "attr1"="value1"', ' (p "id"="p_id")', ' (div "id"="child_1")', ' (div "id"="child_2")', ')', ] def test_i_can_output_error_child_element_indicating_sub_children(): elt = Div(P(id="p_id"), Div(Div(id="child_2"), id="child_1"), attr1="value1") expected = elt path = "" error_output = ErrorOutput(path, elt, expected) error_output.compute() assert error_output.output == ['(div "attr1"="value1"', ' (p "id"="p_id")', ' (div "id"="child_1" ...)', ')', ]