Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
201 lines
9.9 KiB
Python
201 lines
9.9 KiB
Python
"""☰ 서랍 · 위 줄 걷기(PLAN_기기대응 6 · 7 · `app_shell.ts` · `ui_template_workflow_layout.ts`) — 실제 브라우저.
|
||
|
||
① 휴대폰 · 로그인 뒤 B 쪽 = 위 줄 걷힘(본문 위 끝 0) · ☰ 는 위 왼쪽 모서리(폭 36 · 손가락 높이 44) · 떠 있는 진행 판 없음
|
||
② ☰ 서랍 = 왼쪽에서 열림 · ☰ 는 서랍 위에 그대로 · 로고 · 홈 ─ 진행 단계 ─ 메뉴 ─ 언어 · 테마 · 로그인 · 터치 모드 차례
|
||
진행 단계는 진행 판의 **그 요소**(한 벌) · 지금 단계 강조 그대로 · 닫으면 제자리(진행 판)로 돌아감 · 단계 누름 = 이동 + 서랍 닫힘
|
||
③ 휴대폰 · 공개 쪽(A01) = 위 줄 그대로(본문 위 끝 64) · ☰ 는 위 줄 오른쪽
|
||
④ 데스크탑(1440 · 마우스) · 로그인 뒤 B 쪽 = 위 줄 걷힘 · ☰ 왼쪽 위 36×36 · 진행 판 없음 · 위 줄 항목이 모두 서랍에 · Esc 로 닫힘
|
||
⑤ 데스크탑 · 공개 쪽(A01) = 위 줄 그대로 · ☰ 없음
|
||
⑥ (352-4) 좌측 패널이 펼쳐진 동안 ☰ 는 제목 줄 앞 칸에 든 모양(테두리 · 바탕 · 그림자 없음 · 자리 8,8 그대로)
|
||
· 접으면 떠 있는 단추 · 서랍 첫 줄 로고 우측 · 프로젝트 이름 우측 끝 — 글로 봄(서버 없이)
|
||
"""
|
||
|
||
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
import test_device_audit as audit # noqa: E402
|
||
|
||
OUTLET_TOP = "Math.round(document.getElementById('app-outlet').getBoundingClientRect().top)"
|
||
GROUPS = "[...document.querySelectorAll('.app-drawer > *')].map(e => e.className)"
|
||
BURGER = (
|
||
"(() => { const r = document.querySelector('.app-burger').getBoundingClientRect();"
|
||
" return [Math.round(r.left), Math.round(r.top), Math.round(r.width), Math.round(r.height),"
|
||
" Math.round(r.right)]; })()"
|
||
)
|
||
TITLE_ROW_CY = (
|
||
"(() => { const r = document.querySelector("
|
||
"'.ui-workflow-overlay__panel--title > .ui-workflow-overlay__header').getBoundingClientRect();"
|
||
" return Math.round(r.top + r.height / 2); })()"
|
||
)
|
||
PROGRESS_HIDDEN = (
|
||
"!document.querySelector('.ui-workflow-overlay__panel--progress').getClientRects().length"
|
||
)
|
||
LABELS = (
|
||
"(sel) => [...document.querySelectorAll(sel)].map(b => b.textContent.trim()).filter(Boolean)"
|
||
)
|
||
|
||
|
||
def _ctx(browser, cred, size):
|
||
spec = audit.SIZES[size]
|
||
ctx = browser.new_context(
|
||
viewport=spec["viewport"], has_touch=spec["touch"], is_mobile=spec["touch"]
|
||
)
|
||
audit._login(ctx, cred)
|
||
pid = audit._project_id(ctx)
|
||
ctx.add_init_script(f"localStorage.setItem('frd_current_project_id', {json.dumps(pid)})")
|
||
return ctx
|
||
|
||
|
||
@pytest.mark.skipif(not audit._server_up(), reason="sub1 화면 서버(5174) 꺼짐")
|
||
def test_drawer_holds_steps_and_header_folds():
|
||
from playwright.sync_api import sync_playwright
|
||
|
||
cred = json.loads((audit.ROOT / "tmp" / "aislo_cred.json").read_text(encoding="utf-8"))
|
||
with sync_playwright() as p:
|
||
browser = p.chromium.launch(channel="msedge", headless=True)
|
||
page = _ctx(browser, cred, "phone_390x844_touch").new_page()
|
||
audit._open(page, "b07-design-detail")
|
||
|
||
# ①
|
||
assert page.evaluate(OUTLET_TOP) == 0
|
||
burger = page.evaluate(BURGER)
|
||
assert burger[:4] == [8, 8, 36, 44], burger
|
||
assert page.evaluate(PROGRESS_HIDDEN)
|
||
|
||
# ②
|
||
page.evaluate(
|
||
"window.__steps = document.querySelector('.ui-workflow-overlay__panel--progress .ui-workflow-layout__steps')"
|
||
)
|
||
page.evaluate("document.querySelector('.app-burger').click()")
|
||
assert (
|
||
page.evaluate(
|
||
"Math.round(document.querySelector('.app-drawer').getBoundingClientRect().left)"
|
||
)
|
||
== 0
|
||
)
|
||
assert page.evaluate( # ☰ 는 서랍 위에서 그대로 눌림(다시 누르면 닫힘)
|
||
"document.elementFromPoint(26, 30) === document.querySelector('.app-burger')"
|
||
)
|
||
groups = page.evaluate(GROUPS)
|
||
order = [
|
||
next((k for k in ("brand", "steps", "site", "touch") if k in c), "settings")
|
||
for c in groups
|
||
]
|
||
assert order == ["brand", "steps", "site", "settings"], groups
|
||
assert page.evaluate(
|
||
"document.querySelector('.app-drawer__steps').firstElementChild === window.__steps"
|
||
)
|
||
active = page.evaluate(
|
||
"document.querySelector('.app-drawer__steps .ui-workflow-layout__step.is-active')?.textContent"
|
||
)
|
||
assert active and "상세" in active, active
|
||
# 378-1: 홈페이지 메뉴 = 접힘 기본 · summary 누름 = 펼침 + 서랍 열린 채 · 서랍 폭 < 패널 · 바탕 ≠ 패널
|
||
site = "document.querySelector('.app-drawer__site')"
|
||
assert page.evaluate(f"!{site}.open")
|
||
page.evaluate(f"{site}.querySelector('summary').click()")
|
||
assert page.evaluate(f"{site}.open")
|
||
assert page.evaluate(
|
||
"document.querySelector('.app-header').classList.contains('is-drawer-open')"
|
||
)
|
||
page.evaluate(f"{site}.querySelector('summary').click()")
|
||
assert page.evaluate(f"!{site}.open")
|
||
widths = page.evaluate(
|
||
"[document.querySelector('.app-drawer'),"
|
||
" document.querySelector('.ui-workflow-overlay__panel--title')]"
|
||
".map(e => Math.round(e.getBoundingClientRect().width))"
|
||
)
|
||
assert widths[0] <= 240 and widths[0] < widths[1], widths
|
||
bgs = page.evaluate(
|
||
"[document.querySelector('.app-drawer'),"
|
||
" document.querySelector('.ui-workflow-overlay__panel--title')]"
|
||
".map(e => getComputedStyle(e).backgroundColor)"
|
||
)
|
||
assert bgs[0] != bgs[1], bgs
|
||
page.evaluate("document.querySelector('.app-drawer__scrim').click()") # 닫기 = 제자리
|
||
assert page.evaluate(
|
||
"window.__steps.closest('.ui-workflow-overlay__panel--progress') !== null"
|
||
)
|
||
page.evaluate("document.querySelector('.app-burger').click()")
|
||
page.evaluate(
|
||
"[...document.querySelectorAll('.app-drawer__steps .ui-workflow-layout__step.is-enabled')]"
|
||
".find(b => b.textContent.includes('수량')).click()"
|
||
)
|
||
page.wait_for_function("location.hash.includes('b08-quantity')", timeout=10000)
|
||
assert not page.evaluate(
|
||
"document.querySelector('.app-header')?.classList.contains('is-drawer-open')"
|
||
)
|
||
|
||
# ③
|
||
audit._open(page, "a01-home")
|
||
assert page.evaluate(OUTLET_TOP) == 64
|
||
assert page.evaluate(BURGER)[4] >= 390 - 16
|
||
|
||
# ④
|
||
desk = _ctx(browser, cred, audit.DESKTOP).new_page()
|
||
audit._open(desk, "b07-design-detail")
|
||
assert desk.evaluate(OUTLET_TOP) == 0
|
||
assert desk.evaluate(BURGER)[:4] == [8, 8, 36, 36]
|
||
assert desk.evaluate(PROGRESS_HIDDEN)
|
||
# 389-1: ☰ 중심 = 좌측 패널 제목 줄 중심
|
||
assert desk.evaluate(TITLE_ROW_CY) == 26
|
||
head_nav = desk.evaluate(LABELS, ".app-nav button")
|
||
head_actions = desk.evaluate(LABELS, ".app-actions button")
|
||
assert head_nav and head_actions
|
||
desk.click(".app-burger")
|
||
assert desk.evaluate(LABELS, ".app-drawer__nav button") == head_nav
|
||
drawer_all = desk.evaluate(LABELS, ".app-drawer button")
|
||
assert all(label in drawer_all for label in head_actions), (head_actions, drawer_all)
|
||
assert desk.evaluate(
|
||
"document.querySelector('.app-drawer__steps .ui-workflow-layout__step.is-active') !== null"
|
||
)
|
||
desk.keyboard.press("Escape")
|
||
assert not desk.evaluate(
|
||
"document.querySelector('.app-header').classList.contains('is-drawer-open')"
|
||
)
|
||
|
||
# ⑤
|
||
audit._open(desk, "a01-home")
|
||
assert desk.evaluate(OUTLET_TOP) == 64
|
||
assert not desk.evaluate("document.querySelector('.app-burger').getClientRects().length")
|
||
browser.close()
|
||
|
||
|
||
def test_352_4_burger_in_panel_title_row_text():
|
||
root = Path(__file__).resolve().parents[2]
|
||
shell = (root / "A00_Common/app_shell.ts").read_text(encoding="utf-8")
|
||
open_panel = (
|
||
"html.is-bm-route:has(.ui-workflow-overlay__panel--title:not(.is-collapsed)) .app-burger {"
|
||
)
|
||
assert open_panel in shell # 패널 펼침 = 제목 줄에 든 모양
|
||
body = shell.split(open_panel, 1)[1].split("}", 1)[0]
|
||
for line in ("border-color: transparent;", "background: transparent;", "box-shadow: none;"):
|
||
assert line in body
|
||
# 자리(8,8 · 36)는 그대로 — 접으면 아래 기본 규칙(떠 있는 단추)으로 돌아감
|
||
base = shell.split("html.is-bm-route .app-burger {", 1)[1].split("}", 1)[0]
|
||
assert "position: fixed;" in base and "left: var(--spacing-8);" in base
|
||
# 389-1: 마우스 제목 줄 높이 = 8×2 + ☰ 높이(36) = 52 → 중심 26 = ☰ 중심
|
||
overlay_css = (root / "ui_template/ui_template_overlay.css").read_text(encoding="utf-8")
|
||
title_row = overlay_css.split(
|
||
"html.is-bm-route .ui-workflow-overlay__panel--title > .ui-workflow-overlay__header {", 1
|
||
)[1].split("}", 1)[0]
|
||
assert "min-height: calc(var(--spacing-8) * 2 + var(--app-burger-w));" in title_row
|
||
# 389-2: 서랍 8/12 체계 — 묶음 사이 12 + 선 + 12 · 로고 줄 = ☰ 높이 가운데
|
||
assert (
|
||
"gap: var(--spacing-12);" in shell.split(".app-header.is-drawer-open .app-drawer {", 1)[1]
|
||
)
|
||
assert "padding-top: var(--spacing-12);" in shell.split(".app-drawer__group + ", 1)[1]
|
||
assert (
|
||
"min-height: var(--app-burger-w);"
|
||
in shell.split("html.is-bm-route .app-drawer__brand {", 1)[1]
|
||
)
|
||
assert "align-self: flex-end" not in shell
|
||
# 378-1: 프로젝트 화면 서랍 오른쪽 가장자리 = 강조색 2 px
|
||
assert "border-right: 2px solid var(--color-accent);" in shell
|
||
overlay = (root / "ui_template/ui_template_overlay.css").read_text(encoding="utf-8")
|
||
project = overlay.split(".ui-workflow-overlay__project {", 1)[1].split("}", 1)[0]
|
||
assert "margin-left: auto;" in project
|