/** * 항목 고르기 자료 쪽 검증 — 나무 · 찾기 · 돌려줄 summary_item · 폴더 줄(21-1 · 21-9 서버 모양). * 실행: node --experimental-strip-types resources/tester/ui/item-picker-data.test.ts */ import assert from "node:assert/strict"; import { buildTree, filterRows, folderLines, isCurrent, itemName, pathIds, pathOf, rowId, rowTip, shown, toSummary, type PickerRow, type TreeLabels, } from "../../../ui_template/ui_template_item_picker_data.ts"; const labels: TreeLabels = { layers: { system: "마스터", company: "회사", personal: "개인" }, unclassified: "분류 없음", }; /** 서버 줄 그대로 — `열` 은 집계표 열 id · 보이는 이름은 종류 + 분류 */ const row = ( key: string, 열: string, 종류: string, 분류: [string, string, string], layer: PickerRow["layer"], extra: Partial = {}, ): PickerRow => ({ key, 열, 종류, 분류1: 분류[0], 분류2: 분류[1], 분류3: 분류[2], 단위: "m", 일위대가: "UA000004", 자재조합: "UM000001", 도번: "SD01010100", 면적번: "SA01010100", 재료번: "SM01010100", layer, owner: layer === "company" ? "샘플회사" : layer === "personal" ? "샘플회사/사용자" : null, ...extra, }); const rows: PickerRow[] = [ row("SS000001", "rv", "돌기슭막이", ["메쌓기", "1.2m", "기초유"], "system", { names: { 일위대가: "돌기슭막이 메쌓기 일위대가", 자재조합: null, 도번: "돌기슭막이 도면", 면적번: "돌기슭막이 면적산출", 재료번: "돌기슭막이 재료산출", }, }), row("SS000002", "rv2", "돌기슭막이", ["메쌓기", "1.5m", "기초유"], "system"), row("SS000003", "rv3", "돌기슭막이", ["찰쌓기", "1.2m", ""], "system"), row("SS000004", "pv", "콘크리트포장", ["일반", "T=20cm", ""], "system"), row("SS000005", "pg", "관보호공", ["콘크리트", "", ""], "system"), row("SS000101", "c1", "돌기슭막이", ["메쌓기", "1.2m", "기초유"], "company"), row("SS000102", "c2", "옹벽", ["", "H=2m", ""], "company"), row("SS000201", "p1", "돌기슭막이", ["메쌓기", "1.2m", "기초유"], "personal"), ]; // 이름 = 종류 + 분류 이음 · 비면 열 id · 키 assert.equal(itemName(rows[0]), "돌기슭막이 메쌓기 1.2m 기초유"); assert.equal(itemName(rows[6]), "옹벽 H=2m"); assert.equal(itemName({ key: "SS000009", 열: "zz" }), "zz"); assert.equal(itemName({ key: "SS000009", 열: "" }), "SS000009"); // 「새 분류N」 자리표는 이름 · 나무 이름에서 뺌 assert.equal(shown("새 분류1"), ""); assert.equal(shown("새 분류 12"), ""); assert.equal(shown("새 분류"), ""); assert.equal(shown("새 분류표"), "새 분류표"); // 자리표가 아닌 진짜 이름은 그대로 assert.equal(shown(undefined), ""); const fresh = row("SS000300", "n1", "새 분류1", ["새 분류2", "", ""], "personal", { 분류3: "새 분류3", }); assert.equal(itemName(fresh), "n1"); // 이름이 모두 자리표면 열 id assert.equal( itemName(row("SS000301", "n2", "옹벽", ["새 분류1", "H=2m", ""], "personal")), "옹벽 H=2m", ); const placeholderTree = buildTree([fresh], labels); assert.equal(placeholderTree[2].children?.[0].label, "n1"); // 경로가 비면 폴더 없이 그룹 바로 밑 잎 assert.deepEqual(pathIds(fresh, labels), ["personal"]); // 툴팁 = 층 이름 + 키 assert.equal(rowTip(rows[7], labels), "개인 · SS000201"); assert.equal(rowTip(rows[5], labels), "회사 · SS000101"); // 나무 — 그룹은 늘 마스터 · 회사 · 개인 차례 · 그룹 → 종류 → 분류1 → 잎 const tree = buildTree(rows, labels); assert.deepEqual( tree.map((g) => [g.label, g.count]), [ ["마스터", 5], ["회사", 2], ["개인", 1], ], ); const system = tree[0]; assert.deepEqual( system.children?.map((k) => [k.label, k.count]), [ ["돌기슭막이", 3], ["콘크리트포장", 1], ["관보호공", 1], ], ); const cat = system.children?.[0].children?.[0]; assert.equal(cat?.label, "메쌓기"); assert.equal(cat?.count, 2); const size = cat?.children?.[0]; // 폴더 깊이 자유 — 경로 앞부분이 모두 폴더 assert.equal(size?.label, "1.2m"); assert.deepEqual(size?.folder, { layer: "system", path: ["돌기슭막이", "메쌓기", "1.2m"] }); assert.deepEqual(tree[2].folder, { layer: "personal", path: [] }); // 그룹 줄 = 빈 경로 const leaf = size?.children?.[0]; assert.equal(leaf?.label, "돌기슭막이 메쌓기 1.2m 기초유"); assert.equal(leaf?.tip, "마스터 · SS000001"); // 툴팁 = 층 이름 + 키 assert.equal(leaf?.count, null); assert.ok(leaf?.row); // 경로가 짧은 항목은 얕은 폴더에(옹벽 H=2m = 폴더 옹벽 · 잎 「옹벽 H=2m」) assert.equal(tree[1].children?.[1].label, "옹벽"); assert.equal(tree[1].children?.[1].children?.[0].label, "옹벽 H=2m"); assert.equal(tree[1].children?.[1].children?.[0].folder, undefined); // 잎에는 folder 없음 // 서버가 준 `경로` 가 종류 · 분류보다 앞 — 이름 = 경로 이음 · 자리표는 뺌 const deep = row("SS000400", "d1", "무시", ["무시", "", ""], "personal", { 경로: ["가", "나", "다", "라", "마"], }); assert.deepEqual(pathOf(deep), ["가", "나", "다", "라", "마"]); assert.equal(itemName(deep), "가 나 다 라 마"); assert.deepEqual(pathIds(deep), [ "personal", "personal|가", "personal|가|나", "personal|가|나|다", "personal|가|나|다|라", ]); assert.deepEqual(pathOf({ key: "k", 열: "x", 경로: ["새 분류1", "옹벽"] }), ["옹벽"]); const deepTree = buildTree([deep], labels)[2]; assert.equal(deepTree.children?.[0].children?.[0].children?.[0].children?.[0].label, "라"); assert.equal(deepTree.count, 1); // 빈 그룹도 한 줄은 남음(회사 없는 사람의 회사 · 개인 그룹) const onlySystem = buildTree( rows.filter((r) => r.layer === "system"), labels, ); assert.deepEqual( onlySystem.map((g) => [g.id, g.count, g.children?.length]), [ ["system", 5, 3], ["company", 0, 0], ["personal", 0, 0], ], ); // 잎 id 는 층 · 주인 · 키 — 같은 키라도 층이 다르면 다른 줄 const twin = { ...rows[0], layer: "company" as const, owner: "샘플회사" }; assert.notEqual(rowId(rows[0]), rowId(twin)); // 찾기 — 이름 · 키 · 연결 이름 · 여러 낱말은 모두 들어 있어야 함 · 빈 글은 전부 assert.equal(filterRows(rows, "").length, rows.length); assert.equal(filterRows(rows, " ").length, rows.length); assert.deepEqual( filterRows(rows, "옹벽").map((r) => r.key), ["SS000102"], ); assert.deepEqual( filterRows(rows, "ss000004").map((r) => r.key), ["SS000004"], ); assert.deepEqual( filterRows(rows, "돌기슭막이 1.5m").map((r) => r.key), ["SS000002"], ); assert.deepEqual( filterRows(rows, "돌기슭막이 도면").map((r) => r.key), ["SS000001"], ); // 서버가 준 이름(names)으로도 찾음 assert.equal(filterRows(rows, "없는이름").length, 0); // 돌려줄 summary_item — 마스터는 owner null · 회사 · 개인은 주인 · 이름은 보이는 이름 assert.deepEqual(toSummary(rows[0]), { layer: "system", owner: null, key: "SS000001", name: "돌기슭막이 메쌓기 1.2m 기초유", }); const mine = rows[7]; assert.deepEqual(toSummary(mine), { layer: "personal", owner: "샘플회사/사용자", key: "SS000201", name: "돌기슭막이 메쌓기 1.2m 기초유", }); // layer 없는 줄(옛 서버)은 마스터로 봄 assert.equal(toSummary({ key: "SS000009", 열: "임시" }).layer, "system"); // 지금 지정된 항목인지 assert.ok(isCurrent(toSummary(mine), mine)); assert.ok(!isCurrent(toSummary(mine), rows[0])); assert.ok(!isCurrent(null, rows[0])); // 처음 펼칠 자리 assert.deepEqual(pathIds(rows[0], labels), [ "system", "system|돌기슭막이", "system|돌기슭막이|메쌓기", "system|돌기슭막이|메쌓기|1.2m", ]); // 폴더 모양 — 다섯 줄 · 이름은 서버 names · 없으면 키만(작은 글) · 둘 다 없으면 빈 키 const lines = folderLines(rows[0]); assert.deepEqual( lines.map((l) => l.kind), ["drawing", "area", "material", "combo", "unitPrice"], ); assert.deepEqual(lines[0], { kind: "drawing", name: "돌기슭막이 도면", key: "SD01010100" }); assert.deepEqual(lines[3], { kind: "combo", name: null, key: "UM000001" }); // names 에 없음 assert.deepEqual(folderLines(rows[1])[0], { kind: "drawing", name: null, key: "SD01010100" }); // names 자체가 없음 assert.deepEqual(folderLines({ key: "SS1", 열: "x" })[4], { kind: "unitPrice", name: null, key: "", }); console.log("항목 고르기 자료 시험 통과");