Add an internal clipboard on the controller with undoable macro paste, and Copy/Paste menu actions and shortcuts in the main window. Covered by a test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
107 lines
3.2 KiB
Python
107 lines
3.2 KiB
Python
"""Integration tests for the main window: sample, calc, IO, undo, config."""
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from pipeline_editor.main_window import MainWindow
|
|
|
|
|
|
@pytest.fixture
|
|
def win(qtbot):
|
|
w = MainWindow()
|
|
qtbot.addWidget(w)
|
|
return w
|
|
|
|
|
|
def test_sample_builds_network(win):
|
|
win.load_sample()
|
|
assert len(list(win.document.nodes())) == 7
|
|
assert len(list(win.document.edges())) == 6
|
|
assert len(win.scene.node_items) == 7
|
|
assert len(win.scene.edge_items) == 6
|
|
|
|
|
|
def test_solve_flow_sets_edge_flows(win):
|
|
win.load_sample()
|
|
win.solve_flow()
|
|
flows = [e.flow for e in win.document.edges()]
|
|
assert any(f > 0 for f in flows)
|
|
# source edge should carry the sum of downstream demand (50 + 45)
|
|
assert max(flows) == pytest.approx(95)
|
|
|
|
|
|
def test_animation_toggle(win):
|
|
win.load_sample()
|
|
win.toggle_flow(True)
|
|
assert win.animator.running is True
|
|
win.animator.step()
|
|
win.toggle_flow(False)
|
|
assert win.animator.running is False
|
|
|
|
|
|
def test_delete_and_undo_via_controller(win):
|
|
win.load_sample()
|
|
node = next(win.document.nodes())
|
|
win.scene.node_items[node.node_id].setSelected(True)
|
|
n_before = len(list(win.document.nodes()))
|
|
win.delete_selection()
|
|
assert len(list(win.document.nodes())) == n_before - 1
|
|
win.controller.undo()
|
|
assert len(list(win.document.nodes())) == n_before
|
|
|
|
|
|
def test_duplicate_selection(win):
|
|
win.load_sample()
|
|
node = next(win.document.nodes())
|
|
win.scene.node_items[node.node_id].setSelected(True)
|
|
n_before = len(list(win.document.nodes()))
|
|
win.duplicate_selection()
|
|
assert len(list(win.document.nodes())) == n_before + 1
|
|
|
|
|
|
def test_copy_paste(win):
|
|
win.load_sample()
|
|
node = next(win.document.nodes())
|
|
win.scene.node_items[node.node_id].setSelected(True)
|
|
n_before = len(list(win.document.nodes()))
|
|
win.copy_selection()
|
|
win.paste_clipboard()
|
|
assert len(list(win.document.nodes())) == n_before + 1
|
|
win.controller.undo() # paste is one macro step
|
|
assert len(list(win.document.nodes())) == n_before
|
|
|
|
|
|
def test_save_and_open_roundtrip(win, tmp_path):
|
|
win.load_sample()
|
|
path = tmp_path / "diagram.pipe"
|
|
win._current_path = str(path)
|
|
win.save_document()
|
|
assert path.exists()
|
|
data = json.loads(path.read_text())
|
|
assert len(data["nodes"]) == 7
|
|
|
|
# open into a fresh window
|
|
win2 = MainWindow()
|
|
with open(path) as fh:
|
|
win2.document.load_dict(json.load(fh))
|
|
assert len(list(win2.document.nodes())) == 7
|
|
assert len(win2.scene.node_items) == 7 # scene rebuilt from 'cleared' signal
|
|
|
|
|
|
def test_config_change_applies_scheme(win):
|
|
win.load_sample()
|
|
win.config.color_scheme = "high_contrast"
|
|
win.config.emit_changed()
|
|
assert win.document.relations.active_scheme == "high_contrast"
|
|
assert win.document.relations.color("water") == "#0000ff"
|
|
|
|
|
|
def test_property_edit_through_panel_is_undoable(win):
|
|
win.load_sample()
|
|
node = next(n for n in win.document.nodes() if n.template_key == "consumer")
|
|
win.scene.node_items[node.node_id].setSelected(True)
|
|
win._on_property_edited("node", node.node_id, "demand", 123.0)
|
|
assert node.properties.value("demand") == 123.0
|
|
win.controller.undo()
|
|
assert node.properties.value("demand") != 123.0
|