# -*- coding: utf-8 -*- """M02 구조물 라이브러리(PLAN 26-3a) 자료 셈 회귀검증 — 폴더 나무 · 빈 폴더 · 깊이 5 · 찾기 · 폴더 목록. 항목 경로 = 폴더 이름 배열 + 마지막 마디가 항목 이름 · 찾는 글이 있으면 맞는 항목과 위 폴더만. Node 헬퍼(`helper_m02_treepanel.cjs`)가 TS 를 트랜스파일해 돌리고, 여기서 값을 판정한다. """ import json import os import subprocess import pytest HERE = os.path.dirname(os.path.abspath(__file__)) @pytest.fixture(scope="module") def results(): proc = subprocess.run( ["node", os.path.join(HERE, "helper_m02_treepanel.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_빈_폴더도_나무에_있음(results): tops = [f["path"] for f in results["all"]["folders"]] assert ["빈 폴더"] in tops dol = next(f for f in results["all"]["folders"] if f["path"] == ["돌기슭막이"]) assert ["돌기슭막이", "빈 아래"] in [f["path"] for f in dol["folders"]] def test_맨_위_항목은_폴더_밖(results): assert results["all"]["items"] == ["c4"] assert results["count"] == 4 def test_깊이_5_항목까지_이어짐(results): node = next(f for f in results["all"]["folders"] if f["path"] == ["다섯"]) for depth in range(2, 5): node = node["folders"][0] assert len(node["path"]) == depth assert node["items"] == ["c3"] def test_찾기는_맞는_항목과_위_폴더만(results): assert results["foundCount"] == 1 assert [f["path"] for f in results["found"]["folders"]] == [["돌기슭막이"]] assert results["found"]["folders"][0]["items"] == ["c2"] # 빈 폴더는 안 보임 assert results["none"] == {"path": [], "items": [], "folders": []} def test_키로도_찾음(results): assert results["byKey"] == 1 def test_찾기는_낱말_모두_AND(results): """PLAN 29-6 — 띄어쓰기로 나눈 낱말을 모두 담은 것만 · 대소문자 무시 · 빈 글 = 전부.""" assert results["and"] == [True, True, False, True, True] assert results["andTree"] == 1 # 「돌기슭막이 찰쌓기」 = c2 하나 def test_폴더_목록과_위_길(results): assert ["다섯", "둘", "셋", "넷"] in results["folders"] assert ["빈 폴더"] in results["folders"] assert results["ancestors"] == [ ["다섯"], ["다섯", "둘"], ["다섯", "둘", "셋"], ["다섯", "둘", "셋", "넷"], ] assert results["ancestorsTop"] == [] assert results["ancestorsNone"] == [] def test_안_또는_같음_판정(results): assert results["under"] == [True, True, False, False] def test_잎_이름과_폴더(results): assert results["leaf"] == "1.2m" assert results["dir"] == ["돌기슭막이", "메쌓기"] def test_여러_개_고르기_Ctrl_Shift(): """PLAN 30-2 — Ctrl = 하나 넣고 빼기(줄 동작 막음) · Shift = 기준에서 여기까지(보이는 차례) · 그냥 클릭 = 그것 하나(줄 동작 그대로).""" import re pick = os.path.join( HERE, "..", "..", "M02_MasterTemplete", "M02_MasterTemplete_TreePanel_Pick.ts" ) src = open(pick, encoding="utf-8").read() found = re.search(r"^export function pickClick[\s\S]*?^}", src, re.M) assert found, "pickClick 을 못 찾음" script = ( found.group(0).replace("export ", "") + "\nconst order = ['a', 'b', 'c', 'd', 'e'];" + "\nconst show = (r) => [[...r.picked].sort(), r.anchor, r.handled];" + "\nconst one = pickClick(new Set(), null, 'b', {ctrl: false, shift: false}, order);" + "\nconst two = pickClick(one.picked, one.anchor, 'd', {ctrl: true, shift: false}, order);" + "\nconst off = pickClick(two.picked, two.anchor, 'b', {ctrl: true, shift: false}, order);" + "\nconst range = pickClick(one.picked, 'b', 'e', {ctrl: false, shift: true}, order);" + "\nconst back = pickClick(one.picked, 'd', 'a', {ctrl: false, shift: true}, order);" + "\nconsole.log(JSON.stringify([one, two, off, range, back].map(show)));" ) proc = subprocess.run( ["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript", "-e", script], capture_output=True, text=True, encoding="utf-8", timeout=60, ) # fmt: skip assert proc.returncode == 0, proc.stderr assert json.loads(proc.stdout) == [ [["b"], "b", False], [["b", "d"], "d", True], [["d"], "b", True], [["b", "c", "d", "e"], "b", True], [["a", "b", "c", "d"], "d", True], ]