import os import sys import unittest from pathlib import Path from unittest import mock import gi os.environ.setdefault("GSETTINGS_BACKEND", "memory") 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 import messages from cthulhu.scripts.web import script as web_script from cthulhu.scripts.web import script_utilities as web_script_utilities class WebKeyGrabRegressionTests(unittest.TestCase): def _make_partial_script(self): testScript = web_script.Script.__new__(web_script.Script) testScript._lastCommandWasCaretNav = True testScript._lastCommandWasStructNav = True testScript._lastCommandWasMouseButton = True testScript._lastMouseButtonContext = ("old", 7) testScript._madeFindAnnouncement = True testScript._inFocusMode = True testScript._navSuspended = False testScript.removeKeyGrabs = mock.Mock() testScript.refreshKeyGrabs = mock.Mock() testScript._setNavigationSuspended = mock.Mock() testScript.utilities = mock.Mock() testScript.utilities.isZombie.return_value = False testScript.utilities.isDocument.return_value = False testScript.utilities.getTopLevelDocumentForObject.return_value = None return testScript def test_window_deactivate_does_not_drop_key_grabs(self): testScript = self._make_partial_script() result = web_script.Script.onWindowDeactivated(testScript, mock.Mock()) self.assertFalse(result) self.assertFalse(testScript._lastCommandWasCaretNav) self.assertFalse(testScript._lastCommandWasStructNav) self.assertFalse(testScript._lastCommandWasMouseButton) self.assertEqual(testScript._lastMouseButtonContext, (None, -1)) testScript.removeKeyGrabs.assert_not_called() def test_non_document_focus_change_without_prior_document_does_not_refresh_grabs(self): testScript = self._make_partial_script() oldFocus = object() newFocus = object() with mock.patch("cthulhu.scripts.web.script.AXObject.is_dead", return_value=False): result = web_script.Script.locus_of_focus_changed(testScript, None, oldFocus, newFocus) self.assertFalse(result) self.assertFalse(testScript._madeFindAnnouncement) self.assertFalse(testScript._inFocusMode) testScript._setNavigationSuspended.assert_called_once_with( True, "focus left document content", ) testScript.refreshKeyGrabs.assert_not_called() def test_non_document_focus_change_refreshes_grabs_after_leaving_document(self): testScript = self._make_partial_script() oldFocus = object() newFocus = object() oldDocument = object() testScript.utilities.getTopLevelDocumentForObject.side_effect = ( lambda obj: oldDocument if obj is oldFocus else None ) with mock.patch("cthulhu.scripts.web.script.AXObject.is_dead", return_value=False): result = web_script.Script.locus_of_focus_changed(testScript, None, oldFocus, newFocus) self.assertFalse(result) testScript.refreshKeyGrabs.assert_called_once_with() def test_non_document_focus_change_refreshes_grabs_when_old_focus_missing(self): testScript = self._make_partial_script() newFocus = object() with mock.patch("cthulhu.scripts.web.script.AXObject.is_dead", return_value=False): result = web_script.Script.locus_of_focus_changed(testScript, None, None, newFocus) self.assertFalse(result) testScript.refreshKeyGrabs.assert_called_once_with() if __name__ == "__main__": unittest.main() class WebSelectionRegressionTests(unittest.TestCase): def _make_script(self): testScript = web_script.Script.__new__(web_script.Script) testScript.pointOfReference = {} testScript.utilities = mock.Mock() testScript.utilities.sanityCheckActiveWindow = mock.Mock() testScript.utilities.isZombie.return_value = False testScript.utilities.eventIsBrowserUINoise.return_value = False testScript.utilities.eventIsBrowserUIAutocompleteNoise.return_value = False testScript.utilities.lastInputEventWasTab.return_value = False testScript.utilities.inFindContainer.return_value = False testScript.utilities.eventIsFromLocusOfFocusDocument.return_value = True testScript.utilities.textEventIsDueToInsertion.return_value = False testScript.utilities.textEventIsDueToDeletion.return_value = False testScript.utilities.isItemForEditableComboBox.return_value = False testScript.utilities.eventIsAutocompleteNoise.return_value = False testScript.utilities.eventIsSpinnerNoise.return_value = False testScript.utilities.caretMovedOutsideActiveGrid.return_value = False testScript.utilities.treatEventAsSpinnerValueChange.return_value = False testScript.utilities.queryNonEmptyText.return_value = "Darkest Dungeon" testScript.utilities.lastInputEventWasCaretNavWithSelection.return_value = True testScript.utilities.getCharacterContentsAtOffset.return_value = [[None, 0, 1, "D"]] testScript.utilities.pathComparison.return_value = -1 testScript.updateBraille = mock.Mock() testScript.speakContents = mock.Mock() testScript.speakMessage = mock.Mock() testScript._lastCommandWasCaretNav = True testScript._lastCommandWasStructNav = False testScript._lastCommandWasMouseButton = False testScript._lastMouseButtonContext = (None, -1) testScript._inFocusMode = False testScript._focusModeIsSticky = False testScript._browseModeIsSticky = False return testScript def test_broken_web_caret_selection_synthesizes_selection_from_caret_context(self): testScript = self._make_script() document = object() section = object() event = mock.Mock(source=section, detail1=-1) manager = mock.Mock() manager.last_event_was_forward_caret_selection.return_value = True manager.last_event_was_backward_caret_selection.return_value = False testScript.utilities.getTopLevelDocumentForObject.return_value = document testScript.utilities.getCaretContext.return_value = (section, 0) testScript.utilities.nextContext.return_value = (section, 1) testScript.utilities.getCharacterContentsAtOffset.return_value = [[section, 0, 1, "D"]] with ( mock.patch.object(web_script.cthulhu_state, "locusOfFocus", section), mock.patch.object(web_script.input_event_manager, "get_manager", return_value=manager), mock.patch.object(web_script.cthulhu, "setLocusOfFocus") as setLocusOfFocus, ): result = web_script.Script.onCaretMoved(testScript, event) self.assertTrue(result) testScript.utilities.setCaretContext.assert_called_once_with(section, 1, document) setLocusOfFocus.assert_called_once_with(event, section, False, True) testScript.speakContents.assert_called_once_with([[section, 0, 1, "D"]]) testScript.speakMessage.assert_called_once_with(messages.TEXT_SELECTED, interrupt=False) self.assertEqual( testScript.pointOfReference["syntheticWebSelection"]["string"], "D", ) def test_broken_web_text_selection_event_synthesizes_selection_from_event_source(self): testScript = self._make_script() document = object() anchor = object() link = object() event = mock.Mock(source=link) testScript.utilities.getTopLevelDocumentForObject.return_value = document testScript.utilities.getCaretContext.return_value = (anchor, 0) testScript.utilities.nextContext.side_effect = [(anchor, 0)] testScript.utilities.getCharacterContentsAtOffset.side_effect = ( lambda obj, _offset: [[link, 0, 1, "S"]] if obj is link else [] ) testScript.utilities.textEventIsForNonNavigableTextObject.return_value = False testScript.utilities.isContentEditableWithEmbeddedObjects.return_value = False testScript._compareCaretContexts = mock.Mock(return_value=1) with ( mock.patch.object(web_script.cthulhu_state, "locusOfFocus", anchor), mock.patch.object(web_script.AXText, "get_caret_offset", return_value=0), mock.patch.object(web_script.AXText, "get_selected_ranges", return_value=[]), mock.patch.object(web_script.AXText, "get_substring", return_value="S"), mock.patch.object(web_script.cthulhu, "setLocusOfFocus") as setLocusOfFocus, ): result = web_script.Script.onTextSelectionChanged(testScript, event) self.assertTrue(result) testScript.utilities.setCaretContext.assert_called_once_with(link, 0, document) setLocusOfFocus.assert_called_once_with(event, link, False, True) testScript.speakContents.assert_called_once_with([[link, 0, 1, "S"]]) testScript.speakMessage.assert_called_once_with(messages.TEXT_SELECTED, interrupt=False) self.assertEqual( testScript.pointOfReference["syntheticWebSelection"]["string"], "S", ) def test_web_utilities_all_selected_text_falls_back_to_synthetic_selection(self): testScript = mock.Mock(pointOfReference={}) utilities = web_script_utilities.Utilities.__new__(web_script_utilities.Utilities) utilities._script = testScript document = object() obj = object() testScript.pointOfReference["syntheticWebSelection"] = { "document": document, "string": "Dark", } with ( mock.patch.object( web_script_utilities.script_utilities.Utilities, "allSelectedText", return_value=["", 0, 0], ), mock.patch.object(utilities, "inDocumentContent", return_value=True), mock.patch.object(utilities, "getTopLevelDocumentForObject", return_value=document), ): result = web_script_utilities.Utilities.allSelectedText(utilities, obj) self.assertEqual(result, ["Dark", 0, 4])