Merge remote-tracking branch 'origin/dev' into sub_laptop_1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* M02 일위대가 사전조합 층(134-3) 헬퍼 — `M02_MasterTemplete_Api_Fetch.ts` 명시 owner 길 ·
|
||||
* `M02_MasterTemplete_UI_Side_Precombo.ts` `buildPrecomboFolders`(158-1 자유 폴더) 를 트랜스파일해 Node 로 돌림 ·
|
||||
* `M02_MasterTemplete_UI_Side_Library.ts` `buildLibraryFolders`(158-1 자유 폴더 · 716-4) 를 트랜스파일해 Node 로 돌림 ·
|
||||
* `precomboClasses`(134-5 · 구조물군 › 종류 + 공통) · fetch 대역이 부른 주소를 모음 ·
|
||||
* stdin = {rows, def?, view?} · stdout = {urls, tree, classes} JSON. */
|
||||
const fs = require("fs");
|
||||
@@ -31,6 +31,7 @@ global.fetch = async (url) => {
|
||||
const side = loadTs(
|
||||
path.join(ROOT, "M02_MasterTemplete", "M02_MasterTemplete_UI_Side_Precombo.ts"),
|
||||
);
|
||||
const lib = loadTs(path.join(ROOT, "M02_MasterTemplete", "M02_MasterTemplete_UI_Side_Library.ts"));
|
||||
api.setActiveLayer("personal", "7/99"); // 페이지 = 남의 개인을 보는 중
|
||||
await api.readTemplate("personal", "labor", "x1", null); // 페이지 층 = 그 사람
|
||||
await api.readTemplate("personal", "precombo", "SC-000001", null, null); // 사전조합 = 내 개인
|
||||
@@ -38,11 +39,11 @@ global.fetch = async (url) => {
|
||||
await api.saveTemplate("system", "precombo", "SC-000002", null, "v", {}, null);
|
||||
const shape = (f) => ({
|
||||
path: f.path,
|
||||
count: side.docCount(f),
|
||||
count: lib.docCount(f),
|
||||
docs: f.docs.map((r) => r.이름),
|
||||
folders: f.folders.map(shape),
|
||||
});
|
||||
const tree = shape(side.buildPrecomboFolders(input.rows, input.view ?? { 폴더: [], 자리: {} }));
|
||||
const tree = shape(lib.buildLibraryFolders(input.rows, input.view ?? { 폴더: [], 자리: {} }));
|
||||
const classes = input.def ? side.precomboClasses(input.def) : null; // 134-5
|
||||
process.stdout.write(JSON.stringify({ urls, tree, classes }));
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
"""서고 나무 폴더(PLAN 716-4) — 종류 인자 · 안에 든 것째 지움 · 쓰는 곳 409 · 여럿 · 복사 붙여넣기.
|
||||
|
||||
임시 storage.
|
||||
|
||||
`/api/m02/layers/{층}/{종류}-tree` = 일위대가(precombo) · 단가산출(sangeun) 따로 ·
|
||||
폴더 지우기 = 안의 문서 파일째(모두 검사 뒤 한 번에 · 바깥에서 쓰면 409 + 그 이름 ·
|
||||
지우는 것끼리 참조는 됨) ·
|
||||
`대상` 여럿 옮기기 · 지우기 · `…-tree/paste` = 새 키 · 같은 폴더 이름 「(1)」 · 본뜬 = 원본.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from M02_MasterTemplete import M02_Precombo_Folders as pre
|
||||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||||
from resources.tester.test_m02_precombo_folders import Tree, client # noqa: F401 — 같은 임시 층
|
||||
|
||||
MINE = {"층": "personal", "주인": "7/42"}
|
||||
SK = "/api/m02/layers/personal/library/sangeun"
|
||||
|
||||
|
||||
def _sk(c: TestClient, name: str, **doc) -> str:
|
||||
body = {"문서": {"이름": name, "단위": "㎡", "더한줄": [], **doc}}
|
||||
res = c.post(SK, json=body)
|
||||
assert res.status_code == 200, res.text
|
||||
return res.json()["이름"]
|
||||
|
||||
|
||||
def _files(kind: str) -> list[str]:
|
||||
return sorted(p.stem for p in (layers.personal_dir(7, 42) / kind).glob("*.json"))
|
||||
|
||||
|
||||
def _sk_tree(c: TestClient) -> Tree:
|
||||
t = Tree(c)
|
||||
t.url = "/api/m02/layers/personal/sangeun-tree"
|
||||
t.now = c.get(t.url).json()
|
||||
return t
|
||||
|
||||
|
||||
def test_종류마다_따로_자리_파일(client: TestClient): # noqa: F811
|
||||
_sk(client, "갑")
|
||||
sk = _sk_tree(client)
|
||||
sk.do(op="add-folder", 경로=[], 이름="단가")
|
||||
sk.do(op="move", 키="SK-000001", 새경로=["단가"])
|
||||
assert Tree(client).now["폴더"] == [] # 일위대가 나무는 그대로
|
||||
root = layers.personal_dir(7, 42)
|
||||
assert (root / pre.file_of("sangeun")).is_file()
|
||||
assert not (root / pre.file_of("precombo")).exists()
|
||||
assert client.get("/api/m02/layers/personal/labor-tree").status_code == 422 # 모르는 종류
|
||||
|
||||
|
||||
def test_폴더_지우기는_안에_든_것째_지우는_것끼리_참조는_됨(client: TestClient): # noqa: F811
|
||||
a = _sk(client, "바탕")
|
||||
b = _sk(client, "위", 바탕={"참조": {**MINE, "키": a}})
|
||||
sk = _sk_tree(client)
|
||||
sk.do(op="add-folder", 경로=[], 이름="묶음")
|
||||
sk.do(op="add-folder", 경로=["묶음"], 이름="안")
|
||||
sk.do(op="move", 키=a, 새경로=["묶음", "안"])
|
||||
sk.do(op="move", 키=b, 새경로=["묶음"])
|
||||
got = sk.do(op="delete", 경로=["묶음"])
|
||||
assert sorted(got["알림"]["지운문서"]) == [a, b]
|
||||
assert _files("sangeun") == [] and sk.now["폴더"] == []
|
||||
|
||||
|
||||
def test_바깥에서_쓰면_409_그_이름_아무것도_안_지움(client: TestClient): # noqa: F811
|
||||
a = _sk(client, "바탕")
|
||||
body = {"문서": {"이름": "쓰는 일위대가", "담은로직": [{"참조": {**MINE, "키": a}}]}}
|
||||
assert client.post("/api/m02/layers/personal/library/precombo", json=body).status_code == 200
|
||||
sk = _sk_tree(client)
|
||||
sk.do(op="add-folder", 경로=[], 이름="묶음")
|
||||
sk.do(op="move", 키=a, 새경로=["묶음"])
|
||||
detail = sk.do(409, op="delete", 경로=["묶음"])["detail"]
|
||||
assert "쓰는 곳" in detail and "「쓰는 일위대가」" in detail and a in detail
|
||||
assert _files("sangeun") == [a] and client.get(sk.url).json()["폴더"] == [["묶음"]]
|
||||
|
||||
|
||||
def test_여럿_옮기기_지우기(client: TestClient): # noqa: F811
|
||||
t = Tree(client) # 개인 층 일위대가 SC-000001~3
|
||||
t.do(op="add-folder", 경로=[], 이름="가")
|
||||
t.do(op="add-folder", 경로=[], 이름="나")
|
||||
t.do(
|
||||
op="move", 대상=[{"키": "SC-000001"}, {"키": "SC-000002"}, {"경로": ["나"]}], 새경로=["가"]
|
||||
)
|
||||
assert t.now["폴더"] == [["가"], ["가", "나"]]
|
||||
assert t.now["자리"] == {"SC-000001": ["가"], "SC-000002": ["가"]}
|
||||
got = t.do(op="delete", 대상=[{"경로": ["가"]}, {"키": "SC-000001"}, {"키": "SC-000003"}])
|
||||
assert sorted(got["알림"]["지운문서"]) == ["SC-000001", "SC-000002", "SC-000003"]
|
||||
assert _files("precombo") == [] and t.now["폴더"] == []
|
||||
|
||||
|
||||
def test_복사_붙여넣기_새_키_같은_이름_폴더는_1(client: TestClient): # noqa: F811
|
||||
t = Tree(client)
|
||||
t.do(op="add-folder", 경로=[], 이름="가")
|
||||
t.do(op="add-folder", 경로=["가"], 이름="속")
|
||||
t.do(op="move", 키="SC-000001", 새경로=["가", "속"])
|
||||
url = "/api/m02/layers/personal/precombo-tree/paste"
|
||||
picks = [{"경로": ["가"]}, {"경로": [], "키": "SC-000002"}]
|
||||
res = client.post(url, json={"from_layer": "personal", "대상": picks, "경로": ["가"]})
|
||||
assert res.status_code == 200, res.text
|
||||
got = res.json()
|
||||
assert got["알림"]["만든문서"] == ["SC-000004", "SC-000005"]
|
||||
assert ["가", "가"] in got["폴더"] and ["가", "가", "속"] in got["폴더"]
|
||||
assert got["자리"]["SC-000004"] == ["가", "가", "속"] and got["자리"]["SC-000005"] == ["가"]
|
||||
again = client.post(url, json={"from_layer": "personal", "대상": [{"경로": ["가"]}]}).json()
|
||||
assert ["가(1)"] in again["폴더"] # 같은 이름 = 「(1)」
|
||||
doc = client.get("/api/m02/layers/personal/templates/precombo/SC-000005").json()["문서"]
|
||||
assert doc["이름"] == "나" and doc["본뜬"]["키"] == "SC-000002"
|
||||
bad = client.post(url, json={"from_layer": "personal", "대상": [{"키": "SC-000099"}]})
|
||||
assert bad.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize("kind", ["precombo", "sangeun"])
|
||||
def test_순수_지우기_알림(kind: str, tmp_path: Path):
|
||||
doc = {"폴더": [["가"], ["가", "나"]], "자리": {"X-1": ["가", "나"], "X-2": []}}
|
||||
out, note = pre.apply(doc, {"op": "delete", "경로": ["가"]}, {"X-1", "X-2"})
|
||||
assert out == {"폴더": [], "자리": {}} and note["지운문서"] == ["X-1"]
|
||||
assert pre.file_of(kind) == f"{kind}_폴더.json"
|
||||
@@ -45,7 +45,7 @@ def test_이어서_끝내기_지움_겹침_막음(client):
|
||||
|
||||
block = _post(client) # 끝낸 같은 엑셀 = 막음 · 안내
|
||||
assert block.status_code == 409 and "이미 불러온 엑셀" in block.json()["detail"]
|
||||
assert "일위대가 68" in block.json()["detail"] and _keys(client) == []
|
||||
assert "단가산출 68" in block.json()["detail"] and _keys(client) == []
|
||||
assert sorted((folder / "precombo").glob("SC-*.json")) == sc_before
|
||||
|
||||
|
||||
@@ -65,3 +65,34 @@ def test_옛_HP_목록에서만_숨김(client):
|
||||
assert client.get("/api/m02/layers/personal/hopyo-picks").json()["줄"] == []
|
||||
assert _post(client).json()["키"] == "HP-000008" # 옛 번 뒤로
|
||||
assert old.is_file() and _keys(client) == ["HP-000008"]
|
||||
|
||||
|
||||
def test_결과_단가산출_산근_블록_바탕_없음_겹침_없음(client):
|
||||
folder = layers.personal_dir(7, 42)
|
||||
assert _post(client).json()["키"] == "HP-000001"
|
||||
url = URL.format("personal") + "/HP-000001/sangeun"
|
||||
got = client.post(url)
|
||||
assert got.status_code == 200, got.text
|
||||
got = got.json()
|
||||
assert got["블록"] > 0 and len(got["만듦"]) == got["블록"] and got["그대로"] == 0
|
||||
docs = [json.loads(p.read_bytes()) for p in sorted((folder / "sangeun").glob("SK-*.json"))]
|
||||
assert len(docs) == got["블록"]
|
||||
one = docs[0]
|
||||
assert one["바탕"] is None and one["본뜬"]["키"].startswith("HP-000001 단산")
|
||||
assert one["구분"] == "실무자료" and len(one["더한줄"]) == 1 and one["단위"]
|
||||
hp = hs.read(folder, "HP-000001")
|
||||
no = int(one["본뜬"]["키"].rsplit("단산", 1)[1])
|
||||
line = next( # 그 블록을 부르는 첫 엑셀 줄 = 엑셀 단가 그대로
|
||||
x
|
||||
for h in hp["호표"]
|
||||
for x in h["줄"]
|
||||
if (x.get("참조") or {}).get("종류") == "단산" and x["참조"]["번"] == no
|
||||
)
|
||||
price = one["더한줄"][0]["고정"]["단가"]
|
||||
assert sum(price.values()) == sum((line["단가"] or {}).get(k) or 0 for k in price)
|
||||
again = client.post(url).json() # 다시 = 그대로(겹침 없음)
|
||||
assert again["만듦"] == [] and again["그대로"] == got["블록"]
|
||||
res = client.delete(URL.format("personal") + "/HP-000001").json()
|
||||
assert res["만든"] == got["블록"] # 단가산출만 만들어도 지문 기록
|
||||
assert len(list((folder / "sangeun").glob("SK-*.json"))) == got["블록"] # 남음
|
||||
assert _post(client).status_code == 409
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
"""719-1 프로젝트 사본 — M02 단가산출(SK) 풀어 굳힘(`M02_Item_Sangeun` · `M02_Item_Source` ·
|
||||
`M02_Item_Prices` · `B09_Estimation_Bill_Library`) — 임시 storage(견본 층 · 실제 M01 읽기만).
|
||||
|
||||
구조물 사본 · B09 서고 사본 셈 = M02 층 SK 셈 · 굳힌 줄은 사본 `단가.json` 로직 줄 · 원본 SK 를
|
||||
지워도 같은 값 · 같은 SK = 같은 굳힌 키(다시 복사 · SK 고친 뒤도) · 원본 층 지문 그대로 ·
|
||||
서고 manifest 원본판 = SK 바뀌면 다름.
|
||||
"""
|
||||
|
||||
# ruff: noqa: F811 — 임시 층 fixture `world` 를 들여 씀
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from B09_Estimation import B09_Estimation_Bill_Library as library
|
||||
from M02_MasterTemplete import M02_Item_Prices as prices
|
||||
from M02_MasterTemplete import M02_Item_Sangeun as sk
|
||||
from M02_MasterTemplete import M02_Item_Source as source
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from resources.tester.test_715_sangeun_store import PCT, _sk
|
||||
from resources.tester.test_m02_labor_ref import MINE, world # noqa: F401 — 같은 임시 층
|
||||
|
||||
COVER = {"labor:LB000036#0": {"qty": 0.5}}
|
||||
ADD = [{"종류": "인력", "요소": "LB000002", "수량": 0.1}]
|
||||
|
||||
|
||||
def _print(folder):
|
||||
"""층 폴더 지문 — 파일 이름 · 바이트."""
|
||||
out = hashlib.sha256()
|
||||
for path in sorted(p for p in folder.rglob("*") if p.is_file()):
|
||||
out.update(str(path.relative_to(folder)).encode() + path.read_bytes())
|
||||
return out.hexdigest()
|
||||
|
||||
|
||||
def _top(ref, qty=2):
|
||||
with store.at(ref["folder"]):
|
||||
doc = labor.clean({"열": "rv", "담은로직": [{"참조": ref["ref"], "수량": {"손": qty}}]})
|
||||
return doc, labor.calc(doc, {})["줄"][0]
|
||||
|
||||
|
||||
def _mine(world):
|
||||
key = _sk(world["personal"], 바탕={"로직": PCT}, 덮개=COVER, 더한줄=ADD)
|
||||
return key, {"folder": world["personal"], "ref": {**MINE, "키": key}}
|
||||
|
||||
|
||||
def test_구조물_사본_굳힌_줄_값_같음_원본_지워도(world):
|
||||
key, ref = _mine(world)
|
||||
doc, live = _top(ref)
|
||||
assert not live["까닭"] and live["기준"]["계"] > 0
|
||||
before = _print(world["personal"])
|
||||
rows = source.labor_doc_rows(doc)
|
||||
bundle = source.price_bundle(rows, world["personal"])
|
||||
assert _print(world["personal"]) == before # 원본 층 안 건드림
|
||||
rid = refs.ref_id(ref["ref"])
|
||||
frozen = rows["참조사본"][rid]
|
||||
fk = frozen["바탕"]["로직"]
|
||||
assert fk == sk.frozen_key(rid, set()) and fk.startswith("GX9")
|
||||
assert frozen["덮개"] == {} and frozen["더한줄"] == [] and frozen["뺀줄"] == []
|
||||
assert frozen["원본"]["덮개"]["labor:LB000036#0"]["qty"] == 0.5 # 고친 근거
|
||||
assert [r["키"] for r in bundle[sk.FILE]["줄"]] == [fk]
|
||||
json.dumps(rows["참조사본"], ensure_ascii=False) # 사본 파일로 쓸 수 있는 꼴(Decimal 없음)
|
||||
(world["personal"] / "sangeun" / f"{key}.json").unlink() # 원본이 사라져도 사본만 봄
|
||||
got = prices.calc(rows, json.loads(json.dumps(bundle, ensure_ascii=False)), {}, {}, None)
|
||||
line = got["줄"][0]
|
||||
assert not line["까닭"]
|
||||
for part in ("노무비", "재료비", "경비", "계"):
|
||||
assert line["기준"][part] == pytest.approx(live["기준"][part], rel=1e-9)
|
||||
assert line["반영"]["계"] == pytest.approx(live["반영"]["계"], rel=1e-9)
|
||||
|
||||
|
||||
def test_같은_SK_같은_굳힌_키_다른_SK_다른_키(world):
|
||||
key, ref = _mine(world)
|
||||
doc, _ = _top(ref)
|
||||
rid = refs.ref_id(ref["ref"])
|
||||
first = source.labor_doc_rows(doc)["참조사본"][rid]["바탕"]["로직"]
|
||||
with store.at(world["personal"]): # SK 를 고쳐도 굳힌 키 그대로(B09 덮개 짝 지킴)
|
||||
now = store.read("sangeun", key)
|
||||
store.write("sangeun", key, now["판"], {**now["문서"], "덮개": {}})
|
||||
again = source.labor_doc_rows(doc)["참조사본"][rid]
|
||||
assert again["바탕"]["로직"] == first and again["원본"]["덮개"] == {}
|
||||
other = refs.ref_id({"층": "company", "주인": "7", "키": key}) # 같은 번호 다른 층
|
||||
assert sk.frozen_key(other, set()) != first
|
||||
taken = {first}
|
||||
assert sk.frozen_key(rid, taken) != first and len(taken) == 2 # 겹치면 다음 번호
|
||||
|
||||
|
||||
def test_B09_서고_사본_값_같음_원본판(world, tmp_path):
|
||||
_, ref = _mine(world)
|
||||
with store.at(world["personal"]):
|
||||
sc = refs.create(
|
||||
"precombo",
|
||||
{"이름": "조합", "단위": "㎡", "담은로직": [{"참조": ref["ref"], "수량": {"손": 2}}]},
|
||||
)["이름"]
|
||||
_, live = _top({"folder": world["personal"], "ref": {**MINE, "키": sc}}, 1)
|
||||
before = _print(world["personal"])
|
||||
root = tmp_path / "proj"
|
||||
made = library.import_copy(root, "precombo", "personal", "7/42", sc)
|
||||
assert made["만듦"] and _print(world["personal"]) == before
|
||||
folder = library.library_dir(root) / made["폴더"]
|
||||
read = {p.name: json.loads(p.read_text(encoding="utf-8")) for p in folder.glob("*.json")}
|
||||
got = prices.calc(read[source.LABOR_FILE], read[source.PRICE_FILE], {}, {}, None)["줄"][0]
|
||||
assert not got["까닭"]
|
||||
assert got["기준"]["계"] == pytest.approx(live["기준"]["계"], rel=1e-9)
|
||||
mark = read["manifest.json"]["원본판"]
|
||||
built = library._build("precombo", "personal", "7/42", sc)[0]
|
||||
assert library.source_version(built) == mark # 다시 모아도 같은 판
|
||||
again = library.import_copy(root, "precombo", "personal", "7/42", sc)
|
||||
assert again == {"폴더": made["폴더"], "만듦": False}
|
||||
with store.at(world["personal"]): # SK 고침 → 원본판 다름
|
||||
now = store.read("sangeun", ref["ref"]["키"])
|
||||
store.write("sangeun", ref["ref"]["키"], now["판"], {**now["문서"], "덮개": {}})
|
||||
assert library.source_version(library._build("precombo", "personal", "7/42", sc)[0]) != mark
|
||||
|
||||
|
||||
def test_SK_없는_사본_그대로():
|
||||
"""SK 없는 참조사본 = 받은 묶음 그대로(기존 사본 · 공사 금액 전후 같음)."""
|
||||
copies = {"x": {"종류": "사전조합", "담은로직": []}}
|
||||
assert sk.freeze(copies) is copies and sk.rows(copies) == [] and sk.materials(copies) == []
|
||||
@@ -338,9 +338,10 @@ def test_좌측_판_자리와_권한():
|
||||
side = _text("M02_MasterTemplete_UI_Side.ts")
|
||||
assert 'import { buildHopyoSection } from "./M02_MasterTemplete_Hopyo_Side";' in side
|
||||
host = side.split('className: "m02-side__lists ui-sidebar-panel"')[1]
|
||||
# 716-3 차례 — 일위대가 › 구조물 조합 › 구분선 › 불러오기
|
||||
# 716-3 차례 — 일위대가 › 구조물 조합 › 구분선 › 일위대가 불러오기 › 도면 양식
|
||||
order = ("precomboSec,", "summary,", "createDivider(),", "section(hopyo.title, hopyo.body),")
|
||||
assert [host.index(k) for k in order] == sorted(host.index(k) for k in order)
|
||||
assert host.index("section(hopyo.title, hopyo.body),") < host.index("drawing ?")
|
||||
assert "canWrite: canEdit(layer, opt.isAdmin)," in side # 구조물 조합 층 쓰기 권한
|
||||
assert "hopyo.paint();" in side.split("function paint(): void {")[1]
|
||||
hopyo_side = _text("M02_MasterTemplete_Hopyo_Side.ts")
|
||||
@@ -355,7 +356,7 @@ def test_좌측_판_자리와_권한():
|
||||
assert 'accept: ".xls,.xlsx"' in wizard and "input.hidden = true;" in wizard
|
||||
assert "accessFor(layer, null, user).canWrite" in wizard # 결과 층 = 쓸 수 있는 층만
|
||||
assert 'let pick: ManageLayer = "personal";' in wizard # 기본 개인
|
||||
assert "export const SANGEUN_READY = false;" in wizard # 단가산출 결과 = 715-2 뒤 켬
|
||||
assert "let wantSangeun = false;" in wizard and "/sangeun`" in wizard # 결과 단가산출 체크
|
||||
|
||||
|
||||
def test_로직_추가_한_곳과_파일_규칙():
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""일위대가 사전조합 나무 = 자유 폴더(PLAN 158-1 · `M02_Precombo_Folders`) — 임시 storage.
|
||||
|
||||
새 폴더(깊이 자유) · 문서 옮기기(파일은 제자리 · 자리만) · 폴더 이름 바꾸기 ·
|
||||
옮기기(아래 자리 따라감) · 지우기(빈 폴더만 · 문서 있으면 409 까닭) · 판 409 ·
|
||||
옮기기(아래 자리 따라감) · 지우기(안에 든 문서째 · 716-4) · 판 409 ·
|
||||
층(마스터 · 회사 · 개인)마다 따로 · 문서 수(양식 목록) 그대로 · 쓰기 권한 = 층 규칙(마스터 403).
|
||||
"""
|
||||
|
||||
@@ -88,7 +88,7 @@ def test_문서_옮기기는_자리만_파일과_문서_수_그대로(client: Te
|
||||
assert t.now["자리"] == {"SC-000001": ["옹벽", "찰쌓기"], "SC-000002": ["옹벽"]}
|
||||
t.do(op="move", 키="SC-000002", 새경로=[]) # 뿌리로 = 자리 지움
|
||||
assert t.now["자리"] == {"SC-000001": ["옹벽", "찰쌓기"]}
|
||||
assert t.do(404, op="move", 키="SC-000009", 새경로=[])["detail"].startswith("없는 사전조합")
|
||||
assert t.do(404, op="move", 키="SC-000009", 새경로=[])["detail"].startswith("없는 문서")
|
||||
assert t.do(404, op="move", 키="SC-000001", 새경로=["없음"])
|
||||
personal = layers.personal_dir(7, 42)
|
||||
assert sorted(p.name for p in (personal / "precombo").iterdir()) == [
|
||||
@@ -97,7 +97,7 @@ def test_문서_옮기기는_자리만_파일과_문서_수_그대로(client: Te
|
||||
"SC-000003.json",
|
||||
]
|
||||
assert _docs(client) == ["SC-000001", "SC-000002", "SC-000003"] # 자리 파일은 목록에 안 섞임
|
||||
assert (personal / pre.FILE).is_file()
|
||||
assert (personal / pre.file_of("precombo")).is_file()
|
||||
|
||||
|
||||
def test_폴더_이름_바꾸기_옮기기는_아래_자리가_따라감(client: TestClient):
|
||||
@@ -117,17 +117,16 @@ def test_폴더_이름_바꾸기_옮기기는_아래_자리가_따라감(client:
|
||||
assert t.now["알림"]["바뀐이름"] == ["나(1)"] and t.now["자리"]["SC-000001"] == ["나(1)"]
|
||||
|
||||
|
||||
def test_지우기는_빈_폴더만_문서가_있으면_까닭(client: TestClient):
|
||||
def test_지우기는_안에_든_문서째(client: TestClient):
|
||||
t = Tree(client)
|
||||
t.do(op="add-folder", 경로=[], 이름="가")
|
||||
t.do(op="add-folder", 경로=["가"], 이름="나")
|
||||
t.do(op="add-folder", 경로=["가", "나"], 이름="다")
|
||||
t.do(op="move", 키="SC-000003", 새경로=["가", "나", "다"])
|
||||
assert "사전조합 1개" in t.do(409, op="delete", 경로=["가"])["detail"]
|
||||
t.do(op="move", 키="SC-000003", 새경로=[])
|
||||
t.do(op="delete", 경로=["가"]) # 빈 아래 폴더까지
|
||||
got = t.do(op="delete", 경로=["가"]) # 아래 폴더 · 문서까지(716-4)
|
||||
assert got["알림"]["지운문서"] == ["SC-000003"]
|
||||
assert t.now["폴더"] == [] and t.now["자리"] == {}
|
||||
assert _docs(client) == ["SC-000001", "SC-000002", "SC-000003"]
|
||||
assert _docs(client) == ["SC-000001", "SC-000002"]
|
||||
|
||||
|
||||
def test_판이_다르면_409_고침_안_함(client: TestClient):
|
||||
|
||||
@@ -84,7 +84,8 @@ def test_고름에_owner_권한_서고_길에_명시_owner():
|
||||
side = _text("M02_MasterTemplete_UI_Side_Precombo.ts")
|
||||
assert "PRECOMBO_TITLE" not in side # 716-3 — 제목은 locale 키 「일위대가」
|
||||
assert "owner: scope.owner," in side and "canWrite: scope.canWrite," in side
|
||||
assert "listTemplates(at.layer, null, at.owner)" in side
|
||||
lib_side = _text("M02_MasterTemplete_UI_Side_Library.ts") # 716-4 서고 칸 한 벌
|
||||
assert "listTemplates(at.layer, null, at.owner)" in lib_side
|
||||
main = _text("M02_MasterTemplete_UI_Main.ts")
|
||||
assert (
|
||||
"const writable = (at: Selection): boolean => at.canWrite ?? canEdit(at.layer, isAdmin);"
|
||||
@@ -100,23 +101,26 @@ def test_고름에_owner_권한_서고_길에_명시_owner():
|
||||
|
||||
|
||||
def test_분류_고정_폴더_지움_자유_폴더_메뉴_끌어_옮기기():
|
||||
side = _text("M02_MasterTemplete_UI_Side_Precombo.ts")
|
||||
pre_side = _text("M02_MasterTemplete_UI_Side_Precombo.ts")
|
||||
side = _text("M02_MasterTemplete_UI_Side_Library.ts") # 716-4 서고 칸 한 벌
|
||||
for gone in ("groupRows", "fetchStructureDef", "Lib_Unsorted", "shape"):
|
||||
assert gone not in side, gone
|
||||
assert "fetchPrecomboTree(at.layer, at.owner)" in side
|
||||
assert "editPrecomboTree(at.layer, body, view.판, at.owner)" in side
|
||||
assert gone not in side and gone not in pre_side, gone
|
||||
assert "fetchPrecomboTree(at.layer, at.owner, kind)" in side
|
||||
assert "editPrecomboTree(at.layer, body, 판, at.owner, kind)" in side
|
||||
menu = side.split("const menuAt = (")[1].split("\n };")[0]
|
||||
for label in ('L("M02_TreeAddFolder")', 'tc("Lib_NewHere")', 'L("M02_TreeRename")'):
|
||||
for label in ('L("M02_TreeAddFolder")', "opt.text.newHere", 'L("M02_TreeRename")'):
|
||||
assert label in menu, label
|
||||
assert 'L("M02_Delete"), () => void remove(path, count)' in menu
|
||||
assert 'item(L("M02_Delete"), () => void remove([target]))' in menu
|
||||
assert 'opt.scope().canWrite ? undefined : tc("ReadOnly")' in menu # 읽기만 층 = 흐림
|
||||
remove = side.split("const remove = async (")[1].split("\n };")[0]
|
||||
assert 'if (count) return void showToast(tc("Lib_FolderFull")' in remove # 빈 폴더만
|
||||
assert "showConfirmDialog(ask" in remove and 'op: "delete", 대상:' in remove # 안에 든 것째
|
||||
assert 'newHere: tc("Lib_NewHere")' in pre_side
|
||||
assert '({ 이름: name, 구분: "", 상세구분: "", 단위: null, 담은로직: [] })' in pre_side
|
||||
make = side.split("const make = (path: string[]): void => {")[1].split("\n };")[0]
|
||||
assert 'const 문서 = { 이름: name, 구분: "", 상세구분: "", 단위: null, 담은로직: [] };' in make
|
||||
assert "{ 문서: opt.blank(name) }" in make
|
||||
assert 'if (path.length) await edit({ op: "move", 키: got.이름, 새경로: path });' in make
|
||||
assert "attachTreeDrag({" in side and "if (canWrite) drag.arm(row, node.id);" in side
|
||||
assert "openTreeMenu(event, menuAt([], 0));" in side # 빈 자리 = 뿌리 메뉴
|
||||
assert "openTreeMenu(event, menuAt([]));" in side # 빈 자리 = 뿌리 메뉴
|
||||
assert "open: toggled.get(id) ?? count > 0," in side
|
||||
lib = _text("M02_MasterTemplete_Library_List.ts")
|
||||
assert "export const nameDialog = (" in lib
|
||||
|
||||
Reference in New Issue
Block a user