mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-10-28 16:11:27 +01:00
270 lines
8.5 KiB
Python
270 lines
8.5 KiB
Python
# pylint: disable=missing-function-docstring, missing-module-docstring, redefined-outer-name
|
|
import pytest
|
|
from qtpy.QtWidgets import QInputDialog, QMessageBox
|
|
|
|
from bec_widgets.tests.client_mocks import mocked_client
|
|
from bec_widgets.widgets.containers.dock_area.profile_utils import (
|
|
get_open_workspaces,
|
|
list_profiles,
|
|
set_open_workspaces,
|
|
)
|
|
from bec_widgets.widgets.containers.dock_area.workspace_tabs import (
|
|
UNTITLED_WORKSPACE,
|
|
WorkspaceTabs,
|
|
)
|
|
|
|
NAMESPACE = "tabs_test"
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def isolate_profile_storage(tmp_path, monkeypatch):
|
|
root = tmp_path / "profiles_root"
|
|
root.mkdir(parents=True, exist_ok=True)
|
|
monkeypatch.setenv("BECWIDGETS_PROFILE_DIR", str(root))
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
def make_tabs(qtbot, mocked_client):
|
|
created = []
|
|
|
|
def _make(**kwargs):
|
|
tabs = WorkspaceTabs(profile_namespace=NAMESPACE, client=mocked_client, **kwargs)
|
|
qtbot.addWidget(tabs)
|
|
tabs.show()
|
|
qtbot.waitExposed(tabs)
|
|
created.append(tabs)
|
|
return tabs
|
|
|
|
yield _make
|
|
for tabs in created:
|
|
tabs.cleanup()
|
|
|
|
|
|
def _save_profile(tabs, name):
|
|
"""Save the current tab under *name* without dialogs."""
|
|
tabs.current_dock_area().save_profile(name, show_dialog=False)
|
|
|
|
|
|
def test_starts_with_one_untitled_workspace(make_tabs):
|
|
tabs = make_tabs()
|
|
assert tabs.count() == 1
|
|
assert tabs.tabText(0) == UNTITLED_WORKSPACE
|
|
assert tabs.current_dock_area() is not None
|
|
assert tabs.workspace_names() == [None]
|
|
|
|
|
|
def test_new_workspace_adds_and_switches_tab(make_tabs):
|
|
tabs = make_tabs()
|
|
first = tabs.current_dock_area()
|
|
tabs.new_workspace()
|
|
assert tabs.count() == 2
|
|
assert tabs.currentIndex() == 1
|
|
second = tabs.current_dock_area()
|
|
assert second is not first
|
|
assert tabs.dock_areas() == [first, second]
|
|
|
|
|
|
def test_each_tab_keeps_its_own_layout(make_tabs):
|
|
tabs = make_tabs()
|
|
tabs.current_dock_area().new("RingProgressBar")
|
|
tabs.new_workspace()
|
|
assert tabs.current_dock_area().dock_list() == []
|
|
tabs.setCurrentIndex(0)
|
|
assert len(tabs.current_dock_area().dock_list()) == 1
|
|
|
|
|
|
def test_saving_names_the_tab_and_persists_open_tabs(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
assert tabs.tabText(0) == "alignment"
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "scans")
|
|
assert get_open_workspaces(NAMESPACE) == (["alignment", "scans"], "scans")
|
|
|
|
tabs.setCurrentIndex(0)
|
|
assert get_open_workspaces(NAMESPACE) == (["alignment", "scans"], "alignment")
|
|
|
|
|
|
def test_unsaved_tabs_are_not_persisted(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
assert get_open_workspaces(NAMESPACE) == (["alignment"], None)
|
|
|
|
|
|
def test_restores_open_tabs_lazily(make_tabs):
|
|
tabs = make_tabs()
|
|
tabs.current_dock_area().new("RingProgressBar")
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "scans")
|
|
tabs.setCurrentIndex(0)
|
|
tabs.cleanup()
|
|
|
|
restored = make_tabs()
|
|
assert [restored.tabText(i) for i in range(restored.count())] == ["alignment", "scans"]
|
|
assert restored.currentIndex() == 0
|
|
# Only the active tab builds its dock area at startup
|
|
assert [page.is_materialized for page in restored.pages()] == [True, False]
|
|
assert len(restored.current_dock_area().dock_list()) == 1
|
|
|
|
restored.setCurrentIndex(1)
|
|
assert restored.pages()[1].is_materialized
|
|
assert restored.current_dock_area()._current_profile_name == "scans"
|
|
|
|
|
|
def test_restore_skips_missing_profiles(make_tabs):
|
|
set_open_workspaces(["gone", "also_gone"], "gone", namespace=NAMESPACE)
|
|
tabs = make_tabs()
|
|
assert tabs.count() == 1
|
|
assert tabs.workspace_names() == [None]
|
|
|
|
|
|
def test_restore_is_scoped_to_known_profiles(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.cleanup()
|
|
set_open_workspaces(["alignment", "gone"], "gone", namespace=NAMESPACE)
|
|
|
|
restored = make_tabs()
|
|
assert restored.workspace_names() == ["alignment"]
|
|
|
|
|
|
def test_open_workspace_switches_to_existing_tab(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
tabs.open_workspace("alignment")
|
|
assert tabs.count() == 2
|
|
assert tabs.currentIndex() == 0
|
|
|
|
|
|
def test_open_workspace_opens_saved_profile_in_new_tab(make_tabs):
|
|
tabs = make_tabs()
|
|
tabs.current_dock_area().new("RingProgressBar")
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "scans")
|
|
tabs.close_workspace(0)
|
|
assert tabs.workspace_names() == ["scans"]
|
|
|
|
tabs.open_workspace("alignment")
|
|
assert tabs.workspace_names() == ["scans", "alignment"]
|
|
assert len(tabs.current_dock_area().dock_list()) == 1
|
|
|
|
|
|
def test_loading_profile_open_in_other_tab_switches_instead(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "scans")
|
|
|
|
tabs.current_dock_area().load_profile("alignment")
|
|
|
|
assert tabs.currentIndex() == 0
|
|
assert tabs.workspace_names() == ["alignment", "scans"]
|
|
|
|
|
|
def test_saving_under_name_open_in_other_tab_is_refused(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "alignment")
|
|
assert tabs.workspace_names() == ["alignment", None]
|
|
|
|
|
|
def test_close_workspace_keeps_profile_on_disk(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "scans")
|
|
assert tabs.close_workspace(0)
|
|
assert tabs.workspace_names() == ["scans"]
|
|
assert "alignment" in list_profiles(NAMESPACE)
|
|
assert get_open_workspaces(NAMESPACE) == (["scans"], "scans")
|
|
|
|
|
|
def test_closing_last_tab_leaves_empty_workspace(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.close_workspace(0)
|
|
assert tabs.count() == 1
|
|
assert tabs.workspace_names() == [None]
|
|
assert "alignment" in list_profiles(NAMESPACE)
|
|
|
|
|
|
def test_close_unsaved_workspace_with_widgets_asks_first(make_tabs, monkeypatch):
|
|
tabs = make_tabs()
|
|
tabs.current_dock_area().new("RingProgressBar")
|
|
tabs.new_workspace()
|
|
monkeypatch.setattr(
|
|
QMessageBox, "question", lambda *args, **kwargs: QMessageBox.StandardButton.No
|
|
)
|
|
assert not tabs.close_workspace(0)
|
|
assert tabs.count() == 2
|
|
|
|
monkeypatch.setattr(
|
|
QMessageBox, "question", lambda *args, **kwargs: QMessageBox.StandardButton.Yes
|
|
)
|
|
assert tabs.close_workspace(0)
|
|
assert tabs.count() == 1
|
|
|
|
|
|
def test_rename_saves_under_new_name_and_keeps_old_profile(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
assert tabs.rename_workspace(0, "alignment_v2")
|
|
assert tabs.tabText(0) == "alignment_v2"
|
|
profiles = list_profiles(NAMESPACE)
|
|
assert "alignment" in profiles and "alignment_v2" in profiles
|
|
assert get_open_workspaces(NAMESPACE) == (["alignment_v2"], "alignment_v2")
|
|
|
|
|
|
def test_rename_untitled_workspace_saves_it(make_tabs, monkeypatch):
|
|
tabs = make_tabs()
|
|
monkeypatch.setattr(QInputDialog, "getText", lambda *args, **kwargs: ("beamtime", True))
|
|
assert tabs.rename_workspace()
|
|
assert tabs.tabText(0) == "beamtime"
|
|
assert "beamtime" in list_profiles(NAMESPACE)
|
|
|
|
|
|
def test_rename_to_name_open_in_other_tab_is_refused(make_tabs, monkeypatch):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
infos = []
|
|
monkeypatch.setattr(QMessageBox, "information", lambda *args, **kwargs: infos.append(args))
|
|
assert not tabs.rename_workspace(1, "alignment")
|
|
assert infos
|
|
assert tabs.workspace_names() == ["alignment", None]
|
|
|
|
|
|
def test_rename_over_existing_saved_profile_asks_first(make_tabs, monkeypatch):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.close_workspace(0)
|
|
monkeypatch.setattr(
|
|
QMessageBox, "question", lambda *args, **kwargs: QMessageBox.StandardButton.No
|
|
)
|
|
assert not tabs.rename_workspace(0, "alignment")
|
|
assert tabs.workspace_names() == [None]
|
|
|
|
|
|
def test_moving_tabs_updates_persisted_order(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs.new_workspace()
|
|
_save_profile(tabs, "scans")
|
|
tabs.tabBar().moveTab(1, 0)
|
|
assert get_open_workspaces(NAMESPACE)[0] == ["scans", "alignment"]
|
|
|
|
|
|
def test_open_menu_lists_saved_profiles(make_tabs):
|
|
tabs = make_tabs()
|
|
_save_profile(tabs, "alignment")
|
|
tabs._populate_open_menu()
|
|
texts = [action.text() for action in tabs._open_menu.actions()]
|
|
assert "New Workspace" in texts
|
|
assert "alignment" in texts
|