"""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)