131 lines
4.5 KiB
Python
131 lines
4.5 KiB
Python
|
|
"""GUI tests for Options → Plot Properties.
|
||
|
|
|
||
|
|
Covers the dialog itself plus MainWindow's live-apply / persist / revert
|
||
|
|
wiring. QSettings are injected so nothing touches real user state.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
pytest.importorskip("PySide6")
|
||
|
|
pytest.importorskip("pyvistaqt")
|
||
|
|
|
||
|
|
from PySide6.QtCore import QObject, QSettings, Signal
|
||
|
|
from PySide6.QtWidgets import QDialog
|
||
|
|
|
||
|
|
|
||
|
|
def _settings(tmp_path): # type: ignore[no-untyped-def]
|
||
|
|
return QSettings(str(tmp_path / "otko.ini"), QSettings.Format.IniFormat)
|
||
|
|
|
||
|
|
|
||
|
|
def _window(tmp_path, qtbot): # type: ignore[no-untyped-def]
|
||
|
|
from otko.views.main_window import MainWindow
|
||
|
|
|
||
|
|
window = MainWindow(settings=_settings(tmp_path))
|
||
|
|
qtbot.addWidget(window)
|
||
|
|
return window
|
||
|
|
|
||
|
|
|
||
|
|
def test_default_style_is_the_documented_palette(tmp_path, qtbot) -> None: # type: ignore[no-untyped-def]
|
||
|
|
from otko.views.canvas3d.style import RenderStyle
|
||
|
|
|
||
|
|
window = _window(tmp_path, qtbot)
|
||
|
|
assert window._style == RenderStyle()
|
||
|
|
|
||
|
|
|
||
|
|
def test_set_plot_style_reaches_both_canvases_and_persists(tmp_path, qtbot) -> None: # type: ignore[no-untyped-def]
|
||
|
|
window = _window(tmp_path, qtbot)
|
||
|
|
window.swap_canvas_backend("plotly") # build the second canvas too
|
||
|
|
|
||
|
|
window.set_plot_style(
|
||
|
|
window._style.with_updates(element_beam_color="#ff0000", label_font_size=20)
|
||
|
|
)
|
||
|
|
|
||
|
|
assert set(window._canvases) == {"pyvista", "plotly"}
|
||
|
|
for canvas in window._canvases.values():
|
||
|
|
assert canvas._style.element_beam_color == "#ff0000"
|
||
|
|
assert canvas._style.label_font_size == 20
|
||
|
|
|
||
|
|
# A fresh window sharing the settings file reloads the customisation.
|
||
|
|
from otko.views.main_window import MainWindow
|
||
|
|
|
||
|
|
fresh = MainWindow(settings=_settings(tmp_path))
|
||
|
|
qtbot.addWidget(fresh)
|
||
|
|
assert fresh._style.element_beam_color == "#ff0000"
|
||
|
|
assert fresh._style.label_font_size == 20
|
||
|
|
|
||
|
|
|
||
|
|
def test_preview_does_not_persist(tmp_path, qtbot) -> None: # type: ignore[no-untyped-def]
|
||
|
|
window = _window(tmp_path, qtbot)
|
||
|
|
window.set_plot_style(window._style.with_updates(node_color="#010203"), persist=False)
|
||
|
|
|
||
|
|
assert window._canvas._style.node_color == "#010203"
|
||
|
|
assert window._settings.value("plot/props") is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_cancel_reverts_the_style(tmp_path, qtbot, monkeypatch) -> None: # type: ignore[no-untyped-def]
|
||
|
|
window = _window(tmp_path, qtbot)
|
||
|
|
original = window._style
|
||
|
|
preview = original.with_updates(node_color="#010203")
|
||
|
|
|
||
|
|
class _RejectingDialog(QObject):
|
||
|
|
changed = Signal(object)
|
||
|
|
|
||
|
|
def __init__(self, style: object, parent: QObject | None = None) -> None:
|
||
|
|
super().__init__(parent)
|
||
|
|
self._style = preview
|
||
|
|
|
||
|
|
def exec(self) -> QDialog.DialogCode:
|
||
|
|
self.changed.emit(preview) # live preview happens...
|
||
|
|
return QDialog.DialogCode.Rejected # ...but the user cancels
|
||
|
|
|
||
|
|
def plot_style(self): # type: ignore[no-untyped-def]
|
||
|
|
return self._style
|
||
|
|
|
||
|
|
monkeypatch.setattr("otko.views.dialogs.PlotPropertiesDialog", _RejectingDialog)
|
||
|
|
window._on_plot_properties()
|
||
|
|
|
||
|
|
assert window._style == original
|
||
|
|
assert window._canvas._style.node_color == original.node_color
|
||
|
|
assert window._settings.value("plot/props") is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_accept_applies_and_persists(tmp_path, qtbot, monkeypatch) -> None: # type: ignore[no-untyped-def]
|
||
|
|
window = _window(tmp_path, qtbot)
|
||
|
|
preview = window._style.with_updates(node_color="#010203")
|
||
|
|
|
||
|
|
class _AcceptingDialog(QObject):
|
||
|
|
changed = Signal(object)
|
||
|
|
|
||
|
|
def __init__(self, style: object, parent: QObject | None = None) -> None:
|
||
|
|
super().__init__(parent)
|
||
|
|
self._style = preview
|
||
|
|
|
||
|
|
def exec(self) -> QDialog.DialogCode:
|
||
|
|
return QDialog.DialogCode.Accepted
|
||
|
|
|
||
|
|
def plot_style(self): # type: ignore[no-untyped-def]
|
||
|
|
return self._style
|
||
|
|
|
||
|
|
monkeypatch.setattr("otko.views.dialogs.PlotPropertiesDialog", _AcceptingDialog)
|
||
|
|
window._on_plot_properties()
|
||
|
|
|
||
|
|
assert window._style.node_color == "#010203"
|
||
|
|
assert window._canvas._style.node_color == "#010203"
|
||
|
|
assert "#010203" in str(window._settings.value("plot/props"))
|
||
|
|
|
||
|
|
|
||
|
|
def test_dialog_edits_and_reset(tmp_path, qtbot) -> None: # type: ignore[no-untyped-def]
|
||
|
|
from otko.views.canvas3d.style import RenderStyle
|
||
|
|
from otko.views.dialogs import PlotPropertiesDialog
|
||
|
|
|
||
|
|
dialog = PlotPropertiesDialog(RenderStyle())
|
||
|
|
qtbot.addWidget(dialog)
|
||
|
|
assert dialog.plot_style() == RenderStyle()
|
||
|
|
|
||
|
|
dialog._colors["node_color"].set_color("#abcdef")
|
||
|
|
assert dialog.plot_style().node_color == "#abcdef"
|
||
|
|
|
||
|
|
dialog._reset()
|
||
|
|
assert dialog.plot_style() == RenderStyle()
|