feat(b07): 311-2 평면도 · 종단도 · 횡단도 기본 접힘 · 다시 세워도 접힘 이어 받음 · 첫 장 표지(기억한 장 있으면 그 장) · 시험 (요구 204)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
buildCrossLayoutControls,
|
||||
buildDesignInfoPanel,
|
||||
buildDrawingSidePanel,
|
||||
foldedGroups,
|
||||
isCrossSheet,
|
||||
} from "./B07_DesignDetail_UI_Panels";
|
||||
import { workflowSteps } from "../A00_Common/b_page_scaffold";
|
||||
@@ -91,6 +92,8 @@ let reopenCrossSheet: number | undefined;
|
||||
|
||||
export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
|
||||
const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY);
|
||||
// 311 다시 세우면 옛 목록 접힘 그대로 — 기다리기 전에 읽음(처음 들어오면 B07 목록이 없어 기본값)
|
||||
const folded = foldedGroups(root);
|
||||
let workflowState: WorkflowState | undefined;
|
||||
let drawings: DesignDrawingItem[] = [];
|
||||
let drawingError: string | undefined;
|
||||
@@ -565,6 +568,7 @@ export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
|
||||
}
|
||||
: undefined,
|
||||
crossLayoutSlot,
|
||||
folded,
|
||||
);
|
||||
drawingListEl = drawingPanel;
|
||||
const confirmActions = document.createElement("div");
|
||||
@@ -608,7 +612,7 @@ export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
|
||||
layout.root.classList.add("b07-design-layout");
|
||||
root.replaceChildren(layout.root);
|
||||
|
||||
// 페이지 진입 시 첫 도면(종단도)을 자동 선택 — 빈 CAD 화면 방지.
|
||||
// 페이지 진입 시 첫 도면(표지 · 311)을 자동 선택 — 빈 CAD 화면 방지.
|
||||
// CAD가 아직 준비 전이면 cad.load 가 붙들고 있다가 ready 때 보낸다.
|
||||
if (drawings.length > 0) {
|
||||
const crossSheets = drawings.filter(isCrossSheet);
|
||||
@@ -618,7 +622,9 @@ export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
|
||||
const reopen = drawings.findIndex((item) => item.id === reopenId);
|
||||
reopenDrawingId = undefined;
|
||||
reopenCrossSheet = undefined;
|
||||
const first = Math.max(reopen, 0);
|
||||
// 311 기억한 장이 없으면 표지
|
||||
const cover = drawings.findIndex((item) => item.kind === "cover");
|
||||
const first = reopen >= 0 ? reopen : Math.max(cover, 0);
|
||||
void openTab(drawings[first]).then(() => {
|
||||
// 첫 장을 띄운 뒤에 나머지를 받는다 — 진입 속도를 뺏지 않는다.
|
||||
void prefetchAllDrawings();
|
||||
|
||||
@@ -39,6 +39,19 @@ export const DRAWING_GROUPS: readonly {
|
||||
{ label: "토적도(유토곡선)", kind: "mass_haul" },
|
||||
];
|
||||
|
||||
/** 311 기본 접힘 묶음 — 그 밖 묶음(토적도 등)은 기본 펼침. */
|
||||
const FOLDED_KINDS = new Set(["plan", "longitudinal", "cross"]);
|
||||
|
||||
/** 311 지금 목록의 묶음 접힘(kind → 접힘) — 화면을 다시 세울 때 이어 받음 · B07 목록이 없으면 빈 것. */
|
||||
export function foldedGroups(root: ParentNode): Map<string, boolean> {
|
||||
return new Map(
|
||||
[...root.querySelectorAll<HTMLElement>(".b07-drawing-group")].map((section) => [
|
||||
section.dataset.kind ?? "",
|
||||
section.classList.contains("is-collapsed"),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
/** B06 확정 산출물 기반 도면 목록 패널. */
|
||||
export function buildDrawingSidePanel(
|
||||
drawings: DesignDrawingItem[],
|
||||
@@ -48,7 +61,10 @@ export function buildDrawingSidePanel(
|
||||
plan?: PlanManageControls,
|
||||
/** 횡단도 묶음 맨 아래에 붙일 자리(153-1 장 배치 칸) — 내용은 화면 조립이 도면을 열 때마다 채움. */
|
||||
crossTools?: HTMLElement,
|
||||
/** 311 다시 세울 때 옛 접힘(`foldedGroups`) — 없는 묶음은 기본값. */
|
||||
folded = new Map<string, boolean>(),
|
||||
): HTMLDivElement {
|
||||
const isFolded = (kind: string): boolean => folded.get(kind) ?? FOLDED_KINDS.has(kind);
|
||||
const panel = document.createElement("div");
|
||||
panel.className = "b07-drawing-list";
|
||||
const heading = document.createElement("div");
|
||||
@@ -95,7 +111,12 @@ export function buildDrawingSidePanel(
|
||||
// 종류를 다 지워도 묶음은 남긴다 — 그 안에서 다시 더한다.
|
||||
const plans = drawings.filter((item) => item.kind === "plan");
|
||||
panel.append(
|
||||
buildPlanSection(plans, drawingButton, plan ? () => openPlanManager(plan) : undefined),
|
||||
buildPlanSection(
|
||||
plans,
|
||||
drawingButton,
|
||||
plan ? () => openPlanManager(plan) : undefined,
|
||||
isFolded("plan"),
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -133,7 +154,7 @@ export function buildDrawingSidePanel(
|
||||
continue;
|
||||
}
|
||||
// 컨테이너 = 좌측 패널 공용 카드(269 · B05/B06 「구조물 배치」 양식).
|
||||
const section = createPanelCard(`${group.label} ${items.length}`);
|
||||
const section = createPanelCard(`${group.label} ${items.length}`, isFolded(group.kind ?? ""));
|
||||
section.classList.add("b07-drawing-group");
|
||||
if (group.kind) section.dataset.kind = group.kind;
|
||||
if (cross) {
|
||||
|
||||
@@ -257,9 +257,10 @@ export function buildPlanSection(
|
||||
items: DesignDrawingItem[],
|
||||
makeButton: (drawing: DesignDrawingItem, label: string) => HTMLButtonElement,
|
||||
onManage?: () => void,
|
||||
collapsed = false,
|
||||
): HTMLElement {
|
||||
// 컨테이너 = 좌측 패널 공용 카드(269)
|
||||
const section = createPanelCard(`평면도 ${items.length}`);
|
||||
const section = createPanelCard(`평면도 ${items.length}`, collapsed);
|
||||
section.classList.add("b07-drawing-group", "b07-plan");
|
||||
section.dataset.kind = "plan";
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/* 311 좌측 패널 기본 접힘 헬퍼 — B05 「계획노선」 · B06 「횡단 보기 설정」(한 벌 패널 `design_panel`)과
|
||||
* B07 도면 목록(평면도 · 종단도 · 횡단도)을 가짜 DOM 위에 세워 첫 그림 접힘 · 편 뒤 다시 그림 유지를 본다.
|
||||
* 결과는 JSON 한 벌. 꼴은 `helper_design_view_parity.cjs` 와 같음.
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "..");
|
||||
const cache = new Map();
|
||||
|
||||
// ── 화면 그릇 흉내 — 요소 · 클래스 · 속성 · 글자 · 자식 ──────────────────────────
|
||||
class FakeNode {
|
||||
constructor(tag) {
|
||||
this.tagName = tag;
|
||||
this.children = [];
|
||||
this.parent = null;
|
||||
this.dataset = {};
|
||||
this.style = {};
|
||||
this.attrs = {};
|
||||
this._text = "";
|
||||
this._classes = new Set();
|
||||
this.hidden = false;
|
||||
this.disabled = false;
|
||||
this.title = "";
|
||||
this.listeners = {};
|
||||
const self = this;
|
||||
this.classList = {
|
||||
add: (...n) => n.forEach((x) => self._classes.add(x)),
|
||||
remove: (...n) => n.forEach((x) => self._classes.delete(x)),
|
||||
toggle: (n, f) => {
|
||||
const on = f === undefined ? !self._classes.has(n) : !!f;
|
||||
on ? self._classes.add(n) : self._classes.delete(n);
|
||||
return on;
|
||||
},
|
||||
contains: (n) => self._classes.has(n),
|
||||
};
|
||||
}
|
||||
set className(v) {
|
||||
this._classes = new Set(String(v).split(/\s+/).filter(Boolean));
|
||||
}
|
||||
get className() {
|
||||
return [...this._classes].join(" ");
|
||||
}
|
||||
set textContent(v) {
|
||||
this._text = String(v);
|
||||
this.children.forEach((c) => (c.parent = null));
|
||||
this.children = [];
|
||||
}
|
||||
get textContent() {
|
||||
return this._text + this.children.map((c) => c.textContent).join("");
|
||||
}
|
||||
_adopt(kid) {
|
||||
const node =
|
||||
typeof kid === "string" ? Object.assign(new FakeNode("#text"), { _text: kid }) : kid;
|
||||
if (node.parent) node.parent.children = node.parent.children.filter((c) => c !== node);
|
||||
node.parent = this;
|
||||
return node;
|
||||
}
|
||||
append(...kids) {
|
||||
kids.forEach((k) => this.children.push(this._adopt(k)));
|
||||
}
|
||||
prepend(...kids) {
|
||||
this.children.unshift(...kids.map((k) => this._adopt(k)));
|
||||
}
|
||||
insertBefore(node, ref) {
|
||||
const adopted = this._adopt(node);
|
||||
const at = this.children.indexOf(ref);
|
||||
if (at < 0) this.children.push(adopted);
|
||||
else this.children.splice(at, 0, adopted);
|
||||
}
|
||||
replaceChildren(...kids) {
|
||||
this.children = [];
|
||||
this.append(...kids);
|
||||
}
|
||||
setAttribute(k, v) {
|
||||
this.attrs[k] = String(v);
|
||||
}
|
||||
removeAttribute(k) {
|
||||
delete this.attrs[k];
|
||||
if (k === "title") this.title = "";
|
||||
}
|
||||
addEventListener(type, handler) {
|
||||
(this.listeners[type] ||= []).push(handler);
|
||||
}
|
||||
click() {
|
||||
if (this.disabled) return;
|
||||
(this.listeners.click || []).forEach((h) => h({ target: this }));
|
||||
}
|
||||
querySelector(selector) {
|
||||
return this.querySelectorAll(selector)[0] ?? null;
|
||||
}
|
||||
querySelectorAll(selector) {
|
||||
const cls = selector.slice(1);
|
||||
const out = [];
|
||||
const walk = (n) =>
|
||||
n.children.forEach((k) => {
|
||||
if (k.classList && k.classList.contains(cls)) out.push(k);
|
||||
walk(k);
|
||||
});
|
||||
walk(this);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
// 공용 attachCollapsible 이 쓰는 closest(`.클래스` 하나)
|
||||
FakeNode.prototype.closest = function (selector) {
|
||||
for (let n = this; n; n = n.parent)
|
||||
if (n.classList && n.classList.contains(selector.slice(1))) return n;
|
||||
return null;
|
||||
};
|
||||
globalThis.document = { createElement: (tag) => new FakeNode(tag) };
|
||||
/** 제목 누름 — 패널 뿌리에 걸린 공용 위임 손잡이로 올려 보냄(가짜 DOM 은 거품이 없음). */
|
||||
const press = (root, section) => {
|
||||
const target = section.querySelector(".ui-collapsible__title");
|
||||
(root.listeners.click || []).forEach((h) => h({ target }));
|
||||
};
|
||||
|
||||
const node = (tag = "div") => new FakeNode(tag);
|
||||
const STUBS = {
|
||||
"@ui/ui_template_locale": {
|
||||
ui_locales: new Proxy({}, { get: (_, k) => [String(k)] }),
|
||||
currentLanguageIndex: 0,
|
||||
},
|
||||
"@ui/ui_template_elements": {
|
||||
createButton: ({ label }) => Object.assign(node("button"), { _text: label }),
|
||||
createInputField: () => ({ root: node(), input: node("input") }),
|
||||
createSelectField: () => ({ root: node(), select: { value: "" } }),
|
||||
showToast: () => undefined,
|
||||
},
|
||||
"@ui/ui_template_checkbox": { createCheckbox: () => ({ root: node("label"), input: {} }) },
|
||||
"../B05_Profile/B05_Profile_UI_Structures_Fields": { stationFields: () => ({ wrap: node() }) },
|
||||
"./B07_DesignDetail_UI_PlanManage": { openPlanManager: () => undefined },
|
||||
"./B07_DesignDetail_Api_Fetch": {},
|
||||
"./B06_Section_UI_Page_Common": { L: (key) => key },
|
||||
};
|
||||
|
||||
function loadTs(file) {
|
||||
const full = path.resolve(file);
|
||||
if (cache.has(full)) return cache.get(full);
|
||||
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const box = { exports: {} };
|
||||
cache.set(full, box.exports);
|
||||
const localRequire = (request) => {
|
||||
if (STUBS[request]) return STUBS[request];
|
||||
if (request === "@ui/ui_template_collapsible")
|
||||
return loadTs(path.join(ROOT, "ui_template", "ui_template_collapsible.ts"));
|
||||
if (request.endsWith(".css")) return {};
|
||||
if (request.startsWith(".")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`);
|
||||
return require(request);
|
||||
};
|
||||
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
||||
cache.set(full, box.exports);
|
||||
return box.exports;
|
||||
}
|
||||
|
||||
const out = {};
|
||||
const titleOf = (n) => n.children.find((c) => c.tagName === "h3")?.textContent ?? "";
|
||||
const folds = (root) =>
|
||||
Object.fromEntries(
|
||||
root
|
||||
.querySelectorAll(".ui-collapsible")
|
||||
.map((s) => [titleOf(s), s.classList.contains("is-collapsed")]),
|
||||
);
|
||||
|
||||
// ── B05 · B06 한 벌 패널 ───────────────────────────────────────────────────────
|
||||
const design = loadTs(path.join(ROOT, "A00_Common", "design", "design_panel.ts"));
|
||||
loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Panel_Sections.ts"));
|
||||
loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Page_Panel.ts"));
|
||||
const text = (t) => Object.assign(node(), { _text: t });
|
||||
const run = { run: () => undefined };
|
||||
const panel = design.createDesignPanel(
|
||||
{
|
||||
projectId: "p",
|
||||
parts: {
|
||||
structures: { root: node("section"), listRoot: node() },
|
||||
settings: { nodes: [text("a")] },
|
||||
route: {
|
||||
plannedRouteButton: text("plan"),
|
||||
selectedNodes: [text("sel")],
|
||||
bindSelectedRoot: () => undefined,
|
||||
},
|
||||
crossView: { row: text("cross-row") },
|
||||
standardSlot: text("std"),
|
||||
},
|
||||
dock: { reset: run, save: run, confirm: run, switchView: run },
|
||||
},
|
||||
"profile",
|
||||
);
|
||||
const byTitle = (title) =>
|
||||
panel.root.querySelectorAll(".ui-collapsible").find((s) => titleOf(s) === title);
|
||||
out.profileFirst = folds(panel.root);
|
||||
const route = byTitle("계획노선");
|
||||
press(panel.root, route); // 사용자가 폄(공용 attachCollapsible)
|
||||
panel.setView("cross");
|
||||
out.crossFirst = folds(panel.root);
|
||||
press(panel.root, byTitle("B06_View_Title"));
|
||||
panel.setView("profile");
|
||||
panel.setView("cross");
|
||||
out.afterRedraw = folds(panel.root);
|
||||
out.sameRouteNode = byTitle("계획노선") === route;
|
||||
|
||||
// ── B07 도면 목록 ─────────────────────────────────────────────────────────────
|
||||
const b07 = loadTs(path.join(ROOT, "B07_DesignDetail", "B07_DesignDetail_UI_Panels.ts"));
|
||||
const d = (id, kind) => ({ id, kind, label: id, confirmed: false });
|
||||
const drawings = [
|
||||
d("longitudinal", "longitudinal"),
|
||||
d("longitudinal_2", "longitudinal"),
|
||||
d("cross_1", "cross"),
|
||||
d("cross_2", "cross"),
|
||||
d("plan_a", "plan"),
|
||||
d("cover", "cover"),
|
||||
d("cross_standard", "cross_standard"),
|
||||
d("mass_haul", "mass_haul"),
|
||||
d("mass_haul_2", "mass_haul"),
|
||||
];
|
||||
const groups = (p) =>
|
||||
Object.fromEntries(
|
||||
p
|
||||
.querySelectorAll(".b07-drawing-group")
|
||||
.map((s) => [s.dataset.kind, s.classList.contains("is-collapsed")]),
|
||||
);
|
||||
const fresh = node();
|
||||
out.b07EntryFolded = [...b07.foldedGroups(fresh)];
|
||||
const first = b07.buildDrawingSidePanel(
|
||||
drawings,
|
||||
() => undefined,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
b07.foldedGroups(fresh),
|
||||
);
|
||||
out.b07First = groups(first);
|
||||
const host = node();
|
||||
host.append(first);
|
||||
first
|
||||
.querySelectorAll(".b07-drawing-group")
|
||||
.find((s) => s.dataset.kind === "cross")
|
||||
.classList.remove("is-collapsed");
|
||||
first
|
||||
.querySelectorAll(".b07-drawing-group")
|
||||
.find((s) => s.dataset.kind === "mass_haul")
|
||||
.classList.add("is-collapsed");
|
||||
const again = b07.buildDrawingSidePanel(
|
||||
drawings,
|
||||
() => undefined,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
b07.foldedGroups(host),
|
||||
);
|
||||
out.b07Redraw = groups(again);
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
@@ -82,8 +82,8 @@ def test_공용_카드_함수():
|
||||
|
||||
|
||||
def test_B07_컨테이너_공용_카드_도면_단추_공용_꼴():
|
||||
assert "createPanelCard(`${group.label} ${items.length}`)" in B07_PANELS
|
||||
assert "createPanelCard(`평면도 ${items.length}`)" in B07_PLAN
|
||||
assert "createPanelCard(`${group.label} ${items.length}`, isFolded(" in B07_PANELS
|
||||
assert "createPanelCard(`평면도 ${items.length}`, collapsed)" in B07_PLAN
|
||||
for src in (B07_PANELS, B07_PLAN):
|
||||
assert "ui-collapsible ui-sidebar-section" not in src # 손으로 짠 카드 없음
|
||||
assert B07_PANELS.count('"b07-drawing-button ui-panel-btn"') == 2
|
||||
@@ -149,7 +149,7 @@ def test_B05_칸_틀_두_보기_모두_공용_양식():
|
||||
|
||||
def test_B08_B09_카드_공용_카드():
|
||||
assert "createPanelCard(L(title))" in B08_OPTIONS
|
||||
assert 'createPanelCard("데이터 점검")' in B09_SHELL
|
||||
assert 'createPanelCard("데이터 점검", true)' in B09_SHELL
|
||||
for src in (B08_OPTIONS, B09_SHELL):
|
||||
assert "ui-sidebar-card" not in src
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""311 좌측 패널 기본 접힘 · B07 첫 쪽 = 표지 (요구 204).
|
||||
|
||||
못박는 것
|
||||
- 기본 접힘 = B09 「데이터 점검」 · 「공기 옵션」 / B07 「평면도」 · 「종단도」 · 「횡단도」 /
|
||||
B06 「횡단 보기 설정」 / B05 「계획노선」 · 다른 컨테이너 기본값 그대로.
|
||||
- 쪽 안에서 편 것은 다시 그려도 그대로 — B05 · B06 = 한 벌 패널이 칸을 한 번만 세움 · B07 = 다시 세울 때
|
||||
옛 목록 접힘을 이어 받음 · B09 = 좌측 패널을 쪽 그릴 때 한 번만 세움.
|
||||
- B07 처음 들어가면 표지 — 고른 장 기억(모듈 값 `reopenDrawingId` · `reopenCrossSheet`)이 있으면 그 장.
|
||||
|
||||
Node 헬퍼 `helper_311_panel_folded.cjs` 가 가짜 DOM 위에서 B05 · B06 · B07 패널을 돌림.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = Path(HERE).parents[1]
|
||||
|
||||
|
||||
def _read(rel: str) -> str:
|
||||
return (ROOT / rel).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def out():
|
||||
proc = subprocess.run(
|
||||
["node", os.path.join(HERE, "helper_311_panel_folded.cjs")],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def test_B05_B06_첫_그림_접힘(out):
|
||||
assert out["profileFirst"] == {
|
||||
"페이지 설정": True,
|
||||
"계획노선": True,
|
||||
"선택 포인트 상세 설정": False,
|
||||
}
|
||||
assert out["crossFirst"]["B06_View_Title"] is True
|
||||
assert out["crossFirst"]["B06_Std_Title"] is True # 옛 기본 접힘 그대로
|
||||
|
||||
|
||||
def test_B05_B06_편_뒤_다시_그림_유지(out):
|
||||
assert out["crossFirst"]["계획노선"] is False # 편 것 → 보기 바꿔도 그대로
|
||||
assert out["afterRedraw"]["계획노선"] is False
|
||||
assert out["afterRedraw"]["B06_View_Title"] is False
|
||||
assert out["sameRouteNode"] is True
|
||||
|
||||
|
||||
def test_B07_첫_그림_접힘(out):
|
||||
assert out["b07EntryFolded"] == [] # 들어올 때 = 라우터가 비운 그릇 → 기본값
|
||||
assert out["b07First"] == {
|
||||
"plan": True,
|
||||
"longitudinal": True,
|
||||
"cross": True,
|
||||
"mass_haul": False,
|
||||
}
|
||||
|
||||
|
||||
def test_B07_다시_세워도_접힘_이어_받음(out):
|
||||
assert out["b07Redraw"] == {
|
||||
"plan": True,
|
||||
"longitudinal": True,
|
||||
"cross": False,
|
||||
"mass_haul": True,
|
||||
}
|
||||
|
||||
|
||||
def test_B07_다시_세우기는_옛_목록을_기다리기_전에_읽음():
|
||||
page = _read("B07_DesignDetail/B07_DesignDetail_UI_Page.ts")
|
||||
body = page[page.index("export async function renderB07DesignDetail(") :]
|
||||
assert body.index("const folded = foldedGroups(root);") < body.index("await ")
|
||||
assert "crossLayoutSlot,\n folded,\n );" in body.replace("\r\n", "\n")
|
||||
|
||||
|
||||
def test_B07_첫_장은_표지_기억_있으면_그_장():
|
||||
page = _read("B07_DesignDetail/B07_DesignDetail_UI_Page.ts")
|
||||
assert 'const cover = drawings.findIndex((item) => item.kind === "cover");' in page
|
||||
assert "const first = reopen >= 0 ? reopen : Math.max(cover, 0);" in page
|
||||
|
||||
|
||||
def test_B09_기본_접힘_좌측은_한_번만_세움():
|
||||
shell = _read("B09_Estimation/B09_Estimation_UI_Shell.ts")
|
||||
assert 'createPanelCard("데이터 점검", true)' in shell
|
||||
assert 'createPanelCard("공기 옵션", true)' in _read(
|
||||
"B09_Estimation/draft/B09_Draft_Schedule_Panel.ts"
|
||||
)
|
||||
# 다시 그림(show) 은 가운데 판만 바꿈 — 좌측 카드는 쪽 그릴 때 한 번
|
||||
show = shell[shell.index("const show = (tab: TabId") : shell.index("const run = async (")]
|
||||
assert "createPanelCard" not in show and "left." not in show
|
||||
print_page = _read("B09_Estimation/draft/B09_Draft_Print_Page.ts")
|
||||
assert 'createPanelCard("영역")' in print_page and 'createPanelCard("쪽 목록")' in print_page
|
||||
Reference in New Issue
Block a user