diff --git a/README.md b/README.md index 006c42f..721123b 100644 --- a/README.md +++ b/README.md @@ -309,11 +309,13 @@ export _GIT_BRANCH="master" ### Color Themes -You can change to the legacy color scheme by toggling the variable -`_MENU_THEME` between `default` and `legacy` +You can change to the legacy color scheme by toggling the variable `_MENU_THEME` between `default` and `legacy`. +You can completely disable the color theme by setting the `_MENU_THEME` variable to `none`. ```bash export _MENU_THEME="legacy" +# or +export _MENU_THEME="none" ``` ## Contributing diff --git a/git_py_stats/config.py b/git_py_stats/config.py index 97fbd83..8c05c73 100644 --- a/git_py_stats/config.py +++ b/git_py_stats/config.py @@ -120,6 +120,8 @@ def get_config() -> Dict[str, Union[str, int]]: menu_theme: Optional[str] = os.environ.get("_MENU_THEME") if menu_theme == "legacy": config["menu_theme"] = "legacy" + elif menu_theme == "none": + config["menu_theme"] = "none" else: config["menu_theme"] = "" diff --git a/git_py_stats/menu.py b/git_py_stats/menu.py index 59e39b4..30e50e8 100644 --- a/git_py_stats/menu.py +++ b/git_py_stats/menu.py @@ -24,7 +24,7 @@ def interactive_menu(config: Dict[str, Union[str, int]]) -> str: WHITE = "\033[37m" CYAN = "\033[36m" - # Handle default and legacy menu + # Handle default, legacy, and colorless menu theme = config.get("menu_theme", "") if theme == "legacy": @@ -33,6 +33,13 @@ def interactive_menu(config: Dict[str, Union[str, int]]) -> str: NUMS = f"{BOLD}{YELLOW}" HELP_TXT = f"{NORMAL}{YELLOW}" EXIT_TXT = f"{BOLD}{RED}" + elif theme == "none": + TITLES = BOLD + TEXT = "" + NUMS = BOLD + HELP_TXT = "" + EXIT_TXT = BOLD + NORMAL = "" else: TITLES = f"{BOLD}{CYAN}" TEXT = f"{NORMAL}{WHITE}" diff --git a/git_py_stats/tests/test_menu.py b/git_py_stats/tests/test_menu.py index 4e41b08..ed0beec 100644 --- a/git_py_stats/tests/test_menu.py +++ b/git_py_stats/tests/test_menu.py @@ -25,7 +25,9 @@ def setUp(self): # Mock configurations for testing self.config_default = {} # Default theme self.config_legacy = {"menu_theme": "legacy"} # Legacy theme + self.config_none = {"menu_theme": "none"} # Alternate colorless theme alias + # Test cases for default theme @patch("builtins.input", return_value="1") @patch("sys.stdout", new_callable=StringIO) def test_default_theme_option_1(self, mock_stdout, mock_input): @@ -76,6 +78,57 @@ def test_default_theme_invalid_input(self, mock_stdout, mock_input): output = strip_ansi_codes(mock_stdout.getvalue()) self.assertIn("Generate:", output) + @patch("builtins.input", return_value="3") + @patch("sys.stdout", new_callable=StringIO) + def test_none_theme_option_3(self, mock_stdout, mock_input): + """ + Test the interactive_menu with 'none' theme and user selects option '3'. + """ + choice = interactive_menu(self.config_none) + self.assertEqual(choice, "3") + output = mock_stdout.getvalue() + self.assertNotIn("\033[31m", output) + self.assertNotIn("\033[33m", output) + self.assertNotIn("\033[36m", output) + self.assertIn("\033[1m", output) + + @patch("builtins.input", return_value="1") + @patch("sys.stdout", new_callable=StringIO) + def test_none_theme_option_1(self, mock_stdout, mock_input): + """ + Test the interactive_menu with 'none' theme and user selects option '1'. + """ + choice = interactive_menu(self.config_none) + self.assertEqual(choice, "1") + output = mock_stdout.getvalue() + self.assertNotIn("\033[31m", output) + self.assertNotIn("\033[33m", output) + self.assertNotIn("\033[36m", output) + self.assertIn("\033[1m", output) + + @patch("builtins.input", return_value="") + @patch("sys.stdout", new_callable=StringIO) + def test_none_theme_exit(self, mock_stdout, mock_input): + """ + Test the interactive_menu with none theme and user presses Enter to exit. + """ + choice = interactive_menu(self.config_none) + self.assertEqual(choice, "") + output = strip_ansi_codes(mock_stdout.getvalue()) + self.assertIn("press Enter to exit", output) + + @patch("builtins.input", return_value="invalid") + @patch("sys.stdout", new_callable=StringIO) + def test_none_theme_invalid_input(self, mock_stdout, mock_input): + """ + Test the interactive_menu with none theme and user enters an invalid option. + """ + choice = interactive_menu(self.config_none) + self.assertEqual(choice, "invalid") + output = strip_ansi_codes(mock_stdout.getvalue()) + self.assertIn("Generate:", output) + + # Test cases for legacy theme @patch("builtins.input", return_value="1") @patch("sys.stdout", new_callable=StringIO) def test_legacy_theme_option_1(self, mock_stdout, mock_input): @@ -121,6 +174,7 @@ def test_legacy_theme_invalid_input(self, mock_stdout, mock_input): output = strip_ansi_codes(mock_stdout.getvalue()) self.assertIn("Generate:", output) + # Test cases for handling multiple inputs and edge cases @patch("builtins.input", side_effect=["1", ""]) @patch("sys.stdout", new_callable=StringIO) def test_multiple_inputs(self, mock_stdout, mock_input): @@ -145,6 +199,7 @@ def test_input_with_whitespace(self, mock_stdout, mock_input): output = strip_ansi_codes(mock_stdout.getvalue()) self.assertIn("5) My daily status", output) + # Test cases for handling exit commands @patch("builtins.input", return_value="QUIT") @patch("sys.stdout", new_callable=StringIO) def test_input_quit(self, mock_stdout, mock_input):