Files
Aislo/resources/tester/test_m02_area_old_links.py
T
eomsangdonandClaude Opus 5.5 ad511175bf fix(m02): 셀 밀기 · 당기기에 이름 따라감 · 카드 도면 저장도 표 없는 연결표 줄 뺌(114-2)
- 칸 삽입 · 삭제 창(밀기 · 당기기) — 띠 안에 통째로 든 이름정의 범위를 같이 옮김 · 되돌리기 한 걸음
- 연결표 맞춤 몸을 fitLinks 로 떼어 도면 상세 · 카드 도면 저장이 같이 씀
- 시험 — 밀기 여섯 갈래 · 카드 저장 줄 빼기

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
2026-10-08 18:28:48 +09:00

191 lines
8.9 KiB
Python

"""114 — 이름 없는 옛 보낸 범위 연결이 행 넣기를 따라감(화면 · 서버 한 바퀴).
- 114-1 면적 상세를 열 때 화면(`sentRanges` + 책)이 `도면_{연결}` 이름정의를 둠 → 행 넣기 → 면적 [저장]
→ 서버 `refresh_drawing` 이 그 이름을 연결표 줄에 적고 도면 표 칸 글 = 옮겨진 칸.
- 114-2 셀 밀기 · 당기기(칸 삽입 · 삭제 창)에 이름 따라감 · 카드 도면 저장도 표 없는 연결표 줄 뺌(`fitLinks`).
TS 는 Node 로(`AreaToDrawing` 은 그 자리 변환 · 스프레드시트 모듈은 `helper_spreadsheet_d_bundle.cjs`).
"""
from __future__ import annotations
import json
import shutil
import subprocess
from pathlib import Path
import pytest
from common_util import common_util_spreadsheet as sheet_node
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from resources.tester.test_m02_area_table import world # noqa: F401 — 같은 임시 층
ROOT = Path(__file__).resolve().parents[2]
SCRIPT = r"""
const fs = require("fs");
const path = require("path");
const ROOT = process.argv[1];
const { load, SS } = require(path.join(ROOT, "resources/tester/spreadsheet/helper_spreadsheet_d_bundle.cjs"));
const ts = require(path.join(ROOT, "config/node_modules/typescript"));
const input = JSON.parse(fs.readFileSync(0, "utf8"));
const ss = (name) => load(path.join(SS, name + ".ts"));
const tsLoad = (file, stubs) => {
const src = fs.readFileSync(path.join(ROOT, file), "utf8");
const js = ts.transpileModule(src, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
new Function("exports", "module", "require", js)(box.exports, box, (n) => {
if (!(n in stubs)) throw new Error("모르는 가져오기 " + n);
return stubs[n];
});
return box.exports;
};
const toDrawing = tsLoad("M02_MasterTemplete/M02_MasterTemplete_AreaToDrawing.ts", {
"@config/config_frontend": { API_BASE_URL: "/api" },
"@ui/ui_template_elements": {},
"@ui/ui_template_modal": {},
"./M02_MasterTemplete_Api_Fetch": {
getActiveOwner: () => null,
readTemplate: async () => ({ 문서: input.drawing }),
},
"../A00_Common/spreadsheet/spreadsheet_address": ss("spreadsheet_address"),
"../A00_Common/spreadsheet/spreadsheet_area_table": ss("spreadsheet_area_table"),
});
const { applyCommand } = ss("spreadsheet_commands");
(async () => {
const book = input.book;
const area = { layer: "system", projectId: null, kind: "area", name: "rv", label: "x" };
const before = JSON.stringify(book);
const noBook = await toDrawing.sentRanges(area, () => {});
const unchanged = JSON.stringify(book) === before;
const marks = await toDrawing.sentRanges(area, () => {}, book);
const opened = {
marks: marks.map((m) => [m.이름, m.범위이름 ?? null]),
noBook: noBook.map((m) => m.범위이름 ?? null),
unchanged,
names: book.시트[0].이름정의,
};
applyCommand(book, { 종류: "행넣기", 시트: "s1", at: 0, 수: 1 }); // 1행 위에 한 행
// 114-2 — 셀 밀기 · 당기기(칸 삽입 · 삭제 창)에 이름 따라감 · 되돌리기 한 걸음
const dlg = ss("spreadsheet_insert_dialog");
const E7J17 = { r0: 6, c0: 4, r1: 16, c1: 9 };
const shift = (fn, range) => {
const b = { 종류: "통합문서", 판: 1, 열: "t", 서식: [{}], 시트: [
{ id: "s1", 이름: "s1", 칸: { E7: { 값: 1 } }, 이름정의: [{ 이름: "도면_T1", 범위: E7J17 }] },
] };
const sent = [];
const ctx = { book: b, readOnly: false, selection: { 시트: "s1" }, sheet: () => b.시트[0],
dispatch: (c) => (sent.push(c.종류 === "묶음" ? c.명령.map((x) => x.종류).join("+") : c.종류), applyCommand(b, c)) };
dlg[fn](ctx, range);
const n = b.시트[0].이름정의.map((x) => x.범위);
return { names: n, sent, cell: Object.keys(b.시트[0].칸) };
};
const shifted = {
downInside: shift("insertShiftDown", { r0: 9, c0: 4, r1: 9, c1: 9 }),
downAbove: shift("insertShiftDown", { r0: 4, c0: 4, r1: 4, c1: 9 }),
downPartial: shift("insertShiftDown", { r0: 4, c0: 4, r1: 4, c1: 5 }),
upAll: shift("deleteShiftUp", E7J17),
right: shift("insertShiftRight", { r0: 6, c0: 3, r1: 16, c1: 3 }),
left: shift("deleteShiftLeft", { r0: 6, c0: 0, r1: 16, c1: 1 }),
};
// 114-2 — 카드 도면 저장도 표 없는 연결표 줄을 뺌(도면 상세 저장과 같은 몸)
const tbl = (id) => ({ type: "Table", shapeData: { cells: [[{ text: "x" }]], style: { 연결: id } } });
const row = (id) => ({ 연결: id, 시트: "s1", 범위: "A1", 값: {} });
const fit = {
kept: toDrawing.fitLinks({ 도면: { entities: [tbl("T1")] }, 연결표: [row("T1"), row("T2")] }).연결표,
plain: "연결표" in toDrawing.fitLinks({ 도면: { entities: [tbl("T9")] } }),
};
console.log(JSON.stringify({ opened, book, shifted, fit }));
})().catch((e) => {
console.error(e);
process.exit(1);
});
"""
T1_TABLE = {
"type": "Table",
"shapeData": {
"origin": {"x": 0, "y": 0},
"columnWidths": [9, 9],
"rowHeights": [3, 3],
"cells": [[{"text": "낡음"}, {"text": "낡음"}], [None, {"text": "낡음"}]],
"style": {"fontSize": 2.5, "연결": "T1"},
},
}
OLD_ROW = {"연결": "T1", "시트": "s1", "범위": "A1:B2", "글자크기": 2.5, "값": {}}
def _node(payload: dict) -> dict:
if shutil.which("node") is None:
pytest.skip("node 가 없음")
proc = subprocess.run(
["node", "-e", SCRIPT, str(ROOT)],
input=json.dumps(payload, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
timeout=120,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout.strip().splitlines()[-1])
def _texts(cells: list) -> list:
return [[c and c.get("text") for c in row] for row in cells]
def test_옛_연결_열기_행_넣기_저장_도면_표_값_따라감_114_1(world): # noqa: F811
system = world["system"]
with store.at(system):
got = store.read("structure", "rv")
drawing = {**got["문서"], "연결표": [OLD_ROW]}
drawing["도면"] = {**drawing["도면"], "entities": [T1_TABLE]}
store.write("structure", "rv", got["판"], drawing)
area = store.read("area", "rv")
want = sheet_node.area_tables([{"book": area["문서"], "시트": "s1", "범위": "A1:B2"}])[0]
out = _node({"book": area["문서"], "drawing": drawing})
opened = out["opened"]
assert opened["noBook"] == [None] and opened["unchanged"] # 책 없으면 안 고침(옛 길)
assert opened["marks"] == [["T1 도면", "도면_T1"]] # 그 이름으로 점선
assert opened["names"] == [{"이름": "도면_T1", "범위": {"r0": 0, "c0": 0, "r1": 1, "c1": 1}}]
book = out["book"]
assert book["시트"][0]["이름정의"][0]["범위"] == {
"r0": 1,
"c0": 0,
"r1": 2,
"c1": 1,
} # 행 넣기 따라감
with store.at(system):
store.write("area", "rv", area["판"], book) # 면적 [저장] → refresh_drawing
doc = store.read("structure", "rv")["문서"]
row = doc["연결표"][0]
assert (row["이름"], row["범위"]) == ("도면_T1", "A2:B3")
cells = doc["도면"]["entities"][0]["shapeData"]["cells"]
assert _texts(cells) == _texts(want["표"]["shapeData"]["cells"]) # 옮겨진 칸 = 보낸 그 칸
assert _texts(cells)[0] == ["제목", "1,234.50"]
def test_셀_밀기_당기기에_이름_따라감_띠에_걸치기만_하면_그대로_114_2(world): # noqa: F811
with store.at(world["system"]):
area = store.read("area", "rv")
s = _node({"book": area["문서"], "drawing": {"연결표": []}})["shifted"]
box = lambda r0, c0, r1, c1: [{"r0": r0, "c0": c0, "r1": r1, "c1": c1}] # noqa: E731
assert s["downInside"]["names"] == box(6, 4, 17, 9) # 범위 안에 밀어 넣음 = 늘어남 E7:J18
assert s["downAbove"]["names"] == box(7, 4, 17, 9) # 위에서 밀림 = 통째로 E8:J18
assert s["downPartial"]["names"] == box(6, 4, 16, 9) # 띠(E:F)에 걸치기만 = 그대로
assert s["upAll"]["names"] == [] # 다 지운 자리 = 이름 빠짐(저장 때 연결 끊김)
assert s["right"]["names"] == box(6, 5, 16, 10) # F7:K17
assert s["left"]["names"] == box(6, 2, 16, 7) # C7:H17
assert s["downInside"]["sent"] == ["시트통째+칸"] # 한 묶음 = 되돌리기 한 걸음
assert s["downPartial"]["sent"] == ["칸"] # 안 바뀌면 시트통째 안 끼움
assert s["downAbove"]["cell"] == ["E8"] # 값도 함께 밀림
def test_카드_도면_저장도_표_없는_연결표_줄_뺌_114_2(world): # noqa: F811
with store.at(world["system"]):
area = store.read("area", "rv")
fit = _node({"book": area["문서"], "drawing": {"연결표": []}})["fit"]
assert fit["kept"] == [{"연결": "T1", "시트": "s1", "범위": "A1", "값": {}, "장": ""}]
assert fit["plain"] is False # 기록 없던 도면은 칸을 안 만듦