# -*- coding: utf-8 -*- """돋보기(계획서 218-1 · 220) 회귀검증 — 오른쪽 길게 누름 · Z 누름 동안 켬 · 크기 · 배율 · 복제 지움 · 붙는 페이지. Node 헬퍼(`helper_218_1_loupe.cjs`)가 `ui_template/ui_template_loupe.ts` 를 트랜스파일해 가짜 창 · 가짜 시계 · 가짜 요소로 돌리고, 여기서 값을 판정함. """ import json import os import re import subprocess import pytest HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(os.path.dirname(HERE)) @pytest.fixture(scope="module") def r(): proc = subprocess.run( ["node", os.path.join(HERE, "helper_218_1_loupe.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def _read(rel): with open(os.path.join(ROOT, rel), encoding="utf-8") as f: return f.read() def test_크기_배율_상수(r): c = r["consts"] assert (c["d"], c["zoom"], c["hold"], c["refresh"]) == ( 360, 2, 400, 500, ) # 220-1 지름 240 → 360 assert r["capture"]["rootSize"] == ["360px", "360px"] # 커서(500,400)가 원 가운데(180,180)에 2배로 옴 assert r["capture"]["stageTransform"] == "translate(-820px, -620px) scale(2)" def test_창_캡처_단계로_들음(r): assert r["listeners"] == [ [t, True] for t in ( "mousemove", "mouseover", "mousedown", "mouseup", "contextmenu", "keydown", "keyup", ) ] + [["wheel", {"capture": True, "passive": False}], ["blur", True]] def test_길게_누름_0_4초_넘어야_켜고_떼면_끔_메뉴는_안_뜸(r): lg = r["long"] assert lg["before"] == 0 # 0.399 초 = 아직 assert lg["on"] == "show" and lg["show"] == ["show", 300, 200] assert lg["moved"] == ["move", 310, 205] # 커서 따라감 assert lg["off"] == "hide" assert lg["menuPrevented"] and lg["menuStopped"] # 브라우저 · 우리 메뉴 둘 다 안 뜸 assert lg["nextStopped"] is False # 한 번만 먹음 def test_짧게_누르면_지금_우클릭_그대로(r): assert r["short"] == {"log": 0, "menuPrevented": False, "menuStopped": False} def test_왼쪽_단추_무시_창_밖에서_떼면_끔(r): mm = r["mouseMisc"] assert mm["left"] == 0 assert mm["lostBeforeHold"] == 0 # 0.4 초 전에 뗌 — 안 켬 assert mm["lostAfterHold"] == "hide" def test_Z_누름_켬_뗌_끔(r): k = r["key"] assert k["on"] == "show" # 한글 자판(ㅋ)도 code KeyZ assert k["repeatLen"] == 1 # 누르고 있는 동안 다시 안 그림 assert k["off"] == "hide" assert k["noCombo"] # Ctrl · Alt · Meta 조합 · 다른 키는 안 켬 assert k["shift"] == "show" assert k["fieldBlur"] == "show" # 칸 초점 빠짐으론 안 끔 assert k["blur"] == "hide" # 창이 초점을 잃으면 끔 def test_입력_칸에선_안_켬(r): t = r["key"]["typing"] for name in ("input", "inputNoType", "number", "textarea", "select", "editable"): assert t[name] is False, name assert t["checkbox"] and t["button"] def test_붙일_주소_밖이면_안_켬(r): assert r["off"] == {"log": 0, "menuStopped": False} def test_붙는_페이지_B01_B09_M01_M02(r): on = [k for k, v in r["routes"].items() if v] assert on == [ "b01-account", "b02-proj-register", "b03-file-input", "b04-preprocess", "b05-profile", "b06-section", "b07-design-detail", "b08-quantity", "b09-estimation", "m01-master-data", "m02-master-template", ] # 220 — A 영역 · 로그인 · B10 · B11 은 뺌 main = _read("A00_Common/main.ts") assert 'from "@ui/ui_template_loupe"' in main assert re.search(r"initLoupe\(window, \(\) => LOUPE_ROUTE\.test\(currentRoute\(\)\)\)", main) def test_화면_끝에서_안_잘림(r): o = r["origin"] assert o["mid"] == {"left": 320, "top": 220} assert o["topLeft"] == {"left": 4, "top": 4} assert o["bottomRight"] == {"left": 1000 - 360 - 4, "top": 800 - 360 - 4} def test_화면_뜨기(r): c = r["capture"] assert ( c["rootClass"] == "ui-loupe" and c["inert"] and c["ariaHidden"] == "true" and not c["hidden"] ) assert c["pageBox"] == ["0px", "0px", "1000px", "800px"] assert c["isClone"] assert c["canvasDrawn"] == ["canvas2d"] and c["blankDrawn"] == 0 assert c["inputValue"] == "12.5" and c["checkChecked"] assert c["scrollTop"] == 120 assert c["offscreenChildren"] == 0 and c["offscreenSize"] == ["100px", "100px"] # 보이는 영역만 assert c["rowKept"] == 1 # 표 줄은 칸 너비 때문에 그대로 assert c["svgKept"] == 1 # iframe(CAD) 은 복제하지 않음 — 같은 크기 빈 상자(221-1) assert c["frameTag"] == "DIV" and c["frameDrawn"] == [] and c["frameEmpty"] == 0 assert c["frameSize"] == ["320px", "240px"] assert c["watching"] and c["observeOpts"]["subtree"] and c["scrollListen"] def test_바뀌면_0_5초_뒤_다시_뜸_끄면_복제_지움(r): h = r["hide"] assert h["sameBefore"] and h["recaptured"] assert h["nearEdge"] == "translate(4px, 4px)" assert h["stageChildren"] == 0 # 복제 지움 assert h["hidden"] and h["disconnected"] and h["scrollListen"] is False assert h["noRecaptureAfterHide"] == 0 assert h["reuseRoot"] == 2 # 원 · 배율 글 하나씩을 다시 씀 def test_테두리_그림자_토큰(): css = _read("ui_template/ui_template_loupe.css") assert "--loupe-border: var(--color-accent)" in css assert "--loupe-shadow: var(--shadow-lg)" in css assert "border-radius: 50%" in css and "pointer-events: none" in css assert ".ui-loupe__cross" in css theme = _read("ui_template/ui_template_theme.css") dark = theme[theme.index(':root[data-theme="dark"]') :] assert "--color-accent" in dark and "--shadow-lg" in dark # 어두움에서도 토큰이 바뀜