diff --git a/config/scripts/verify-computer-native.mjs b/config/scripts/verify-computer-native.mjs index 56efeb33521..ce3f0b58e31 100644 --- a/config/scripts/verify-computer-native.mjs +++ b/config/scripts/verify-computer-native.mjs @@ -53,6 +53,12 @@ const checks = [ ], enabled: process.platform === 'linux' }, + { + name: 'Linux set_value tests', + command: 'python3', + args: ['native/computer-use-linux/set_value_test.py'], + enabled: true + }, { name: 'Windows provider PowerShell parse', command: process.platform === 'win32' ? 'powershell.exe' : 'pwsh', diff --git a/native/computer-use-linux/runtime.py b/native/computer-use-linux/runtime.py index ef217185cf3..fecce949aaa 100644 --- a/native/computer-use-linux/runtime.py +++ b/native/computer-use-linux/runtime.py @@ -1047,10 +1047,9 @@ def write_clipboard(value): def set_value(node, value): - if node is not None and bool(attempt(node.is_editable_text, False)): - editable = attempt(node.get_editable_text_iface) - if editable is not None and attempt(lambda: Atspi.EditableText.set_text_contents(editable, str(value)), False): - return True + editable = attempt(node.get_editable_text_iface) if node is not None else None + if editable is not None and attempt(lambda: Atspi.EditableText.set_text_contents(editable, str(value)), False): + return True value_iface = attempt(node.get_value_iface) if node is not None else None if value_iface is not None: return bool(attempt(lambda: Atspi.Value.set_current_value(value_iface, float(value)), False)) diff --git a/native/computer-use-linux/set_value_test.py b/native/computer-use-linux/set_value_test.py new file mode 100644 index 00000000000..05059585950 --- /dev/null +++ b/native/computer-use-linux/set_value_test.py @@ -0,0 +1,35 @@ +import importlib.util +import sys +import types +import unittest +from pathlib import Path +from unittest import mock + + +def load_runtime(): + editable_text = types.SimpleNamespace(set_text_contents=lambda editable, value: editable.update(text=value) or True) + repository = types.ModuleType("gi.repository") + repository.Atspi = types.SimpleNamespace(EditableText=editable_text) + gi = types.ModuleType("gi") + gi.repository = repository + gi.require_version = lambda *_: None + path = Path(__file__).with_name("runtime.py") + with mock.patch.dict(sys.modules, {"gi": gi, "gi.repository": repository}): + spec = importlib.util.spec_from_file_location("orca_linux_runtime_set_value_test", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class SetValueTest(unittest.TestCase): + def test_sets_text_on_bindings_without_is_editable_text(self): + # Why: at-spi2-core < 2.53 (e.g. Ubuntu 24.04) does not expose Accessible.is_editable_text to GI. + editable = {"text": ""} + node = types.SimpleNamespace(get_editable_text_iface=lambda: editable, get_value_iface=lambda: None) + + self.assertTrue(load_runtime().set_value(node, "hello")) + self.assertEqual(editable["text"], "hello") + + +if __name__ == "__main__": + unittest.main()