7e2c2b1b08
real_profile_data_dir hard-wired Linux to $XDG_CONFIG_HOME/<name>, and the xdg fragment map only knew the native package names. Ubuntu's default snap Chromium (xdg reports chromium_chromium.desktop, profile under ~/snap/chromium/common/chromium) and Flatpak builds (~/.var/app/<id>/config/…) therefore ended in 'profile directory was not found' for a browser the user runs every day, and Flatpak Chrome (com.google.Chrome.desktop) was reported as 'not a supported Chromium browser'. Try the native, snap and Flatpak locations and return the first that exists; fall back to the native path so the error message still names a concrete directory. Map the Flatpak application ids in the xdg lookup. Tests cover the xdg names for all four browsers in native and Flatpak form, and the directory preference order with a temp HOME.
173 lines
6.7 KiB
Python
173 lines
6.7 KiB
Python
"""Default-Chromium detection and profile-dir resolution (hermes_cli.browser_connect).
|
|
|
|
These exercise the parsers with real command output shapes instead of
|
|
patching the detectors themselves, so a change in what macOS / xdg report is
|
|
caught here rather than in a user's browser session.
|
|
"""
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
import hermes_cli.browser_connect as bc
|
|
|
|
|
|
def _ls_dump(*entries: str) -> str:
|
|
return "(\n" + ",\n".join(entries) + "\n)\n"
|
|
|
|
|
|
def _handler(scheme: str, bundle: str) -> str:
|
|
return (
|
|
" {\n"
|
|
" LSHandlerPreferredVersions = {\n"
|
|
' LSHandlerRoleAll = "-";\n'
|
|
" };\n"
|
|
f' LSHandlerRoleAll = "{bundle}";\n'
|
|
f" LSHandlerURLScheme = {scheme};\n"
|
|
" }"
|
|
)
|
|
|
|
|
|
def _content_type_handler(uti: str, bundle: str) -> str:
|
|
return (
|
|
" {\n"
|
|
f' LSHandlerContentType = "{uti}";\n'
|
|
f' LSHandlerRoleViewer = "{bundle}";\n'
|
|
" }"
|
|
)
|
|
|
|
|
|
class TestLaunchServicesHttpsHandler:
|
|
def test_https_entry_wins_over_other_schemes(self):
|
|
dump = _ls_dump(
|
|
_handler("ftp", "com.google.chrome"),
|
|
_handler("https", "com.apple.safari"),
|
|
)
|
|
assert bc._launchservices_https_handler(dump) == "com.apple.safari"
|
|
|
|
def test_content_type_registration_is_not_an_https_handler(self):
|
|
dump = _ls_dump(_content_type_handler("public.html", "com.google.chrome"))
|
|
assert bc._launchservices_https_handler(dump) is None
|
|
|
|
def test_no_entries_means_no_recorded_handler(self):
|
|
assert bc._launchservices_https_handler("(\n)\n") is None
|
|
assert bc._launchservices_https_handler("") is None
|
|
|
|
def test_nested_dictionary_does_not_split_the_entry(self):
|
|
dump = _ls_dump(_handler("https", "com.microsoft.edgemac"))
|
|
assert bc._launchservices_https_handler(dump) == "com.microsoft.edgemac"
|
|
|
|
|
|
class TestDetectDefaultDarwin:
|
|
def _run_with(self, dump: str):
|
|
class _Proc:
|
|
stdout = dump
|
|
|
|
return patch.object(bc.subprocess, "run", return_value=_Proc())
|
|
|
|
def test_chrome_as_https_handler(self):
|
|
with self._run_with(_ls_dump(_handler("https", "com.google.chrome"))):
|
|
assert bc._detect_default_darwin() == "chrome"
|
|
|
|
def test_safari_default_with_chrome_installed_fails_closed(self):
|
|
"""The old fallback returned the first installed Chromium app; a
|
|
non-Chromium default must resolve to None even when Chrome exists."""
|
|
dump = _ls_dump(
|
|
_handler("https", "com.apple.safari"),
|
|
_handler("ftp", "com.google.chrome"),
|
|
)
|
|
with self._run_with(dump), \
|
|
patch.object(bc, "chromium_executable", return_value="/Applications/Google Chrome.app/x"):
|
|
assert bc._detect_default_darwin() is None
|
|
|
|
def test_no_handler_recorded_fails_closed(self):
|
|
with self._run_with("(\n)\n"), \
|
|
patch.object(bc, "chromium_executable", return_value="/Applications/Google Chrome.app/x"):
|
|
assert bc._detect_default_darwin() is None
|
|
|
|
def test_firefox_default_fails_closed(self):
|
|
with self._run_with(_ls_dump(_handler("https", "org.mozilla.firefox"))):
|
|
assert bc._detect_default_darwin() is None
|
|
|
|
def test_reader_failure_fails_closed(self):
|
|
with patch.object(bc.subprocess, "run", side_effect=OSError("no defaults")):
|
|
assert bc._detect_default_darwin() is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"bundle,expected",
|
|
[
|
|
("com.google.Chrome", "chrome"),
|
|
("com.brave.Browser", "brave"),
|
|
("com.microsoft.edgemac", "edge"),
|
|
("org.chromium.Chromium", "chromium"),
|
|
],
|
|
)
|
|
def test_bundle_map(self, bundle, expected):
|
|
with self._run_with(_ls_dump(_handler("https", bundle))):
|
|
assert bc._detect_default_darwin() == expected
|
|
|
|
|
|
class TestDetectDefaultLinux:
|
|
def _run_with(self, output: str):
|
|
class _Proc:
|
|
stdout = output
|
|
|
|
return patch.object(bc.subprocess, "run", return_value=_Proc())
|
|
|
|
@pytest.mark.parametrize(
|
|
"desktop,expected",
|
|
[
|
|
("google-chrome.desktop", "chrome"),
|
|
("com.google.Chrome.desktop", "chrome"),
|
|
("chromium_chromium.desktop", "chromium"),
|
|
("org.chromium.Chromium.desktop", "chromium"),
|
|
("brave-browser.desktop", "brave"),
|
|
("com.brave.Browser.desktop", "brave"),
|
|
("microsoft-edge.desktop", "edge"),
|
|
("com.microsoft.Edge.desktop", "edge"),
|
|
("firefox.desktop", None),
|
|
("org.mozilla.firefox.desktop", None),
|
|
("", None),
|
|
],
|
|
)
|
|
def test_xdg_desktop_names(self, desktop, expected):
|
|
with self._run_with(desktop + "\n"):
|
|
assert bc._detect_default_linux() == expected
|
|
|
|
def test_missing_xdg_settings_fails_closed(self):
|
|
with patch.object(bc.subprocess, "run", side_effect=FileNotFoundError("xdg-settings")):
|
|
assert bc._detect_default_linux() is None
|
|
|
|
|
|
class TestLinuxProfileDir:
|
|
def _env(self, monkeypatch, home):
|
|
monkeypatch.setenv("HOME", str(home))
|
|
monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
|
|
|
|
def test_native_path_when_nothing_exists(self, tmp_path, monkeypatch):
|
|
self._env(monkeypatch, tmp_path)
|
|
assert bc.real_profile_data_dir("chromium", "Linux") == str(tmp_path / ".config" / "chromium")
|
|
|
|
def test_snap_chromium_profile_is_found(self, tmp_path, monkeypatch):
|
|
self._env(monkeypatch, tmp_path)
|
|
snap = tmp_path / "snap" / "chromium" / "common" / "chromium"
|
|
snap.mkdir(parents=True)
|
|
assert bc.real_profile_data_dir("chromium", "Linux") == str(snap)
|
|
|
|
def test_flatpak_chrome_profile_is_found(self, tmp_path, monkeypatch):
|
|
self._env(monkeypatch, tmp_path)
|
|
flatpak = tmp_path / ".var" / "app" / "com.google.Chrome" / "config" / "google-chrome"
|
|
flatpak.mkdir(parents=True)
|
|
assert bc.real_profile_data_dir("chrome", "Linux") == str(flatpak)
|
|
|
|
def test_native_profile_wins_when_present(self, tmp_path, monkeypatch):
|
|
self._env(monkeypatch, tmp_path)
|
|
native = tmp_path / ".config" / "BraveSoftware" / "Brave-Browser"
|
|
native.mkdir(parents=True)
|
|
(tmp_path / ".var" / "app" / "com.brave.Browser" / "config" / "BraveSoftware" / "Brave-Browser").mkdir(parents=True)
|
|
assert bc.real_profile_data_dir("brave", "Linux") == str(native)
|
|
|
|
def test_xdg_config_home_is_honoured(self, tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
|
monkeypatch.setenv("XDG_CONFIG_HOME", "/home/t/.config")
|
|
assert bc.real_profile_data_dir("edge", "Linux") == "/home/t/.config/microsoft-edge"
|