Files
Aislo/resources/tester/test_app_settings.py
T
eomsangdonandClaude Opus 5.5 aaecfa3438 feat(설정): 332-2 ☰ [설정] 창 — 마우스 · CAD 단축키 · 다른 곳 단축키 탭 · [저장] 때만 계정 pref · 닫을 때 물음
- app_shell ☰ 설정 묶음에 [설정] 단추(서랍 닫고 창은 처음 누를 때 불러옴)
- 새 app_settings.ts(틀 · 탭 셋 · [저장] · askLeave) · app_settings_cad.ts(명령 표 · 내 별칭 · 검사) · app_settings.css
- 새 openwebcad/src/commands/command-catalog.json(명령 153 · 한글 이름 · 기본 별칭) + 거울 시험
- locale 문구(ui_template_locale_common)
- 시험: test_app_settings · cad/command-catalog 새로 · test_device_audit · test_touch_bridge 화면 주소 환경값(AISLO_UI_BASE · AISLO_UI_PROJECT)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
2026-10-15 12:45:03 +09:00

188 lines
7.4 KiB
Python

"""PLAN 332-2 — ☰ [설정] 창(`A00_Common/app_settings.ts` · `app_settings_cad.ts` · `app_shell.ts`).
① CAD 별칭 검사(Node) — 영문 · 숫자만 · 겹침 금지 · 명령 이름 금지 · 칸 글 나눔
② 실제 브라우저(sub2 화면 서버 5177 · 로그인 없음 = 이 브라우저만 · 새 창이라 남는 것 없음)
☰ → [설정] = 서랍 닫히고 창 · 탭 셋 전환 · 고쳐도 [저장] 전엔 안 씀 · 닫을 때 물음(취소 = 머묾 ·
버림 = 안 씀 · 저장 = 한 번) · [저장] 한 번 = 한 번 씀 · 저장 뒤 읽기 층 방향 뒤집힘 · 잘못 별칭은 안 씀
"""
from __future__ import annotations
import json
import os
import subprocess
import urllib.request
import pytest
HERE = os.path.dirname(__file__)
ROOT = os.path.join(HERE, "..", "..")
BASE = "http://localhost:5177" # sub2 화면 서버(OWNERS 자기 줄)
KEY = "aislo:pref:input-prefs"
# 앱 스크립트보다 먼저 — 설정 키에 쓰는 횟수를 셈
COUNT_WRITES = f"""
window.__writes = [];
const set = Storage.prototype.setItem;
Storage.prototype.setItem = function (k, v) {{
if (k === {json.dumps(KEY)}) window.__writes.push(v);
return set.call(this, k, v);
}};
"""
# 앱이 불러온 그 읽기 층 사본(vite 가 ?t= 를 붙였을 수 있음)
WHEEL_IN = (
"async (d) => (await import(performance.getEntriesByType('resource')"
".map((e) => e.name).find((n) => n.includes('/input_prefs.ts')))).wheelIn(d)"
)
@pytest.fixture(scope="module")
def node() -> dict:
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", os.path.join(HERE, "helper_app_settings.cjs")],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_별칭_검사(node):
assert node["commands"] == 153
assert node["parse"] == [["LL", "CC", "X1"], [], []]
assert node["ok"] == {} # 남의 기본 별칭(C = CIRCLE)을 내 것으로 덮는 건 됨(acad.pgp 식)
assert node["chars"] == {"LINE": "chars", "CIRCLE": "chars"}
assert node["dup"] == {"LINE": "dup", "CIRCLE": "dup", "ARC": "dup"}
assert node["name"] == {"CIRCLE": "name"}
def _up() -> bool:
try:
urllib.request.urlopen(BASE, timeout=3)
return True
except Exception:
return False
def _open_settings(page):
page.click(".app-burger")
page.click(".app-drawer__settings")
page.wait_for_selector(".app-settings")
assert not page.evaluate(
"document.querySelector('.app-header, header')?.classList.contains('is-drawer-open')"
)
def _writes(page) -> list:
return page.evaluate("window.__writes")
def _visible_panel_has(page, selector: str) -> bool:
return page.evaluate(
"(s) => [...document.querySelectorAll('.app-settings .ui-modal__body > div:not([role]), "
".app-settings .ui-modal__body > table')].some((p) => !p.hidden && p.querySelector(s))",
selector,
)
def _save(page):
page.click(".app-settings .ui-modal__actions button")
def _close(page):
page.click(".app-settings .ui-modal__top > button")
@pytest.mark.skipif(not _up(), reason="sub2 화면 서버(5177) 꺼짐")
def test_설정_창_탭_저장_물음():
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
)
ctx.add_init_script(COUNT_WRITES)
page = ctx.new_page()
page.goto(f"{BASE}/#/a01-home")
page.wait_for_selector(".app-burger")
assert page.evaluate(WHEEL_IN, 100) is True # 기본 = 당김
# ☰ → [설정] · 탭 셋 · 첫 탭 = 마우스
_open_settings(page)
tabs = page.evaluate(
"[...document.querySelectorAll('.app-settings [role=tab]')]"
".map((b) => [b.dataset.settingsTab, b.getAttribute('aria-selected')])"
)
assert tabs == [["mouse", "true"], ["cad", "false"], ["keys", "false"]]
assert _visible_panel_has(page, "input[type=radio]")
page.click("[data-settings-tab=cad]")
assert _visible_panel_has(page, "input[data-cad-command]")
assert not _visible_panel_has(page, "input[type=radio]")
assert page.locator(".app-settings input[data-cad-command]").count() == 153
page.click("[data-settings-tab=keys]")
assert (
page.evaluate(
"document.querySelector('[data-settings-tab=keys]').getAttribute('aria-selected')"
)
== "true"
)
page.click("[data-settings-tab=mouse]")
# 밀기 고름 — [저장] 전엔 안 씀 · 닫으면 물음 · 취소 = 머묾
page.check("input[name=app-settings-wheel][value=push]")
assert _writes(page) == []
page.keyboard.press("Escape")
page.wait_for_selector(".ui-confirm")
page.click(".ui-confirm button >> nth=0") # 취소
assert page.locator(".app-settings").count() == 1 and _writes(page) == []
# [저장] = 한 번 씀 · 읽기 층 방향 뒤집힘 · 닫을 때 안 물음
_save(page)
assert len(_writes(page)) == 1
assert json.loads(_writes(page)[0]) == {"wheelZoom": "push", "cadAliases": {}}
assert page.evaluate(WHEEL_IN, 100) is False and page.evaluate(WHEEL_IN, -100) is True
_save(page) # 고친 것 없음 — 또 안 씀
assert len(_writes(page)) == 1
_close(page)
assert page.locator(".app-settings").count() == 0
assert page.locator(".ui-confirm").count() == 0
# CAD 별칭 — 잘못이면 안 씀 · 칸 표시
_open_settings(page)
assert page.is_checked("input[name=app-settings-wheel][value=push]") # 저장값으로 엶
page.click("[data-settings-tab=cad]")
line = "input[data-cad-command=LINE]"
circle = "input[data-cad-command=CIRCLE]"
for line_v, circle_v in (("L!", ""), ("LL", "ll"), ("", "LINE")):
page.fill(line, line_v)
page.fill(circle, circle_v)
_save(page)
assert page.inner_text(".app-settings__error").strip(), (line_v, circle_v)
assert page.locator(".app-settings input[aria-invalid=true]").count() >= 1
assert len(_writes(page)) == 1
page.fill(line, "ll")
page.fill(circle, "")
_save(page)
assert page.inner_text(".app-settings__error").strip() == ""
assert len(_writes(page)) == 2
assert json.loads(_writes(page)[1]) == {"wheelZoom": "push", "cadAliases": {"LINE": ["LL"]}}
# 닫을 때 물음 — 버림 = 안 씀 · 저장 = 한 번 더
page.click("[data-settings-tab=mouse]")
page.check("input[name=app-settings-wheel][value=pull]")
_close(page)
page.click(".ui-confirm button >> nth=1") # 버리고 넘어감
assert page.locator(".app-settings").count() == 0 and len(_writes(page)) == 2
assert page.evaluate(WHEEL_IN, 100) is False
_open_settings(page)
page.check("input[name=app-settings-wheel][value=pull]")
_close(page)
page.click(".ui-confirm button >> nth=2") # 저장 후 넘어감
assert page.locator(".app-settings").count() == 0 and len(_writes(page)) == 3
assert json.loads(_writes(page)[2])["wheelZoom"] == "pull"
assert page.evaluate(WHEEL_IN, 100) is True
browser.close()