test(511-C 화면 · 둘레): 검증_sub4 B05 유토 배분 판 server 대 both — server = /haul-plan 1 · 배분 모듈 안 받음 · both = /haul-plan 0 · 모듈 받음 · 유토곡선 그림 같음 · 계산 자리 창 E2 열림(기본 꺼짐) · 서버만 G3 D1 D2 N1 까닭 넷 다름(test_511_c_haul_browser) · 511-B1 판 열린 줄에 haul — 요구 320-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 15:55:10 +09:00
co-authored by Claude Opus 5.5
parent 6010704c55
commit f16c25bb2e
2 changed files with 158 additions and 0 deletions
@@ -94,6 +94,7 @@ def test_판_다섯_줄_잠금_기본값_그대로():
"route", # B-5 = 엶(511-B B-5)
"stations", # B-2 끝 = 엶(511-B B-2)
"profile", # B-3 엶(511-B · test_511_b3_profile_node)
"haul", # 511-C E2 엶(기본 server · 판 파일 바뀜 0)
"fixed_browser",
}
committed = subprocess.run( # noqa: S603 — 고정 실행 파일
+157
View File
@@ -0,0 +1,157 @@
"""PLAN 511-C 화면 — 자기 포트 Playwright 머리 없는 브라우저 · 서버가 떠 있을 때만 · 검증_sub4.
- 판 haul 기본(server) = B05 계획고 ▲ 뒤 /haul-plan 요청 · 배분 모듈 안 받음(첫 묶음 밖)
- 판 haul both = /haul-plan 0 · 배분 모듈을 그때 받아 셈 · 유토곡선(배분 balloon 포함) 그림
= server 판과 같음
- 계산 자리 창 = E2 열린 줄(기본 꺼짐) · 서버만 G3 D1 D2 N1 줄마다 다른 까닭
- 저장 안 함(세션 초안만) · 판 = 파일 바이트 되돌림 · 시험 브라우저 표식 시스템 로그 행만 지움
./venv/Scripts/python.exe -m pytest resources/tester/test_511_c_haul_browser.py -q -s
"""
from __future__ import annotations
import asyncio
import json
import sys
from pathlib import Path
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
if str(HERE) not in sys.path:
sys.path.insert(0, str(HERE))
from helper_verify_project import own_ports # noqa: E402
from test_511_a_calc_sites_browser import ( # noqa: E402, F401
_UP,
JS_GRADE, # noqa: E402
_started,
)
API, BASE = own_ports()
BOARD = ROOT / "resources" / "master_template" / "calc_sites.json"
PROJECT = "c725d747-50ae-46b0-87ce-4d4e56a53cf3" # 검증_sub4(OWNERS.md)
MARK = "AisloTest-511-C"
CARD = "계산 자리"
pytestmark = pytest.mark.skipif(
_UP is None or _UP != _started(API), reason="이 폴더 서버 없음 · vite 가 이 폴더 백엔드로 안 감"
)
# 그림 글 — 그릴 때마다 새로 짓는 clipPath 이름만 한 글자로(값 아님)
JS_CHART = r"""() => {
const chart = document.querySelector('.b05-profile__masshaul-chart');
return chart ? chart.innerHTML.replace(/mass-clip-[a-z0-9]+/g, 'mass-clip') : null;
}"""
JS_LOADED = r"""() => performance.getEntriesByType('resource').map((e) => e.name)
.some((n) => /common_util_mass_haul_balance\.ts/.test(n))"""
@pytest.fixture(scope="module")
def ctx():
sync = pytest.importorskip("playwright.sync_api")
committed = BOARD.read_bytes()
cred = json.loads((ROOT / "tmp" / "aislo_cred.json").read_text(encoding="utf-8"))
try:
with sync.sync_playwright() as play:
chrome = play.chromium.launch(channel="msedge", headless=True)
context = chrome.new_context(viewport={"width": 1600, "height": 1000}, user_agent=MARK)
login = {k: cred[k] for k in ("email", "password")}
r = context.request.post(f"{BASE}/api/auth/login/request", data=login, timeout=60000)
assert r.ok, f"로그인 실패 {r.status}" # 비밀번호는 안 찍음
try:
yield context
finally:
chrome.close()
finally:
BOARD.write_bytes(committed)
_drop_test_logs()
def _drop_test_logs() -> None:
from config.config_db import close_db_pool, init_db_pool
async def run():
pool = await init_db_pool()
try:
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute("DELETE FROM system_audit_logs WHERE user_agent = %s", (MARK,))
await connection.commit()
finally:
await close_db_pool()
asyncio.run(run())
def _board(haul: str | None) -> None:
board = json.loads(BOARD.read_text("utf-8"))
board["sites"].pop("haul", None)
if haul:
board["sites"]["haul"] = haul
BOARD.write_text(json.dumps(board, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def _run(context) -> dict:
page = context.new_page()
page.add_init_script(
f"localStorage.setItem('frd_current_project_id', '{PROJECT}');"
" performance.setResourceTimingBufferSize(20000);"
)
posts: list[dict] = []
def on_response(res):
if res.url.endswith("/haul-plan") and res.request.method == "POST":
posts.append(res.json())
page.on("response", on_response)
page.goto(f"{BASE}/#/b05-profile", wait_until="domcontentloaded")
page.wait_for_function("() => !!window.__aisloPipeline", timeout=90000)
page.wait_for_timeout(9000)
page.click(".ui-workflow-overlay__handle--bottom") # 종단 펼침
page.wait_for_timeout(2000)
if page.evaluate(JS_CHART) is None:
page.click(".b05-profile__masshaul-handle") # 유토곡선 펼침
page.evaluate(JS_GRADE) # 계획고 ▲ 한 번(가운데 측점 +0.3 m) — 새 누가토량 → 배분 다시
page.wait_for_timeout(5000)
out = {"posts": posts, "chart": page.evaluate(JS_CHART), "loaded": page.evaluate(JS_LOADED)}
page.close()
return out
def test_haul_server_both_그림_같음(ctx):
_board(None) # 판 파일에 haul 없음 = 기본 server
server = _run(ctx)
_board("both")
both = _run(ctx)
_board(None)
assert server["posts"] and all(p["haul_plan"] for p in server["posts"])
assert any(p["haul_plan"]["transfers"] for p in server["posts"])
assert not server["loaded"] # 기본값 = 배분 모듈 안 받음
assert both["posts"] == [] and both["loaded"]
assert server["chart"] and "balloon" in server["chart"]
assert both["chart"] == server["chart"]
print("posts", len(server["posts"]), "chart bytes", len(server["chart"]))
def test_계산_자리_창_haul_엶_서버만_까닭(ctx):
page = ctx.new_page()
page.goto(f"{BASE}/#/b01-account", wait_until="domcontentloaded")
page.wait_for_selector(".b01-dashboard__setting-card", timeout=60000)
page.get_by_text(CARD, exact=True).click()
page.wait_for_selector(".b01-dashboard__calc-sites", timeout=30000)
rows = page.eval_on_selector_all(
".b01-dashboard__calc-sites tbody tr",
"(trs) => trs.map((tr) => ({ name: tr.cells[0].textContent.trim(),"
" why: tr.cells[3].textContent.trim(),"
" boxes: [...tr.querySelectorAll('input')].map((i) => [i.checked, i.disabled]) }))",
)
page.close()
calcs = {row["name"].split(" · ")[0]: row for row in rows if " · " in row["name"]}
assert calcs["E2"]["boxes"] == [[False, False], [True, True]] # 열림 · 기본 server(꺼짐)
whys = {no: calcs[no]["why"] for no in ("G3", "D1", "D2", "N1")}
for no, word in {"G3": "DB", "D1": "래스터", "D2": "D1", "N1": "지형"}.items():
assert word in whys[no] and calcs[no]["boxes"][0] == [False, True], (no, whys[no])
assert len(set(whys.values())) == 4
print(whys)