- 서버: 지오빅데이터(KIGAM) 수치지질도 1:50,000 — WFS 암상 면(깨진 한글 칸은 기호별 점 조회로 채움) · 받은 범위 ∪ 노선으로 자름 · 저장본 · 노선 따라 1 m 표본 구간 · 키는 서버만(.env GEOBIGDATA_API_KEY · 쿼리 key) - 그림 조각 대리에 geology(키 쿼리 이름 칸 key_param · 옛 겹 serviceKey 그대로) - 화면(D03 · B05): 「제한·재해」에 「암상 지질도」 — 그림 조각 + 갈래 색 면 · 「기호 · 암상」 글 · 범례 · 노선 구간 표(참고 — 토사 · 암 판정 아님) - 켠 밖 겹마다 범례 맨 아래 출처 줄(브이월드 · 국토지리정보원 · 산림청 · 환경부 · 생활안전지도 공공누리 · 한국지질자원연구원 CC BY-NC) - 범례 칸 높이 한도 · 밀기(낮은 화면) - 시험: test_760_1 새로 · test_634_2 · test_553_4 겹 차례 표에 geology 한 줄 더함(새 겹 · 낡은 표) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
288 lines
14 KiB
Python
288 lines
14 KiB
Python
"""553-4 예정노선(D01~D04) · 안정성평가(E01~E07) 부품 옮김 지킴 시험 — 서버 · 화면 없이 소스만 본다.
|
|
옮긴 페이지는 날 `<input>` · `<progress>` · 날 색 · 안 쓰는 변종 CSS 가 다시 생기면 안 됨."""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def _src(path: str) -> str:
|
|
return (ROOT / path).read_text(encoding="utf-8")
|
|
|
|
|
|
def _no_hex(path: str) -> None:
|
|
css = re.sub(r"/\*.*?\*/", "", _src(path), flags=re.S)
|
|
assert not re.search(r"#[0-9a-fA-F]{3,8}\b", css), f"{path}: 날 색"
|
|
|
|
|
|
def test_d03_points_parts():
|
|
"""591-2 — D02 걷음 · 지점 칸 = 목록 줄 · 아이콘 단추 · 우클릭 메뉴 부품(598-2 = `UI_Menu`)."""
|
|
assert not (ROOT / "D02_PlanBase" / "D02_PlanBase_UI_Page.ts").exists()
|
|
ts = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Points.ts")
|
|
assert "createListRow(" in ts and "createIconButton(" in ts
|
|
assert "createMapContextMenu(" in _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Menu.ts")
|
|
for raw in ('createElement("button")', 'createElement("input")', 'el("input"'):
|
|
assert raw not in ts
|
|
|
|
|
|
def test_d03_parts():
|
|
ts = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts")
|
|
three = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_3D.ts")
|
|
css = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css")
|
|
draw = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts")
|
|
draw_panel = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts")
|
|
tools = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts")
|
|
layers = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Layers.ts")
|
|
assert 'createElement("progress")' not in ts and "createProgressBar(" not in ts
|
|
assert (
|
|
"createProgressCircle({ size: 16, inline: true })" in ts
|
|
) # 604-3 받기 = 위치 칸 제목 서클
|
|
site = _src(
|
|
"D03_PlanAnalysis/D03_PlanAnalysis_UI_Site.ts"
|
|
) # 596-2 위치 칸 · 596-5 등록 찾기 = 같은 부품
|
|
assert "createFindList(" in site and "searchPlaces(" in site and "createPlanSite(" in ts
|
|
assert "createPlaceFind(" in _src("D01_PlanRegister/D01_PlanRegister_UI_Page.ts")
|
|
for raw in ('createElement("button")', 'createElement("input")'):
|
|
assert raw not in site, raw
|
|
# 562-3 — 날 단추 · 입력 · 고르기 없음 · 도구줄 · 제약 칸 · 연도 고름은 부품
|
|
for src in (ts, draw, draw_panel, tools, layers):
|
|
for raw in ('createElement("button")', 'createElement("input")', 'createElement("select")'):
|
|
assert raw not in src, raw
|
|
# 568-3 — 도구줄 = 공용 지도 도구줄 · 아홉([?] · 되돌리기 · 다시하기 · 3D · 돌리기 둘 · 측점 · 기울기 · 재기)
|
|
assert "createMapTools({" in tools and "hint:" in tools and "leftPan" not in tools
|
|
assert "createToolbar(" not in tools and "createIconButton(" not in tools
|
|
for act in ("undo", "redo", "view3d", "measure", "stations", "slopes"):
|
|
assert f'mapToolButton("{act}"' in tools or f'markButton("{act}"' in tools, act
|
|
table = _src("A00_Common/map/map_toolbar.ts") # 624-2 — 차례 · 보임 = 공용 도구줄 표
|
|
assert 'mapToolGroups("d03"' in tools and re.search(r'key: "rotate-ccw".*pages: BOTH', table)
|
|
for b05_only in ("footprint", "station-snap", "contour-minor"):
|
|
assert f'"{b05_only}"' not in tools, b05_only
|
|
paint = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts") # 580 — 그림 나눔
|
|
assert "map.stations" in paint and "map.slopes" in paint
|
|
# 569-2 · 581-1 · 581-2 — 바탕 겹 단추(공용 지도 도구줄 한 줄 · 묶음 셋) · 처음 켬 셋 · 취향 기억
|
|
defs = _src("A00_Common/map/map_layer_list.ts") # 624-2 공용 겹 목록
|
|
bar = _src("A00_Common/map/map_layer_bar.ts")
|
|
assert (
|
|
"createMapTools({" in bar
|
|
and "createIconButton({" in bar
|
|
and "createMapLayerBar({" in layers
|
|
)
|
|
assert "createToolbar(" not in layers and "createToolbar(" not in bar
|
|
assert '"aislo:pref:d03.layers"' in defs and "writeByKey(MAP_LAYER_PAGES[page].pref" in bar
|
|
# 634-2 — 표 차례(요구 546 그룹1 22 + 도로300m) · row(열쇠, 아이콘, 호버제목, 묶음[, true])
|
|
rows = re.findall(r'row\("(\w+)", "\w+", "[^"]+", (?:"\w+"|null)(, true)?\)', defs)
|
|
keys = [(key, "true" if first else "false") for key, first in rows]
|
|
on = {"satellite", "contour", "stream"}
|
|
order = "satellite hillshade contour slope stream water zeroOrder bands road nationalRoad"
|
|
order += " forestRoadFile roadBands cadastral building place zones soils ecoForest ecology"
|
|
order += " landslide landslideZones geology faults lineaments" # 760-1 암상 더함
|
|
assert keys == [(key, str(key in on).lower()) for key in order.split()], keys
|
|
assert ".d03-plan__icons" in css and ".d03-plan__legend" in css # 634-3 한 행
|
|
# 599-1 — 방식 칸 = 마디 고름 2 × 2 · 길이 · 기울기 두 열 · 노드 판(598-4)과 같은 함수
|
|
# 626-3 — 방식 칸은 공용 `A00_Common/map/map_draw_kind`(B05 평면도 씀) · D03 칸은 다시 내보냄
|
|
kind = _src("A00_Common/map/map_draw_kind.ts")
|
|
kind_css = _src("A00_Common/map/map_draw_kind.css")
|
|
assert "createSegment({" in kind and kind.count("createInputField(") == 2
|
|
assert "columns: 2" in kind and "export function createDrawKindFields(" in kind
|
|
assert (
|
|
"createDrawKindFields," in draw_panel
|
|
and 'from "../A00_Common/map/map_draw_kind"' in draw_panel
|
|
)
|
|
assert ".map-draw-kind__fields" in kind_css and "createDrawKindFields(" in draw
|
|
assert '"길이·기울기 고정"' in kind and '"둘 다"' not in kind
|
|
assert "d03-plan__hint" not in draw # 칸 설명 글 걷음(요구 463-6)
|
|
# 599-4 — 연도 구역 마커 = 그리기 칸 단추(끌어 놓기) · 지도 도구줄에서 뺌
|
|
assert "YEAR_MARK_DRAG" in draw_panel and '"year-mark"' not in tools
|
|
props = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Props.ts") # 585-2 조각 연도 = 속성 판
|
|
assert "createSelectField(" in props and "createFloatPanel(" in props and "createKv(" in props
|
|
assert 'createElement("button")' not in props and 'createElement("select")' not in props
|
|
assert (
|
|
'createElement("select")' not in three and "createSelectField" not in three
|
|
) # 601 선 고름 걷음
|
|
assert (
|
|
"createCheckbox" not in three and "setCorridors(" in three
|
|
) # 601 노선망 예상 코리도 한 번에
|
|
for dead in (".d03-plan__bar", ".d03-plan__mode", "is-picked"):
|
|
assert dead not in css, dead
|
|
assert "b05-route__3d-modal" not in three and "b05-route__3d-body" not in three # 빌림 끊음
|
|
assert 'dialogClass: "d03-plan__3d-modal"' in three and ".d03-plan__3d-body" in css
|
|
scenes = _src("A05_EduDetail/guide/D03/_scenes.json")
|
|
assert "b05-route__3d-modal" not in scenes # 촬영 장면이 `.d03-plan__3d-modal` 로 잡음
|
|
|
|
|
|
def test_d04_parts():
|
|
ts = _src("D04_PlanConfirm/D04_PlanConfirm_UI_Page.ts")
|
|
css = _src("D04_PlanConfirm/D04_PlanConfirm_UI_Style.css")
|
|
assert 'el("input"' not in ts
|
|
assert ts.count("createInputField(") == 2 and "createCheckbox(" in ts
|
|
assert 'createBanner({ tone: "warning"' in ts
|
|
assert "input:checked" in ts # [확정] 이 고른 연도를 읽는 길
|
|
assert "border:" not in css.split(".d04-confirm__input")[1].split("}")[0]
|
|
assert ".d04-confirm__dirty" not in css
|
|
assert 'el("table"' not in ts and "tableShell(columns)" in ts
|
|
assert "d04-confirm__table" not in css # 표 모양은 표 부품 · 이름은 촬영 장면용 걸이
|
|
|
|
|
|
def test_e01_missing_is_banner_part():
|
|
ts = _src("E01_StabRegister/E01_StabRegister_UI_Missing.ts")
|
|
assert "createBanner" in ts and '"details"' not in ts
|
|
assert 'dataset.role = "missing-stations"' in ts # 시험 · 촬영이 잡음
|
|
assert not (ROOT / "E01_StabRegister/E01_StabRegister_UI_Missing.css").exists()
|
|
|
|
|
|
def test_e02_parts():
|
|
tables = _src("E02_StabGround/E02_StabGround_UI_Tables.ts")
|
|
page = _src("E02_StabGround/E02_StabGround_UI_Page.ts")
|
|
css = _src("E02_StabGround/E02_StabGround_UI_Style.css")
|
|
assert 'el("input"' not in tables and 'el("select"' not in tables
|
|
assert "createInputField(" in tables and "createSelectField(" in tables
|
|
assert "createCard({ title" in tables
|
|
assert "is-invalid" not in page + css and "is-error" in page
|
|
for dead in (".e02-card__head", ".e02-card__title", ".e02-hint", ".e02-empty"):
|
|
assert dead not in css, dead
|
|
_no_hex("E02_StabGround/E02_StabGround_UI_Style.css")
|
|
assert "createTable(" in tables and "createTHead" not in tables
|
|
assert 'className: "ui-field e02-field"' in tables # 칸 이름 줄은 입력 부품의 ui-field
|
|
assert ".e02-field" not in css and "border-collapse" not in css
|
|
|
|
|
|
def test_e03_parts():
|
|
names = [
|
|
"List", "Page", "Plan", "Prob", "ProbChart", "Reinforce", "Result", "Section", "Support", "View3D",
|
|
] # fmt: skip
|
|
ts = {n: _src(f"E03_StabSection/E03_StabSection_UI_{n}.ts") for n in names}
|
|
css = _src("E03_StabSection/E03_StabSection_UI_Style.css")
|
|
for n, src in ts.items():
|
|
assert 'el("table"' not in src, n # 표 = 표 부품(`E03_StabSection_UI_Table.ts`)
|
|
assert 'el("input"' not in src and 'el("select"' not in src, n
|
|
assert "e03-legend__item" not in src and "e03-legend__swatch" not in src, n
|
|
assert "createTable(" in _src("E03_StabSection/E03_StabSection_UI_Table.ts")
|
|
assert "dataset.role = role" in _src("E03_StabSection/E03_StabSection_UI_Table.ts")
|
|
assert "createLegend(" in _src("E03_StabSection/E03_StabSection_UI_Parts.ts")
|
|
assert "openModal(" in ts["View3D"] and 'className: "e03-view3d"' not in ts["View3D"]
|
|
for key in ("station-list", "slice-table", "methods", "whatif-table", "support-table"):
|
|
assert f'"{key}"' in "".join(ts.values()), key # 촬영 장면 · 시험이 잡는 data-role
|
|
for dead in (
|
|
".e03-legend__item", ".e03-legend__swatch", ".e03-table", ".e03-verdict", ".e03-kpi",
|
|
".e03-view3d {", ".e03-swatch-none", ".e03-list table", ".e03-list th {",
|
|
): # fmt: skip
|
|
assert dead not in css, dead
|
|
|
|
|
|
def test_e04_parts():
|
|
page = _src("E04_Stab3D/E04_Stab3D_UI_Page.ts")
|
|
view = _src("E04_Stab3D/E04_Stab3D_UI_View3D.ts")
|
|
css = _src("E04_Stab3D/E04_Stab3D_UI_Style.css")
|
|
assert 'el("progress"' not in page and "createProgressBar()" in page
|
|
for part in (
|
|
"createSegment(",
|
|
"createListRow(",
|
|
"createKv(",
|
|
"createFloatPanel(",
|
|
"createBanner(",
|
|
):
|
|
assert part in page, part
|
|
assert 'el("input"' not in view and "createCheckbox(" in view
|
|
assert (
|
|
'classList.add("e04-toggle")' in page
|
|
) # 촬영 장면이 `.e04-toggle button[data-value]` 로 잡음
|
|
assert 'row.classList.add("e04-section")' in page
|
|
for dead in (
|
|
".e04-toggle .ui-btn",
|
|
".e04-legend__row",
|
|
".e04-legend__swatch",
|
|
".e04-result__list",
|
|
".e04-section:hover",
|
|
):
|
|
assert dead not in css, dead
|
|
|
|
|
|
def test_e05_parts():
|
|
ts = _src("E05_StabReport/E05_StabReport_UI_Page.ts")
|
|
css = _src("E05_StabReport/E05_StabReport_UI_Style.css")
|
|
for part in ("createBanner(", "openModal(", "createEmpty(", "createText("):
|
|
assert part in ts, part
|
|
assert 'el("p"' not in ts and "previewBox" not in ts
|
|
assert 'classList.add("e05-stale")' in ts # 촬영 장면이 `.e05-stale .ui-btn` 으로 잡음
|
|
assert ".e05-stale[hidden]" in css # 띠 알림은 flex 라 hidden 을 덮음
|
|
for dead in (".e05-preview {", ".e05-preview__head", ".e05-status.is-error"):
|
|
assert dead not in css, dead
|
|
|
|
|
|
def test_e06_parts():
|
|
names = ["Joints", "Page", "Result", "Stereonet"]
|
|
ts = {n: _src(f"E06_StabRock/E06_StabRock_UI_{n}.ts") for n in names}
|
|
parts = _src("E06_StabRock/E06_StabRock_UI_Parts.ts")
|
|
css = _src("E06_StabRock/E06_StabRock_UI_Style.css")
|
|
for n, src in ts.items():
|
|
assert 'el("table"' not in src and 'el("input"' not in src, n
|
|
assert 'el("select"' not in src and 'el("p"' not in src, n
|
|
for part in ("createTable(", "createBadge(", "createSegment(", "createText(", "createEmpty("):
|
|
assert part in parts, part
|
|
assert "createLegend(" in ts["Stereonet"] and "createBanner(" in ts["Page"]
|
|
assert "createInputField(" in ts["Joints"] and "createSelectField(" in ts["Joints"]
|
|
assert "compact: true" in ts["Joints"] and ".e06-setting" not in css # 설정 줄 부품 좁은 꼴
|
|
assert (
|
|
'classList.add("e06-toggle")' in parts
|
|
) # 촬영 장면이 `.e06-toggle button[data-value]` 로 잡음
|
|
assert '"station-list"' in ts["Result"] # 촬영 장면 · 시험이 잡는 data-role
|
|
for dead in (
|
|
".e06-verdict",
|
|
".e06-list th",
|
|
".e06-cases",
|
|
".e06-table th",
|
|
".e06-legend__row",
|
|
".e06-toggle .ui-btn",
|
|
"is-invalid",
|
|
):
|
|
assert dead not in css, dead
|
|
assert "is-invalid" not in ts["Joints"] + ts["Page"]
|
|
|
|
|
|
def test_e07_parts():
|
|
page = _src("E07_StabArea/E07_StabArea_UI_Page.ts")
|
|
css = _src("E07_StabArea/E07_StabArea_UI_Style.css")
|
|
assert 'el("table"' not in page and 'el("progress"' not in page and 'el("p"' not in page
|
|
for part in (
|
|
"createSegment(",
|
|
"createFloatPanel(",
|
|
"createLegend(",
|
|
"createKv(",
|
|
"createTable(",
|
|
):
|
|
assert part in page, part
|
|
for part in ("createProgressBar()", "createBanner(", "createEmpty("):
|
|
assert part in page, part
|
|
assert (
|
|
'toggle.classList.add("e07-toggle")' in page
|
|
) # 촬영 장면이 `.e07-toggle button[data-value]` 로 잡음
|
|
assert "data-chainage" in css or "dataset.chainage" in page # 줄 누름 = 지도 이동
|
|
for dead in (
|
|
".e07-toggle .ui-btn",
|
|
".e07-legend__row",
|
|
".e07-summary",
|
|
".e07-table th",
|
|
".e07-progress {",
|
|
):
|
|
assert dead not in css, dead
|
|
|
|
|
|
def test_map_tips_use_tooltip_part():
|
|
for path, hook in (
|
|
("E03_StabSection/E03_StabSection_UI_Section.ts", "e03-tip"),
|
|
("E04_Stab3D/E04_Stab3D_UI_Map.ts", "e04-map__tip"),
|
|
("E07_StabArea/E07_StabArea_UI_Map.ts", "e07-map__tip"),
|
|
):
|
|
ts = _src(path)
|
|
assert 'tooltipBox("")' in ts and f'classList.add("{hook}")' in ts, path
|
|
for path, dead in (
|
|
(
|
|
"E03_StabSection/E03_StabSection_UI_Style.css",
|
|
"background: var(--color-surface);\n border:",
|
|
),
|
|
("E04_Stab3D/E04_Stab3D_UI_Style.css", "white-space: pre;"),
|
|
("E07_StabArea/E07_StabArea_UI_Style.css", "white-space: pre;"),
|
|
):
|
|
assert dead not in _src(path).replace("\r\n", "\n"), path
|