84 lines
3.8 KiB
Python
84 lines
3.8 KiB
Python
"""Only click a freshly checked, fully painted expand control."""
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from backend.browser import tools
|
|
|
|
|
|
FIXTURE = Path(__file__).with_name('reply_dom_fixture_d41.cjs')
|
|
|
|
|
|
def tree(*, clipped=False, viewport_edge=False):
|
|
control = {'attrs': {'id':'expand','class':'show-more'}, 'text':'展开1条回复'}
|
|
if viewport_edge:
|
|
control['rect'] = {'left':0,'right':100,'top':585,'bottom':620,'width':100,'height':35}
|
|
area = {'attrs': {'id':'reply-area','class':'reply-container'}, 'children':[control]}
|
|
if clipped:
|
|
area['style'] = {'overflowY':'auto'}
|
|
area['rect'] = {'left':0,'right':300,'top':0,'bottom':40,'width':300,'height':40}
|
|
root = {'attrs': {'id':'root-comment','class':'comment-item'}, 'children':[
|
|
{'attrs': {'id':'root-content','class':'content'},'text':'根评论'}]}
|
|
return {'attrs': {'id':'noteContainer'}, 'children':[
|
|
{'attrs': {'id':'detail-desc'},'text':'正文'},
|
|
{'attrs': {'id':'thread','class':'parent-comment'},'children':[root,area]}]}
|
|
|
|
|
|
def observe(code, arg, value):
|
|
result = subprocess.run(['node',str(FIXTURE)], input=json.dumps({'code':code,'arg':arg,'tree':value}),
|
|
capture_output=True,text=True,check=True,timeout=5)
|
|
return json.loads(result.stdout)
|
|
|
|
|
|
@pytest.mark.parametrize('options', [{'clipped':True},{'viewport_edge':True}])
|
|
def test_partial_control_can_use_an_actually_painted_point_without_implicit_scroll(options):
|
|
value = tree(**options)
|
|
inspected = observe(tools._INSPECT_SOURCE_CANDIDATES, {'max_comments':5}, value)
|
|
assert inspected['reply_controls'][0]['in_view'] is True
|
|
checked = observe(tools._OBSERVE_REPLY_CONTROL, {'selector':'#expand'}, value)
|
|
assert checked['click_point']['y'] < (600 if options.get('viewport_edge') else 40)
|
|
|
|
|
|
def test_full_control_has_a_program_observed_native_point():
|
|
result = observe(tools._OBSERVE_REPLY_CONTROL, {'selector':'#expand'}, tree())
|
|
assert result['click_point'] == {'x':150,'y':27,'viewport_width':400,'viewport_height':600}
|
|
|
|
|
|
def test_real_occlusion_is_rejected_by_paint_hit_testing():
|
|
value=tree();value['occluded']=True
|
|
result=observe(tools._OBSERVE_REPLY_CONTROL,{'selector':'#expand'},value)
|
|
assert result['error']=='reply_control_occluded'
|
|
|
|
|
|
async def test_changed_control_is_not_clicked_after_reading_candidates(tmp_path,monkeypatch):
|
|
tid = 'current-visible-point-d42'
|
|
tools.clear_task_context(tid)
|
|
tools.set_prospecting_context(tid)
|
|
monkeypatch.setattr(tools,'task_doc_dir',lambda _:tmp_path)
|
|
class Page:
|
|
observed = 0
|
|
clicks = 0
|
|
async def get_url(self):return 'https://www.xiaohongshu.com/explore/'+'a'*24
|
|
async def evaluate(self,code,arg=None):
|
|
if code==tools._OBSERVE_REPLY_CONTROL:
|
|
self.observed+=1
|
|
return {'url':await self.get_url(),'fingerprint':str(self.observed),'owner_identity':'stable-root',
|
|
'owner_selector':'#thread','reply_count':0,'visible_reply_selectors':[],
|
|
'reply_snapshot_complete':True,'click_point':{'x':150,'y':27,'viewport_width':400,'viewport_height':600}}
|
|
assert code==tools._INSPECT_SOURCE_CANDIDATES
|
|
return {'url':await self.get_url(),'candidates':[]}
|
|
async def get_elements_by_css_selector(self,_selector):return [SimpleNamespace()]
|
|
@property
|
|
async def mouse(self):
|
|
async def click(**_point):self.clicks+=1
|
|
return SimpleNamespace(click=click)
|
|
page=Page()
|
|
async def current():return page
|
|
with pytest.raises(ValueError,match='点击前回复入口或讨论已变化'):
|
|
await tools._expand_replies(tid,SimpleNamespace(get_current_page=current),'#expand')
|
|
assert page.clicks==0
|
|
tools.clear_task_context(tid)
|