feat(636-2): 추천노선 메뉴 · 선 그리기 공용(map_route_preview) · 예정노선 가이드 선(지점 잇기 · 20 m · ±14 % · 급경사 문턱 = 범위 둘째 경계 · 보기만)(요구 546)
- B05 추천 미리보기 = 공용 메뉴 · 그리기 부름(저장본 넷 · 동작 그대로) - 도구 표 recommend 줄 = 두 페이지 - test_626_1 = 메뉴 검사를 공용 파일로 옮김(B05 파일이 createPickMenu 를 직접 안 부름) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/* =============================================================================
|
||||
* map_route_preview.ts
|
||||
* 공용 「추천노선」 미리보기(PLAN 626-1 → 636-2 공용 · 요구 530 · 546) — B05 평면 · 예정노선(D03)이 같이 씀.
|
||||
* 메뉴 = 도구줄 아이콘 하나(compass · 호버 「추천노선」) + 아래 판(항목 하나만 · 맨 끝 「끔」 · 부품 `createPickMenu`).
|
||||
* 그리기 = 지도 위 선(노랑 `--draw-map-sketch` · 검은 테 · 기본 점선) — 위성 · 어두운 바탕 모두 보임.
|
||||
* 항목 · 선 셈은 페이지 몫: B05 = 노선 추천 저장본 넷(`B05_Profile_UI_RouteEdit_RecPreview`) ·
|
||||
* 예정노선 = 가이드 선(`D03_PlanAnalysis_UI_Guide`).
|
||||
* ========================================================================== */
|
||||
|
||||
import { createPickMenu, type PickMenu, type PickMenuItem } from "@ui/ui_template_context_menu";
|
||||
import { themeColor } from "@ui/ui_template_palette";
|
||||
|
||||
type Vertex = [number, number] | [number, number, number];
|
||||
|
||||
/** 「추천노선」 메뉴 — 고름 = `onPick(값)` · 「끔」 = `onPick(null)`. */
|
||||
export function createRoutePreviewMenu(opts: {
|
||||
items: PickMenuItem[];
|
||||
onPick: (value: string | null) => void;
|
||||
}): PickMenu {
|
||||
const menu = createPickMenu({
|
||||
icon: "compass",
|
||||
title: "추천노선", // 마우스 글 = 공용 표 호버제목(634-2 · `mapToolGroups` 가 맞춤)
|
||||
items: opts.items,
|
||||
offLabel: "끔",
|
||||
onPick: opts.onPick,
|
||||
});
|
||||
menu.root.dataset.act = "recommend";
|
||||
return menu;
|
||||
}
|
||||
|
||||
/** 미리보기 선 하나 — 검은 테(5 px) 위 색 선(3 px) · 기본 노랑 점선. */
|
||||
export function paintRoutePreview(
|
||||
context: CanvasRenderingContext2D,
|
||||
line: readonly Vertex[],
|
||||
toScreen: (vertex: [number, number]) => [number, number],
|
||||
style: { dash?: number[]; color?: string } = {},
|
||||
): void {
|
||||
if (line.length < 2) return;
|
||||
context.save();
|
||||
context.setLineDash(style.dash ?? [10, 6]);
|
||||
context.lineJoin = "round";
|
||||
context.beginPath();
|
||||
line.forEach((vertex, index) => {
|
||||
const [x, y] = toScreen([vertex[0], vertex[1]]);
|
||||
if (index) context.lineTo(x, y);
|
||||
else context.moveTo(x, y);
|
||||
});
|
||||
// 지도 위 그림 색(테마 안 따름) + 검은 테 — 위성 · 어두운 바탕 모두 보임
|
||||
context.strokeStyle = "rgba(0, 0, 0, 0.6)";
|
||||
context.lineWidth = 5;
|
||||
context.stroke();
|
||||
context.strokeStyle = style.color ?? themeColor("--draw-map-sketch", "#ffe94d");
|
||||
context.lineWidth = 3;
|
||||
context.stroke();
|
||||
context.restore();
|
||||
}
|
||||
@@ -75,8 +75,8 @@ export const MAP_TOOLBAR: readonly (readonly MapToolRow[])[] = [
|
||||
{
|
||||
key: "recommend",
|
||||
title: "추천노선",
|
||||
does: "노선 추천 저장본 선 하나만 보기 · 끔(626-1 · 예정노선 = 636-2)",
|
||||
pages: B05,
|
||||
does: "B05 = 노선 추천 저장본 선 하나만 · 예정노선 = 가이드 선(지점 잇기 · 636-2) · 끔",
|
||||
pages: BOTH,
|
||||
},
|
||||
],
|
||||
[{ key: "terrain", title: "수치지형도/라이다", does: "지형 원천 보기 · 고르기", pages: BOTH }],
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
* 선 = 노선 추천 저장본(`B05_Profile/route_recommend/` · 지금 설계 지형 · 체인이 미리 셈) 그대로 — 다시 셈 · 쓰기 없음 ·
|
||||
* 저장본 없으면(409) 알림 · 끔. 옛 추천 모달 · 좌측 「노선 추천」 칸 · [적용](`as_initial`) 은 걷음(서버 길 · 시험은 남김).
|
||||
* 부르는 곳 = `_Chrome`(도구줄 `recommend` 칸) · `_RouteEdit`(그리기 `overStations` 앞).
|
||||
* 메뉴 · 선 그리기 = 공용 `A00_Common/map/map_route_preview`(636-2 · 예정노선 가이드 선과 같이 씀).
|
||||
* ========================================================================== */
|
||||
|
||||
import { createPickMenu } from "@ui/ui_template_context_menu";
|
||||
import { showToast } from "@ui/ui_template_elements";
|
||||
import { themeColor } from "@ui/ui_template_palette";
|
||||
import { createRoutePreviewMenu, paintRoutePreview } from "../A00_Common/map/map_route_preview";
|
||||
import type { AnalysisTerrain } from "./B05_Profile_Api_Replan";
|
||||
import { fetchRouteInitial, type RouteInitialLogic } from "./B05_Profile_Api_RouteInitial";
|
||||
import { LOGIC_LABEL } from "./B05_Profile_UI_RouteEdit_Initial_Model";
|
||||
@@ -41,14 +41,10 @@ export function createRecPreview(opts: {
|
||||
const lines = new Map<string, Vertex[]>();
|
||||
let shown: Vertex[] | null = null;
|
||||
let turn = 0;
|
||||
const menu = createPickMenu({
|
||||
icon: "compass",
|
||||
title: "추천노선", // 마우스 글 = 공용 표 호버제목(634-2 · `mapToolGroups` 가 맞춤)
|
||||
const menu = createRoutePreviewMenu({
|
||||
items: MODES.map((mode) => ({ value: mode, label: LOGIC_LABEL[mode] })),
|
||||
offLabel: "끔",
|
||||
onPick: (mode) => void pick(mode as RouteInitialLogic | null),
|
||||
});
|
||||
menu.root.dataset.act = "recommend";
|
||||
|
||||
async function pick(mode: RouteInitialLogic | null): Promise<void> {
|
||||
const mine = ++turn;
|
||||
@@ -81,24 +77,7 @@ export function createRecPreview(opts: {
|
||||
return {
|
||||
root: menu.root,
|
||||
draw(context, toScreen) {
|
||||
if (!shown) return;
|
||||
context.save();
|
||||
context.setLineDash([10, 6]);
|
||||
context.lineJoin = "round";
|
||||
context.beginPath();
|
||||
shown.forEach((vertex, index) => {
|
||||
const [x, y] = toScreen(vertex);
|
||||
if (index) context.lineTo(x, y);
|
||||
else context.moveTo(x, y);
|
||||
});
|
||||
// 지도 위 그림 색(테마 안 따름) + 검은 테 — 위성 · 어두운 바탕 모두 보임
|
||||
context.strokeStyle = "rgba(0, 0, 0, 0.6)";
|
||||
context.lineWidth = 5;
|
||||
context.stroke();
|
||||
context.strokeStyle = themeColor("--draw-map-sketch", "#ffe94d");
|
||||
context.lineWidth = 3;
|
||||
context.stroke();
|
||||
context.restore();
|
||||
if (shown) paintRoutePreview(context, shown, toScreen);
|
||||
},
|
||||
state: () => ({ mode: menu.value(), points: shown?.length ?? 0 }),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/* =============================================================================
|
||||
* D03_PlanAnalysis_UI_Guide.ts
|
||||
* 예정노선 가이드 추천선 화면(PLAN 636-2 · 요구 546) — 도구줄 「추천노선」 = 「가이드 선(지점 잇기)」 · 「끔」.
|
||||
* 지점 = 지점 목록 차례 · 지점 둘 이상일 때만 열림 · 높이 = 지형 칩 원천 격자 · 걸음 20 m · 기울기 ±14 % 고정(사용자 원문 ·
|
||||
* 그리기 칸 값과 따로) · 급경사 문턱 = 급경사 범위 둘째 경계(기본 70 %).
|
||||
* 셈 = 공용 `map_guide_route`(브라우저만 · 저장 안 함) · 켠 동안 지점 · 지형 · 급경사 범위가 바뀌면 다시 셈(끌기 중엔 멈춘 뒤 한 번).
|
||||
* 그림 = 닿은 선 노랑 실선 · 못 닿은 구간 = 간 데 → 다음 지점 빨강 점선 + 알림 · 끝 지점 옆 짧은 글(길이 · 최대 기울기 · 급경사 %).
|
||||
* 메뉴 · 선 그리기 = 공용 `map_route_preview`(B05 추천 미리보기와 같음).
|
||||
* ========================================================================== */
|
||||
|
||||
import { showToast } from "@ui/ui_template_elements";
|
||||
import { mapToolTip } from "@ui/ui_template_map_tools";
|
||||
import { themeColor } from "@ui/ui_template_palette";
|
||||
import {
|
||||
guideRoute,
|
||||
guideTerrainFromGrid,
|
||||
type GuideResult,
|
||||
} from "../A00_Common/map/logic/map_guide_route";
|
||||
import { createRoutePreviewMenu, paintRoutePreview } from "../A00_Common/map/map_route_preview";
|
||||
import { readSlopeBands } from "../A00_Common/map/map_slope_bands";
|
||||
import type { DtmGrid } from "../common_util/common_util_surface_sampler";
|
||||
import { planLabel } from "./D03_PlanAnalysis_UI_DrawPaint";
|
||||
import type { PlanMapLayer } from "./D03_PlanAnalysis_UI_Map";
|
||||
|
||||
type Vertex = [number, number];
|
||||
|
||||
/** 사용자 원문(요구 546) — 걸음 · 기울기 고정. */
|
||||
const GUIDE_STEP_M = 20;
|
||||
const GUIDE_MAX_GRADE = 0.14;
|
||||
/** 끌기 · 연이은 고침이 멈춘 뒤 셈(ms). */
|
||||
const SETTLE_MS = 150;
|
||||
|
||||
export interface GuideState {
|
||||
on: boolean;
|
||||
open: boolean;
|
||||
segments: number;
|
||||
reached: number;
|
||||
lengthM: number;
|
||||
maxGradePct: number;
|
||||
steepPct: number;
|
||||
steepLimitPct: number;
|
||||
ms: number | null;
|
||||
}
|
||||
|
||||
/** 급경사 문턱 = 범위 경계를 작은 차례로 늘어놓은 둘째(기본 26.8 · 70 · … → 70). */
|
||||
function steepLimit(): number {
|
||||
const edges = [...new Set(readSlopeBands().flatMap((band) => [band.low, band.high]))];
|
||||
edges.sort((a, b) => a - b);
|
||||
return edges[1] ?? edges[0] ?? 70;
|
||||
}
|
||||
|
||||
export function createPlanGuide(opts: {
|
||||
points: () => Vertex[];
|
||||
/** 지형 칩 원천 격자(없으면 null). */
|
||||
grid: () => DtmGrid | null;
|
||||
redraw: () => void;
|
||||
}): { root: HTMLElement; layer: PlanMapLayer; state: () => GuideState } {
|
||||
let result: GuideResult | null = null;
|
||||
let shownFor: Vertex[] = [];
|
||||
let doneKey = "";
|
||||
let doneGrid: DtmGrid | null = null;
|
||||
let steepPct = 70;
|
||||
let timer = 0;
|
||||
let open: boolean | null = null;
|
||||
const menu = createRoutePreviewMenu({
|
||||
items: [{ value: "guide", label: "가이드 선(지점 잇기)" }],
|
||||
onPick: (value) => {
|
||||
[result, doneKey, doneGrid] = [null, "", null];
|
||||
if (value) compute(true);
|
||||
opts.redraw();
|
||||
},
|
||||
});
|
||||
const on = (): boolean => menu.value() === "guide";
|
||||
|
||||
function compute(picked = false): void {
|
||||
window.clearTimeout(timer);
|
||||
const points = opts.points().map(([x, y]) => [x, y] as Vertex);
|
||||
const grid = opts.grid();
|
||||
if (!grid) {
|
||||
menu.set(null);
|
||||
result = null;
|
||||
showToast("가이드 선 — 지형이 아직 없음", "info");
|
||||
return opts.redraw();
|
||||
}
|
||||
steepPct = steepLimit();
|
||||
[doneKey, doneGrid] = [JSON.stringify([points, steepPct]), grid];
|
||||
result = guideRoute(points, guideTerrainFromGrid(grid), {
|
||||
step: GUIDE_STEP_M,
|
||||
maxGrade: GUIDE_MAX_GRADE,
|
||||
steepPct,
|
||||
});
|
||||
shownFor = points;
|
||||
const missed = result.segments.filter((segment) => !segment.reached).length;
|
||||
if (missed) showToast(`가이드 선 — ${missed} 구간 ±14 % 로 못 닿음(점선 = 곧은 이음)`, "info");
|
||||
else if (picked) showToast(`가이드 선 — ${Math.round(result.ms)} ms`, "info");
|
||||
opts.redraw();
|
||||
}
|
||||
|
||||
/** 지점 둘 미만 = 막힘(켜져 있으면 끔). */
|
||||
function syncOpen(count: number): void {
|
||||
const next = count >= 2;
|
||||
if (next === open) return;
|
||||
open = next;
|
||||
menu.root.disabled = !next;
|
||||
mapToolTip(menu.root, next ? null : "지점 둘 이상일 때");
|
||||
if (!next && on()) {
|
||||
menu.set(null);
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
|
||||
function summary(): Omit<GuideState, "on" | "open"> {
|
||||
const segments = result?.segments ?? [];
|
||||
const lengthM = segments.reduce((sum, segment) => sum + segment.length, 0);
|
||||
const steep = segments.reduce((sum, segment) => sum + segment.steepRatio * segment.length, 0);
|
||||
return {
|
||||
segments: segments.length,
|
||||
reached: segments.filter((segment) => segment.reached).length,
|
||||
lengthM,
|
||||
maxGradePct: Math.max(0, ...segments.map((segment) => segment.maxGrade)) * 100,
|
||||
steepPct: lengthM ? (steep / lengthM) * 100 : 0,
|
||||
steepLimitPct: steepPct,
|
||||
ms: result ? result.ms : null,
|
||||
};
|
||||
}
|
||||
|
||||
const layer: PlanMapLayer = {
|
||||
draw(context, map) {
|
||||
const points = opts.points();
|
||||
syncOpen(points.length);
|
||||
if (!on()) return;
|
||||
const key = JSON.stringify([points.map(([x, y]) => [x, y]), steepLimit()]);
|
||||
if (key !== doneKey || opts.grid() !== doneGrid) {
|
||||
window.clearTimeout(timer);
|
||||
timer = window.setTimeout(() => compute(), SETTLE_MS);
|
||||
}
|
||||
if (!result) return;
|
||||
const warn = themeColor("--draw-map-warn", "#ff5a4d");
|
||||
result.segments.forEach((segment, index) => {
|
||||
paintRoutePreview(context, segment.vertices, map.toScreen, { dash: [] });
|
||||
const goal = shownFor[index + 1];
|
||||
const last = segment.vertices[segment.vertices.length - 1] ?? shownFor[index];
|
||||
if (!segment.reached && goal && last)
|
||||
paintRoutePreview(context, [last, goal], map.toScreen, { color: warn });
|
||||
});
|
||||
const end = shownFor[shownFor.length - 1];
|
||||
if (!end) return;
|
||||
const s = summary();
|
||||
const [x, y] = map.toScreen(end);
|
||||
const text = `가이드 ${(s.lengthM / 1000).toFixed(2)} km · 최대 ${s.maxGradePct.toFixed(1)} % · 급경사 지남 ${s.steepPct.toFixed(0)} %`;
|
||||
planLabel(context, map, x, y - 22, text, "bold 12px");
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
root: menu.root,
|
||||
layer,
|
||||
state: () => ({ on: on(), open: !!open, ...summary() }),
|
||||
};
|
||||
}
|
||||
@@ -124,6 +124,8 @@ export interface PlanMapEvents {
|
||||
onMeasure: () => void;
|
||||
/** 도구줄 끝 지형 칸(624-4 · 칩 + LAS 올리기 = `D03_PlanAnalysis_UI_LidarUpload`). */
|
||||
terrain: HTMLElement;
|
||||
/** 도구줄 「추천노선」 = 가이드 선 메뉴(636-2 · `D03_PlanAnalysis_UI_Guide`). */
|
||||
recommend?: HTMLElement;
|
||||
}
|
||||
|
||||
export interface PlanTools {
|
||||
@@ -248,6 +250,7 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
|
||||
slopes: markButton("slopes"),
|
||||
measure: measureButton,
|
||||
terrain: events.terrain,
|
||||
recommend: events.recommend,
|
||||
}),
|
||||
});
|
||||
const toolbar = tools.root;
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
type TerrainStart,
|
||||
} from "./D03_PlanAnalysis_Api";
|
||||
import { openPlan3d } from "./D03_PlanAnalysis_UI_3D";
|
||||
import { createPlanGuide } from "./D03_PlanAnalysis_UI_Guide";
|
||||
import { bindPlanInput, createPlanKit } from "./D03_PlanAnalysis_UI_Kit";
|
||||
import { createPlanLayers } from "./D03_PlanAnalysis_UI_Layers";
|
||||
import { createPlanTerrain } from "./D03_PlanAnalysis_UI_LidarUpload";
|
||||
@@ -210,8 +211,15 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
|
||||
work,
|
||||
onChange: () => (void backdrop.reterrain(), refresh()),
|
||||
});
|
||||
// 가이드 추천선(636-2) — 지점 차례 · 지형 칩 원천 · 보기만
|
||||
const guide = createPlanGuide({
|
||||
points: () => points,
|
||||
grid: () => terrain.surface(surface)?.grid ?? null,
|
||||
redraw: () => map.redraw(),
|
||||
});
|
||||
const map = createPlanMap({
|
||||
terrain: terrain.root,
|
||||
recommend: guide.root,
|
||||
onUndo: undo,
|
||||
onRedo: redo,
|
||||
on3d: open3d,
|
||||
@@ -314,7 +322,10 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
|
||||
|
||||
/** 겹 차례 = 키트(바탕 겹 아래 · 재기 · 보기 가운데 = 고르기 위 · 핀 그림 · 우클릭 메뉴 맨 위). */
|
||||
const layerList = (): PlanMapLayer[] =>
|
||||
kit.layers(backdrop.layers, [...(measuring ? [measure.layer] : []), viewWatch]);
|
||||
kit.layers(
|
||||
[...backdrop.layers, guide.layer],
|
||||
[...(measuring ? [measure.layer] : []), viewWatch],
|
||||
);
|
||||
|
||||
/** 점들이 받은 범위 안인가. */
|
||||
const inside = (list: Vertex[]): boolean =>
|
||||
@@ -568,6 +579,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
|
||||
heightAt: z,
|
||||
layers: backdrop.state,
|
||||
terrain: terrain.state,
|
||||
guide: guide.state,
|
||||
};
|
||||
// 키 = 등록부(588-3 · 한글 자판도 자판 자리로) · 되돌리기 · 다시하기 · 재기 끔 = 키트 `onKey` 먼저
|
||||
const onKey = (event: KeyboardEvent): void => {
|
||||
|
||||
@@ -42,7 +42,9 @@ def test_미리보기_넷_하나만_저장본만() -> None:
|
||||
src = read(B05 / "B05_Profile_UI_RouteEdit_RecPreview.ts")
|
||||
modes = re.search(r"const MODES = \[(.*?)\]", src, re.S).group(1)
|
||||
assert re.findall(r'"(\w+)"', modes) == ["gradeline", "earthwork", "gentle", "weighted"]
|
||||
assert "recompute" not in src and "createPickMenu({" in src and 'offLabel: "끔"' in src
|
||||
assert "recompute" not in src and "createRoutePreviewMenu({" in src
|
||||
shared = read(ROOT / "A00_Common" / "map" / "map_route_preview.ts") # 636-2 공용 메뉴 · 그리기
|
||||
assert "createPickMenu({" in shared and 'offLabel: "끔"' in shared
|
||||
assert "terrain: opts.terrain()" in src # 지금 설계 지형 저장본(D7)
|
||||
chrome = read(B05 / "B05_Profile_UI_RouteEdit_Chrome.ts")
|
||||
assert "recommend: preview.root" in chrome and "terrain: terrain.root" in chrome
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""PLAN 636-2 — 「추천노선」 메뉴 · 선 그리기 공용 · 예정노선 가이드 선(요구 546).
|
||||
|
||||
- 공용 `map_route_preview` = 메뉴(compass · 「끔」 · data-act) + 노랑 · 검은 테 선 — B05 · 예정노선이 같이 씀.
|
||||
- 도구 표 `recommend` 줄 = 두 페이지 · 호버 「추천노선」.
|
||||
- 예정노선 = 「가이드 선(지점 잇기)」 하나 · 20 m · ±14 % 고정 · 급경사 문턱 = 범위 둘째 경계 · 저장 · 서버 없음.
|
||||
- B05 = 저장본 넷 그대로 · 가이드 항목 없음.
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_636_2_guide_screen.py -q
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
read = lambda *parts: ROOT.joinpath(*parts).read_text(encoding="utf-8") # noqa: E731
|
||||
|
||||
|
||||
def test_공용_메뉴_그리기() -> None:
|
||||
src = read("A00_Common", "map", "map_route_preview.ts")
|
||||
assert 'icon: "compass"' in src and 'offLabel: "끔"' in src
|
||||
assert 'dataset.act = "recommend"' in src and "--draw-map-sketch" in src
|
||||
for name in (
|
||||
"B05_Profile/B05_Profile_UI_RouteEdit_RecPreview.ts",
|
||||
"D03_PlanAnalysis/D03_PlanAnalysis_UI_Guide.ts",
|
||||
):
|
||||
page = read(name)
|
||||
assert "createRoutePreviewMenu({" in page and "paintRoutePreview(" in page, name
|
||||
assert "createPickMenu" not in page, name
|
||||
|
||||
|
||||
def test_도구_표_두_페이지() -> None:
|
||||
src = read("A00_Common", "map", "map_toolbar.ts")
|
||||
row = src[src.index('key: "recommend"') :]
|
||||
row = row[: row.index("}")]
|
||||
assert 'title: "추천노선"' in row and "pages: BOTH" in row
|
||||
assert "recommend: events.recommend" in read("D03_PlanAnalysis", "D03_PlanAnalysis_UI_Map.ts")
|
||||
|
||||
|
||||
def test_예정노선_가이드_고정값_보기만() -> None:
|
||||
src = read("D03_PlanAnalysis", "D03_PlanAnalysis_UI_Guide.ts")
|
||||
assert "GUIDE_STEP_M = 20" in src and "GUIDE_MAX_GRADE = 0.14" in src
|
||||
assert 'label: "가이드 선(지점 잇기)"' in src
|
||||
assert "guideRoute(" in src and "guideTerrainFromGrid(" in src and "readSlopeBands()" in src
|
||||
assert "count >= 2" in src # 지점 둘 이상일 때만
|
||||
for banned in ("fetch(", "writeState", "sessionStorage", "constraint"):
|
||||
assert banned not in src, banned
|
||||
page = read("D03_PlanAnalysis", "D03_PlanAnalysis_UI_Page.ts")
|
||||
assert (
|
||||
"recommend: guide.root" in page and "guide.layer" in page and "guide: guide.state" in page
|
||||
)
|
||||
assert len(page.splitlines()) <= 700
|
||||
|
||||
|
||||
def test_B05_저장본_넷_그대로() -> None:
|
||||
src = read("B05_Profile", "B05_Profile_UI_RouteEdit_RecPreview.ts")
|
||||
assert '"gradeline",\n "earthwork",\n "gentle",\n "weighted",' in src.replace("\r\n", "\n")
|
||||
assert "가이드 선(지점 잇기)" not in src and "guideRoute" not in src
|
||||
Reference in New Issue
Block a user