import sys import unittest from pathlib import Path from unittest import mock import gi gi.require_version("Gdk", "3.0") gi.require_version("Gtk", "3.0") sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) soundGeneratorModule = sys.modules.get("cthulhu.sound_generator") if soundGeneratorModule is not None and not hasattr(soundGeneratorModule, "SoundGenerator"): class _StubSoundGenerator: pass soundGeneratorModule.SoundGenerator = _StubSoundGenerator from cthulhu.scripts.apps.steamwebhelper import script as steam_script from cthulhu.scripts.apps.steamwebhelper import script_utilities as steam_script_utilities class SteamSelectionChangedTests(unittest.TestCase): def test_selection_changed_tolerates_scalar_any_data(self): testScript = steam_script.Script.__new__(steam_script.Script) source = object() event = mock.Mock(source=source, any_data=0) chromiumCalls = [] def displayedText(obj): if obj is source: return "" raise TypeError("argument self: Expected Atspi.Accessible, but got int") def get_name(obj): if obj is source: return "Notifications" raise TypeError("argument self: Expected Atspi.Accessible, but got int") testScript.utilities = mock.Mock() testScript.utilities.displayedText.side_effect = displayedText def chromiumOnSelectionChanged(self, selectionEvent): chromiumCalls.append((self, selectionEvent)) return True with ( mock.patch.object(steam_script.AXObject, "get_name", side_effect=get_name), mock.patch.object(steam_script.AXObject, "get_description", return_value=""), mock.patch.object(steam_script.AXObject, "get_role_name", return_value="page tab list"), mock.patch.object(steam_script.AXObject, "get_path", return_value=[1, 2, 3]), mock.patch.object( steam_script.Chromium.Script, "onSelectionChanged", new=chromiumOnSelectionChanged, ), ): self.assertTrue(testScript.onSelectionChanged(event)) self.assertEqual(chromiumCalls, [(testScript, event)]) class SteamReturnActivationTests(unittest.TestCase): def test_return_activates_focused_steam_button(self): testScript = steam_script.Script.__new__(steam_script.Script) button = object() keyboardEvent = mock.Mock(event_string="Return", modifiers=0) keyboardEvent.is_pressed_key.return_value = True testScript.utilities = mock.Mock() testScript.utilities.inDocumentContent.return_value = True testScript.inFocusMode = mock.Mock(return_value=True) testScript.presentMessage = mock.Mock() testScript._presentDelayedMessage = mock.Mock() testScript._restoreFocusAfterClick = mock.Mock() def has_action(obj, action_name): return obj is button and action_name == "press" with ( mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", button), mock.patch.object(steam_script.AXUtilities, "is_entry", return_value=False), mock.patch.object(steam_script.AXUtilities, "is_text", return_value=False), mock.patch.object(steam_script.AXUtilities, "is_password_text", return_value=False), mock.patch.object(steam_script.AXUtilities, "is_combo_box", return_value=False), mock.patch.object(steam_script.AXUtilities, "is_button", return_value=True), mock.patch.object(steam_script.AXUtilities, "is_push_button", return_value=False), mock.patch.object(steam_script.AXUtilities, "is_link", return_value=False), mock.patch.object(steam_script.AXObject, "has_action", side_effect=has_action), mock.patch.object(steam_script.Script, "_performClickableAction", return_value=True) as performAction, ): self.assertTrue(testScript.shouldConsumeKeyboardEvent(keyboardEvent, None)) performAction.assert_called_once_with(button) class SteamVirtualizedListMutationTests(unittest.TestCase): def test_children_added_skips_generic_web_cache_dump_for_virtualized_list_churn(self): testScript = steam_script.Script.__new__(steam_script.Script) source = object() focus = object() event = mock.Mock( type="object:children-changed:add", source=source, any_data=object(), detail1=0, ) chromiumCalls = [] testScript.utilities = mock.Mock() testScript.utilities.isSteamVirtualizedList.return_value = True testScript.utilities.clearSteamVirtualizedListCaches = mock.Mock() def chromiumOnChildrenAdded(self, addedEvent): chromiumCalls.append((self, addedEvent)) return False with ( mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", focus), mock.patch.object(steam_script.AXObject, "is_dead", return_value=False), mock.patch.object(steam_script.AXObject, "find_ancestor", return_value=source), mock.patch.object(steam_script.AXObject, "clear_cache_now") as clearCache, mock.patch.object(steam_script.Script, "_isSteamNotification", return_value=False), mock.patch.object( steam_script.Chromium.Script, "onChildrenAdded", new=chromiumOnChildrenAdded, ), ): self.assertTrue(testScript.onChildrenAdded(event)) clearCache.assert_called_once_with("children-changed event.") testScript.utilities.clearSteamVirtualizedListCaches.assert_called_once_with() self.assertEqual(chromiumCalls, []) def test_children_removed_skips_generic_web_cache_dump_for_virtualized_list_churn(self): testScript = steam_script.Script.__new__(steam_script.Script) source = object() focus = object() event = mock.Mock( type="object:children-changed:remove", source=source, any_data=object(), detail1=0, ) chromiumCalls = [] testScript.utilities = mock.Mock() testScript.utilities.isSteamVirtualizedList.return_value = True testScript.utilities.clearSteamVirtualizedListCaches = mock.Mock() def chromiumOnChildrenRemoved(self, removedEvent): chromiumCalls.append((self, removedEvent)) return False with ( mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", focus), mock.patch.object(steam_script.AXObject, "is_dead", return_value=False), mock.patch.object(steam_script.AXObject, "find_ancestor", return_value=source), mock.patch.object(steam_script.AXObject, "clear_cache_now") as clearCache, mock.patch.object( steam_script.Chromium.Script, "onChildrenRemoved", new=chromiumOnChildrenRemoved, ), ): self.assertTrue(testScript.onChildrenRemoved(event)) clearCache.assert_called_once_with("children-changed event.") testScript.utilities.clearSteamVirtualizedListCaches.assert_called_once_with() self.assertEqual(chromiumCalls, []) def test_children_removed_defers_to_generic_handler_when_focus_is_dead(self): testScript = steam_script.Script.__new__(steam_script.Script) source = object() focus = object() event = mock.Mock( type="object:children-changed:remove", source=source, any_data=object(), detail1=0, ) chromiumCalls = [] testScript.utilities = mock.Mock() testScript.utilities.isSteamVirtualizedList.return_value = True def chromiumOnChildrenRemoved(self, removedEvent): chromiumCalls.append((self, removedEvent)) return False with ( mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", focus), mock.patch.object(steam_script.AXObject, "is_dead", return_value=True), mock.patch.object( steam_script.Chromium.Script, "onChildrenRemoved", new=chromiumOnChildrenRemoved, ), ): self.assertFalse(testScript.onChildrenRemoved(event)) self.assertEqual(chromiumCalls, [(testScript, event)]) testScript.utilities.clearSteamVirtualizedListCaches.assert_not_called() class SteamLabelRecoveryTests(unittest.TestCase): def test_displayed_label_recovers_friends_list_tab_text_from_parent_context(self): testScript = mock.Mock(generatorCache={}) utilities = steam_script_utilities.Utilities(testScript) button = object() parent = object() textSibling = object() utilities.inDocumentContent = mock.Mock(return_value=True) def get_attribute(obj, name): if obj is button and name == "class": return "FriendsListTab Active Panel Focusable gpfocus" return None def get_name(obj): if obj is textSibling: return "Friends" return "" def iter_children(obj, pred=None): children = [textSibling, button] if obj is not parent: children = [] if pred is not None: children = [child for child in children if pred(child)] return iter(children) with ( mock.patch.object(steam_script_utilities.ChromiumUtilities, "displayedLabel", return_value=""), mock.patch.object(steam_script_utilities.AXObject, "get_parent", side_effect=lambda obj: parent if obj is button else None), mock.patch.object(steam_script_utilities.AXObject, "get_attribute", side_effect=get_attribute), mock.patch.object(steam_script_utilities.AXObject, "get_name", side_effect=get_name), mock.patch.object(steam_script_utilities.AXObject, "supports_text", return_value=False), mock.patch.object(steam_script_utilities.AXObject, "iter_children", side_effect=iter_children), mock.patch.object(steam_script_utilities.AXUtilities, "is_button", side_effect=lambda obj: obj is button), mock.patch.object(steam_script_utilities.AXUtilities, "is_push_button", return_value=False), ): self.assertEqual(utilities.displayedLabel(button), "Friends") def test_displayed_label_maps_add_friend_button_class_to_fallback_name(self): testScript = mock.Mock(generatorCache={}) utilities = steam_script_utilities.Utilities(testScript) button = object() utilities.inDocumentContent = mock.Mock(return_value=True) def get_attribute(obj, name): if obj is button and name == "class": return "friendListButton AddFriendButton Panel Focusable" return None with ( mock.patch.object(steam_script_utilities.ChromiumUtilities, "displayedLabel", return_value=""), mock.patch.object(steam_script_utilities.AXObject, "get_attribute", side_effect=get_attribute), mock.patch.object(steam_script_utilities.AXObject, "get_name", return_value=""), mock.patch.object(steam_script_utilities.AXUtilities, "is_button", side_effect=lambda obj: obj is button), mock.patch.object(steam_script_utilities.AXUtilities, "is_push_button", return_value=False), ): self.assertEqual(utilities.displayedLabel(button), "Add Friend") if __name__ == "__main__": unittest.main()