from types import SimpleNamespace
from windows_mcp.tree.views import (
BoundingBox,
Center,
TreeElementNode,
TreeState,
)
class TestBoundingBox:
def test_get_center_standard(self, sample_bounding_box):
center = sample_bounding_box.get_center()
assert center.x == 200
assert center.y == 100
def test_get_center_zero_size(self):
bb = BoundingBox(left=50, top=50, right=50, bottom=50, width=0, height=0)
center = bb.get_center()
assert center.x == 50
assert center.y == 50
def test_get_center_negative_coords(self):
bb = BoundingBox(left=-100, top=-200, right=100, bottom=0, width=200, height=200)
center = bb.get_center()
assert center.x == 0
assert center.y == -100
def test_xywh_to_string(self, sample_bounding_box):
assert sample_bounding_box.xywh_to_string() == "(100,50,200,100)"
def test_xyxy_to_string(self, sample_bounding_box):
assert sample_bounding_box.xyxy_to_string() == "(100,50,300,150)"
def test_convert_xywh_to_xyxy(self, sample_bounding_box):
x1, y1, x2, y2 = sample_bounding_box.convert_xywh_to_xyxy()
assert (x1, y1, x2, y2) == (100, 50, 300, 150)
def test_from_bounding_rectangle(self):
mock_rect = SimpleNamespace(left=10, top=20, right=110, bottom=70)
mock_rect.width = lambda: 100
mock_rect.height = lambda: 50
bb = BoundingBox.from_bounding_rectangle(mock_rect)
assert bb.left == 10
assert bb.top == 20
assert bb.right == 110
assert bb.bottom == 70
assert bb.width == 100
assert bb.height == 50
class TestCenter:
def test_to_string_standard(self, sample_center):
assert sample_center.to_string() == "(200,100)"
def test_to_string_negative(self):
c = Center(x=-10, y=-20)
assert c.to_string() == "(-10,-20)"
class TestTreeState:
def test_interactive_elements_to_string_empty(self):
ts = TreeState()
assert ts.interactive_elements_to_string() == "No interactive elements"
def test_interactive_elements_to_string_with_elements(self, sample_tree_element_node):
ts = TreeState(interactive_nodes=[sample_tree_element_node])
result = ts.interactive_elements_to_string()
lines = result.split("\n")
assert lines[0] == "# id|window|control_type|name|coords|focus"
assert lines[1] == "0|Notepad|Button|OK|(200,100)|True"
def test_interactive_elements_indices(self, sample_tree_element_node):
node2 = TreeElementNode(
bounding_box=sample_tree_element_node.bounding_box,
center=sample_tree_element_node.center,
name="Cancel",
control_type="Button",
window_name="Notepad",
)
ts = TreeState(interactive_nodes=[sample_tree_element_node, node2])
result = ts.interactive_elements_to_string()
lines = result.split("\n")
assert lines[1].startswith("0|")
assert lines[2].startswith("1|")
def test_scrollable_elements_to_string_empty(self):
ts = TreeState()
assert ts.scrollable_elements_to_string() == "No scrollable elements"
def test_scrollable_elements_to_string_with_elements(
self, sample_scroll_element_node, sample_tree_element_node
):
ts = TreeState(
interactive_nodes=[sample_tree_element_node],
scrollable_nodes=[sample_scroll_element_node],
)
result = ts.scrollable_elements_to_string()
lines = result.split("\n")
assert (
lines[0] == "# id|window|control_type|name|coords|h_scroll|h_pct|v_scroll|v_pct|focus"
)
# base_index = len(interactive_nodes) = 1
assert lines[1].startswith("1|")
def test_scrollable_elements_base_index_offset(self, sample_scroll_element_node):
bb = BoundingBox(left=0, top=0, right=10, bottom=10, width=10, height=10)
c = Center(x=5, y=5)
interactive = [TreeElementNode(bounding_box=bb, center=c, name=f"btn{i}") for i in range(3)]
ts = TreeState(
interactive_nodes=interactive,
scrollable_nodes=[sample_scroll_element_node],
)
result = ts.scrollable_elements_to_string()
lines = result.split("\n")
# base_index = 3 (three interactive nodes)
assert lines[1].startswith("3|")
class TestTreeElementNode:
def test_to_row(self, sample_tree_element_node):
row = sample_tree_element_node.to_row(0)
assert row == [0, "Notepad", "Button", "OK", "", "Alt+O", "(200,100)", True]
def test_update_from_node(self, sample_tree_element_node):
target = TreeElementNode(
bounding_box=BoundingBox(left=0, top=0, right=0, bottom=0, width=0, height=0),
center=Center(x=0, y=0),
)
target.update_from_node(sample_tree_element_node)
assert target.name == "OK"
assert target.control_type == "Button"
assert target.window_name == "Notepad"
assert target.value == ""
assert target.shortcut == "Alt+O"
assert target.xpath == "/Pane/Button"
assert target.is_focused is True
assert target.bounding_box is sample_tree_element_node.bounding_box
assert target.center is sample_tree_element_node.center
class TestScrollElementNode:
def test_to_row_with_base_index(self, sample_scroll_element_node):
row = sample_scroll_element_node.to_row(index=0, base_index=5)
assert row[0] == 5 # base_index + index
assert row[1] == "Notepad"
assert row[2] == "Pane"
assert row[3] == "Document"
assert row[4] == "(200,100)"
assert row[5] is False # horizontal_scrollable
assert row[6] == 0.0 # horizontal_scroll_percent
assert row[7] is True # vertical_scrollable
assert row[8] == 42.5 # vertical_scroll_percent
assert row[9] is False # is_focused