diff --git a/DESIGN.md b/DESIGN.md index bcc0503..d11ef34 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -323,6 +323,7 @@ The `testium_assist` editor extension is a thin LSP client that spawns `testium Both Flatpak and AppImage export `TESTIUM_VERSION` from a launcher (Flatpak: launcher script in `org.testium.Testium.yaml`; AppImage: `runtime.env` in `AppImageBuilder.yml`). `get_testium_version()` checks `/.flatpak-info` / `APPIMAGE` and reads `TESTIUM_VERSION` rather than relying on package metadata or repo introspection. ## Recent fixes / notable changes +- Show Results (GUI): the toolbar action stays enabled during a run (the log grows live, so it is useful mid-test), not just after. In Flatpak `QDesktopServices.openUrl` routes through the OpenURI portal and often opens no editor for a `.log`; `bins.host_open_path()` now spawns `xdg-open` on the host via `flatpak-spawn --host` (returns False outside Flatpak so the caller falls back to `openUrl`). - Test-tree search (GUI): a Ctrl+F find bar highlights + navigates matching items, with Name/Type/Doc field checkboxes. Search modifications run under `blockSignals` (else `setBackground`→`itemChanged`→`on_testChecked` storms the controller), and the search/run highlights share one flag-driven `_refresh_highlight()` (run > search > default) so overlapping layers never leave a stale colour. See "## Test-tree search (GUI)". - `pytest` item: pytest analogue of `unittest`, but runs on the **host interpreter in a subprocess** (`bins.python_bin()`, like `py_func`) so it works across every packaging channel. A stdlib-only pytest plugin streams collected node-ids + per-test results back over stdout via sentinels; each test becomes a child item with its own PASS/FAIL/SKIP, duration and failure message. Params: `test_file`, `test_method`. Validation item: `test/validation/items/pytest/` (the validation venv now pip-installs `pytest`). See "### `pytest` item". - Graceful item load: a self-loading item that fails to load its module/file (e.g. a `unittest` test file importing a missing module, or `pytest` not installed on the host) no longer aborts the **whole** test load. `TestSet._load_item()` wraps the item's `load()`, emits a `tm.print_warn(...)` at load time and records the reason in `item._load_error`; the `@test_run` wrapper turns a non-None `_load_error` into a clean run-time `FAILURE` (message printed once via `write_footer`). The rest of the campaign loads and runs normally. Applies to module-loading items (`unittest`, `pytest`); structural action loading stays fail-fast. diff --git a/src/testium/interpreter/utils/bins.py b/src/testium/interpreter/utils/bins.py index 78af2f7..f2bc1bf 100644 --- a/src/testium/interpreter/utils/bins.py +++ b/src/testium/interpreter/utils/bins.py @@ -199,6 +199,23 @@ def host_console_command(shell_cmd, cwd): return ["flatpak-spawn", "--host", f"--directory={cwd}", *argv] +def host_open_path(path): + """Open *path* with the host default application (Flatpak only). + + QDesktopServices/openUrl routes through the OpenURI portal inside Flatpak, + which often fails to open a plain editor for a log file. Spawn xdg-open on + the host so the user's real default app is used. Returns True on dispatch; + False (incl. outside Flatpak) so the caller can fall back to openUrl. + """ + if not _in_flatpak(): + return False + try: + subprocess.Popen(["flatpak-spawn", "--host", "xdg-open", path]) + return True + except (FileNotFoundError, PermissionError): + return False + + def _which_host_flatpak(name): """Resolve a binary name (or absolute path) on the host via flatpak-spawn. diff --git a/src/testium/main_win/test_runner.py b/src/testium/main_win/test_runner.py index c3ca591..ac15b21 100644 --- a/src/testium/main_win/test_runner.py +++ b/src/testium/main_win/test_runner.py @@ -181,7 +181,8 @@ class TestRunner: w.actionStart_test.setText("Pause test") w.actionPreferences.setDisabled(True) w.actionRefresh_test.setDisabled(True) - w.actionShow_Results.setDisabled(True) + # Show Results stays available during the run (log grows live). + w.actionShow_Results.setEnabled(True) w.actionSave_report.setDisabled(True) w.logSettingsBox.setDisabled(True) w.actionStop_test.setEnabled(True) diff --git a/src/testium/main_win/testium_win.py b/src/testium/main_win/testium_win.py index 8e701c5..6ae5797 100755 --- a/src/testium/main_win/testium_win.py +++ b/src/testium/main_win/testium_win.py @@ -40,6 +40,7 @@ from runtime.string_queue import StringQueue from interpreter.process import TestProcess from interpreter.utils.test_ctrl import TestSetController from interpreter.utils.icons import icon_prefix +from interpreter.utils import bins from main_win.test_run.outlog import OutLog from main_win.test_run.test_run import ThreadTestStatus @@ -639,7 +640,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.statusBar().showMessage( "Opening the logfile (" + s + "): " + self.logFileName, 100000 ) - QDesktopServices.openUrl(QUrl.fromLocalFile(self.logFileName)) + if not bins.host_open_path(self.logFileName): + QDesktopServices.openUrl(QUrl.fromLocalFile(self.logFileName)) @Slot() def on_actionHelp_triggered(self):