diff --git a/contact/ui/user_config.py b/contact/ui/user_config.py index bc59b6c..51d1544 100644 --- a/contact/ui/user_config.py +++ b/contact/ui/user_config.py @@ -1,5 +1,4 @@ import os -import json import curses from typing import Any, List, Dict, Optional @@ -429,22 +428,15 @@ def json_editor(stdscr: curses.window, menu_state: Any) -> bool: menu_state.selected_index = 0 # Track the selected option made_changes = False # Track if any changes were made - script_dir = os.path.dirname(os.path.abspath(__file__)) - parent_dir = os.path.abspath(os.path.join(script_dir, os.pardir)) - file_path = os.path.join(parent_dir, "config.json") + file_path = config.json_file_path menu_state.show_save_option = True # Always show the Save button menu_state.help_win = None menu_state.help_text = {} - # Ensure the file exists - if not os.path.exists(file_path): - with open(file_path, "w") as f: - json.dump({}, f) - - # Load JSON data - with open(file_path, "r", encoding="utf-8") as f: - original_data = json.load(f) + # Use the same path and defaults as startup, including the writable + # fallback for shared installations and CONTACT_CONFIG_ROOT overrides. + original_data = config.initialize_config() data = original_data # Reference to the original data menu_state.current_menu = data # Track the current level of the menu diff --git a/tests/test_user_config.py b/tests/test_user_config.py index c76d1df..eab8ccb 100644 --- a/tests/test_user_config.py +++ b/tests/test_user_config.py @@ -1,5 +1,9 @@ import unittest -from tempfile import NamedTemporaryFile +import json +from contextlib import ExitStack +from pathlib import Path +from tempfile import NamedTemporaryFile, TemporaryDirectory +from types import SimpleNamespace from unittest import mock import contact.ui.default_config # Initialize config before the color helpers. @@ -7,6 +11,35 @@ from contact.ui import user_config class UserConfigTests(unittest.TestCase): + def test_editor_reads_and_saves_active_config_outside_installation(self): + for existing in (False, True): + with self.subTest(existing=existing), TemporaryDirectory() as directory, ExitStack() as stack: + root = Path(directory) + config_path = root / "config.json" + package = root / "site-packages" / "contact" + (package / "ui").mkdir(parents=True) + if existing: + config_path.write_text(json.dumps({"message_prefix": "Custom"}), encoding="utf-8") + stack.enter_context(mock.patch.object(user_config, "__file__", str(package / "ui" / "user_config.py"))) + stack.enter_context(mock.patch.object(user_config.config, "json_file_path", str(config_path))) + reload_config = stack.enter_context(mock.patch.object(user_config.config, "reload_config")) + stack.enter_context(mock.patch.object(user_config, "reload_translations")) + stack.enter_context(mock.patch.object(user_config, "update_app_settings_help")) + window = mock.Mock() + state = SimpleNamespace() + stack.enter_context(mock.patch.object(user_config, "display_menu", return_value=(window, mock.Mock(), []))) + + def save_from_editor(): + self.assertEqual(state.current_menu["message_prefix"], "Custom" if existing else ">>") + state.current_menu["message_prefix"] = "Edited" + return 10 # Save button. + + window.getch.side_effect = save_from_editor + self.assertFalse(user_config.json_editor(mock.Mock(), state)) + self.assertEqual(json.loads(config_path.read_text())["message_prefix"], "Edited") + self.assertFalse((package / "config.json").exists()) + reload_config.assert_called_once_with() + def test_ping_bot_enabled_uses_boolean_picker(self) -> None: with mock.patch.object(user_config, "get_list_input", return_value="True") as picker: result = user_config.edit_value("enabled", "Enabled", "False")