From ddb1489fa7d9f54a79613c9a18ea753d54f030fc Mon Sep 17 00:00:00 2001 From: iamprakashs Date: Sat, 4 Jul 2026 03:55:41 +0530 Subject: [PATCH] Fix WindowsACI.hotkey() splitting key names into characters Fixed bug where hotkey() method splits single key names like 'enter' into individual characters ('e', 'n', 't', 'e', 'r') when passed as a string instead of a list. Changes: - Added type check to ensure keys parameter is always a list - If string is passed, wrap it in a list before processing - Added comprehensive unit tests for various input formats Fixes #195 Co-authored-by: Cursor --- gui_agents/s1/aci/WindowsOSACI.py | 3 ++ tests/test_windows_aci.py | 57 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/test_windows_aci.py diff --git a/gui_agents/s1/aci/WindowsOSACI.py b/gui_agents/s1/aci/WindowsOSACI.py index 24b56629..69fc2eb6 100644 --- a/gui_agents/s1/aci/WindowsOSACI.py +++ b/gui_agents/s1/aci/WindowsOSACI.py @@ -405,6 +405,9 @@ def hotkey(self, keys: List[str]): Args: keys:List[str] the keys to press in combination in a list format (e.g. ['shift', 'c']) """ + # Ensure keys is a list - if a string is passed, wrap it + if isinstance(keys, str): + keys = [keys] keys = [_normalize_key(k) for k in keys] keys = [f"'{key}'" for key in keys] command = f"import pyautogui; pyautogui.hotkey({', '.join(keys)}, interval=0.5)" diff --git a/tests/test_windows_aci.py b/tests/test_windows_aci.py new file mode 100644 index 00000000..871f2a70 --- /dev/null +++ b/tests/test_windows_aci.py @@ -0,0 +1,57 @@ +import unittest +from gui_agents.s1.aci.WindowsOSACI import WindowsACI + + +class TestWindowsACIHotkey(unittest.TestCase): + def setUp(self): + """Set up test fixtures""" + self.aci = WindowsACI(top_app_only=True, ocr=False) + + def test_hotkey_with_string_single_key(self): + """Test that passing a string like 'enter' works correctly""" + result = self.aci.hotkey('enter') + expected = "import pyautogui; pyautogui.hotkey('enter', interval=0.5)" + self.assertEqual(result, expected) + + def test_hotkey_with_list_single_key(self): + """Test that passing a list with a single key works correctly""" + result = self.aci.hotkey(['enter']) + expected = "import pyautogui; pyautogui.hotkey('enter', interval=0.5)" + self.assertEqual(result, expected) + + def test_hotkey_with_list_multiple_keys(self): + """Test that passing a list with multiple keys works correctly""" + result = self.aci.hotkey(['alt', 'tab']) + expected = "import pyautogui; pyautogui.hotkey('alt', 'tab', interval=0.5)" + self.assertEqual(result, expected) + + def test_hotkey_with_control_normalization(self): + """Test that 'control' is normalized to 'ctrl'""" + result = self.aci.hotkey(['control', 'c']) + expected = "import pyautogui; pyautogui.hotkey('ctrl', 'c', interval=0.5)" + self.assertEqual(result, expected) + + def test_hotkey_with_string_special_keys(self): + """Test various special key strings""" + test_cases = [ + ('escape', "import pyautogui; pyautogui.hotkey('escape', interval=0.5)"), + ('space', "import pyautogui; pyautogui.hotkey('space', interval=0.5)"), + ('backspace', "import pyautogui; pyautogui.hotkey('backspace', interval=0.5)"), + ('delete', "import pyautogui; pyautogui.hotkey('delete', interval=0.5)"), + ] + for key, expected in test_cases: + with self.subTest(key=key): + result = self.aci.hotkey(key) + self.assertEqual(result, expected) + + def test_hotkey_does_not_split_string(self): + """Regression test: ensure 'enter' doesn't become 'e', 'n', 't', 'e', 'r'""" + result = self.aci.hotkey('enter') + # This should NOT contain individual characters + self.assertNotIn("'e', 'n', 't', 'e', 'r'", result) + # This SHOULD contain the full key name + self.assertIn("'enter'", result) + + +if __name__ == "__main__": + unittest.main()