"""Scene/interaction tests: node items, port connect, move reroute, delete, undo.""" import pytest from pipeline_editor.config import AppConfig from pipeline_editor.controller import EditorController from pipeline_editor.model.document import DiagramDocument from pipeline_editor.model.node_library import default_library from pipeline_editor.view.node_item import NodeItem from pipeline_editor.view.edge_item import EdgeItem from pipeline_editor.view.scene import DiagramScene @pytest.fixture def env(qapp): doc = DiagramDocument() lib = default_library() ctrl = EditorController(doc, lib) cfg = AppConfig() scene = DiagramScene(doc, cfg, controller=ctrl) return doc, lib, ctrl, cfg, scene def test_add_node_creates_item(env): doc, lib, ctrl, cfg, scene = env node = scene.create_node("pump", 100, 100) assert node is not None assert node.node_id in scene.node_items assert isinstance(scene.node_items[node.node_id], NodeItem) def test_connect_ports_creates_edge_item(env): doc, lib, ctrl, cfg, scene = env a = ctrl.add_node("source", 0, 100) b = ctrl.add_node("pump", 200, 100) src_port = scene.node_items[a.node_id].port_item("out") dst_port = scene.node_items[b.node_id].port_item("in") scene._begin_edge(src_port) scene._finish_edge(dst_port) assert len(scene.edge_items) == 1 edge = next(doc.edges()) assert edge.source_node == a.node_id and edge.target_node == b.node_id def test_incompatible_ports_do_not_connect(env): doc, lib, ctrl, cfg, scene = env a = ctrl.add_node("source", 0, 100) # water t = ctrl.add_node("transformer", 200, 100) # power scene._begin_edge(scene.node_items[a.node_id].port_item("out")) scene._finish_edge(scene.node_items[t.node_id].port_item("hv")) assert len(scene.edge_items) == 0 def test_move_node_reroutes_edge(env): doc, lib, ctrl, cfg, scene = env a = ctrl.add_node("source", 0, 100) b = ctrl.add_node("pump", 200, 100) ctrl.connect(a.node_id, "out", b.node_id, "in") edge_item = next(iter(scene.edge_items.values())) before = edge_item.path_points()[-1] # move target node down; edge end should follow scene.node_items[b.node_id].setPos(200, 300) after = edge_item.path_points()[-1] assert after != before assert after[1] > before[1] def test_selection_emits_model(env): doc, lib, ctrl, cfg, scene = env a = ctrl.add_node("pump", 0, 0) received = [] scene.selection_changed.connect(lambda m: received.append(m)) scene.node_items[a.node_id].setSelected(True) assert received and received[-1] is a def test_delete_and_undo_roundtrip(env): doc, lib, ctrl, cfg, scene = env a = ctrl.add_node("source", 0, 100) b = ctrl.add_node("pump", 200, 100) ctrl.connect(a.node_id, "out", b.node_id, "in") assert len(scene.edge_items) == 1 ctrl.delete(node_ids=[a.node_id]) assert a.node_id not in scene.node_items assert len(scene.edge_items) == 0 # cascaded ctrl.undo() assert a.node_id in scene.node_items assert len(scene.edge_items) == 1 # edge restored def test_move_undo(env): doc, lib, ctrl, cfg, scene = env a = ctrl.add_node("pump", 40, 40) ctrl.record_move(a.node_id, (40, 40), (120, 200)) assert (a.x, a.y) == (120, 200) ctrl.undo() assert (a.x, a.y) == (40, 40) assert scene.node_items[a.node_id].pos().x() == 40