otko/tests/gui/test_grid_dialog.py

188 lines
6.2 KiB
Python
Raw Normal View History

2026-09-08 02:12:15 -04:00
"""SAP2000-style grid dialog tests — DefineGridSystemDataDialog and friends."""
from __future__ import annotations
import pytest
pytest.importorskip("PySide6")
from otko.core import ( # noqa: E402
CoordinateGridSystem,
CoordinateSystem,
GridLine,
GridSystem,
make_grid_lines,
)
# ────────────────────────── Quick Grid Lines ──────────────────────────
@pytest.mark.gui
def test_quick_grid_lines_produces_ordinates(qtbot) -> None: # type: ignore[no-untyped-def]
from otko.views.dialogs.quick_grid_lines import QuickGridLinesDialog
2026-09-08 02:12:15 -04:00
dlg = QuickGridLinesDialog()
qtbot.addWidget(dlg)
dlg._x_n.setValue(4)
dlg._x_s.setValue(3.0)
dlg._x_f.setValue(0.0)
dlg._y_n.setValue(2)
dlg._y_s.setValue(4.0)
dlg._y_f.setValue(0.0)
dlg._z_n.setValue(3)
dlg._z_s.setValue(3.0)
dlg._z_f.setValue(-3.0)
2026-09-08 02:12:15 -04:00
xs, ys, zs = dlg.ordinates()
assert xs == [0.0, 3.0, 6.0, 9.0]
assert ys == [0.0, 4.0]
assert zs == [-3.0, 0.0, 3.0]
@pytest.mark.gui
def test_quick_grid_zero_lines_produces_empty(qtbot) -> None: # type: ignore[no-untyped-def]
from otko.views.dialogs.quick_grid_lines import QuickGridLinesDialog
2026-09-08 02:12:15 -04:00
dlg = QuickGridLinesDialog()
qtbot.addWidget(dlg)
dlg._x_n.setValue(0)
dlg._y_n.setValue(0)
dlg._z_n.setValue(0)
xs, ys, zs = dlg.ordinates()
assert xs == [] and ys == [] and zs == []
# ────────────────────── Coord Location + Orientation ──────────────────
@pytest.mark.gui
def test_locate_origin_round_trip(qtbot) -> None: # type: ignore[no-untyped-def]
from otko.views.dialogs.locate_origin import (
CoordSystemLocationOrientationDialog,
)
2026-09-08 02:12:15 -04:00
dlg = CoordSystemLocationOrientationDialog(
origin=(1.5, -2.0, 3.0),
rotation_deg=(0.0, 0.0, 45.0),
)
qtbot.addWidget(dlg)
origin, rot = dlg.location()
assert origin == pytest.approx((1.5, -2.0, 3.0))
assert rot == pytest.approx((0.0, 0.0, 45.0))
# ────────────────────── Define Grid System Data form ──────────────────
@pytest.mark.gui
def test_define_grid_data_round_trip(qtbot) -> None: # type: ignore[no-untyped-def]
"""Loading an existing system and reading .system() must round-trip."""
from otko.views.dialogs.define_grid_data import (
DefineGridSystemDataDialog,
)
2026-09-08 02:12:15 -04:00
cs = CoordinateGridSystem(
name="Floor2",
coord=CoordinateSystem(origin=(0, 0, 3.5)),
grid=GridSystem(
x_grid_lines=make_grid_lines("X", [0.0, 4.0, 8.0]),
y_grid_lines=make_grid_lines("Y", [0.0, 6.0]),
z_grid_lines=make_grid_lines("Z", [0.0]),
hide_all=False,
glue_to_grid=True,
bubble_size=18,
),
)
dlg = DefineGridSystemDataDialog(existing=cs)
qtbot.addWidget(dlg)
result = dlg.system()
assert result.name == "Floor2"
assert result.coord.origin == (0.0, 0.0, 3.5)
assert result.grid.x_lines == [0.0, 4.0, 8.0]
assert result.grid.y_lines == [0.0, 6.0]
assert result.grid.z_lines == [0.0]
assert result.grid.glue_to_grid is True
assert result.grid.bubble_size == 18
@pytest.mark.gui
def test_define_grid_data_spacing_mode_conversion(qtbot) -> None: # type: ignore[no-untyped-def]
"""Switching to Spacing mode and back must preserve ordinate values."""
from otko.views.dialogs.define_grid_data import (
DefineGridSystemDataDialog,
)
2026-09-08 02:12:15 -04:00
cs = CoordinateGridSystem(
name="Test",
grid=GridSystem(x_grid_lines=make_grid_lines("X", [0.0, 3.0, 6.0, 10.0])),
)
dlg = DefineGridSystemDataDialog(existing=cs)
qtbot.addWidget(dlg)
# Toggle to Spacing mode.
dlg._rb_spacing.setChecked(True)
# Then back to Ordinates.
dlg._rb_ordinates.setChecked(True)
result = dlg.system()
assert result.grid.x_lines == pytest.approx([0.0, 3.0, 6.0, 10.0])
@pytest.mark.gui
def test_define_grid_data_global_name_locked(qtbot) -> None: # type: ignore[no-untyped-def]
"""For the Global system, the Name field is disabled."""
from otko.views.dialogs.define_grid_data import (
DefineGridSystemDataDialog,
)
2026-09-08 02:12:15 -04:00
dlg = DefineGridSystemDataDialog(is_global=True)
qtbot.addWidget(dlg)
assert dlg._name_edit.text() == "Global"
assert not dlg._name_edit.isEnabled()
assert not dlg._btn_locate.isEnabled()
@pytest.mark.gui
def test_define_grid_data_add_and_delete_row(qtbot) -> None: # type: ignore[no-untyped-def]
"""Add Row / Delete Row buttons modify the X tab table directly."""
from otko.views.dialogs.define_grid_data import (
DefineGridSystemDataDialog,
)
2026-09-08 02:12:15 -04:00
dlg = DefineGridSystemDataDialog()
qtbot.addWidget(dlg)
# Start with empty X table.
assert dlg._tab_x._table.rowCount() == 0
dlg._tab_x._on_add_row()
dlg._tab_x._on_add_row()
assert dlg._tab_x._table.rowCount() == 2
dlg._tab_x._table.selectRow(0)
dlg._tab_x._on_delete_row()
assert dlg._tab_x._table.rowCount() == 1
# ────────────────────── GridLine + migration tests ────────────────────
def test_grid_line_defaults() -> None:
ln = GridLine(id="A", ordinate=3.0)
assert ln.id == "A"
assert ln.line_type == "Primary"
assert ln.visible is True
assert ln.bubble_loc == "End"
assert ln.color == "#808080"
def test_grid_system_migrates_flat_x_lines() -> None:
"""Legacy constructor with list[float] must convert to GridLine records."""
g = GridSystem(x_lines=[0.0, 2.5, 5.0])
assert len(g.x_grid_lines) == 3
ids = [ln.id for ln in g.x_grid_lines]
assert ids == ["X1", "X2", "X3"]
assert g.x_lines == [0.0, 2.5, 5.0]
def test_grid_system_hide_all_and_glue_persist() -> None:
g = GridSystem(
x_grid_lines=make_grid_lines("X", [0.0, 1.0]),
hide_all=True,
glue_to_grid=True,
bubble_size=24,
)
d = g.model_dump()
r = GridSystem.model_validate(d)
assert r.hide_all is True
assert r.glue_to_grid is True
assert r.bubble_size == 24