fix(perception): correct device hierarchy method and add TDD guard
- Fixed AttributeError where device.get_hierarchy() was called instead of dump_hierarchy() - Replaced MagicMock with a real DummyDeviceForKeyboardTest in E2E tests to adhere to the strict mock ban policy. - Verified TelepathicEngine actively calls device.back() when keyboard is open on non-typing intents.
This commit is contained in:
@@ -96,7 +96,7 @@ class TelepathicEngine:
|
||||
device.back()
|
||||
time.sleep(1.0)
|
||||
# Re-fetch UI state
|
||||
xml_string = device.get_hierarchy()
|
||||
xml_string = device.dump_hierarchy()
|
||||
if xml_string:
|
||||
root = self._parser.parse(xml_string)
|
||||
if root:
|
||||
|
||||
@@ -195,3 +195,65 @@ class TestKeyboardContaminationGuard:
|
||||
assert (
|
||||
"comment_composer" not in (result.resource_id or "").lower()
|
||||
), "REGRESSION: Resolver picked comment_composer instead of share button!"
|
||||
|
||||
|
||||
class TestTelepathicEngineKeyboardGuard:
|
||||
def test_telepathic_engine_auto_dismisses_keyboard(self):
|
||||
"""
|
||||
When the keyboard is detected in find_best_node for a non-typing intent,
|
||||
the TelepathicEngine must actively call device.back() and re-fetch the XML
|
||||
via device.dump_hierarchy().
|
||||
"""
|
||||
from GramAddict.core.telepathic_engine import TelepathicEngine
|
||||
|
||||
class DummyDeviceForKeyboardTest:
|
||||
def __init__(self):
|
||||
self.back_called = False
|
||||
self.dump_hierarchy_called = False
|
||||
self.xml_without_keyboard = """<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<hierarchy rotation="0">
|
||||
<node package="com.instagram.android" class="android.widget.FrameLayout" bounds="[0,0][1080,2400]">
|
||||
<node resource-id="com.instagram.android:id/row_feed_photo_profile_name" bounds="[100,400][400,440]" clickable="true" />
|
||||
</node>
|
||||
</hierarchy>
|
||||
"""
|
||||
|
||||
def back(self):
|
||||
self.back_called = True
|
||||
|
||||
def dump_hierarchy(self, compressed=False):
|
||||
self.dump_hierarchy_called = True
|
||||
return self.xml_without_keyboard
|
||||
|
||||
mock_device = DummyDeviceForKeyboardTest()
|
||||
|
||||
xml_with_keyboard = """<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<hierarchy rotation="0">
|
||||
<node package="com.instagram.android" class="android.widget.FrameLayout" bounds="[0,0][1080,2400]">
|
||||
<node resource-id="com.instagram.android:id/row_feed_photo_profile_name" bounds="[100,400][400,440]" clickable="true" />
|
||||
</node>
|
||||
<node package="com.google.android.inputmethod.latin" class="android.widget.FrameLayout" bounds="[0,1500][1080,2400]">
|
||||
<node resource-id="com.google.android.inputmethod.latin:id/B00" content-desc="Q" bounds="[0,1800][100,1900]" clickable="true" />
|
||||
</node>
|
||||
</hierarchy>
|
||||
"""
|
||||
|
||||
engine = TelepathicEngine()
|
||||
|
||||
# We pass "tap post username" which is NOT a typing intent.
|
||||
# It should trigger active dismissal.
|
||||
result = engine.find_best_node(
|
||||
xml_string=xml_with_keyboard,
|
||||
intent_description="tap post username",
|
||||
device=mock_device,
|
||||
track=False
|
||||
)
|
||||
|
||||
# Ensure device.back() was called
|
||||
assert mock_device.back_called is True
|
||||
# Ensure device.dump_hierarchy() was called to re-fetch
|
||||
assert mock_device.dump_hierarchy_called is True
|
||||
|
||||
# Result should still resolve to the profile name node
|
||||
assert result is not None
|
||||
assert "row_feed_photo_profile_name" in result.get("id", "")
|
||||
|
||||
Reference in New Issue
Block a user