339 lines
13 KiB
Python
339 lines
13 KiB
Python
import pytest
|
|
from fastcore.basics import NotStr
|
|
from fasthtml.components import *
|
|
|
|
from myfasthtml.test.matcher import matches, StartsWith, Contains, DoesNotContain, Empty, DoNotCheck, ErrorOutput, \
|
|
ErrorComparisonOutput
|
|
from myfasthtml.test.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_constant():
|
|
elt = 123
|
|
expected = elt
|
|
path = ""
|
|
error_output = ErrorOutput(path, elt, expected)
|
|
error_output.compute()
|
|
assert error_output.output == ['123']
|
|
|
|
|
|
def test_i_can_output_error_constant_wrong_value():
|
|
elt = 123
|
|
expected = 456
|
|
path = ""
|
|
error_output = ErrorOutput(path, elt, expected)
|
|
error_output.compute()
|
|
assert error_output.output == ['123',
|
|
'^^^']
|
|
|
|
|
|
def test_i_can_output_error_when_predicate():
|
|
elt = "before value after"
|
|
expected = Contains("value")
|
|
path = ""
|
|
error_output = ErrorOutput(path, elt, expected)
|
|
error_output.compute()
|
|
assert error_output.output == ["before value after"]
|
|
|
|
|
|
def test_i_can_output_error_when_predicate_wrong_value():
|
|
elt = "before after"
|
|
expected = Contains("value")
|
|
path = ""
|
|
error_output = ErrorOutput(path, elt, expected)
|
|
error_output.compute()
|
|
assert error_output.output == ["before after",
|
|
"^^^^^^^^^^^^"]
|
|
|
|
|
|
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" ...)',
|
|
')',
|
|
]
|
|
|
|
|
|
def test_i_can_output_error_child_element_wrong_value():
|
|
elt = Div(P(id="p_id"), Div(id="child_2"), attr1="value1")
|
|
expected = Div(P(id="p_id"), Div(id="child_1"), attr1="value1")
|
|
path = ""
|
|
error_output = ErrorOutput(path, elt, expected)
|
|
error_output.compute()
|
|
assert error_output.output == ['(div "attr1"="value1"',
|
|
' (p "id"="p_id")',
|
|
' (div "id"="child_2")',
|
|
' ^^^^^^^^^^^^^^',
|
|
')',
|
|
]
|
|
|
|
|
|
def test_i_can_output_error_fewer_elements():
|
|
elt = Div(P(id="p_id"), attr1="value1")
|
|
expected = Div(P(id="p_id"), Div(id="child_1"), attr1="value1")
|
|
path = ""
|
|
error_output = ErrorOutput(path, elt, expected)
|
|
error_output.compute()
|
|
assert error_output.output == ['(div "attr1"="value1"',
|
|
' (p "id"="p_id")',
|
|
' ! ** MISSING ** !',
|
|
')',
|
|
]
|
|
|
|
|
|
def test_i_can_output_comparison():
|
|
actual = Div(P(id="p_id"), attr1="value1")
|
|
expected = actual
|
|
actual_out = ErrorOutput("", actual, expected)
|
|
expected_out = ErrorOutput("", expected, expected)
|
|
|
|
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
|
|
|
res = comparison_out.render()
|
|
|
|
assert "\n" + res == '''
|
|
(div "attr1"="value1" | (div "attr1"="value1"
|
|
(p "id"="p_id") | (p "id"="p_id")
|
|
) | )'''
|
|
|
|
|
|
def test_i_can_output_comparison_with_path():
|
|
actual = Div(P(id="p_id"), attr1="value1")
|
|
expected = actual
|
|
actual_out = ErrorOutput("div#div_id.span[class=cls].div", actual, expected)
|
|
expected_out = ErrorOutput("div#div_id.span[class=cls].div", expected, expected)
|
|
|
|
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
|
|
|
res = comparison_out.render()
|
|
|
|
assert "\n" + res == '''
|
|
(div "id"="div_id" ... | (div "id"="div_id" ...
|
|
(span "class"="cls" ... | (span "class"="cls" ...
|
|
(div "attr1"="value1" | (div "attr1"="value1"
|
|
(p "id"="p_id") | (p "id"="p_id")
|
|
) | )'''
|
|
|
|
|
|
def test_i_can_output_comparison_when_missing_attributes():
|
|
actual = Div(P(id="p_id"), attr1="value1")
|
|
expected = Div(P(id="p_id"), attr2="value1")
|
|
actual_out = ErrorOutput("", actual, expected)
|
|
expected_out = ErrorOutput("", expected, expected)
|
|
|
|
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
|
|
|
res = comparison_out.render()
|
|
|
|
assert "\n" + res == '''
|
|
(div "attr2"="** MISSING **" | (div "attr2"="value1"
|
|
^^^^^^^^^^^^^^^^^^^^^^^ |
|
|
(p "id"="p_id") | (p "id"="p_id")
|
|
) | )'''
|
|
|
|
|
|
def test_i_can_output_comparison_when_wrong_attributes():
|
|
actual = Div(P(id="p_id"), attr1="value2")
|
|
expected = Div(P(id="p_id"), attr1="value1")
|
|
actual_out = ErrorOutput("", actual, expected)
|
|
expected_out = ErrorOutput("", expected, expected)
|
|
|
|
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
|
|
|
res = comparison_out.render()
|
|
|
|
assert "\n" + res == '''
|
|
(div "attr1"="value2" | (div "attr1"="value1"
|
|
^^^^^^^^^^^^^^^^ |
|
|
(p "id"="p_id") | (p "id"="p_id")
|
|
) | )'''
|
|
|
|
|
|
def test_i_can_output_comparison_when_fewer_elements():
|
|
actual = Div(P(id="p_id"), attr1="value1")
|
|
expected = Div(Span(id="s_id"), P(id="p_id"), attr1="value1")
|
|
actual_out = ErrorOutput("", actual, expected)
|
|
expected_out = ErrorOutput("", expected, expected)
|
|
|
|
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
|
|
|
res = comparison_out.render()
|
|
|
|
assert "\n" + res == '''
|
|
(div "attr1"="value1" | (div "attr1"="value1"
|
|
(p "id"="p_id") | (span "id"="s_id")
|
|
^ ^^^^^^^^^^^ |
|
|
! ** MISSING ** ! | (p "id"="p_id")
|
|
) | )'''
|
|
|
|
|
|
def test_i_can_see_the_diff_when_matching():
|
|
actual = Div(attr1="value1")
|
|
expected = Div(attr1=Contains("value2"))
|
|
|
|
with pytest.raises(AssertionError) as exc_info:
|
|
matches(actual, expected)
|
|
|
|
debug_output = str(exc_info.value)
|
|
assert "\n" + debug_output == """
|
|
Path : 'div'
|
|
Error : The condition 'Contains(value2)' is not satisfied.
|
|
(div "attr1"="value1") | (div "attr1"="Contains(value2)")
|
|
^^^^^^^^^^^^^^^^ |"""
|