"""The device-setup dialog: connection and bus initialisation, out of the rail. Both groups are touched once per machine start-up. In the rail they cost ~460 px of height above the axis readout, which is read all day — so they moved into a modeless dialog opened from the status strip, its state chips, or the File menu. What these tests protect: * the rail no longer carries them, and the dialog is not open at start-up; * every entry point works and lands on the right tab; * the window's own handlers still receive the panels' signals, from inside the dialog — the connect and download code paths did not move, only the widgets did; * the dialog survives a language change and remembers its size and tab. """ from __future__ import annotations import pytest from orisys_app_shared.i18n import set_locale, t from orisys_autocal.motion import ConnectionSpec from orisys_autocal.ui.device_setup import DeviceSetupDialog def test_the_rail_no_longer_holds_the_startup_panels(make_window): """The point of the change: the rail is the run screen, not the setup form.""" window = make_window() try: rail = window.rail_content.findChildren(object) names = {type(child).__name__ for child in rail} assert "AxisReadoutPanel" in names assert "ConnectionPanel" not in names assert "BusPanel" not in names # And they are in the dialog, once each. (The parent is Qt's internal # stacked widget, so ask about ancestry rather than the direct parent.) dialog = window.device_setup assert window.connection_panel is dialog.connection_panel assert window.bus_panel is dialog.bus_panel assert dialog.tabs.isAncestorOf(window.connection_panel) assert dialog.tabs.isAncestorOf(window.bus_panel) finally: window.close() def test_the_dialog_is_hidden_until_it_is_asked_for(make_window): window = make_window() try: assert not window.device_setup.isVisible() window._show_device_setup() # noqa: SLF001 assert window.device_setup.isVisible() # Showing it again raises rather than ignores: a second click that appears # to do nothing reads as a broken button. window._show_device_setup("bus") # noqa: SLF001 assert window.device_setup.isVisible() assert window.device_setup.current_tab() == "bus" finally: window.close() def test_the_strip_button_opens_the_setup(make_window): window = make_window() try: window.status_strip.setup_button.click() assert window.device_setup.isVisible() assert window.status_strip.setup_button.text() == t("setup.open") finally: window.close() @pytest.mark.parametrize( "chip, tab", [("chip_connection", "connection"), ("chip_bus", "bus"), ("chip_init", "bus")], ) def test_the_state_chips_open_the_tab_they_report(make_window, chip, tab): """A chip that says "not connected" is the natural place to click.""" window = make_window() try: getattr(window.status_strip, chip).clicked.emit() assert window.device_setup.isVisible() assert window.device_setup.current_tab() == tab finally: window.close() def test_the_file_menu_opens_the_setup(make_window): window = make_window() try: window.act_device_setup.trigger() assert window.device_setup.isVisible() assert window.device_setup.current_tab() == "connection" assert window.act_device_setup.text() == t("menu.device_setup") finally: window.close() def test_connecting_from_the_dialog_reaches_the_window(make_window): """The widgets moved; the code path did not. The connect button lives in the dialog now, so this drives the *button* rather than emitting the signal directly: it is the signal wiring across the new parent that could have been lost. """ window = make_window() try: seen = [] window.connection_panel.connectRequested.connect(seen.append) panel = window.connection_panel panel.mode_combo.setCurrentIndex(panel.mode_combo.findData("demo")) window.device_setup.open_for("connection") panel.open_button.click() assert seen, "the connection request never left the dialog" assert isinstance(seen[0], ConnectionSpec) assert seen[0].mode == "demo" finally: window.close() def test_downloading_the_bas_from_the_dialog_reaches_the_window(make_window): window = make_window() try: # A path *must* be set first: with none, the window opens a modal file # chooser, which offscreen is a hang rather than a test. from orisys_autocal.ui.bus_panel import default_bas_dir window.bus_panel.set_bas_path(str(default_bas_dir() / "ECAT.bas")) seen = [] window.bus_panel.downloadRequested.connect(lambda: seen.append(True)) window.device_setup.open_for("bus") window.bus_panel.download_button.click() assert seen, "the download request never left the dialog" finally: window.close() def test_the_dialog_survives_a_language_change(make_window): window = make_window() try: window._set_language("en_US") # noqa: SLF001 assert window.device_setup.windowTitle() == t("setup.title") assert window.device_setup.tabs.tabText(1) == t("setup.tab_bus") assert window.status_strip.setup_button.text() == t("setup.open") # The panels inside are retranslated too, not just the dialog's own labels. assert window.connection_panel.open_button.text() == t("connection.open") window._set_language("zh_CN") # noqa: SLF001 set_locale("zh_CN") finally: window.close() def test_the_dialog_remembers_its_size_and_tab(make_window): window = make_window() try: window.device_setup.select_tab("bus") window.device_setup.resize(640, 480) stored = window.device_setup.settings() assert stored["tab"] == "bus" assert stored["size"] == [640, 480] fresh = DeviceSetupDialog(ConnectionSpec(mode="eth", ip="10.0.0.1")) try: fresh.apply_settings(stored) assert fresh.size().width() == 640 assert fresh.current_tab() == "bus" # Anything malformed is ignored, not raised on: this comes from a file # a human may have edited. fresh.apply_settings({"size": ["wide", 2], "tab": 7}) fresh.apply_settings(None) finally: fresh.deleteLater() finally: window.close() def test_the_axis_mapping_page_holds_the_mapping_and_refuses_duplicates(make_window): """The page and the service must never disagree about which drive moves what.""" from orisys_autocal.motion import DEFAULT_AXIS_ROLES window = make_window() try: dialog = window.device_setup assert dialog.tabs.count() == 3 page = dialog.roles_page assert page.roles() == DEFAULT_AXIS_ROLES assert page.problem() == "" assert window.poller.service.axis_roles == DEFAULT_AXIS_ROLES # Two roles on one drive: reported on the page *and* by the guard. page.combos["pitch"].setCurrentIndex(page.combos["pitch"].findData(3)) assert page.problem() != "" assert window.poller.service.roles_problem() != "" assert window.poller.service.check_axes_move({"yaw": 1.0}) is not None # Correcting it clears both. page.combos["pitch"].setCurrentIndex(page.combos["pitch"].findData(2)) assert page.problem() == "" assert window.poller.service.roles_problem() == "" finally: window.close() def test_the_mapping_page_opens_on_the_stored_mapping(make_window): window = make_window( axis_roles={"x": 1, "y": 0, "pitch": 2, "yaw": 3, "z": 4} ) try: assert window.device_setup.roles_page.roles()["x"] == 1 assert window.poller.service.axis_roles["x"] == 1 finally: window.close() def test_opening_the_setup_does_not_touch_the_connection(make_window): """Opening a dialog is not a command: it must not connect or disconnect.""" window = make_window() try: window.device_setup.open_for("connection") assert not window._last_snapshot.connected # noqa: SLF001 window.device_setup.close() assert window.connection_panel.close_button.isEnabled() is False assert window.connection_panel.open_button.isEnabled() is True finally: window.close()