Merge branch 'testing' into wine-access
This commit is contained in:
@@ -3441,6 +3441,20 @@ class Script(script.Script):
|
|||||||
|
|
||||||
return requested
|
return requested
|
||||||
|
|
||||||
|
def should_interrupt_for_mode_announcement(
|
||||||
|
self,
|
||||||
|
requested: bool,
|
||||||
|
already_handled: bool = False,
|
||||||
|
) -> bool:
|
||||||
|
"""Returns whether a mode announcement should interrupt prior output."""
|
||||||
|
|
||||||
|
if requested and already_handled:
|
||||||
|
msg = "DEFAULT: Mode announcement interruption already handled by input dispatch"
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return requested
|
||||||
|
|
||||||
def presentKeyboardEvent(self, event):
|
def presentKeyboardEvent(self, event):
|
||||||
"""Convenience method to present the KeyboardEvent event. Returns True
|
"""Convenience method to present the KeyboardEvent event. Returns True
|
||||||
if we fully present the event; False otherwise."""
|
if we fully present the event; False otherwise."""
|
||||||
|
|||||||
@@ -1523,7 +1523,11 @@ class Script(default.Script):
|
|||||||
self.utilities.setCaretContext(AXObject.get_parent(parent), -1)
|
self.utilities.setCaretContext(AXObject.get_parent(parent), -1)
|
||||||
if not self._loadingDocumentContent:
|
if not self._loadingDocumentContent:
|
||||||
if inputEvent is not None:
|
if inputEvent is not None:
|
||||||
self.presentMessage(messages.MODE_BROWSE, interrupt=True)
|
interrupt = self.should_interrupt_for_mode_announcement(
|
||||||
|
True,
|
||||||
|
already_handled=True,
|
||||||
|
)
|
||||||
|
self.presentMessage(messages.MODE_BROWSE, interrupt=interrupt)
|
||||||
else:
|
else:
|
||||||
self.presentMessage(messages.MODE_BROWSE)
|
self.presentMessage(messages.MODE_BROWSE)
|
||||||
if not self._shouldSuppressBrowseModeSound(obj, inputEvent):
|
if not self._shouldSuppressBrowseModeSound(obj, inputEvent):
|
||||||
@@ -1536,7 +1540,11 @@ class Script(default.Script):
|
|||||||
self.utilities.grabFocus(obj)
|
self.utilities.grabFocus(obj)
|
||||||
|
|
||||||
if inputEvent is not None:
|
if inputEvent is not None:
|
||||||
self.presentMessage(messages.MODE_FOCUS, interrupt=True)
|
interrupt = self.should_interrupt_for_mode_announcement(
|
||||||
|
True,
|
||||||
|
already_handled=True,
|
||||||
|
)
|
||||||
|
self.presentMessage(messages.MODE_FOCUS, interrupt=interrupt)
|
||||||
else:
|
else:
|
||||||
self.presentMessage(messages.MODE_FOCUS)
|
self.presentMessage(messages.MODE_FOCUS)
|
||||||
sound_theme_manager.getManager().playFocusModeSound()
|
sound_theme_manager.getManager().playFocusModeSound()
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ structNavTriggersFocusMode = False
|
|||||||
caretNavTriggersFocusMode = False
|
caretNavTriggersFocusMode = False
|
||||||
nativeNavTriggersFocusMode = True
|
nativeNavTriggersFocusMode = True
|
||||||
|
|
||||||
layoutMode = True
|
layoutMode = False
|
||||||
|
|
||||||
rewindAndFastForwardInSayAll = False
|
rewindAndFastForwardInSayAll = False
|
||||||
structNavInSayAll = False
|
structNavInSayAll = False
|
||||||
|
|||||||
@@ -269,13 +269,33 @@ class WebPresentationModeSpeechRegressionTests(unittest.TestCase):
|
|||||||
self.assertFalse(testScript._browseModeIsSticky)
|
self.assertFalse(testScript._browseModeIsSticky)
|
||||||
testScript.refreshKeyGrabs.assert_called_once_with()
|
testScript.refreshKeyGrabs.assert_called_once_with()
|
||||||
|
|
||||||
def test_manual_presentation_mode_toggle_still_interrupts(self):
|
def test_manual_focus_mode_announcement_does_not_interrupt_after_keyboard_dispatch(self):
|
||||||
testScript = self._make_script(inFocusMode=False)
|
testScript = self._make_script(inFocusMode=False)
|
||||||
|
|
||||||
with mock.patch.object(web_script.sound_theme_manager, "getManager", return_value=mock.Mock()):
|
with mock.patch.object(web_script.sound_theme_manager, "getManager", return_value=mock.Mock()):
|
||||||
web_script.Script.togglePresentationMode(testScript, object(), "document")
|
web_script.Script.togglePresentationMode(testScript, object(), "document")
|
||||||
|
|
||||||
testScript.presentMessage.assert_called_once_with(messages.MODE_FOCUS, interrupt=True)
|
testScript.presentMessage.assert_called_once_with(messages.MODE_FOCUS, interrupt=False)
|
||||||
|
|
||||||
|
def test_manual_browse_mode_announcement_does_not_interrupt_after_keyboard_dispatch(self):
|
||||||
|
testScript = self._make_script(inFocusMode=True)
|
||||||
|
|
||||||
|
with (
|
||||||
|
mock.patch.object(web_script.AXObject, "get_parent", return_value=None),
|
||||||
|
mock.patch.object(web_script.AXUtilities, "is_list_box", return_value=False),
|
||||||
|
mock.patch.object(web_script.AXUtilities, "is_menu", return_value=False),
|
||||||
|
mock.patch.object(web_script.sound_theme_manager, "getManager", return_value=mock.Mock()),
|
||||||
|
):
|
||||||
|
web_script.Script.togglePresentationMode(testScript, object(), "document")
|
||||||
|
|
||||||
|
testScript.presentMessage.assert_called_once_with(messages.MODE_BROWSE, interrupt=False)
|
||||||
|
|
||||||
|
def test_non_keyboard_mode_announcement_keeps_interrupt_request(self):
|
||||||
|
testScript = self._make_script(inFocusMode=False)
|
||||||
|
|
||||||
|
result = web_script.Script.should_interrupt_for_mode_announcement(testScript, True)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
|
||||||
class WebObjectPresentationInterruptionRegressionTests(unittest.TestCase):
|
class WebObjectPresentationInterruptionRegressionTests(unittest.TestCase):
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
|
gi.require_version("Atspi", "2.0")
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||||
|
|
||||||
|
from cthulhu import settings
|
||||||
|
from cthulhu import settings_manager
|
||||||
|
from cthulhu.scripts.web import script_utilities as web_script_utilities
|
||||||
|
|
||||||
|
|
||||||
|
class WebLayoutModeSettingsRegressionTests(unittest.TestCase):
|
||||||
|
def test_layout_mode_defaults_to_object_mode(self):
|
||||||
|
self.assertFalse(settings.layoutMode)
|
||||||
|
|
||||||
|
def test_explicit_persisted_layout_mode_true_is_preserved(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tempDir:
|
||||||
|
with mock.patch.object(settings, "layoutMode", False):
|
||||||
|
manager = settings_manager.SettingsManager(mock.Mock())
|
||||||
|
manager.activate(prefsDir=tempDir)
|
||||||
|
settingsPath = Path(tempDir, "user-settings.toml")
|
||||||
|
settingsText = settingsPath.read_text(encoding="utf-8")
|
||||||
|
settingsPath.write_text(
|
||||||
|
settingsText.replace("layoutMode = false", "layoutMode = true"),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
reloadedManager = settings_manager.SettingsManager(mock.Mock())
|
||||||
|
reloadedManager.activate(prefsDir=tempDir)
|
||||||
|
|
||||||
|
self.assertTrue(reloadedManager.getSetting("layoutMode"))
|
||||||
|
|
||||||
|
def test_application_specific_layout_mode_true_is_preserved(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tempDir:
|
||||||
|
with mock.patch.object(settings, "layoutMode", False):
|
||||||
|
manager = settings_manager.SettingsManager(mock.Mock())
|
||||||
|
manager.activate(prefsDir=tempDir)
|
||||||
|
manager._backend.saveAppSettings(
|
||||||
|
"Browser",
|
||||||
|
manager.profile,
|
||||||
|
{"layoutMode": True},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
script = mock.Mock()
|
||||||
|
script.app = object()
|
||||||
|
script.getKeyBindings.return_value = mock.Mock()
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
settings_manager.AXObject,
|
||||||
|
"get_name",
|
||||||
|
return_value="Browser",
|
||||||
|
):
|
||||||
|
manager.loadAppSettings(script)
|
||||||
|
|
||||||
|
self.assertTrue(manager.getSetting("layoutMode"))
|
||||||
|
|
||||||
|
|
||||||
|
class WebLayoutModeLineRegressionTests(unittest.TestCase):
|
||||||
|
def _make_utilities(self):
|
||||||
|
obj = object()
|
||||||
|
contents = [[obj, 0, 4, "Text"]]
|
||||||
|
utilities = web_script_utilities.Utilities.__new__(
|
||||||
|
web_script_utilities.Utilities
|
||||||
|
)
|
||||||
|
utilities._script = mock.Mock()
|
||||||
|
utilities._script.inFocusMode.return_value = False
|
||||||
|
utilities._currentLineContents = []
|
||||||
|
utilities._debugContentsInfo = mock.Mock()
|
||||||
|
utilities.findObjectInContents = mock.Mock(return_value=-1)
|
||||||
|
utilities.treatAsEndOfLine = mock.Mock(return_value=False)
|
||||||
|
utilities.getExtents = mock.Mock(return_value=[0, 0, 10, 10])
|
||||||
|
utilities.isInlineListDescendant = mock.Mock(return_value=False)
|
||||||
|
utilities._getContentsForObj = mock.Mock(return_value=contents)
|
||||||
|
utilities.isLink = mock.Mock(return_value=False)
|
||||||
|
utilities.isMath = mock.Mock(return_value=False)
|
||||||
|
utilities.isMathTopLevel = mock.Mock(return_value=False)
|
||||||
|
utilities.getDocumentForObject = mock.Mock(return_value=object())
|
||||||
|
utilities.findPreviousCaretInOrder = mock.Mock(return_value=(None, -1))
|
||||||
|
utilities.findNextCaretInOrder = mock.Mock(return_value=(None, -1))
|
||||||
|
utilities.isContentEditableWithEmbeddedObjects = mock.Mock(return_value=False)
|
||||||
|
return utilities, obj, contents
|
||||||
|
|
||||||
|
def _get_line_for_setting(self, layoutMode):
|
||||||
|
utilities, obj, contents = self._make_utilities()
|
||||||
|
settingsManager = mock.Mock()
|
||||||
|
settingsManager.getSetting.return_value = layoutMode
|
||||||
|
|
||||||
|
with (
|
||||||
|
mock.patch.object(
|
||||||
|
web_script_utilities.cthulhu.cthulhuApp,
|
||||||
|
"settingsManager",
|
||||||
|
settingsManager,
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
web_script_utilities.AXObject,
|
||||||
|
"is_dead",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
web_script_utilities.AXObject,
|
||||||
|
"find_ancestor",
|
||||||
|
return_value=None,
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
web_script_utilities.AXUtilities,
|
||||||
|
"is_tool_bar",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
web_script_utilities.AXUtilities,
|
||||||
|
"is_menu_bar",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
web_script_utilities.AXUtilities,
|
||||||
|
"is_table_row",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
result = web_script_utilities.Utilities._getLineContentsAtOffset(
|
||||||
|
utilities,
|
||||||
|
obj,
|
||||||
|
0,
|
||||||
|
layoutMode=None,
|
||||||
|
useCache=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return utilities, obj, contents, result
|
||||||
|
|
||||||
|
def test_unset_profile_setting_uses_object_mode_for_web_line(self):
|
||||||
|
utilities, obj, contents, result = self._get_line_for_setting(False)
|
||||||
|
|
||||||
|
self.assertEqual(result, contents)
|
||||||
|
utilities.getDocumentForObject.assert_not_called()
|
||||||
|
utilities._debugContentsInfo.assert_called_once_with(
|
||||||
|
obj,
|
||||||
|
0,
|
||||||
|
contents,
|
||||||
|
"Line (not layout mode)",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_explicit_layout_mode_true_uses_visual_web_line(self):
|
||||||
|
utilities, obj, contents, result = self._get_line_for_setting(True)
|
||||||
|
|
||||||
|
self.assertEqual(result, contents)
|
||||||
|
utilities.getDocumentForObject.assert_called_once_with(obj)
|
||||||
|
utilities._debugContentsInfo.assert_called_once_with(
|
||||||
|
obj,
|
||||||
|
0,
|
||||||
|
contents,
|
||||||
|
"Line (layout mode)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user