Files
Aislo/resources/tester/test_364_1_view_no_step.py
T
eomsangdonandClaude Opus 5.5 002d0e85ac refactor(387-1): 설계 화면 세션 직접 접근 → 등록부 · 프로젝트 없는 열쇠 걷음
- 되돌리기 사진 = 연 프로젝트 초안 칸만(projectStateKeys) · 옛 b05/b06 접두 지움
- 관 자리 b05:pipes 걷음 → 가게 pipes 초안으로 되돌림
- 조정창 · 연동 · 세월교 · BOX 세션 맵 = readByKey · writeByKey
- 노선 편집 횡단 반폭 = routeStateKeys · 계획고 옛 키(프로젝트 없음) 안 받음
- 새 test_384_1_session_keys · 사진첩 시험 헬퍼 = 진짜 등록부 부름

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
2026-10-17 17:29:17 +09:00

174 lines
6.8 KiB
Python

"""364-1 보기 손길 · 쉬는 동안 온 값은 되돌리기 걸음 아님 — 설계 값이 바뀐 때만 걸음.
· 새 페이지 → 뒤에서 온 값(관 자리 불러옴 · 표준 횡단 · 따라간 구조물) 뒤 판 열기 · 회전 · 맞춤 · 미리보기 · 도움말 = canUndo false
· 꺾임점 끌기 · 계획고 ▲ = 걸음 하나씩 · 되돌리기 한 걸음 = 그 편집만
· 막이 — 놓은 뒤 기다리는 사이 손길 · 글 치는 중 다른 곳 누름은 앞 편집을 덮지 않음
./venv/Scripts/python.exe -m pytest resources/tester/test_364_1_view_no_step.py -q
"""
from __future__ import annotations
import json
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, seedPageHistory } = loadTs(process.argv[4]);
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
const fire = (type) => window.dispatchEvent(new Event(type));
/** 누르고 놓음 — 보기 손길(세션 안 씀) 또는 편집(누른 동안 write). */
async function click(write) {
fire("pointerdown");
write?.();
fire("pointerup");
await wait(450);
}
const K = {
route: "aislo:draft:route-draft:p1",
grade: "aislo:draft:alignment-draft:p1:7",
std: "aislo:draft:std-cross:p1",
structures: "aislo:draft:structures:p1",
cross: "aislo:draft:crossdesign:p1:7",
pipes: "aislo:draft:pipes:p1",
};
const out = {};
// 새 페이지 — 관 초안이 손길 없이 뒤에서 옴(찍기 직전에 쓰는 흉내)
let pipes = [];
sessionStorage.setItem(K.route, "r0");
const h = createProfileHistory(() => {}, 100, () => sessionStorage.setItem(K.pipes, JSON.stringify(pipes)));
h.record(); // 열 때 찍기
// 손길 없이 뒤에서 옴 — 배수 판이 관 자리 불러옴 · 표준 횡단 · 따라간 구조물
pipes = [10, 20];
sessionStorage.setItem(K.std, "std");
sessionStorage.setItem(K.structures, "s0");
await click(); // [횡단] 판 열기
out.sideOpen = h.canUndo();
sessionStorage.setItem(K.structures, "s1"); // 또 뒤에서 옴(파생 값)
await click(); // 회전
fire("keydown"); // 도움말 · 맞춤 단축키
await wait(450);
await click(); // 측점 눈금 횡단 미리보기
await click(); // 판 닫기
out.views = h.canUndo();
// 꺾임점 끌기 — 누른 동안 여러 번 찍어도 놓을 때 한 걸음
fire("pointerdown");
for (const v of ["r1", "r2", "r3"]) {
sessionStorage.setItem(K.route, v);
h.record();
}
fire("pointerup");
await wait(450);
out.drag = h.canUndo();
sessionStorage.setItem(K.structures, "s2"); // 끈 노선 따라 구조물 다시 섬(뒤에서)
await click(); // 맞춤
// 계획고 ▲
await click(() => sessionStorage.setItem(K.grade, "g1"));
out.gradeUndo = h.undo();
out.afterGradeUndo = { route: sessionStorage.getItem(K.route), grade: sessionStorage.getItem(K.grade), structures: sessionStorage.getItem(K.structures) };
out.dragUndo = h.undo();
out.afterDragUndo = { route: sessionStorage.getItem(K.route), structures: sessionStorage.getItem(K.structures), pipes: sessionStorage.getItem(K.pipes), std: sessionStorage.getItem(K.std) };
out.noMore = !h.canUndo();
// 막이 1 — 놓은 뒤 기다리는 사이(400 ms) 다른 곳 누름은 앞 편집을 덮지 않음
data.clear();
const h2 = createProfileHistory(() => {});
fire("pointerdown");
h2.record(); // 누른 동안 찍기 → 놓을 때 찍힘(바뀐 것 없음)
fire("pointerup");
await wait(0);
sessionStorage.setItem(K.cross, "c1"); // 카드 단추 — 놓은 뒤 씀 · 찍는 줄 없음
await wait(50);
await click(); // 곧바로 판 열기
out.settleKept = h2.canUndo();
// 막이 2 — 글 치는 중(키마다 씀) 다른 곳 누름 = 앞 편집 안 덮음
data.clear();
const h3 = createProfileHistory(() => {});
fire("keydown");
sessionStorage.setItem(K.grade, "typed");
await wait(450);
await click(); // 칸 밖 누름
fire("change");
await wait(450);
out.typedKept = h3.canUndo();
// 씨앗 — 계획고 고친 뒤 [횡단 보기] 첫 열기가 비어 있던 표준 횡단을 채움(누른 사이) = 걸음 아님 · 되돌려도 남음
data.clear();
const h4 = createProfileHistory(() => {});
await click(() => sessionStorage.setItem(K.grade, "g1"));
await click(() => {
sessionStorage.setItem(K.std, "seed");
seedPageHistory(K.std);
});
out.seedSteps = [h4.undo(), h4.canUndo()];
out.seedKept = { std: sessionStorage.getItem(K.std), grade: sessionStorage.getItem(K.grade) };
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_364_1")
(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["sideOpen"] is False
assert result["views"] is False
def test_편집만_걸음_되돌리기는_그_편집만(result) -> None:
assert result["drag"] is True
assert result["gradeUndo"] is True
# 계획고만 되돌아감 — 끌기 · 뒤에서 온 구조물은 그대로
assert result["afterGradeUndo"] == {"route": "r3", "grade": None, "structures": "s2"}
assert result["dragUndo"] is True
# 끌기 전 모습 = 열고 쉬는 동안 온 값까지(그건 걸음이 아님)
assert result["afterDragUndo"] == {
"route": "r0",
"structures": "s1",
"pipes": "[10,20]",
"std": "std",
}
assert result["noMore"] is True
def test_씨앗_값은_걸음_아님_되돌려도_남음(result) -> None:
assert result["seedSteps"] == [True, False] # 계획고 한 걸음뿐
assert result["seedKept"] == {"std": "seed", "grade": None}
def test_앞_편집은_덮지_않음(result) -> None:
assert result["settleKept"] is True
assert result["typedKept"] is True