Added TreeView and Panel
This commit is contained in:
@@ -3,6 +3,11 @@ import pytest
|
||||
from myfasthtml.core.instances import SingleInstance
|
||||
|
||||
|
||||
class RootInstanceForTests(SingleInstance):
|
||||
def __init__(self, session):
|
||||
super().__init__(None, session, _id="TestRoot")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def session():
|
||||
return {
|
||||
@@ -20,4 +25,4 @@ def session():
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def root_instance(session):
|
||||
return SingleInstance(None, session, "TestRoot")
|
||||
return RootInstanceForTests(session=session)
|
||||
|
||||
398
tests/controls/test_treeview.py
Normal file
398
tests/controls/test_treeview.py
Normal file
@@ -0,0 +1,398 @@
|
||||
"""Unit tests for TreeView component."""
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
from fasthtml.components import *
|
||||
|
||||
from myfasthtml.controls.Keyboard import Keyboard
|
||||
from myfasthtml.controls.TreeView import TreeView, TreeNode
|
||||
from myfasthtml.test.matcher import matches, TestObject, TestCommand
|
||||
from .conftest import root_instance
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def cleanup_db():
|
||||
shutil.rmtree(".myFastHtmlDb", ignore_errors=True)
|
||||
|
||||
|
||||
class TestTreeviewBehaviour:
|
||||
"""Tests for TreeView behavior and logic."""
|
||||
|
||||
def test_i_can_create_tree_node_with_auto_generated_id(self):
|
||||
"""Test that TreeNode generates UUID automatically."""
|
||||
node = TreeNode(label="Test Node", type="folder")
|
||||
|
||||
assert node.id is not None
|
||||
assert isinstance(node.id, str)
|
||||
assert len(node.id) > 0
|
||||
|
||||
def test_i_can_create_tree_node_with_default_values(self):
|
||||
"""Test that TreeNode has correct default values."""
|
||||
node = TreeNode()
|
||||
|
||||
assert node.label == ""
|
||||
assert node.type == "default"
|
||||
assert node.parent is None
|
||||
assert node.children == []
|
||||
|
||||
def test_i_can_initialize_tree_view_state(self, root_instance):
|
||||
"""Test that TreeViewState initializes with default values."""
|
||||
tree_view = TreeView(root_instance)
|
||||
state = tree_view._state
|
||||
|
||||
assert isinstance(state.items, dict)
|
||||
assert len(state.items) == 0
|
||||
assert state.opened == []
|
||||
assert state.selected is None
|
||||
assert state.editing is None
|
||||
assert state.icon_config == {}
|
||||
|
||||
def test_i_can_create_empty_treeview(self, root_instance):
|
||||
"""Test creating an empty TreeView."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
assert tree_view is not None
|
||||
assert len(tree_view._state.items) == 0
|
||||
|
||||
def test_i_can_add_node_to_treeview(self, root_instance):
|
||||
"""Test adding a root node to the TreeView."""
|
||||
tree_view = TreeView(root_instance)
|
||||
node = TreeNode(label="Root Node", type="folder")
|
||||
|
||||
tree_view.add_node(node)
|
||||
|
||||
assert node.id in tree_view._state.items
|
||||
assert tree_view._state.items[node.id].label == "Root Node"
|
||||
assert tree_view._state.items[node.id].parent is None
|
||||
|
||||
def test_i_can_add_child_node(self, root_instance):
|
||||
"""Test adding a child node to a parent."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child = TreeNode(label="Child", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
assert child.id in tree_view._state.items
|
||||
assert child.id in parent.children
|
||||
assert child.parent == parent.id
|
||||
|
||||
def test_i_can_set_icon_config(self, root_instance):
|
||||
"""Test setting icon configuration."""
|
||||
tree_view = TreeView(root_instance)
|
||||
config = {
|
||||
"folder": "fluent.folder",
|
||||
"file": "fluent.document"
|
||||
}
|
||||
|
||||
tree_view.set_icon_config(config)
|
||||
|
||||
assert tree_view._state.icon_config == config
|
||||
assert tree_view._state.icon_config["folder"] == "fluent.folder"
|
||||
|
||||
def test_i_can_toggle_node(self, root_instance):
|
||||
"""Test expand/collapse a node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
node = TreeNode(label="Node", type="folder")
|
||||
tree_view.add_node(node)
|
||||
|
||||
# Initially closed
|
||||
assert node.id not in tree_view._state.opened
|
||||
|
||||
# Toggle to open
|
||||
tree_view._toggle_node(node.id)
|
||||
assert node.id in tree_view._state.opened
|
||||
|
||||
# Toggle to close
|
||||
tree_view._toggle_node(node.id)
|
||||
assert node.id not in tree_view._state.opened
|
||||
|
||||
def test_i_can_expand_all_nodes(self, root_instance):
|
||||
"""Test that expand_all opens all nodes with children."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Create hierarchy: root -> child1 -> grandchild
|
||||
# -> child2 (leaf)
|
||||
root = TreeNode(label="Root", type="folder")
|
||||
child1 = TreeNode(label="Child 1", type="folder")
|
||||
grandchild = TreeNode(label="Grandchild", type="file")
|
||||
child2 = TreeNode(label="Child 2", type="file")
|
||||
|
||||
tree_view.add_node(root)
|
||||
tree_view.add_node(child1, parent_id=root.id)
|
||||
tree_view.add_node(grandchild, parent_id=child1.id)
|
||||
tree_view.add_node(child2, parent_id=root.id)
|
||||
|
||||
# Initially all closed
|
||||
assert len(tree_view._state.opened) == 0
|
||||
|
||||
# Expand all
|
||||
tree_view.expand_all()
|
||||
|
||||
# Nodes with children should be opened
|
||||
assert root.id in tree_view._state.opened
|
||||
assert child1.id in tree_view._state.opened
|
||||
|
||||
# Leaf nodes should not be in opened list
|
||||
assert grandchild.id not in tree_view._state.opened
|
||||
assert child2.id not in tree_view._state.opened
|
||||
|
||||
def test_i_can_select_node(self, root_instance):
|
||||
"""Test selecting a node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
node = TreeNode(label="Node", type="folder")
|
||||
tree_view.add_node(node)
|
||||
|
||||
tree_view._select_node(node.id)
|
||||
|
||||
assert tree_view._state.selected == node.id
|
||||
|
||||
def test_i_can_start_rename_node(self, root_instance):
|
||||
"""Test starting rename mode for a node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
node = TreeNode(label="Old Name", type="folder")
|
||||
tree_view.add_node(node)
|
||||
|
||||
tree_view._start_rename(node.id)
|
||||
|
||||
assert tree_view._state.editing == node.id
|
||||
|
||||
def test_i_can_save_rename_node(self, root_instance):
|
||||
"""Test saving renamed node with new label."""
|
||||
tree_view = TreeView(root_instance)
|
||||
node = TreeNode(label="Old Name", type="folder")
|
||||
tree_view.add_node(node)
|
||||
tree_view._start_rename(node.id)
|
||||
|
||||
tree_view._save_rename(node.id, "New Name")
|
||||
|
||||
assert tree_view._state.items[node.id].label == "New Name"
|
||||
assert tree_view._state.editing is None
|
||||
|
||||
def test_i_can_cancel_rename_node(self, root_instance):
|
||||
"""Test canceling rename operation."""
|
||||
tree_view = TreeView(root_instance)
|
||||
node = TreeNode(label="Name", type="folder")
|
||||
tree_view.add_node(node)
|
||||
tree_view._start_rename(node.id)
|
||||
|
||||
tree_view._cancel_rename()
|
||||
|
||||
assert tree_view._state.editing is None
|
||||
assert tree_view._state.items[node.id].label == "Name"
|
||||
|
||||
def test_i_can_delete_leaf_node(self, root_instance):
|
||||
"""Test deleting a node without children."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child = TreeNode(label="Child", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Delete child (leaf node)
|
||||
tree_view._delete_node(child.id)
|
||||
|
||||
assert child.id not in tree_view._state.items
|
||||
assert child.id not in parent.children
|
||||
|
||||
def test_i_can_add_sibling_node(self, root_instance):
|
||||
"""Test adding a sibling node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child1 = TreeNode(label="Child 1", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child1, parent_id=parent.id)
|
||||
|
||||
# Add sibling to child1
|
||||
tree_view._add_sibling(child1.id, new_label="Child 2")
|
||||
|
||||
assert len(parent.children) == 2
|
||||
# Sibling should be after child1
|
||||
assert parent.children.index(child1.id) < len(parent.children) - 1
|
||||
|
||||
def test_i_cannot_delete_node_with_children(self, root_instance):
|
||||
"""Test that deleting a node with children raises an error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child = TreeNode(label="Child", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Try to delete parent (has children)
|
||||
with pytest.raises(ValueError, match="Cannot delete node.*with children"):
|
||||
tree_view._delete_node(parent.id)
|
||||
|
||||
def test_i_cannot_add_sibling_to_root(self, root_instance):
|
||||
"""Test that adding sibling to root node raises an error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
root = TreeNode(label="Root", type="folder")
|
||||
tree_view.add_node(root)
|
||||
|
||||
# Try to add sibling to root (no parent)
|
||||
with pytest.raises(ValueError, match="Cannot add sibling to root node"):
|
||||
tree_view._add_sibling(root.id)
|
||||
|
||||
def test_i_cannot_select_nonexistent_node(self, root_instance):
|
||||
"""Test that selecting a nonexistent node raises an error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to select node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._select_node("nonexistent_id")
|
||||
|
||||
def test_add_node_prevents_duplicate_children(self, root_instance):
|
||||
"""Test that add_node prevents adding duplicate child IDs."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child = TreeNode(label="Child", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Try to add the same child again
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Child should appear only once in parent's children list
|
||||
assert parent.children.count(child.id) == 1
|
||||
|
||||
def test_sibling_is_inserted_at_correct_position(self, root_instance):
|
||||
"""Test that _add_sibling inserts sibling exactly after reference node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child1 = TreeNode(label="Child 1", type="file")
|
||||
child3 = TreeNode(label="Child 3", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child1, parent_id=parent.id)
|
||||
tree_view.add_node(child3, parent_id=parent.id)
|
||||
|
||||
# Add sibling after child1
|
||||
tree_view._add_sibling(child1.id, new_label="Child 2")
|
||||
|
||||
# Get the newly added sibling
|
||||
sibling_id = parent.children[1]
|
||||
|
||||
# Verify order: child1, sibling (child2), child3
|
||||
assert parent.children[0] == child1.id
|
||||
assert tree_view._state.items[sibling_id].label == "Child 2"
|
||||
assert parent.children[2] == child3.id
|
||||
assert len(parent.children) == 3
|
||||
|
||||
def test_add_child_auto_expands_parent(self, root_instance):
|
||||
"""Test that _add_child automatically expands the parent node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
|
||||
# Parent should not be expanded initially
|
||||
assert parent.id not in tree_view._state.opened
|
||||
|
||||
# Add child
|
||||
tree_view._add_child(parent.id, new_label="Child")
|
||||
|
||||
# Parent should now be expanded
|
||||
assert parent.id in tree_view._state.opened
|
||||
|
||||
def test_i_cannot_add_child_to_nonexistent_parent(self, root_instance):
|
||||
"""Test that adding child to nonexistent parent raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to add child to parent that doesn't exist
|
||||
with pytest.raises(ValueError, match="Parent node.*does not exist"):
|
||||
tree_view._add_child("nonexistent_parent_id")
|
||||
|
||||
def test_delete_node_clears_selection_if_selected(self, root_instance):
|
||||
"""Test that deleting a selected node clears the selection."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child = TreeNode(label="Child", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Select the child
|
||||
tree_view._select_node(child.id)
|
||||
assert tree_view._state.selected == child.id
|
||||
|
||||
# Delete the selected child
|
||||
tree_view._delete_node(child.id)
|
||||
|
||||
# Selection should be cleared
|
||||
assert tree_view._state.selected is None
|
||||
|
||||
def test_delete_node_removes_from_opened_if_expanded(self, root_instance):
|
||||
"""Test that deleting an expanded node removes it from opened list."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child = TreeNode(label="Child", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Expand the parent
|
||||
tree_view._toggle_node(parent.id)
|
||||
assert parent.id in tree_view._state.opened
|
||||
|
||||
# Delete the child (making parent a leaf)
|
||||
tree_view._delete_node(child.id)
|
||||
|
||||
# Now delete the parent (now a leaf node)
|
||||
# First remove it from root by creating a grandparent
|
||||
grandparent = TreeNode(label="Grandparent", type="folder")
|
||||
tree_view.add_node(grandparent)
|
||||
parent.parent = grandparent.id
|
||||
grandparent.children.append(parent.id)
|
||||
|
||||
tree_view._delete_node(parent.id)
|
||||
|
||||
# Parent should be removed from opened list
|
||||
assert parent.id not in tree_view._state.opened
|
||||
|
||||
def test_i_cannot_start_rename_nonexistent_node(self, root_instance):
|
||||
"""Test that starting rename on nonexistent node raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to start rename on node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._start_rename("nonexistent_id")
|
||||
|
||||
def test_i_cannot_save_rename_nonexistent_node(self, root_instance):
|
||||
"""Test that saving rename for nonexistent node raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to save rename for node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._save_rename("nonexistent_id", "New Name")
|
||||
|
||||
def test_i_cannot_add_sibling_to_nonexistent_node(self, root_instance):
|
||||
"""Test that adding sibling to nonexistent node raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to add sibling to node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._add_sibling("nonexistent_id")
|
||||
|
||||
|
||||
class TestTreeViewRender:
|
||||
"""Tests for TreeView HTML rendering."""
|
||||
|
||||
def test_i_can_render_empty_treeview(self, root_instance):
|
||||
"""Test that TreeView generates correct HTML structure."""
|
||||
tree_view = TreeView(root_instance)
|
||||
expected = Div(
|
||||
TestObject(Keyboard, combinations={"esc": TestCommand("CancelRename")}),
|
||||
_id=tree_view.get_id(),
|
||||
cls="mf-treeview"
|
||||
)
|
||||
|
||||
assert matches(tree_view.__ft__(), expected)
|
||||
|
||||
def test_node_action_buttons_are_rendered(self):
|
||||
"""Test that action buttons are present in rendered HTML."""
|
||||
# Signature only - implementation later
|
||||
pass
|
||||
@@ -3,361 +3,440 @@ from fastcore.basics import NotStr
|
||||
from fasthtml.components import *
|
||||
|
||||
from myfasthtml.test.matcher import matches, StartsWith, Contains, DoesNotContain, Empty, DoNotCheck, ErrorOutput, \
|
||||
ErrorComparisonOutput, AttributeForbidden, AnyValue, NoChildren
|
||||
ErrorComparisonOutput, AttributeForbidden, AnyValue, NoChildren, TestObject
|
||||
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"))),
|
||||
(Div(attr1="value"), Div(attr1=AnyValue())),
|
||||
(None, DoNotCheck()),
|
||||
(123, DoNotCheck()),
|
||||
(Div(), DoNotCheck()),
|
||||
([Div(), Span()], DoNotCheck()),
|
||||
(NotStr("123456"), NotStr("123")), # for NotStr, only the beginning is checked
|
||||
(Div(), Div(Empty())),
|
||||
(Div(), Div(NoChildren())),
|
||||
(Div(attr1="value"), Div(NoChildren())),
|
||||
(Div(attr1="value1"), Div(AttributeForbidden("attr2"))),
|
||||
(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)
|
||||
class Dummy:
|
||||
def __init__(self, attr1, attr2=None):
|
||||
self.attr1 = attr1
|
||||
self.attr2 = attr2
|
||||
|
||||
|
||||
@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)'"),
|
||||
(Div(attr1=None), Div(attr1=AnyValue()), "'attr1' is not found in Actual"),
|
||||
(Div(), Div(attr1=AnyValue()), "'attr1' is not found in Actual"),
|
||||
(NotStr("456"), NotStr("123"), "Notstr values are different"),
|
||||
(Div(attr="value"), Div(Empty()), "The condition 'Empty()' is not satisfied"),
|
||||
(Div(Span()), Div(NoChildren()), "The condition 'NoChildren()' is not satisfied"),
|
||||
(Div(120), Div(Empty()), "The condition 'Empty()' is not satisfied"),
|
||||
(Div(Span()), Div(Empty()), "The condition 'Empty()' is not satisfied"),
|
||||
(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"),
|
||||
(Div(attr1="value1"), Div(AttributeForbidden("attr1")), "condition 'AttributeForbidden(attr1)' is not satisfied"),
|
||||
])
|
||||
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)
|
||||
class Dummy2:
|
||||
def __init__(self, attr1, attr2):
|
||||
self.attr1 = attr1
|
||||
self.attr2 = attr2
|
||||
|
||||
|
||||
@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
|
||||
class TestMatches:
|
||||
|
||||
with pytest.raises(AssertionError) as exc_info:
|
||||
actual = _construct_test_element(element, "Actual")
|
||||
expected = _construct_test_element(element, "Expected")
|
||||
matches(actual, expected)
|
||||
@pytest.mark.parametrize('actual, expected', [
|
||||
(None, None),
|
||||
(123, 123),
|
||||
(Div(), Div()),
|
||||
([Div(), Span()], [Div(), Span()]),
|
||||
({"key": Div(attr="value")}, {"key": Div(attr="value")}),
|
||||
({"key": Dummy(attr1="value")}, {"key": TestObject(Dummy, attr1="value")}),
|
||||
(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"))),
|
||||
(Div(attr1="value"), Div(attr1=AnyValue())),
|
||||
(None, DoNotCheck()),
|
||||
(123, DoNotCheck()),
|
||||
(Div(), DoNotCheck()),
|
||||
([Div(), Span()], DoNotCheck()),
|
||||
(NotStr("123456"), NotStr("123")), # for NotStr, only the beginning is checked
|
||||
(Div(), Div(Empty())),
|
||||
(Div(), Div(NoChildren())),
|
||||
(Div(attr1="value"), Div(NoChildren())),
|
||||
(Div(attr1="value1"), Div(AttributeForbidden("attr2"))),
|
||||
(Div(123), Div(123)),
|
||||
(Div(Span(123)), Div(Span(123))),
|
||||
(Div(Span(123)), Div(DoNotCheck())),
|
||||
(Dummy(123, "value"), TestObject(Dummy, attr1=123, attr2="value")),
|
||||
(Dummy(123, "value"), TestObject(Dummy, attr2="value")),
|
||||
(Div(Dummy(123, "value")), Div(TestObject(Dummy, attr1=123))),
|
||||
(Dummy(123, "value"), TestObject("Dummy", attr1=123, attr2="value")),
|
||||
])
|
||||
def test_i_can_match(self, actual, expected):
|
||||
assert 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():
|
||||
"""I can display error when the condition predicate is not satisfied."""
|
||||
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():
|
||||
"""I can display error when the element has children"""
|
||||
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_text():
|
||||
"""I can display error when the children is not a FT"""
|
||||
elt = Div("Hello world", 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"',
|
||||
' "Hello world"',
|
||||
' (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)
|
||||
@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)'"),
|
||||
(Div(attr1=None), Div(attr1=AnyValue()), "'attr1' is not found in Actual"),
|
||||
(Div(), Div(attr1=AnyValue()), "'attr1' is not found in Actual"),
|
||||
(NotStr("456"), NotStr("123"), "Notstr values are different"),
|
||||
(Div(attr="value"), Div(Empty()), "The condition 'Empty()' is not satisfied"),
|
||||
(Div(Span()), Div(NoChildren()), "The condition 'NoChildren()' is not satisfied"),
|
||||
(Div(120), Div(Empty()), "The condition 'Empty()' is not satisfied"),
|
||||
(Div(Span()), Div(Empty()), "The condition 'Empty()' is not satisfied"),
|
||||
(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"),
|
||||
(Div(attr1="value1"), Div(AttributeForbidden("attr1")), "condition 'AttributeForbidden(attr1)' is not satisfied"),
|
||||
(Div(123, "value"), TestObject(Dummy, attr1=123, attr2="value2"), "The types are different:"),
|
||||
(Dummy(123, "value"), TestObject(Dummy, attr1=123, attr3="value3"), "'attr3' is not found in Actual"),
|
||||
(Dummy(123, "value"), TestObject(Dummy, attr1=123, attr2="value2"), "The values are different for 'attr2'"),
|
||||
(Div(Div(123, "value")), Div(TestObject(Dummy, attr1=123, attr2="value2")), "The types are different:"),
|
||||
(Div(Dummy(123, "value")), Div(TestObject(Dummy, attr1=123, attr3="value3")), "'attr3' is not found in Actual"),
|
||||
(Div(Dummy(123, "value")), Div(TestObject(Dummy, attr1=123, attr2="value2")), "are different for 'attr2'"),
|
||||
(Div(123, "value"), TestObject("Dummy", attr1=123, attr2="value2"), "The types are different:"),
|
||||
(Dummy(123, "value"), TestObject("Dummy", attr1=123, attr2=Contains("value2")), "The condition 'Contains(value2)' is not satisfied"),
|
||||
|
||||
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
||||
])
|
||||
def test_i_can_detect_errors(self, actual, expected, error_message):
|
||||
with pytest.raises(AssertionError) as exc_info:
|
||||
matches(actual, expected)
|
||||
assert error_message in str(exc_info.value)
|
||||
|
||||
res = comparison_out.render()
|
||||
@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(self, 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)
|
||||
|
||||
|
||||
class TestErrorOutput:
|
||||
def test_i_can_output_error_path(self):
|
||||
"""The output follows the representation of the given 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 )']
|
||||
|
||||
assert "\n" + res == '''
|
||||
def test_i_can_output_error_attribute(self):
|
||||
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(self):
|
||||
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(self):
|
||||
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(self):
|
||||
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(self):
|
||||
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(self):
|
||||
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(self):
|
||||
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(self):
|
||||
"""I can display error when the condition predicate is not satisfied."""
|
||||
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(self):
|
||||
"""I can display error when the element has children"""
|
||||
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_text(self):
|
||||
"""I can display error when the children is not a FT"""
|
||||
elt = Div("Hello world", 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"',
|
||||
' "Hello world"',
|
||||
' (div "id"="child_1")',
|
||||
' (div "id"="child_2")',
|
||||
')',
|
||||
]
|
||||
|
||||
def test_i_can_output_error_child_element_indicating_sub_children(self):
|
||||
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(self):
|
||||
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(self):
|
||||
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_error_test_object(self):
|
||||
elt = TestObject(Dummy, attr1=123, attr2="value2")
|
||||
expected = elt
|
||||
path = ""
|
||||
error_output = ErrorOutput(path, elt, expected)
|
||||
error_output.compute()
|
||||
assert error_output.output == ['(Dummy "attr1"="123" "attr2"="value2")']
|
||||
|
||||
def test_i_can_output_error_test_object_wrong_type(self):
|
||||
elt = Div(attr1=123, attr2="value2")
|
||||
expected = TestObject(Dummy, attr1=123, attr2="value2")
|
||||
path = ""
|
||||
error_output = ErrorOutput(path, elt, expected)
|
||||
error_output.compute()
|
||||
assert error_output.output == [
|
||||
'(div "attr1"="123" "attr2"="value2")',
|
||||
' ^^^ '
|
||||
]
|
||||
|
||||
def test_i_can_output_error_test_object_wrong_type_2(self):
|
||||
elt = Dummy2(attr1=123, attr2="value2")
|
||||
expected = TestObject(Dummy, attr1=123, attr2="value2")
|
||||
path = ""
|
||||
error_output = ErrorOutput(path, elt, expected)
|
||||
error_output.compute()
|
||||
assert error_output.output == [
|
||||
'(Dummy2 "attr1"="123" "attr2"="value2")',
|
||||
' ^^^^^^ '
|
||||
]
|
||||
|
||||
def test_i_can_output_error_test_object_wrong_type_3(self):
|
||||
elt = Div(attr1=123, attr2="value2")
|
||||
expected = TestObject("Dummy", attr1=123, attr2="value2")
|
||||
path = ""
|
||||
error_output = ErrorOutput(path, elt, expected)
|
||||
error_output.compute()
|
||||
assert error_output.output == [
|
||||
'(div "attr1"="123" "attr2"="value2")',
|
||||
' ^^^ '
|
||||
]
|
||||
|
||||
def test_i_can_output_error_test_object_wrong_value(self):
|
||||
elt = Dummy(attr1="456", attr2="value2")
|
||||
expected = TestObject(Dummy, attr1="123", attr2="value2")
|
||||
path = ""
|
||||
error_output = ErrorOutput(path, elt, expected)
|
||||
error_output.compute()
|
||||
assert error_output.output == [
|
||||
'(Dummy "attr1"="456" "attr2"="value2")',
|
||||
' ^^^^^^^^^^^^^ '
|
||||
]
|
||||
|
||||
|
||||
class TestErrorComparisonOutput:
|
||||
def test_i_can_output_comparison(self):
|
||||
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 == '''
|
||||
def test_i_can_output_comparison_with_path(self):
|
||||
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 == '''
|
||||
def test_i_can_output_comparison_when_missing_attributes(self):
|
||||
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 == '''
|
||||
def test_i_can_output_comparison_when_wrong_attributes(self):
|
||||
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 == '''
|
||||
def test_i_can_output_comparison_when_fewer_elements(self):
|
||||
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 == """
|
||||
def test_i_can_see_the_diff_when_matching(self):
|
||||
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)")
|
||||
^^^^^^^^^^^^^^^^ |"""
|
||||
|
||||
def test_i_can_see_the_diff_with_test_object_when_wrong_type(self):
|
||||
actual = Div(attr1=123, attr2="value2")
|
||||
expected = TestObject(Dummy, attr1=123, attr2="value2")
|
||||
|
||||
actual_out = ErrorOutput("dummy", actual, expected)
|
||||
expected_out = ErrorOutput("div", expected, expected)
|
||||
|
||||
comparison_out = ErrorComparisonOutput(actual_out, expected_out)
|
||||
|
||||
res = comparison_out.render()
|
||||
|
||||
assert "\n" + res == '''
|
||||
(div "attr1"="123" "attr2"="value2") | (Dummy "attr1"="123" "attr2"="value2")
|
||||
^^^ |'''
|
||||
|
||||
Reference in New Issue
Block a user