diff --git a/resources/tester/helper_323_9_save_redraw.cjs b/resources/tester/helper_323_9_save_redraw.cjs index c8bb2a808..0a6d2d843 100644 --- a/resources/tester/helper_323_9_save_redraw.cjs +++ b/resources/tester/helper_323_9_save_redraw.cjs @@ -2,25 +2,12 @@ * `B06_Section_UI_Page_Persist.ts`(횡단 뒤처리) · `B06_Section_Section_Store.ts`(상세 캐시)를 Node 로 돌림. * 서버 창구만 가짜 — 상세는 저장 전(옛 자리) · 저장 뒤(새 자리) 두 벌. */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); -const memory = () => { - const map = new Map(); - return { - getItem: (key) => (map.has(key) ? map.get(key) : null), - setItem: (key, value) => map.set(key, String(value)), - removeItem: (key) => map.delete(key), - key: (index) => [...map.keys()][index] ?? null, - get length() { - return map.size; - }, - }; -}; -globalThis.window = { sessionStorage: memory(), localStorage: memory() }; +globalThis.window = { sessionStorage: memoryStorage(), localStorage: memoryStorage() }; globalThis.sessionStorage = globalThis.window.sessionStorage; const calls = []; @@ -66,29 +53,27 @@ STUBS[path.join(ROOT, "A00_Common", "design", "design_save_api.ts")] = { }, }; -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (STUBS[full]) return STUBS[full]; - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +// 라우터(302-1 draft 길 `import.meta.glob`) — 이 로더로 못 읽음 · 저장 차례는 지금 길만 봄 +STUBS[path.join(ROOT, "A00_Common", "router.ts")] = { + currentRoute: () => "", + remountRoute: () => undefined, + navigateTo: () => undefined, + setLeaveGuard: () => undefined, +}; +const anyModule = () => new Proxy({}, { get: () => () => undefined }); +const { loadTs } = makeLoader({ + root: ROOT, + fileStub: (full) => STUBS[full], + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (CATALOG.isCatalogRequest(request)) return CATALOG; if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); if (request.endsWith(".css")) return {}; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - if (request.startsWith("@")) return new Proxy({}, { get: () => () => undefined }); + if (request.startsWith("@")) return anyModule(); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); // 측점 열쇠 한 벌(344-3 · 346-2)은 진짜로 — 빈 대역은 열쇠 목록을 못 냄 STUBS["@util/common_util_station_key"] = loadTs( diff --git a/resources/tester/helper_398_2_curve_input.cjs b/resources/tester/helper_398_2_curve_input.cjs index 659ac5430..fa6d9c201 100644 --- a/resources/tester/helper_398_2_curve_input.cjs +++ b/resources/tester/helper_398_2_curve_input.cjs @@ -3,13 +3,11 @@ * 같은 길(스냅 켬 = `fitCurveStations` → 길이 고정 R · 하한 → `buildEditedPolyline`)로 흉내. * node helper_398_2_curve_input.cjs */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const B05 = path.join(ROOT, "B05_Profile"); -const cache = new Map(); let handlers = null; const shows = []; const STUBS = { @@ -23,23 +21,13 @@ const STUBS = { "@ui/ui_template_elements": { showToast: () => {} }, // 곡선 삭제 경고(430) }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => - STUBS[request] ?? - (request.startsWith(".") - ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request)); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +// 곡선 판 가둠(505-3) — 판 크기 감시 · 가짜 그릇엔 판이 없어 빈 감시 +globalThis.ResizeObserver = class { + observe() {} + disconnect() {} +}; + +const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS }); const curve = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Curve.ts")); const edits = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Edits.ts")); @@ -101,7 +89,12 @@ function editor(snap) { status.textContent = `예상노선 — ${message} 꼬리`; }; made = bar.createCurveBar({ - canvas: { getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }) }, + canvas: { + getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }), + closest: () => null, + parentElement: null, + isConnected: true, + }, container: { querySelector: (sel) => (sel === ".b05-routeedit__status" ? status : null) }, state: () => state, toScreen: (v) => [v[0], v[1]], diff --git a/resources/tester/helper_408_radius_downstream.cjs b/resources/tester/helper_408_radius_downstream.cjs index 6f53ddc1b..1852090c1 100644 --- a/resources/tester/helper_408_radius_downstream.cjs +++ b/resources/tester/helper_408_radius_downstream.cjs @@ -3,13 +3,11 @@ * 같은 길(스냅 켬 = `fitStart` → `fitCurveStations` → 길이 고정 R · 하한 → `buildEditedPolyline`)로 흉내. * node helper_408_radius_downstream.cjs */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const B05 = path.join(ROOT, "B05_Profile"); -const cache = new Map(); let handlers = null; const STUBS = { "./B05_Profile_UI_RouteEdit_Label": { @@ -22,23 +20,13 @@ const STUBS = { "@ui/ui_template_elements": { showToast: () => {} }, // 곡선 삭제 경고(430) }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => - STUBS[request] ?? - (request.startsWith(".") - ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request)); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +// 곡선 판 가둠(505-3) — 판 크기 감시 · 가짜 그릇엔 판이 없어 빈 감시 +globalThis.ResizeObserver = class { + observe() {} + disconnect() {} +}; + +const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS }); const curve = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Curve.ts")); const edits = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Edits.ts")); @@ -108,7 +96,12 @@ function editor(snap, apexLock = []) { committed = copy(state); }; bar.createCurveBar({ - canvas: { getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }) }, + canvas: { + getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }), + closest: () => null, + parentElement: null, + isConnected: true, + }, container: { querySelector: (sel) => (sel === ".b05-routeedit__status" ? status : null) }, state: () => state, toScreen: (v) => [v[0], v[1]], diff --git a/resources/tester/helper_54_9_etc_pill.cjs b/resources/tester/helper_54_9_etc_pill.cjs index 807b5d93f..605c613fa 100644 --- a/resources/tester/helper_54_9_etc_pill.cjs +++ b/resources/tester/helper_54_9_etc_pill.cjs @@ -3,19 +3,12 @@ * * ⚠ 확장자가 `.cjs` 인 까닭은 `helper_b05_structures_form_summary.cjs` 머리와 같다. */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); -const session = new Map(); -const fakeStorage = { - getItem: (k) => session.get(k) ?? null, - setItem: (k, v) => session.set(k, String(v)), - removeItem: (k) => session.delete(k), -}; +const fakeStorage = memoryStorage(); globalThis.window = { sessionStorage: fakeStorage, localStorage: fakeStorage }; globalThis.sessionStorage = fakeStorage; globalThis.localStorage = fakeStorage; @@ -24,25 +17,7 @@ const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api", API_TIMEOUT_MS: 1000 }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { - if (STUBS[request]) return STUBS[request]; - if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); - if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - if (request.startsWith("@")) return new Proxy({}, { get: () => () => {} }); - return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS }); const marks = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Structures_Marks.ts")); diff --git a/resources/tester/test_323_9_save_redraw.py b/resources/tester/test_323_9_save_redraw.py index a9d92f71d..ebf2504a9 100644 --- a/resources/tester/test_323_9_save_redraw.py +++ b/resources/tester/test_323_9_save_redraw.py @@ -42,13 +42,11 @@ def test_after_save_cards_show_server_new_detail(out: dict) -> None: def test_no_redraw_from_stale_cache_while_saving(out: dict) -> None: assert out["savingSeen"] == [True, False] and out["savingIdle"] is False - # 저장 도중 신호는 건너뜀 · 뒤처리가 새로 한 번 받음(오버레이 안) + # 저장 도중 신호는 건너뜀 · 뒤처리가 새로 한 번 받음 · 노선 그대로 [저장] = 가림막 없음(498-3 D1) assert out["calls"] == [ - "overlay:on", "refresh:skip", "cross:save", "fetch:새 자리 4+8 · 47+0", - "overlay:off", ] diff --git a/resources/tester/test_app_settings.py b/resources/tester/test_app_settings.py index a453f953a..071a1f6b1 100644 --- a/resources/tester/test_app_settings.py +++ b/resources/tester/test_app_settings.py @@ -53,7 +53,7 @@ def node() -> dict: def test_별칭_검사(node): - assert node["commands"] == 153 + assert node["commands"] == 154 # 488-3 IMPORT 더함 assert node["parse"] == [["LL", "CC", "X1"], [], []] assert node["ok"] == {} # 남의 기본 별칭(C = CIRCLE)을 내 것으로 덮는 건 됨(acad.pgp 식) assert node["chars"] == {"LINE": "chars", "CIRCLE": "chars"} @@ -140,7 +140,7 @@ def test_설정_창_탭_저장_물음(): page.click("[data-settings-tab=cad]") assert _visible_panel_has(page, "input[data-cad-command]") assert not _visible_panel_has(page, "input[type=radio]") - assert page.locator(".app-settings input[data-cad-command]").count() == 153 + assert page.locator(".app-settings input[data-cad-command]").count() == 154 page.click("[data-settings-tab=keys]") assert ( page.evaluate(