Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
137 lines
5.0 KiB
Python
137 lines
5.0 KiB
Python
"""손가락 → 마우스 번역(`A00_Common/touch_bridge.ts` · PLAN_기기대응 3-1) — 실제 브라우저 터치.
|
|
|
|
sub1 화면 서버(5174) 첫 화면을 터치 기기로 열고 시험용 상자에 CDP 터치를 넣어 봄:
|
|
① 길게 누름 → contextmenu 한 번 · 뒤따르는 click 없음 ② 짧은 톡 → contextmenu 없음
|
|
③ 두 번 톡 → dblclick 한 번 ④ 두 손가락 벌림 → Ctrl+휠(deltaY < 0) · 오므림 → deltaY > 0
|
|
⑤ [data-touch-own] 상자는 번역 안 함 ⑥ 마우스 우클릭 · 더블클릭은 그대로(번역이 끼지 않음)
|
|
"""
|
|
|
|
import json
|
|
import urllib.request
|
|
|
|
import pytest
|
|
|
|
BASE = "http://localhost:5174"
|
|
|
|
|
|
def _up() -> bool:
|
|
try:
|
|
urllib.request.urlopen(BASE, timeout=2)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
SETUP = """
|
|
() => {
|
|
window.__log = [];
|
|
for (const id of ['tb-box', 'tb-own']) {
|
|
const b = document.createElement('div');
|
|
b.id = id;
|
|
Object.assign(b.style, { position: 'fixed', left: id === 'tb-box' ? '20px' : '220px', top: '120px',
|
|
width: '180px', height: '180px', background: '#eee', zIndex: 99999, touchAction: 'none' });
|
|
if (id === 'tb-own') b.dataset.touchOwn = 'x';
|
|
for (const t of ['contextmenu', 'click', 'dblclick', 'wheel'])
|
|
b.addEventListener(t, (e) => {
|
|
window.__log.push({ id, t, ctrl: !!e.ctrlKey, dy: e.deltaY || 0, trusted: e.isTrusted });
|
|
if (t === 'contextmenu' || t === 'wheel') e.preventDefault();
|
|
}, { passive: false });
|
|
document.body.append(b);
|
|
}
|
|
}
|
|
"""
|
|
|
|
|
|
def _touch(cdp, kind, points):
|
|
cdp.send(
|
|
"Input.dispatchTouchEvent",
|
|
{
|
|
"type": kind,
|
|
"touchPoints": [{"x": x, "y": y, "id": i} for i, (x, y) in enumerate(points)],
|
|
},
|
|
)
|
|
|
|
|
|
def _log(page):
|
|
out = page.evaluate("() => window.__log.splice(0)")
|
|
return [(e["id"], e["t"]) for e in out], out
|
|
|
|
|
|
@pytest.mark.skipif(not _up(), reason="sub1 화면 서버(5174) 꺼짐")
|
|
def test_touch_bridge_translates_finger_gestures():
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
with sync_playwright() as p:
|
|
browser = p.chromium.launch(channel="msedge", headless=True)
|
|
ctx = browser.new_context(
|
|
viewport={"width": 390, "height": 844}, has_touch=True, is_mobile=True
|
|
)
|
|
page = ctx.new_page()
|
|
page.goto(f"{BASE}/#/a01-home")
|
|
page.wait_for_timeout(1500)
|
|
assert page.evaluate("() => document.documentElement.dataset.touch") == "on"
|
|
page.evaluate(SETUP)
|
|
cdp = ctx.new_cdp_session(page)
|
|
|
|
# ① 길게 누름
|
|
_touch(cdp, "touchStart", [(100, 200)])
|
|
page.wait_for_timeout(700)
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(500)
|
|
names, _ = _log(page)
|
|
assert names.count(("tb-box", "contextmenu")) == 1, names
|
|
assert ("tb-box", "click") not in names, names
|
|
|
|
# ② 짧은 톡
|
|
page.wait_for_timeout(400)
|
|
_touch(cdp, "touchStart", [(100, 200)])
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(700)
|
|
names, _ = _log(page)
|
|
assert ("tb-box", "contextmenu") not in names, names
|
|
assert ("tb-box", "click") in names, names
|
|
|
|
# ③ 두 번 톡
|
|
page.wait_for_timeout(400)
|
|
for _ in range(2):
|
|
_touch(cdp, "touchStart", [(100, 200)])
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(80)
|
|
page.wait_for_timeout(600)
|
|
names, _ = _log(page)
|
|
assert names.count(("tb-box", "dblclick")) == 1, names
|
|
|
|
# ④ 두 손가락 벌림 · 오므림
|
|
def pinch(box_x, start, end):
|
|
_touch(cdp, "touchStart", [(box_x - start, 210), (box_x + start, 210)])
|
|
steps = 8
|
|
for i in range(1, steps + 1):
|
|
d = start + (end - start) * i / steps
|
|
_touch(cdp, "touchMove", [(box_x - d, 210), (box_x + d, 210)])
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(200)
|
|
return _log(page)
|
|
|
|
names, raw = pinch(110, 20, 80)
|
|
wheels = [e for e in raw if e["t"] == "wheel" and e["id"] == "tb-box"]
|
|
assert wheels and all(e["ctrl"] and e["dy"] < 0 for e in wheels), raw
|
|
names, raw = pinch(110, 80, 20)
|
|
wheels = [e for e in raw if e["t"] == "wheel" and e["id"] == "tb-box"]
|
|
assert wheels and all(e["ctrl"] and e["dy"] > 0 for e in wheels), raw
|
|
|
|
# ⑤ 자체 처리 상자 — 길게 누름 · 벌림 번역 없음
|
|
page.wait_for_timeout(400)
|
|
_touch(cdp, "touchStart", [(300, 200)])
|
|
page.wait_for_timeout(700)
|
|
_touch(cdp, "touchEnd", [])
|
|
names, raw = pinch(310, 20, 70)
|
|
assert not [e for e in raw if not e["trusted"] and e["id"] == "tb-own"], raw
|
|
|
|
# ⑥ 마우스 — 우클릭 · 더블클릭은 브라우저 것 하나씩만
|
|
page.mouse.click(100, 200, button="right")
|
|
page.mouse.dblclick(100, 200)
|
|
page.wait_for_timeout(500)
|
|
_, raw = _log(page)
|
|
assert all(e["trusted"] for e in raw), json.dumps(raw)
|
|
browser.close()
|