Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
127 lines
5.0 KiB
Python
127 lines
5.0 KiB
Python
"""스프레드시트 손가락(PLAN_기기대응 4-2 · `spreadsheet_mouse.ts` · `_editor.ts` · `_grid.ts`) — 실제 브라우저 터치.
|
|
|
|
sub1 화면 서버(5174)에 견본 통합문서를 붙여 CDP 터치를 넣음:
|
|
① 톡 = 칸 고름 · 화면 자판 안 뜸(inputMode none) ② 고른 칸에서 끌기 = 범위
|
|
③ 안 고른 칸에서 끌기 = 고름 그대로(화면 밀기) ④ 두 번 톡 = 고침 · 자판 뜸(inputMode text)
|
|
⑤ ⋯ 단추(터치 모드) = 칸 메뉴 ⑥ 자판 없는 단추 줄 · 🔍 = 찾기
|
|
"""
|
|
|
|
import urllib.request
|
|
|
|
import pytest
|
|
|
|
BASE = "http://localhost:5174"
|
|
|
|
MOUNT = """
|
|
async () => {
|
|
const m = await import('/spreadsheet/spreadsheet.ts');
|
|
const book = await (await fetch('/spreadsheet/spreadsheet_sample.json')).json();
|
|
const host = document.createElement('div');
|
|
Object.assign(host.style, { position: 'fixed', inset: '0', zIndex: 99999, background: '#fff', display: 'flex' });
|
|
document.body.append(host);
|
|
window.__sel = null;
|
|
window.__ss = m.createSpreadsheet(host, book, { onSelect: (s) => (window.__sel = s) });
|
|
await new Promise((r) => setTimeout(r, 500));
|
|
const g = document.querySelector('.aislo-grid').getBoundingClientRect();
|
|
return [g.left, g.top, g.width, g.height];
|
|
}
|
|
"""
|
|
|
|
|
|
def _up() -> bool:
|
|
try:
|
|
urllib.request.urlopen(BASE, timeout=2)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
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)],
|
|
},
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(not _up(), reason="sub1 화면 서버(5174) 꺼짐")
|
|
def test_spreadsheet_touch_select_range_edit():
|
|
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(1000)
|
|
gx, gy, gw, gh = page.evaluate(MOUNT)
|
|
cdp = ctx.new_cdp_session(page)
|
|
sel = lambda: page.evaluate("window.__sel") # noqa: E731
|
|
ime = lambda: page.evaluate("document.activeElement && document.activeElement.inputMode") # noqa: E731
|
|
|
|
def tap(x, y):
|
|
_touch(cdp, "touchStart", [(x, y)])
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(450)
|
|
|
|
def drag(x0, y0, x1, y1):
|
|
_touch(cdp, "touchStart", [(x0, y0)])
|
|
for i in range(1, 7):
|
|
_touch(cdp, "touchMove", [(x0 + (x1 - x0) * i / 6, y0 + (y1 - y0) * i / 6)])
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(450)
|
|
|
|
# ⑥ 자판 없는 단추 줄(4-3) — 6개 보임 · 🔍 = 찾기 칸 열림
|
|
n = page.evaluate(
|
|
"[...document.querySelectorAll('.ss-touch-keys > button')].filter(b => b.getClientRects().length).length"
|
|
)
|
|
assert n == 6, n
|
|
page.evaluate("document.querySelector('.ss-touch-keys > button').click()")
|
|
page.wait_for_function( # 찾기 칸은 늦게 불러옴(크기 예산)
|
|
"() => [...document.querySelectorAll('.ss-find__input')].some(e => e.getClientRects().length)",
|
|
timeout=10000,
|
|
)
|
|
page.keyboard.press("Escape")
|
|
page.evaluate("document.activeElement.blur()")
|
|
gx, gy, gw, gh = page.evaluate( # 찾기 칸이 자리를 먹어 격자가 옮겨졌을 수 있음
|
|
"(() => { const g = document.querySelector('.aislo-grid').getBoundingClientRect();"
|
|
" return [g.left, g.top, g.width, g.height]; })()"
|
|
)
|
|
|
|
a = (gx + 90, gy + 60)
|
|
tap(*a) # ① 고름
|
|
first = sel()
|
|
assert first and ":" not in first["범위"], first
|
|
assert ime() == "none", ime()
|
|
|
|
drag(*a, a[0] + 120, a[1] + 60) # ② 고른 칸에서 끌기 = 범위
|
|
ranged = sel()
|
|
assert ":" in ranged["범위"] and ranged["범위"].startswith(first["칸"]), (first, ranged)
|
|
|
|
tap(gx + 90, gy + 260) # 다른 칸 고름
|
|
before = sel()
|
|
drag(gx + 200, gy + 330, gx + 200, gy + 200) # ③ 안 고른 칸 끌기 = 고름 그대로
|
|
assert sel() == before, (before, sel())
|
|
|
|
b = (gx + 90, gy + 260)
|
|
_touch(cdp, "touchStart", [b]) # ④ 두 번 톡 = 고침
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(120)
|
|
_touch(cdp, "touchStart", [b])
|
|
_touch(cdp, "touchEnd", [])
|
|
page.wait_for_timeout(500)
|
|
assert ime() == "text", ime()
|
|
page.evaluate("document.activeElement.blur()")
|
|
|
|
page.evaluate("document.querySelector('.ss-toolbar__more').click()") # ⑤ ⋯ = 칸 메뉴
|
|
page.wait_for_timeout(300)
|
|
assert page.evaluate(
|
|
"[...document.querySelectorAll('.ui-ctx-menu')].some(m => !m.hidden && m.getClientRects().length)"
|
|
)
|
|
|
|
browser.close()
|