lazyBoy/tests/control.test.py

76 lines
3.8 KiB
Python
Raw Normal View History

2026-09-07 09:05:14 +00:00
import contextlib
import io
import json
import importlib.util
import subprocess
import unittest
from pathlib import Path
from unittest.mock import patch
def module(name, path):
spec=importlib.util.spec_from_file_location(name,path)
mod=importlib.util.module_from_spec(spec);spec.loader.exec_module(mod);return mod
clipboard=module('clipboard',Path('crates/control/src/clipboard.py'))
cdp=module('cdp',Path('crates/control/src/cdp.py'))
class ClipboardTest(unittest.TestCase):
def test_confirmed_unicode_then_terminal_shortcut(self):
calls=[]
def fake(argv,**kwargs):
calls.append(argv)
data=b''
if '-out' in argv: data='中文\nemoji🙂'.encode()
if 'getactivewindow' in argv:data=b'123'
if 'WM_CLASS' in argv:data=b'xfce4-terminal'
return subprocess.CompletedProcess(argv,0,stdout=data)
with patch.object(clipboard,'run',fake): clipboard.paste('中文\nemoji🙂')
self.assertEqual(calls[-1][-1],'ctrl+shift+v')
self.assertEqual(sum('key' in a for a in calls),1)
def test_no_paste_when_sync_fails(self):
calls=[]
def fake(argv,**kwargs):calls.append(argv);return subprocess.CompletedProcess(argv,0,stdout=b'stale')
with patch.object(clipboard,'run',fake),patch.object(clipboard.time,'monotonic',side_effect=[0,3]):
with self.assertRaises(RuntimeError):clipboard.paste('new')
self.assertFalse(any('key' in a for a in calls))
def test_cdp_handles_events_before_response(self):
class Socket:
def __init__(self):self.items=iter(['{"method":"Page.event"}','{"id":1,"result":{"ok":true}}'])
def send(self,x):pass
def settimeout(self,x):pass
def recv(self):return next(self.items)
ws=cdp.Ws.__new__(cdp.Ws);ws.sock=Socket();ws.n=0
self.assertEqual(ws.call('Runtime.test'),{'ok':True})
2026-09-07 09:05:14 +00:00
class BrowserActionTest(unittest.TestCase):
def run_action(self, request, evaluation=None):
calls=[]
class Socket:
def call(self, method, params=None): calls.append((method, params)); return {}
def close(self): pass
output=io.StringIO()
def evaluate(ws, expression, arg=None):
if expression == cdp.SNAP_JS:
return {"url":"https://example.test/next", "title":"Next", "text":"Saved", "elements":[{"id":1,"title":"Continue"}]}
return evaluation if evaluation is not None else {"ok":True,"x":10,"y":20}
with patch.object(cdp.sys,'argv',['cdp',json.dumps(request)]), patch.object(cdp,'probe',return_value=True), patch.object(cdp,'connect',return_value=Socket()), patch.object(cdp,'evaluate',side_effect=evaluate), patch.object(cdp,'pointer'), patch.object(cdp,'wait_for_visual_update'), patch.object(cdp,'wait_until_enabled',return_value=0), patch.object(cdp.time,'sleep'), contextlib.redirect_stdout(output):
try: cdp.main()
except SystemExit: pass
return json.loads(output.getvalue()), calls
def test_every_browser_action_returns_current_elements_and_text(self):
for action in ['click','type','press','wait','navigate']:
with self.subTest(action=action):
result,_=self.run_action({"action":action,"selector":"#field","text":"hello","url":"https://example.test"})
self.assertTrue(result['ok'])
self.assertEqual(result['text'],'Saved')
self.assertEqual(result['elements'][0]['title'],'Continue')
def test_missing_field_never_types_into_previous_focus(self):
result,calls=self.run_action({"action":"type","selector":"#gone","text":"private"},{"ok":False,"error":"element gone"})
self.assertFalse(result['ok'])
self.assertFalse(any(method=='Input.insertText' for method,_ in calls))
if __name__=='__main__':unittest.main()