- 얹기: attachShapes · attach_shapes 에 소단(section.berm · 덮는 소단 없음 = null) · SHAPE_KEYS 에 berm · TS attachBermSpecs ↔ 파이썬 attach_berm_specs · berm_spans_from 짝 - 읽기: 저장 노선 다시 셈(브라우저 · Node cross.refresh) = 얹힌 section.berm(칸 없음 = 저장분 design.berm) · 노선 초안 · 서버 Node 묶음 cross = crossWithShapes 의 얹힌 제원(bermAt · crossWithBerm 걷음) - 계단은 계산 안쪽 그대로 · [저장] 길(구조물 정본 → cross.berm_spans) 그대로 - 지움: 시설 줄 syncBerm · putBerm(C4.copy) · writeBermSpans · 세션 칸 berm · bermRequest · design_shared bermAt - 시험: test_272_berm_source 측점 얹기 거울로 · test_323_4 소단 얹음 · 477 · 511-A · 472 떠남 · 365 · 272 고름 헬퍼가 사본 대신 얹힌 소단 · design_pipeline_page 700 줄 → 699(주석 한 줄) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
5.4 KiB
Python
144 lines
5.4 KiB
Python
"""477-3 · 477-4 (요구 433) — ↶ 바로 켜짐 · 소단 사본 = 결과 통.
|
|
|
|
못박는 것
|
|
- 477-3 조작 놓은 직후(찍기 기다림 400 ms 안) ↶ 켜짐 · 단추 맞춤 알림 · 바뀐 것 없는 누름은 안 켜짐.
|
|
- 기다림 중 ↶ = 먼저 찍고 되돌림(한 번에 조작 전) · ↷ = 다시 · 서버 다시 셈 뒤 쓰기 묶음은 그대로 한 걸음.
|
|
- 477-4 결과 통 칸은 사진 밖 · 소단 사본 칸 `berm` 은 걷음(577-2 · 측점에 얹힌 제원) ·
|
|
떠남 목록에 줄 없음.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_477_undo_now_berm.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
TS_FILE = ROOT / "B05_Profile" / "B05_Profile_UI_Profile_History.ts"
|
|
LOADER = Path(__file__).resolve().parent / "helper_ts_cjs_load.cjs"
|
|
|
|
DRIVER = r"""
|
|
import { writeFileSync } from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
const data = new Map();
|
|
globalThis.sessionStorage = {
|
|
get length() { return data.size; },
|
|
key: (i) => [...data.keys()][i] ?? null,
|
|
getItem: (k) => (data.has(k) ? data.get(k) : null),
|
|
setItem: (k, v) => data.set(k, String(v)),
|
|
removeItem: (k) => data.delete(k),
|
|
};
|
|
globalThis.window = new EventTarget();
|
|
window.setTimeout = setTimeout;
|
|
window.sessionStorage = sessionStorage;
|
|
window.localStorage = { getItem: (k) => (k === "frd_current_project_id" ? "p1" : null) };
|
|
const { loadTs } = createRequire(import.meta.url)(process.argv[3]);
|
|
const { createProfileHistory, onPageHistory } = loadTs(process.argv[4]);
|
|
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
const PIPES = "aislo:draft:pipes:p1";
|
|
const CROSS = "aislo:draft:crossdesign:p1:7";
|
|
const BERM = "aislo:result:design-stations:p1";
|
|
const out = {};
|
|
let changes = 0;
|
|
onPageHistory({ change: () => (changes += 1) });
|
|
const press = () => window.dispatchEvent(new Event("pointerdown"));
|
|
const lift = () => window.dispatchEvent(new Event("pointerup"));
|
|
|
|
// 1. 관 지움 — 놓은 한 틱 뒤 ↶ 켜짐(기다림 400 ms 안)
|
|
sessionStorage.setItem(PIPES, '[{"c":10},{"c":20}]');
|
|
sessionStorage.setItem(BERM, "[]");
|
|
const h = createProfileHistory(() => {});
|
|
out.openCanUndo = h.canUndo();
|
|
press();
|
|
sessionStorage.setItem(PIPES, '[{"c":10}]');
|
|
lift();
|
|
await wait(30);
|
|
out.soonCanUndo = h.canUndo();
|
|
out.soonChanges = changes;
|
|
// 바로 ↶ 단추(누름 · 놓음 · 누름 처리) — 기다리던 찍기 먼저 · 한 번에 조작 전
|
|
press();
|
|
lift();
|
|
out.undo = h.undo();
|
|
out.afterUndo = sessionStorage.getItem(PIPES);
|
|
out.undoLeft = h.canUndo();
|
|
out.canRedo = h.canRedo();
|
|
await wait(450);
|
|
out.redo = h.redo();
|
|
out.afterRedo = sessionStorage.getItem(PIPES);
|
|
out.redoLeft = h.canRedo();
|
|
|
|
// 2. 서버 다시 셈 뒤 쓰기(200 ms) — 기다림이 끝나면 조작과 한 걸음
|
|
data.clear();
|
|
sessionStorage.setItem(PIPES, "a");
|
|
sessionStorage.setItem(CROSS, "x");
|
|
const h2 = createProfileHistory(() => {});
|
|
press();
|
|
sessionStorage.setItem(PIPES, "b");
|
|
lift();
|
|
await wait(200);
|
|
sessionStorage.setItem(CROSS, "y");
|
|
sessionStorage.setItem(BERM, '[{"s":1}]'); // 결과 통 칸 — 사진 밖
|
|
await wait(300);
|
|
h2.undo();
|
|
out.grouped = [sessionStorage.getItem(PIPES), sessionStorage.getItem(CROSS)];
|
|
out.groupedLeft = h2.canUndo();
|
|
out.bermKept = sessionStorage.getItem(BERM);
|
|
|
|
// 3. 바뀐 것 없는 누름 — ↶ 안 켜짐(깜빡임 없음)
|
|
data.clear();
|
|
sessionStorage.setItem(PIPES, "a");
|
|
const h3 = createProfileHistory(() => {});
|
|
press();
|
|
lift();
|
|
await wait(30);
|
|
out.idleCanUndo = h3.canUndo();
|
|
writeFileSync(process.argv[2], JSON.stringify(out));
|
|
"""
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def result(tmp_path_factory: pytest.TempPathFactory) -> dict:
|
|
out = tmp_path_factory.mktemp("ts_477")
|
|
(out / "driver.mjs").write_text(DRIVER, encoding="utf-8")
|
|
run = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(out / "driver.mjs"), str(out / "out.json"), str(LOADER), str(TS_FILE)],
|
|
capture_output=True, text=True, cwd=str(out),
|
|
) # fmt: skip
|
|
assert run.returncode == 0, run.stderr
|
|
return json.loads((out / "out.json").read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_놓은_직후_되돌리기_켜짐(result) -> None:
|
|
assert result["openCanUndo"] is False
|
|
assert result["soonCanUndo"] is True
|
|
assert result["soonChanges"] >= 1 # 단추 맞춤 알림이 기다림 전에 감
|
|
|
|
|
|
def test_기다림_중_되돌리기_먼저_찍고_한_걸음(result) -> None:
|
|
assert result["undo"] is True
|
|
assert result["afterUndo"] == '[{"c":10},{"c":20}]'
|
|
assert result["undoLeft"] is False and result["canRedo"] is True
|
|
assert result["redo"] is True and result["afterRedo"] == '[{"c":10}]'
|
|
assert result["redoLeft"] is False
|
|
|
|
|
|
def test_서버_다시_셈_묶음_그대로(result) -> None:
|
|
assert result["grouped"] == ["a", "x"] and result["groupedLeft"] is False
|
|
assert result["bermKept"] == '[{"s":1}]' # 결과 통 칸은 되돌리기가 안 건드림
|
|
|
|
|
|
def test_바뀐_것_없는_누름은_안_켜짐(result) -> None:
|
|
assert result["idleCanUndo"] is False
|
|
|
|
|
|
def test_소단_사본_칸_없음() -> None:
|
|
state = (ROOT / "A00_Common" / "b_page_state.ts").read_text(encoding="utf-8")
|
|
assert not re.search(r"^\s*berm: \{", state, re.M) # 577-2 — 측점에 얹힌 제원(사본 걷음)
|
|
leave = (ROOT / "A00_Common" / "design" / "design_leave.ts").read_text(encoding="utf-8")
|
|
assert '"berm"' not in leave
|