Files
Aislo/resources/tester/test_511_b4_cross_server_browser.py
T

129 lines
5.3 KiB
Python

"""PLAN 511-B B-4 (요구 320-4) — 계산 자리 cross = server 화면 길(검증_sub2 · 이 폴더 서버 · Playwright).
못박는 것
- 판 both 와 server(판 응답만 바꿈 · 판 파일 안 씀)에서 페이지 묶음 cross 출력 · 저장 상세 설계(구조물 폐회로
면적 층까지)가 0 차 — 저장 노선(`cross.refresh` · 다시 셈 창구) · 노선 초안 둔 채 다시 열기(`stage.cross`).
- server 일 때만 그 id 를 서버에 묻고 · both 면 /calc 0.
- 왕복 시간(`__aisloCalcServer.calls`)을 남김(PLAN 시간 줄).
저장 안 함 · 세션만(노선 초안은 창과 함께 버림).
./venv/Scripts/python.exe -m pytest resources/tester/test_511_b4_cross_server_browser.py -q
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
for path in (ROOT, HERE):
if str(path) not in sys.path:
sys.path.insert(0, str(path))
from test_511_b1_calc_node_stages import BASE, JS_DRAFT, PROJECT, _diff, screen # noqa: E402
pytestmark = pytest.mark.skipif(
not (ROOT / "config" / "node_modules" / "vite").is_dir(), reason="프론트엔드 의존성 미설치"
)
OUT = ROOT / "tmp" / "511b4_capture.json"
# 묶음 cross 출력 · 저장 상세 설계(면적 · 구조물 층) · 서버 부름 기록
JS_STATE = r"""async () => {
const urls = performance.getEntriesByType('resource').map((e) => e.name);
const mod = async (re) => {
const u = urls.find((n) => re.test(n));
if (!u) throw new Error('모듈 없음 ' + re);
return import(u);
};
const page = await mod(/design_pipeline_page\.ts/);
const pid = localStorage.getItem('frd_current_project_id');
const pipe = page.pagePipeline(pid);
await page.reconcilePagePipeline(pid);
await pipe.settled();
const data = await mod(/design_page_data\.ts/);
const routeId = data.pageRouteId(pid);
const detail = (await mod(/B06_Section_Section_Canon\.ts/)).savedSectionDetail(pid, routeId);
const shown = page.pipelineDetail(pid) ?? detail;
const calls = (window.__aisloCalcServer?.calls ?? []).map((c) => ({
id: c.id, roundMs: c.roundMs, nodeMs: c.nodeMs, wallMs: c.wallMs, bytes: c.bytes,
}));
return JSON.parse(JSON.stringify({
cross: pipe.output('cross'),
designs: (shown?.cross_sections ?? []).map((s) => ({
chainage_m: s.chainage_m, design: s.design, samples: (s.samples ?? []).length,
})),
calls,
runs: (window.__aisloPipeline?.runs ?? []).slice(-1).map((r) => r.outcome),
}));
}"""
def _open(play, cred: dict, cross: str) -> tuple:
chrome = play.chromium.launch(channel="msedge", headless=True)
context = chrome.new_context(viewport={"width": 1600, "height": 1000})
login = {k: cred[k] for k in ("email", "password")}
assert context.request.post(f"{BASE}/api/auth/login/request", data=login, timeout=60000).ok
def board(route): # 판 응답만 바꿈(판 파일 안 씀)
response = route.fetch()
body = response.json()
body["sites"]["cross"] = cross
route.fulfill(response=response, json=body)
context.route("**/api/dashboard/calc-sites", board)
page = context.new_page()
page.add_init_script(
f"localStorage.setItem('frd_current_project_id', '{PROJECT}');"
" performance.setResourceTimingBufferSize(20000);"
)
return chrome, page
def _shot(page) -> dict:
page.goto(f"{BASE}/#/b05-profile", wait_until="domcontentloaded")
page.wait_for_function("() => !!window.__aisloPipeline", timeout=90000)
page.wait_for_timeout(9000)
return page.evaluate(JS_STATE)
@pytest.fixture(scope="module")
def shots():
sync = pytest.importorskip("playwright.sync_api")
cred = json.loads((ROOT / "tmp" / "aislo_cred.json").read_text(encoding="utf-8"))
out = {}
with sync.sync_playwright() as play:
for cross in ("both", "server"):
chrome, page = _open(play, cred, cross)
try:
out[f"saved_{cross}"] = _shot(page)
page.evaluate(JS_DRAFT) # 노선 초안(가운데 교각점 옮김 · 세션만)
page.wait_for_timeout(3000)
page.reload(wait_until="domcontentloaded")
page.wait_for_function("() => !!window.__aisloPipeline", timeout=90000)
page.wait_for_timeout(9000)
out[f"draft_{cross}"] = page.evaluate(JS_STATE)
finally:
chrome.close() # 세션 초안은 창과 함께 버림
OUT.write_text(json.dumps(out, ensure_ascii=False), encoding="utf-8")
return out
@screen
@pytest.mark.parametrize("case", ["saved", "draft"])
def test_cross_server_화면_값_브라우저와_0차(shots, case):
both, server = shots[f"{case}_both"], shots[f"{case}_server"]
assert both["runs"] == ["done"] and server["runs"] == ["done"], (both["runs"], server["runs"])
assert both["cross"]["byKey"], "cross 출력 없음"
hit = _diff(server["cross"], both["cross"], "cross")
assert hit is None, hit
hit = _diff(server["designs"], both["designs"], "designs")
assert hit is None, hit
# 부름 — both = /calc 없음 · server = 그 길 id
assert not [c for c in both["calls"] if "cross" in c["id"]]
want = "cross.refresh" if case == "saved" else "stage.cross"
assert any(c["id"] == want for c in server["calls"]), server["calls"]