diff --git a/dzgui/api/shortcuts.py b/dzgui/api/shortcuts.py
index 144bb44..2ecb076 100644
--- a/dzgui/api/shortcuts.py
+++ b/dzgui/api/shortcuts.py
@@ -10,7 +10,7 @@ from pathlib import Path
from typing import Any
from dzgui.api.steam import find_user_id_32
-from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, GTK_APP_ID, IMAGES_PATH
+from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
from dzgui.util.dirs import copy_dzgui_to_xdg_data, make_parents
from dzgui.util.strings import unknown
@@ -158,7 +158,7 @@ class Shortcuts:
NEW_ENTRY["DevkitGameID"] = ""
NEW_ENTRY["DevkitOverrideAppID"] = 0
NEW_ENTRY["LastPlayTime"] = 0
- NEW_ENTRY["FlatpakAppID"] = GTK_APP_ID
+ NEW_ENTRY["FlatpakAppID"] = ""
NEW_ENTRY["sortas"] = ""
NEW_ENTRY["tags"] = {}
diff --git a/dzgui/api/steam.py b/dzgui/api/steam.py
index 7253d1c..2580bd0 100644
--- a/dzgui/api/steam.py
+++ b/dzgui/api/steam.py
@@ -13,6 +13,7 @@ from warnings import deprecated
from dzgui.api.acf import ACF
from dzgui.init.prereqs import has_any_client
+from dzgui.init.proc import has_cmd
from dzgui.const.constants import (
APPID_DAYZ,
APPID_DAYZ_EXP,
@@ -22,10 +23,12 @@ from dzgui.const.constants import (
DEBIAN_STEAM_PATH,
DEFAULT_STEAM_PATH,
DOWNLOADS_PATH,
+ FLATPAK_CMD,
FLATPAK_STEAM_PATH,
LIBRARYFOLDERS_PATH,
- UBUNTU_STEAM_PATH,
REQUEST_TIMEOUT,
+ STEAM_CMD,
+ UBUNTU_STEAM_PATH,
VDF_PATH,
)
from dzgui.const.endpoints import (
@@ -82,6 +85,10 @@ def get_steam_paths() -> list[tuple[Path, str]]:
}
for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH:
+ if path is FLATPAK_PATH and has_cmd(FLATPAK_CMD) is False:
+ continue
+ if path is not FLATPAK_PATH and has_cmd(STEAM_CMD) is False:
+ continue
if path.joinpath(VDF_PATH).is_file():
paths.append((path, human[path]))
return paths
@@ -198,6 +205,7 @@ def load_to_menu(client: str, appid: int, name: str, mods: list[str]) -> int:
"""Loads to the menu screen with the selected mods; used for AFK/pre-joining"""
concat = concat_mods(mods)
client_args = concat_bash_args(client)
+ logger.debug(f"Setting client arguments to: {client_args}")
params = [
"-applaunch",
str(appid),
diff --git a/dzgui/app_init.py b/dzgui/app_init.py
index 26df94d..dbd01ea 100644
--- a/dzgui/app_init.py
+++ b/dzgui/app_init.py
@@ -7,7 +7,7 @@ from pathlib import Path
from typing import TYPE_CHECKING
-from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, STEAM_ICON, ICONS_PATH
+from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, FLATPAK_ROOT_PATH, ICONS_PATH
from dzgui.const.enum import Preferences
from dzgui.config.query import lookup
from dzgui.config.userprefs import UserPrefs
@@ -18,7 +18,7 @@ from dzgui.init.migrate import (
migrate_cols_file,
)
from dzgui.init.flock import lock_acquire
-from dzgui.init.prereqs import has_any_client
+from dzgui.init.prereqs import has_flatpak_client, has_steam_client
from dzgui.strings import boot
from dzgui.util.deck import is_steam_deck, is_game_mode
@@ -142,7 +142,7 @@ def load_gui(version: str, is_debug: bool) -> None:
migrate_cols_file(XDG.columns)
copy_state_files(xdg_paths["XDG_STATE_HOME"])
# TODO: add logging inside wizard
- wizard = SetupWizard(_is_steam_deck, XDG)
+ wizard = SetupWizard(_is_steam_deck, XDG, xdg_paths)
if not wizard.is_setup_complete():
return
@@ -155,7 +155,11 @@ def load_gui(version: str, is_debug: bool) -> None:
# TODO: sudo escalation dialog
# count = get_map_count()
# TODO: move into module
- if has_any_client() is False:
+ if FLATPAK_ROOT_PATH in lookup(XDG.config, Preferences.DEFAULT):
+ has_client = has_flatpak_client()
+ else:
+ has_client = has_steam_client()
+ if has_client is False:
EarlyAlertDialog(init.requires_steam)
bootwin = BootWindow(XDG, version)
diff --git a/dzgui/config/xdg.py b/dzgui/config/xdg.py
index 47f747a..1d7f5b3 100644
--- a/dzgui/config/xdg.py
+++ b/dzgui/config/xdg.py
@@ -3,7 +3,13 @@ import os
from pathlib import Path
from dataclasses import dataclass
-from dzgui.const.constants import APP_NAME_LOWER, DEBUG_LOG, SYSTEM_LOG
+from dzgui.const.constants import (
+ APP_NAME_LOWER,
+ DEBUG_LOG,
+ FLATPAK_ROOT_PATH,
+ SYSTEM_LOG,
+)
+from dzgui.init.proc import is_sandboxed
@dataclass
@@ -53,10 +59,12 @@ def get_xdg_paths() -> dict:
"XDG_DATA_HOME": home / ".local" / "share",
"XDG_CACHE_HOME": home / ".cache",
}
-
resolved_paths = {}
for path in xdg_paths:
real_path = os.environ.get(path)
+ if real_path is not None and is_sandboxed():
+ search_string = FLATPAK_ROOT_PATH + "/"
+ real_path = real_path.replace(search_string, "")
if real_path is not None and is_writeable(real_path):
new_path = Path(real_path)
else:
diff --git a/dzgui/init/proc.py b/dzgui/init/proc.py
index 3d76516..0c1a2a0 100644
--- a/dzgui/init/proc.py
+++ b/dzgui/init/proc.py
@@ -1,7 +1,8 @@
-import psutil
-import subprocess
-import shutil
import logging
+import os
+import psutil
+import shutil
+import subprocess
from dzgui.const.constants import (
APP_NAME,
@@ -26,7 +27,13 @@ def is_steam_running(cmd: str) -> bool:
raise TypeError("Not a valid Steam client selection")
+def is_sandboxed() -> bool:
+ return bool(os.environ.get("FLATPAK_ID"))
+
+
def is_flatpak_steam_running() -> bool:
+ if is_sandboxed():
+ return True
if has_cmd(FLATPAK_CMD) is False:
return False
proc = subprocess.check_output(
diff --git a/dzgui/strings/wizard.py b/dzgui/strings/wizard.py
index 9f499b0..de83762 100644
--- a/dzgui/strings/wizard.py
+++ b/dzgui/strings/wizard.py
@@ -3,6 +3,8 @@ title_intro = "Welcome"
blurb_intro = """
This wizard will help you set up some common config options before launching the application.
"""
+error_sandbox = "Setup cannot be run from inside Flatpak sandbox.\nPlease launch the application directly."
+welcome = "Welcome to {0}!"
### SteamPathPage
heading_steam_path = "Steam path"
@@ -17,7 +19,7 @@ desc_flatpak_path = (
desc_ubuntu_path = "This is the default Steam path on Ubuntu-based systems."
desc_debian_path = "This is the default Steam path on Debian-based systems."
no_valid_paths = (
- "No valid Steam paths found on system. Please install Steam to continue."
+ "No valid Steam path or Steam command found on system. Please install Steam to continue."
)
button_scan = "Scan for Steam"
@@ -80,6 +82,17 @@ checkbox_start_menu = (
)
checkbox_desktop_shortcut = ("Add a desktop shortcut", "Depends on the option above.")
+### Flatpak page
+heading_flatpak = "Flatpak sandbox overrides"
+blurb_flatpak = (
+ "You chose to add a shortcut directly to Flatpak Steam. "
+ "To launch DZGUI from Flatpak's sandbox, you must grant "
+ "permission to read from and write to the following locations."
+)
+tree_column = "Paths"
+overrides_applied = "Overrides applied."
+button_flatpak = "Grant permissions"
+
### Completion page
heading_completion = "Setup complete"
blurb_completion = "Configuration completed successfully. Click 'Close' to close this dialog and launch DZGUI."
diff --git a/dzgui/util/bash.py b/dzgui/util/bash.py
index 9c2a73d..d612b94 100644
--- a/dzgui/util/bash.py
+++ b/dzgui/util/bash.py
@@ -1,5 +1,12 @@
import shlex
+from dzgui.const.constants import STEAM_CMD
+from dzgui.init.proc import is_sandboxed
+
def concat_bash_args(command: str) -> list[str]:
+ # NOTE: NSGs spawned from sandbox cannot access host application commands,
+ # so fallback to Steam internal URIs
+ if is_sandboxed():
+ return [STEAM_CMD]
return shlex.split(command)
diff --git a/dzgui/util/flatpak.py b/dzgui/util/flatpak.py
new file mode 100644
index 0000000..a432970
--- /dev/null
+++ b/dzgui/util/flatpak.py
@@ -0,0 +1,31 @@
+import logging
+import subprocess
+
+from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID
+
+logger = logging.getLogger(APP_NAME)
+
+
+def apply_overrides(xdg: dict[str, str]) -> None:
+ try:
+ # NOTE: D-Bus registration required for sandbox
+ args = [
+ "flatpak",
+ "--user",
+ "override",
+ f"--own-name={GTK_APP_ID}",
+ FLATPAK_APPID,
+ ]
+ subprocess.run([*args], capture_output=True)
+
+ for path in xdg.values():
+ args = [
+ "flatpak",
+ "--user",
+ "override",
+ f"--filesystem={path}",
+ FLATPAK_APPID,
+ ]
+ subprocess.run([*args], capture_output=True)
+ except Exception as e:
+ logger.critical(e)
diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py
index 30a7fc6..91c41ea 100644
--- a/dzgui/util/strings.py
+++ b/dzgui/util/strings.py
@@ -297,7 +297,7 @@ class Options:
init = Init(
is_steam_running="Is Steam running? DZGUI must be run on top of Steam.",
is_dayz_running="Is DayZ already running? DZGUI cannot launch DayZ if another process is using it.",
- requires_steam="DZGUI requires that Steam or Flatpak Steam be installed on the system.",
+ requires_steam="Your chosen client (Steam/Flatpak Steam) must be installed on the system.",
)
mod_panel = ModPanelStrings(
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index b3e54f4..1c12d40 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -12,22 +12,24 @@ from dzgui.const.constants import (
APP_NAME,
APP_NAME_LOWER,
HERO_PATH,
- FLATPAK_STEAM_PATH,
+ FLATPAK_ROOT_PATH,
LEGACY_CONFIG_PATH,
)
from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP
from dzgui.config import freedesktop
-from dzgui.init.proc import path_to_steam_cmd
+from dzgui.init.proc import is_sandboxed, path_to_steam_cmd
from dzgui.init.migrate import migrate_legacy_conf
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import wizard
from dzgui.util._json import write_json
+from dzgui.util.flatpak import apply_overrides
from dzgui.util.open_links import open_link_by_url
from dzgui.util.css import add_class, load_css
from dzgui.views.components.buttons import WebButton
from dzgui.views.components.entry import APIEntry
from dzgui.views.mixins.colorscheme import ColorAwareApp
+from dzgui.views.trees.tree_simple import SimpleTree
import gi
@@ -45,7 +47,8 @@ class PageNum(Enum):
STEAM_API = 4
USER_PREFS = 5
SHORTCUTS = 6
- FINAL = 7
+ FLATPAK = 7
+ FINAL = 8
class OptionalPageMixin:
@@ -144,7 +147,7 @@ class NotificationFrame(Gtk.Frame):
margin_start=50,
margin_end=50,
)
- wrapped = textwrap.fill(label, width=80)
+ wrapped = textwrap.fill(label, width=60)
self.label = Gtk.Label(
label=wrapped,
justify=Gtk.Justification.CENTER,
@@ -158,7 +161,7 @@ class NotificationFrame(Gtk.Frame):
add_class(self, "error-frame")
def set_text(self, text: str) -> None:
- wrapped = textwrap.fill(text, width=80)
+ wrapped = textwrap.fill(text, width=60)
self.label.set_markup(wrapped)
@@ -243,12 +246,23 @@ class IntroductionPage(EnumeratedWizardPage):
def __init__(self) -> None:
super().__init__(
enum=PageNum.INTRO,
- heading=f"Welcome to {APP_NAME}!",
+ heading=wizard.welcome.format(APP_NAME),
description=wizard.blurb_intro,
)
self.page_type = Gtk.AssistantPageType.INTRO
self.set_title(wizard.title_intro)
+ self.error_box = NotificationFrame(wizard.error_sandbox, error=True)
+ self.add_start(self.error_box)
+
+ self.connect("map", self._on_map)
+
+ def _on_map(self, page: "EnumeratedWizardPage") -> None:
+ self.error_box.hide()
+ if is_sandboxed():
+ self.error_box.show()
+ EMITTER.emit("step_pending")
+
class Heading(Gtk.Label):
def __init__(self, label: str):
@@ -405,6 +419,42 @@ class PreferencesPage(EnumeratedWizardPage):
EMITTER.emit("step_complete")
+class FlatpakOverridePage(EnumeratedWizardPage):
+ def __init__(self, xdg_paths: dict[str, str]) -> None:
+ super().__init__(
+ enum=PageNum.FLATPAK,
+ heading=wizard.heading_flatpak,
+ description=wizard.blurb_flatpak,
+ )
+ self.xdg_paths = xdg_paths
+ self.page_type = Gtk.AssistantPageType.INTRO
+
+ store = Gtk.ListStore(str)
+ for path in xdg_paths.values():
+ store.append((str(path),))
+ tree = SimpleTree(store, wizard.tree_column)
+ tree.set_halign(Gtk.Align.CENTER)
+
+ self.authorize_button = Gtk.Button(
+ label=wizard.button_flatpak, halign=Gtk.Align.CENTER
+ )
+ self.authorize_button.connect("clicked", self._on_authorize)
+
+ self.notice = NotificationFrame(wizard.overrides_applied)
+ self.connect("map", self._on_map)
+
+ for el in tree, self.authorize_button, self.notice:
+ self.add_start(el)
+
+ def _on_map(self, page: "EnumeratedWizardPage") -> None:
+ self.notice.hide()
+
+ def _on_authorize(self, button: Gtk.Button) -> None:
+ apply_overrides(self.xdg_paths)
+ self.notice.show()
+ EMITTER.emit("step_complete")
+
+
class CompletionPage(EnumeratedWizardPage):
def __init__(self) -> None:
super().__init__(
@@ -418,7 +468,7 @@ class CompletionPage(EnumeratedWizardPage):
class Assistant(Gtk.Assistant):
- def __init__(self, is_deck: bool, XDG: "Xdg"):
+ def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]):
super().__init__()
if is_deck:
# NOTE: deemed to be "safe" dimensions that exclude taskbar size
@@ -437,6 +487,7 @@ class Assistant(Gtk.Assistant):
self.page_api = SteamValidationPage()
self.page_prefs = PreferencesPage()
self.page_shortcuts = ShortcutCreationPage(XDG.shortcut)
+ self.page_flatpak = FlatpakOverridePage(xdg_paths)
self.page_completion = CompletionPage()
self.set_forward_page_func(self._advance_page)
@@ -456,6 +507,7 @@ class Assistant(Gtk.Assistant):
self.page_api,
self.page_prefs,
self.page_shortcuts,
+ self.page_flatpak,
self.page_completion,
):
# NOTE: skip config migration page if no legacy config file
@@ -476,7 +528,10 @@ class Assistant(Gtk.Assistant):
def write_config(self, config: dict[str, Any]) -> None:
write_json(config, self.config_path)
- def get_final_page(self) -> int:
+ def get_completion_page(self) -> int:
+ return self.pages[self.page_completion]
+
+ def get_shortcut_page(self) -> int:
return self.pages[self.page_shortcuts]
def _advance_page(self, index: int) -> int:
@@ -489,7 +544,7 @@ class Assistant(Gtk.Assistant):
sp = page.migrated_conf["default_steam_path"]
self.page_shortcuts.set_steam_path(sp)
self.config_values = self.page_migration.migrated_conf
- return self.get_final_page()
+ return self.get_shortcut_page()
case SteamPathPage():
path = page.get_path_from_radio()
self.config_values["default_steam_path"] = path
@@ -506,10 +561,22 @@ class Assistant(Gtk.Assistant):
)
case ShortcutCreationPage():
page.create_shortcuts()
+ if not self.needs_flatpak_override():
+ return self.get_completion_page()
+ case FlatpakOverridePage():
+ pass
case _:
raise AttributeError("Trying to advance a non-canonical page")
return index + 1
+ def needs_flatpak_override(self) -> bool:
+ if (
+ self.page_shortcuts.is_steam_checkbox_active()
+ and FLATPAK_ROOT_PATH in self.config_values["default_steam_path"]
+ ):
+ return True
+ return False
+
def is_setup_complete(self) -> bool:
return self.setup_complete
@@ -625,12 +692,6 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
self.add_start(self.checks_area)
self.show_all()
self.connect("map", self._on_map)
- self.connect("map", self._toggle_checkbox)
-
- def _toggle_checkbox(self, page: "EnumeratedWizardPage") -> None:
- if FLATPAK_STEAM_PATH in str(self.steam_path):
- self.steam_checkbox.set_active(False)
- self.steam_checkbox.set_sensitive(False)
def _on_start_menu_toggled(self, button: Gtk.CheckButton) -> None:
state = button.get_active()
@@ -641,6 +702,9 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
def set_steam_path(self, path: Path) -> None:
self.steam_path = path
+ def is_steam_checkbox_active(self) -> bool:
+ return self.steam_checkbox.get_active()
+
def create_shortcuts(self) -> None:
# NOTE: best-effort, permissive even on failure (page is already marked as complete)
if self.steam_checkbox.get_active():
@@ -709,10 +773,10 @@ class SteamPathPage(EnumeratedWizardPage):
class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore
- def __init__(self, is_deck: bool, XDG: "Xdg") -> None:
+ def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None:
super().__init__()
GLib.set_prgname(APP_NAME)
- self.win = Window(is_deck, XDG)
+ self.win = Window(is_deck, XDG, xdg_paths)
Gtk.main()
def is_setup_complete(self) -> int:
@@ -720,9 +784,9 @@ class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore
class Window(Gtk.Window):
- def __init__(self, is_deck: bool, XDG: "Xdg") -> None:
+ def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None:
super().__init__(title=APP_NAME, icon_name=APP_NAME)
- self.assistant = Assistant(is_deck, XDG)
+ self.assistant = Assistant(is_deck, XDG, xdg_paths)
class Emitter(GObject.GObject):
diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py
index 64c5d51..ebce129 100644
--- a/dzgui/views/pages/options.py
+++ b/dzgui/views/pages/options.py
@@ -266,7 +266,6 @@ class FlatpakToggleField(ToggleField):
def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
cmd = FLATPAK_RUN_CMD if self.radio1.get_active() else FLATPAK_SANDBOX
- print(context)
self.controller.update_config(context, cmd)
diff --git a/dzgui/views/trees/tree_simple.py b/dzgui/views/trees/tree_simple.py
new file mode 100644
index 0000000..be96668
--- /dev/null
+++ b/dzgui/views/trees/tree_simple.py
@@ -0,0 +1,35 @@
+from typing import TYPE_CHECKING, Union
+
+if TYPE_CHECKING:
+ from dzgui.model.model_factory import FastInsertListStore
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk # noqa
+
+
+class SimpleTree(Gtk.ScrolledWindow):
+ """Simple scrollable 1-column tree"""
+
+ def __init__(
+ self, model: Union[Gtk.ListStore, "FastInsertListStore"], title: str
+ ) -> None:
+ super().__init__(
+ overlay_scrolling=False,
+ propagate_natural_width=True,
+ propagate_natural_height=True,
+ )
+
+ self.tree = Gtk.TreeView(model=model, enable_search=False)
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn(title, renderer, text=0)
+ column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
+ column.set_resizable(False)
+ column.set_sort_column_id(0)
+ self.tree.append_column(column)
+
+ self.add(self.tree)
+
+ def get_tree(self) -> Gtk.TreeView:
+ return self.tree
diff --git a/pyproject.toml b/pyproject.toml
index 1d52faa..b7a9729 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -110,10 +110,11 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } }
markers = [
"apitest: checks remote endpoints",
"config: config file keys/values",
- "realconfig: depends on actual generated local config file",
+ "flatpak: depends on local flatpak installation",
"mods: tests mod metadata/link creation",
"pefile: validate PE files",
"post_install: requires a completed installation",
+ "realconfig: depends on actual generated local config file",
"redact: log redaction mechanisms",
"slow: long-running tests",
"webtest: depends on remote endpoint",
diff --git a/tests/test_flatpak_override.py b/tests/test_flatpak_override.py
new file mode 100644
index 0000000..d34307d
--- /dev/null
+++ b/tests/test_flatpak_override.py
@@ -0,0 +1,43 @@
+import configparser
+import pytest
+
+from dzgui.const.constants import FLATPAK_APPID, GTK_APP_ID
+from dzgui.config.xdg import get_xdg_paths
+from dzgui.util.flatpak import apply_overrides
+
+PATHS_KEY = "Context"
+PATHS_SUBKEY = "filesystems"
+BUS_KEY = "Session Bus Policy"
+
+pytestmark = pytest.mark.flatpak
+
+
+@pytest.fixture
+def xdg_paths() -> dict[str, str]:
+ return get_xdg_paths()
+
+
+@pytest.fixture
+def override(xdg_paths) -> None:
+ apply_overrides(xdg_paths)
+
+
+@pytest.fixture
+def config(xdg_paths) -> configparser.ConfigParser:
+ conf_path = xdg_paths["XDG_DATA_HOME"].parent.joinpath(
+ f"flatpak/overrides/{FLATPAK_APPID}"
+ )
+ config = configparser.ConfigParser()
+ config.read(conf_path)
+ return config
+
+
+def test_session_bus(override, config) -> None:
+ bus = config[BUS_KEY][GTK_APP_ID]
+ assert bus == "own"
+
+
+def test_paths(override, xdg_paths, config) -> None:
+ expect = xdg_paths
+ paths = config[PATHS_KEY][PATHS_SUBKEY].split(";")
+ assert all(str(path) in paths for path in expect.values())
diff --git a/tests/test_flatpak_toggle.py b/tests/test_flatpak_toggle.py
new file mode 100644
index 0000000..eec56e7
--- /dev/null
+++ b/tests/test_flatpak_toggle.py
@@ -0,0 +1,30 @@
+import pytest
+
+from dzgui.const.constants import FLATPAK_RUN_CMD, FLATPAK_SANDBOX
+from dzgui.const.enum import Preferences
+from dzgui.views.pages.options import FlatpakToggleField
+
+
+class MockController:
+ def __init__(self) -> None:
+ self.command: str
+
+ def update_config(self, context: Preferences, cmd: str) -> None:
+ self.command = cmd
+
+ def get_command(self) -> str:
+ return self.command
+
+
+@pytest.mark.parametrize("index, expect", [(0, FLATPAK_RUN_CMD), (1, FLATPAK_SANDBOX)])
+def test_toggle(index: int, expect: str) -> None:
+ controller = MockController()
+ toggle = FlatpakToggleField(controller)
+ match index:
+ case 0:
+ toggle.radio2.set_active(True)
+ toggle.radio1.set_active(True)
+ case 1:
+ toggle.radio1.set_active(True)
+ toggle.radio2.set_active(True)
+ assert controller.get_command() == expect
diff --git a/tests/test_html_escaping.py b/tests/test_html_escaping.py
index 471bb1a..904e5b2 100644
--- a/tests/test_html_escaping.py
+++ b/tests/test_html_escaping.py
@@ -1,3 +1,5 @@
+import pytest
+
from dzgui.api.servers import Details
from dzgui.views.dialogs.servers import ServerDetailsDialog
@@ -21,6 +23,7 @@ class MockController:
return None
+@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_html_escaping() -> None:
name = "Test server"
desc = " Game & mods"
@@ -31,5 +34,7 @@ def test_html_escaping() -> None:
controller = MockController()
dialog = ServerDetailsDialog(controller, details)
dialog.destroy()
+ while Gtk.events_pending():
+ Gtk.main_iteration()
assert dialog.description.get_text() == desc
diff --git a/tests/test_process_detection.py b/tests/test_process_detection.py
index 23f0956..8b25c9b 100644
--- a/tests/test_process_detection.py
+++ b/tests/test_process_detection.py
@@ -1,3 +1,4 @@
+import pytest
import unittest
from unittest.mock import patch
@@ -9,6 +10,8 @@ from dzgui.const.constants import (
)
from dzgui.init import proc
+pytestmark = pytest.mark.flatpak
+
class TestSteamDetection(unittest.TestCase):
def test_flatpak_detection_with_multiple_running_apps(self):