"""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()