feat(837-1): 일위대가 불러오기 층 가르기 걷음 — 누가 하든 자기 개인 층
- 화면: 왼쪽 층 고름(개인·회사·마스터) · 머리 층 배지 · 작업 중 줄의 층 글 걷음(Hopyo_Side · Hopyo_Import) - 서버: 불러오기 길(Hopyo_Router)은 경로 층 · 주인 · 몸 층을 무시하고 개인 층 고정 · 단산 블록 → 가져온 식(from-hopyo · preview)도 개인 층 - 시험: 새 지킴 시험 test_837_1 · 낡은 시험 고침(785_4 층 고름 → 층 가르기 없음 · dirty 글 / 705_2 남의 층 403 → 경로 층 무시 200) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
그대로 쓰는 길(목록 · 올림 · 엑셀 보기 머리 · 격자 · 지우기 · 모두 고정 · 붙여넣기 · 다시 잇기 ·
|
||||
구조물 조합 · 칠 = B09 · M01 이 씀) · 옛 맞춤 길(호표 하나 맞춤 · 미리 셈 · 실무 호표 · 확인 만들기 · 안 이은 줄 ·
|
||||
단가산출 만들기)은 785-7 에서 지움.
|
||||
층 권한 = `Router_Items._root` · ⚠ 등록은 `main.py`(로그인만).
|
||||
층 = 늘 개인(요구 855 · 위 `_root` 가 고정) · ⚠ 등록은 `main.py`(로그인만).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -33,11 +33,17 @@ from M02_MasterTemplete import M02_Hopyo_View as hopyo_view_
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_San_MatPick as mat_pick
|
||||
from M02_MasterTemplete import M02_Sangeun_FromHopyo as from_hopyo
|
||||
from M02_MasterTemplete.M02_MasterTemplete_Router_Items import Layer, Session, _call, _root
|
||||
from M02_MasterTemplete.M02_MasterTemplete_Router_Items import Layer, Session, _call
|
||||
from M02_MasterTemplete.M02_MasterTemplete_Router_Items import _root as _layer_root
|
||||
|
||||
router = APIRouter(prefix="/api/m02", tags=["M02 Hopyo"])
|
||||
|
||||
|
||||
def _root(session: Session, layer: str, owner: str | None = None, write: bool = False) -> Path:
|
||||
"""불러오기 자리 = 늘 자기 개인 층(요구 855) — 경로 층 · 주인 글 · 몸 층은 받되 무시(회사 · 마스터 · 남의 층 불가)."""
|
||||
return _layer_root(session, "personal", None, write=write)
|
||||
|
||||
|
||||
# ── [수정] 보기(809-1) — 경쟁사 로직 인정 · 요소만 연결 · 읽기만 ─────────────────
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Hopyo_Import.ts
|
||||
* 일위대가 불러오기 본 칸 보기(PLAN 785-4 · 요구 642-11 — 마법사 `Hopyo_Wizard` 갈음 · 가운데 상세 페이지 없음) —
|
||||
* 왼쪽 [불러오기] → 층 · 파일 고름 → 올림(`Hopyo_Side`) → 바로 이 보기:
|
||||
* · 머리 막대 = 엑셀 보기 몸(`Hopyo_Sheet` — 시트 · 호표 · 검산 · 연결)의 머리에 층 · [끝내기]를 덧붙임
|
||||
* 왼쪽 [불러오기] → 파일 고름 → 올림(`Hopyo_Side` · 층 가르기 없음 — 누가 하든 자기 개인 층 · 요구 855) → 바로 이 보기:
|
||||
* · 머리 막대 = 엑셀 보기 몸(`Hopyo_Sheet` — 시트 · 호표 · 검산 · 연결)의 머리에 [끝내기]를 덧붙임
|
||||
* · 아래 = 엑셀(탭 셋) + 오른쪽 호표 · 단가산출 목록(`Hopyo_Sheet` · `Hopyo_SheetSk`) · 만들기 = 호표 줄 [내 일위대가로](807-1)
|
||||
* · [끝내기] = 물음(안 만든 단가산출 알림) → 서버가 이번 HP · 격자 저장본 지움 ·
|
||||
* 만든 일위대가 · 단가산출(엑셀 사본 걷고 출처만) · 별칭 장부 남음 · 옛 「안 이은 줄」 띠는 785-7 에서 지움(판정 「고를 것」)
|
||||
@@ -10,34 +10,19 @@
|
||||
* 하루 지난 것은 서버가 지움 · 엑셀 자료는 보관하지 않음(필요하면 다시 올림)
|
||||
* ========================================================================== */
|
||||
|
||||
import { createBadge } from "@ui/ui_template_badge";
|
||||
import { createButton, el, showConfirmDialog, showToast } from "@ui/ui_template_elements";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import { createText } from "@ui/ui_template_text";
|
||||
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
|
||||
import { deleteHopyo, type HopyoFile, type HopyoScope } from "./M02_MasterTemplete_Hopyo_Api";
|
||||
import type { HopyoSheetBody } from "./M02_MasterTemplete_Hopyo_Sheet";
|
||||
import { accessFor, type ManageLayer } from "./M02_MasterTemplete_Layer";
|
||||
import "./M02_MasterTemplete_Hopyo.css";
|
||||
|
||||
export const LAYERS: { layer: ManageLayer; text: string }[] = [
|
||||
{ layer: "personal", text: "개인" },
|
||||
{ layer: "company", text: "회사" },
|
||||
{ layer: "system", text: "마스터" },
|
||||
];
|
||||
export const layerText = (layer: string): string =>
|
||||
LAYERS.find((x) => x.layer === layer)?.text ?? layer;
|
||||
/** 불러오기 자리 = 늘 자기 개인 층(요구 855 — 층 가르기 없음) */
|
||||
export const MINE: HopyoScope = { layer: "personal", owner: null };
|
||||
|
||||
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
|
||||
|
||||
/** 쓸 수 있는 자기 층(개인 · 회사 · 마스터 차례) */
|
||||
export async function writableLayers(): Promise<ManageLayer[]> {
|
||||
const user = await fetchSessionUser().catch(() => null);
|
||||
return LAYERS.map((x) => x.layer).filter((layer) => accessFor(layer, null, user).canWrite);
|
||||
}
|
||||
|
||||
export interface HopyoImportArgs {
|
||||
layer: ManageLayer;
|
||||
file: HopyoFile;
|
||||
/** [끝내기] 로 올린 파일을 지웠음 */
|
||||
onEnded: () => void;
|
||||
@@ -52,8 +37,8 @@ export interface HopyoImport {
|
||||
}
|
||||
|
||||
export function buildHopyoImport(a: HopyoImportArgs): HopyoImport {
|
||||
const { layer, file } = a;
|
||||
const scope: HopyoScope = { layer, owner: null };
|
||||
const { file } = a;
|
||||
const scope = MINE;
|
||||
let held = true;
|
||||
let body: HopyoSheetBody | null = null;
|
||||
|
||||
@@ -74,7 +59,6 @@ export function buildHopyoImport(a: HopyoImportArgs): HopyoImport {
|
||||
// ── 머리 막대에 덧붙일 것 ──
|
||||
const finish = createButton({ label: "끝내기", variant: "filled", size: 28, onClick: () => void end() }); // prettier-ignore
|
||||
finish.dataset.hopyoFinish = "";
|
||||
const layerNote = createBadge(`${layerText(layer)} 층`, "neutral");
|
||||
|
||||
async function end(): Promise<void> {
|
||||
finish.disabled = true;
|
||||
@@ -111,7 +95,7 @@ export function buildHopyoImport(a: HopyoImportArgs): HopyoImport {
|
||||
파일: file.파일,
|
||||
target: scope,
|
||||
canWrite: true,
|
||||
headExtra: [layerNote, finish],
|
||||
headExtra: [finish],
|
||||
});
|
||||
body = view;
|
||||
root.replaceChildren(...view.parts);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* 좌측 「일위대가 불러오기」 컨테이너(PLAN 324-4 · 718-1 한시 · 785-4) —
|
||||
* · [불러오기](구조물 조합 층 단추 꼴 `m02-layers__btn`) = 바로 파일 하나 고르기 창(엑셀 · 경쟁사 .estx · .STC · 814-1) → 올림 → 본 칸 불러오기 보기
|
||||
* (`M02_MasterTemplete_Hopyo_Import` · 엑셀 + 오른쪽 호표 목록 · 가운데 상세 페이지 없음 · 누름 하나)
|
||||
* · 결과 층 고름 = 작은 고름(쓸 수 있는 층 둘 이상일 때만 · 일반 = 개인 하나라 안 보임)
|
||||
* · 「작업 중」 줄 = 층마다 서버 목록(같은 엑셀 작업 중 이어서) · 누르면 본 칸 불러오기 보기
|
||||
* · 층 가르기 없음 — 누가 하든 올린 것 · 만든 것 = 자기 개인 층(요구 855 · 공유는 만든 뒤 본인이)
|
||||
* · 「작업 중」 줄 = 자기 개인 층 서버 목록(같은 엑셀 작업 중 이어서) · 누르면 본 칸 불러오기 보기
|
||||
* · 다른 엑셀 [불러오기] · 다른 화면으로 = 지금 보기 떠남(올린 파일 지움 · 743-1) — 고르고 안 만든 확인 캐시가 있으면 물음 한 번
|
||||
* 옛 [별칭 장부](338-3)는 걷음(815-2) — 별칭은 M01 요소 「별칭」 칸 · M01 별칭 모아 보기.
|
||||
* ========================================================================== */
|
||||
@@ -12,22 +12,9 @@
|
||||
import { createButton, el, showConfirmDialog, showToast } from "@ui/ui_template_elements";
|
||||
import { createEmpty } from "@ui/ui_template_empty";
|
||||
import { createListRow } from "@ui/ui_template_list_row";
|
||||
import { createSegment } from "@ui/ui_template_segment";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import {
|
||||
listHopyo,
|
||||
uploadHopyo,
|
||||
type HopyoFile,
|
||||
type HopyoScope,
|
||||
} from "./M02_MasterTemplete_Hopyo_Api";
|
||||
import {
|
||||
buildHopyoImport,
|
||||
LAYERS,
|
||||
layerText,
|
||||
writableLayers,
|
||||
type HopyoImport,
|
||||
} from "./M02_MasterTemplete_Hopyo_Import";
|
||||
import type { ManageLayer } from "./M02_MasterTemplete_Layer";
|
||||
import { listHopyo, uploadHopyo, type HopyoFile } from "./M02_MasterTemplete_Hopyo_Api";
|
||||
import { buildHopyoImport, MINE, type HopyoImport } from "./M02_MasterTemplete_Hopyo_Import";
|
||||
|
||||
export interface HopyoSectionOptions {
|
||||
/** 저장 안 한 고침을 버릴지 확인 — 취소면 false */
|
||||
@@ -46,12 +33,9 @@ export interface HopyoSection {
|
||||
}
|
||||
|
||||
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
|
||||
const scopeOf = (layer: ManageLayer): HopyoScope => ({ layer, owner: null });
|
||||
|
||||
export function buildHopyoSection(opt: HopyoSectionOptions): HopyoSection {
|
||||
const title = L("M02_HopyoImport");
|
||||
let layers: ManageLayer[] = [];
|
||||
let pick: ManageLayer = "personal";
|
||||
const input = el("input", {
|
||||
attrs: { type: "file", accept: ".xls,.xlsx,.estx,.stc", "data-hopyo-file": "" },
|
||||
}) as HTMLInputElement;
|
||||
@@ -65,50 +49,25 @@ export function buildHopyoSection(opt: HopyoSectionOptions): HopyoSection {
|
||||
start.title =
|
||||
"파일 하나 — 경쟁사 엑셀(.xls · .xlsx) 또는 경쟁사 파일(.estx · .STC) → 우리 정보로 다시 만듦(한시 · 끝내거나 떠나면 올린 파일 지움)";
|
||||
start.dataset.hopyoWizardOpen = "";
|
||||
const seg = el("div", { className: "m02-side__row" });
|
||||
seg.hidden = true;
|
||||
const jobs = el("div", { className: "m02-hopyo-side", attrs: { "data-hopyo-jobs": "" } });
|
||||
const body = el("div", {
|
||||
className: "m02-hopyo-side",
|
||||
children: [el("div", { className: "m02-side__row", children: [start, input] }), seg, jobs],
|
||||
children: [el("div", { className: "m02-side__row", children: [start, input] }), jobs],
|
||||
});
|
||||
let open: { key: string; view: HTMLElement; imp?: HopyoImport } | null = null;
|
||||
|
||||
/** 층 고름 · 작업 중 줄 — 쓸 수 있는 층을 처음 한 번 읽음 */
|
||||
/** 작업 중 줄 — 자기 개인 층 서버 목록 */
|
||||
async function refresh(): Promise<void> {
|
||||
if (!layers.length) {
|
||||
layers = await writableLayers();
|
||||
if (!layers.includes(pick)) pick = layers[0] ?? "personal";
|
||||
if (layers.length > 1) {
|
||||
const s = createSegment({
|
||||
options: LAYERS.filter((x) => layers.includes(x.layer)).map((x) => ({ value: x.layer, label: x.text })), // prettier-ignore
|
||||
value: pick,
|
||||
size: 24,
|
||||
onChange: (v) => (pick = v as ManageLayer),
|
||||
});
|
||||
s.root.dataset.hopyoLayer = "";
|
||||
seg.replaceChildren(s.root);
|
||||
seg.hidden = false;
|
||||
}
|
||||
}
|
||||
const rows = (
|
||||
await Promise.all(
|
||||
layers.map((layer) =>
|
||||
listHopyo(scopeOf(layer))
|
||||
.then((list) => list.map((f) => ({ layer, f })))
|
||||
.catch(() => []),
|
||||
),
|
||||
)
|
||||
).flat();
|
||||
const rows = await listHopyo(MINE).catch(() => []);
|
||||
jobs.replaceChildren(
|
||||
...(rows.length
|
||||
? rows.map(({ layer, f }) => {
|
||||
? rows.map((f) => {
|
||||
const row = createListRow({
|
||||
label: f.파일,
|
||||
sub: `호표 ${f.호표수} · ${layerText(layer)} — 이어서`,
|
||||
sub: `호표 ${f.호표수} — 이어서`,
|
||||
size: 28,
|
||||
active: open?.imp !== undefined && open.key === f.키,
|
||||
onClick: () => void show(layer, f),
|
||||
onClick: () => void show(f),
|
||||
});
|
||||
row.dataset.hopyoKey = f.키;
|
||||
return row;
|
||||
@@ -119,8 +78,6 @@ export function buildHopyoSection(opt: HopyoSectionOptions): HopyoSection {
|
||||
|
||||
/** [불러오기] — 지금 보기를 떠날 수 있는지 묻고 바로 파일 고르기 창 */
|
||||
async function choose(): Promise<void> {
|
||||
if (!layers.length) await refresh().catch(() => {});
|
||||
if (!layers.length) return void showToast("쓸 수 있는 층 없음", "warning");
|
||||
if (!(await leaveOk())) return;
|
||||
input.click();
|
||||
}
|
||||
@@ -129,14 +86,14 @@ export function buildHopyoSection(opt: HopyoSectionOptions): HopyoSection {
|
||||
input.value = "";
|
||||
if (!file) return;
|
||||
start.disabled = true;
|
||||
uploadHopyo(scopeOf(pick), file)
|
||||
uploadHopyo(MINE, file)
|
||||
.then((got: HopyoFile & { 이어서?: boolean; 원파일?: boolean }) => {
|
||||
const tool = /\.estx$/i.test(file.name) ? "EST" : "STmate"; // 810-3 원파일 올림 안내
|
||||
if (got.이어서) showToast(`같은 파일 작업 중 — ${got.키} 이어서`, "info");
|
||||
else if (got.원파일) showToast(`${tool} 파일 — 단가산출표 · 산출근거`, "success");
|
||||
else showToast(`올림 — 호표 ${got.호표수}`, got.어긋남 ? "warning" : "success");
|
||||
if (got.읽기불가) showToast(`가져온 식으로 열 수 없음 — ${got.읽기불가}`, "warning");
|
||||
return show(pick, got, true);
|
||||
return show(got, true);
|
||||
})
|
||||
.catch((error) => showToast(why(error), "error"))
|
||||
.finally(() => (start.disabled = false));
|
||||
@@ -153,12 +110,11 @@ export function buildHopyoSection(opt: HopyoSectionOptions): HopyoSection {
|
||||
}
|
||||
|
||||
/** 본 칸에 불러오기 보기 — 같은 파일이면 그대로 */
|
||||
async function show(layer: ManageLayer, file: HopyoFile, checked = false): Promise<void> {
|
||||
async function show(file: HopyoFile, checked = false): Promise<void> {
|
||||
if (open?.imp && open.key === file.키 && open.view.isConnected) return;
|
||||
if (!checked && !(await leaveOk())) return;
|
||||
open?.imp?.leave(); // 불러오기 떠남 = 올린 파일 지움(743-1)
|
||||
const imp = buildHopyoImport({
|
||||
layer,
|
||||
file,
|
||||
onEnded: () => {
|
||||
open = null;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
가져온 식(793-2 · `M02_Sangeun_Imported`) `…/sangeun/imported`(만들기 · 층 쓰기) · `/price` ·
|
||||
`/link`(화면 캐시 문서 고침 · 저장 없음 · 층 읽기 · 기초로 바꾸기 없음 · 809-1) ·
|
||||
불러오기 단산 블록 → 가져온 식(793-3b · `M02_Sangeun_FromHopyo`) `…/sangeun/from-hopyo`
|
||||
(만들 층 쓰기 · 몸 `문서` = 미리 보기에서 고친 문서) · 미리 보기 `…/from-hopyo/preview`(층 읽기 · 안 씀 · 835-1).
|
||||
(늘 개인 층 쓰기 · 몸 `문서` = 미리 보기에서 고친 문서) · 미리 보기 `…/from-hopyo/preview`(층 읽기 · 안 씀 · 835-1).
|
||||
로직 사본(795-1 · `M02_Sangeun_Copy`) `…/sangeun/copy/price` [단가 갱신] · `/regen` [재생성]
|
||||
(화면 캐시 문서에 할일 얹음 · 저장 없음 · 층 읽기).
|
||||
수식 탭(796-2b · `M02_Sangeun_Equation`) `…/sangeun/equation`(층 읽기) · `/preview`(고침 미리 셈 ·
|
||||
@@ -176,8 +176,8 @@ def sangeun_from_hopyo(
|
||||
) -> dict:
|
||||
"""불러오기 단산 블록 → 가져온 식 SK(793-3b) — 경로 층 = 만들 층 · 몸 층 = 파일 층 ·
|
||||
답 = {이름, 검산, 단가기준, 다시씀}(같은 내용 = 그 SK)."""
|
||||
folder = _root(session, layer, owner, write=True)
|
||||
source = _root(session, body.층, body.주인)
|
||||
folder = _root(session, "personal", None, write=True) # 불러오기 = 늘 개인 층(요구 855)
|
||||
source = _root(session, "personal", None)
|
||||
mine = _root(session, "personal", None, write=body.고름 is not None)
|
||||
fp, name = _call(source, mat_pick.fingerprint, source, body.키) # 827-3 그 파일 재료 고름
|
||||
if body.고름 is not None and fp:
|
||||
@@ -200,8 +200,8 @@ def sangeun_from_hopyo_preview(
|
||||
) -> dict:
|
||||
"""[가져온 식으로 열기] 미리 보기(835-1 · 요구 852-1) — 층에 안 씀(재료 고름도 메모리) ·
|
||||
답 = {문서, 검산, 같은것(같은 내용 SK 키 | None)} · [저장] = `from-hopyo` 몸 `문서`."""
|
||||
folder = _root(session, layer, owner)
|
||||
source = _root(session, body.층, body.주인)
|
||||
folder = _root(session, "personal", None) # 불러오기 = 늘 개인 층(요구 855)
|
||||
source = _root(session, "personal", None)
|
||||
mine = _root(session, "personal", None)
|
||||
fp, _ = _call(source, mat_pick.fingerprint, source, body.키)
|
||||
picks = mat_pick.merge(mat_pick.read(mine, fp)["고름"], body.고름) if fp else {}
|
||||
|
||||
@@ -352,10 +352,11 @@ def test_구조물_조합으로_규격_묶음_울진_돌기슭막이(client):
|
||||
assert master_hash() == before
|
||||
|
||||
|
||||
def test_구조물_조합으로_남의_층_403(client):
|
||||
def test_구조물_조합으로_경로_층_무시_늘_개인(client): # 837-1 · 요구 855 — 옛 「남의 층 403」 갈음
|
||||
key = _upload(client, "대흥")
|
||||
body = {"층": "personal", "키": key, "번": 17}
|
||||
assert client.post("/api/m02/layers/company/hopyo-item", json=body).status_code == 403
|
||||
assert client.post("/api/m02/layers/company/hopyo-item", json=body).status_code == 200
|
||||
assert list((layers_dir() / "labor").glob("*.json")) # 쓴 곳 = 개인 층(`layers_dir`)
|
||||
|
||||
|
||||
def test_구조물_조합으로_같은_내용_409_씀_새로(client):
|
||||
|
||||
@@ -27,20 +27,20 @@ def test_누름_하나_바로_엑셀():
|
||||
choose = side.split("async function choose()")[1].split("input.addEventListener")[0]
|
||||
assert "input.click();" in choose # [불러오기] = 바로 파일 고르기 창
|
||||
change = side.split('input.addEventListener("change"')[1].split("async function leaveOk")[0]
|
||||
assert (
|
||||
"uploadHopyo(scopeOf(pick), file)" in change and "return show(pick, got, true);" in change
|
||||
)
|
||||
assert "uploadHopyo(MINE, file)" in change and "return show(got, true);" in change
|
||||
show = side.split("async function show(")[1].split("async function showLedger")[0]
|
||||
assert "buildHopyoImport({" in show and "opt.show(" in show # 올림 뒤 본 칸 바로
|
||||
imp = _src("Import")
|
||||
assert "m.buildHopyoSheet({" in imp and "headExtra: [layerNote, finish]" in imp
|
||||
assert "m.buildHopyoSheet({" in imp and "headExtra: [finish]" in imp
|
||||
|
||||
|
||||
def test_결과_층_고름_둘_이상일_때만():
|
||||
side = _src("Side")
|
||||
assert "layers.length > 1" in side and "seg.hidden = false;" in side
|
||||
assert "seg.hidden = true;" in side # 처음 숨김 — 일반(개인 하나)은 안 보임
|
||||
assert 'let pick: ManageLayer = "personal";' in side
|
||||
def test_층_가르기_없음_늘_개인(): # 837-1 · 요구 855 — 예전 「결과 층 고름」 걷음
|
||||
side, imp = _src("Side"), _src("Import")
|
||||
for gone in ("createSegment", "writableLayers", "LAYERS", "layerText", "seg.hidden", "pick"):
|
||||
assert gone not in side, gone
|
||||
for gone in ("LAYERS", "layerText", "writableLayers", "layerNote", "createBadge"):
|
||||
assert gone not in imp, gone
|
||||
assert 'export const MINE: HopyoScope = { layer: "personal", owner: null };' in imp
|
||||
|
||||
|
||||
def test_끝내기_머리_막대_끝냄_뒤_빈_본_칸():
|
||||
@@ -71,5 +71,5 @@ def test_부품_상자만_700줄_늘리지_않음():
|
||||
def test_해시_캐시_열쇠_없음():
|
||||
# 809-1 — 판정 고름 캐시(`Hopyo_Picks`) 걷음 · 옮기기 = [내 일위대가로]
|
||||
assert not (M02 / "logic" / "M02_MasterTemplete_Hopyo_Picks.ts").exists()
|
||||
assert "draftKey" not in _src("Import") and "dirty: () => false" in _src("Import")
|
||||
assert "draftKey" not in _src("Import") and "dirty: () => body?.matDirty()" in _src("Import")
|
||||
assert "location.hash" not in _src("Import") + _src("Side") # 새 해시 열쇠 없음
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""837-1 일위대가 불러오기 = 늘 자기 개인 층(요구 855) — 서버 · 화면 없이 돎.
|
||||
불러오기 길(`M02_Hopyo_Router` · `Sangeun_Router` from-hopyo)은 경로 층 · 주인 · 몸 층을 무시하고 개인 층만 씀."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Router as hopyo # noqa: E402
|
||||
|
||||
|
||||
def test_불러오기_길_층_고정(monkeypatch):
|
||||
seen: list[tuple] = []
|
||||
monkeypatch.setattr(
|
||||
hopyo,
|
||||
"_layer_root",
|
||||
lambda s, layer, owner=None, write=False: seen.append((layer, owner, write)),
|
||||
)
|
||||
for layer, owner in (("company", "3"), ("system", None), ("personal", "3/9")):
|
||||
hopyo._root({}, layer, owner, write=True)
|
||||
hopyo._root({}, layer, owner)
|
||||
assert seen == [("personal", None, True), ("personal", None, False)] * 3
|
||||
|
||||
|
||||
def test_라우터_루트_모두_고정_함수():
|
||||
src = (ROOT / "M02_MasterTemplete" / "M02_Hopyo_Router.py").read_text(encoding="utf-8")
|
||||
assert "import _root as _layer_root" in src
|
||||
assert "_layer_root(" in src.split("def _root(")[1].split("\n\n")[0]
|
||||
assert src.count("_layer_root(") == 1 # 층 고르는 길은 이 한 곳뿐
|
||||
|
||||
|
||||
def test_단산_블록_만들기_개인_층만():
|
||||
src = (ROOT / "M02_MasterTemplete" / "M02_Sangeun_Router.py").read_text(encoding="utf-8")
|
||||
for fn in ("def sangeun_from_hopyo(", "def sangeun_from_hopyo_preview("):
|
||||
body = src.split(fn)[1].split("\n@router")[0]
|
||||
assert "body.층" not in body and "_root(session, layer" not in body, fn
|
||||
assert '_root(session, "personal", None' in body, fn
|
||||
|
||||
|
||||
def test_화면_층_고름_없음():
|
||||
m02 = ROOT / "M02_MasterTemplete"
|
||||
side = (m02 / "M02_MasterTemplete_Hopyo_Side.ts").read_text(encoding="utf-8")
|
||||
imp = (m02 / "M02_MasterTemplete_Hopyo_Import.ts").read_text(encoding="utf-8")
|
||||
assert "createSegment" not in side and "writableLayers" not in side + imp
|
||||
assert '"회사"' not in side + imp and '"마스터"' not in side + imp
|
||||
Reference in New Issue
Block a user