/** * CAD 설정 배선(PLAN 332-3) — ☰ [설정] 휠 확대 방향 · 내 별칭(`A00_Common/input_prefs.ts`). * 휠 = 기본 밂(앞으로 굴리면 확대 · 오토캐드 · 337) · 당김 저장 뒤 뒤집힘 · 핀치는 늘 벌리면 확대. * 별칭 = 명령 이름 > 내 별칭 > 기본 별칭 · 저장 즉시 따름. * 바꾼 키(588-3) = 새 키만 먹음 · 옛 키는 안 먹음 · 한글 자판도 자판 자리로. * 옮기기 버튼(588-4) = 오른쪽을 고르면 오른쪽 끌기로 화면 옮김 · 기본(가운데)이면 오른쪽 끌기는 안 옮김. * 실행: cd B07_DesignDetail/openwebcad && npx vitest run --dir ../../resources/tester/cad input-prefs-cad */ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { INPUT_PREFS_KEY } from "../../../A00_Common/input_prefs"; import { wheelZoom } from "../../../B07_DesignDetail/openwebcad/src/inputController/input-wheel"; import { matchCommandPrefixes, resolveCommandInput, } from "../../../B07_DesignDetail/openwebcad/src/commands/registry"; import { getActiveToolActor, getPanStartLocation, getScreenCanvasDrawController, getSnapEnabled, } from "../../../B07_DesignDetail/openwebcad/src/state"; import { initApplication } from "../../../B07_DesignDetail/openwebcad/test/helpers/init-application"; const store = new Map(); const savePrefs = (value: unknown) => store.set(INPUT_PREFS_KEY, JSON.stringify(value)); beforeEach(() => { store.clear(); vi.stubGlobal("localStorage", { getItem: (key: string) => store.get(key) ?? null, setItem: (key: string, value: string) => store.set(key, value), removeItem: (key: string) => store.delete(key), }); }); afterEach(() => { vi.unstubAllGlobals(); }); const wheel = (deltaY: number, ctrlKey = false) => wheelZoom({ deltaY, deltaMode: 0, ctrlKey })?.delta; describe("휠 확대 방향", () => { it("저장 없음 = 밂(앞으로 deltaY < 0 → 확대 = 음수 delta) — 오토캐드(337)", () => { expect(wheel(-100)).toBe(-100); expect(wheel(100)).toBe(100); }); it("당김 저장 → 뒤집힘 · 깨진 값 = 밂", () => { savePrefs({ wheelZoom: "pull" }); expect(wheel(100)).toBe(-100); expect(wheel(-100)).toBe(100); store.set(INPUT_PREFS_KEY, "{깨짐"); expect(wheel(100)).toBe(100); }); it("트랙패드 핀치(ctrl · 잔 델타)는 설정과 상관없이 벌리면(deltaY < 0) 확대", () => { expect(wheel(-7, true)).toBe(-7); expect(wheel(7, true)).toBe(7); savePrefs({ wheelZoom: "pull" }); expect(wheel(-7, true)).toBe(-7); // 마우스 Ctrl+휠(큰 델타)은 설정 방향 expect(wheel(100, true)).toBe(-100); }); }); describe("내 별칭", () => { it("기본 별칭 그대로 · 내 별칭이 기본 별칭을 이김 · 명령 이름이 내 별칭을 이김", () => { expect(resolveCommandInput("L")?.id).toBe("LINE"); savePrefs({ cadAliases: { CIRCLE: ["L"], ARC: ["LINE"] } }); expect(resolveCommandInput("l")?.id).toBe("CIRCLE"); expect(resolveCommandInput("LINE")?.id).toBe("LINE"); }); it("새 별칭 · 자동완성 후보 맨 앞 · 없는 명령 이름은 무시", () => { savePrefs({ cadAliases: { RECTANG: ["QQ"], NOPE: ["QZ"] } }); expect(resolveCommandInput("QQ")?.id).toBe("RECTANG"); expect(matchCommandPrefixes("Q")[0]?.id).toBe("RECTANG"); expect(resolveCommandInput("QZ")?.id).not.toBe("NOPE"); }); it("CAD 키 입력 — 「qq␣」 = 사각형 명령", () => { savePrefs({ cadAliases: { RECTANG: ["QQ"] } }); const controller = initApplication(); getActiveToolActor()?.send({ type: "ESC" }); // 쉬는 상태에서(337 — 점 묻는 중 명령 이름 거절) for (const k of ["q", "q", " "]) { controller.handleKeyStroke({ key: k, code: "", keyCode: 0, isComposing: false, shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, repeat: false, target: null, preventDefault: vi.fn(), stopPropagation: vi.fn(), } as unknown as KeyboardEvent); } expect(getActiveToolActor()?.getSnapshot()?.context.type).toBe("RECTANGLE"); }); }); describe("바꾼 키(588-3)", () => { const key = (controller: ReturnType, extra: Partial) => controller.handleKeyStroke({ key: "", code: "", keyCode: 0, isComposing: false, shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, repeat: false, target: null, preventDefault: vi.fn(), stopPropagation: vi.fn(), ...extra, } as unknown as KeyboardEvent); it("스냅 F3 → Ctrl+Q — 새 키(한글 자판 「ㅂ」)만 켜고 끔 · F3 안 먹음", () => { const controller = initApplication(); const before = getSnapEnabled(); key(controller, { key: "F3", code: "F3" }); expect(getSnapEnabled()).toBe(!before); savePrefs({ keys: { "cad.snap": ["Ctrl+Q"] } }); key(controller, { key: "F3", code: "F3" }); expect(getSnapEnabled()).toBe(!before); key(controller, { key: "ㅂ", code: "KeyQ", ctrlKey: true }); expect(getSnapEnabled()).toBe(before); }); }); describe("옮기기 버튼(588-4)", () => { const mouse = (button: number, x: number, y = 100) => ({ button, clientX: x, clientY: y, shiftKey: false, ctrlKey: false, target: null, preventDefault: vi.fn(), stopPropagation: vi.fn(), }) as unknown as MouseEvent; const rightDrag = (controller: ReturnType) => { const before = getScreenCanvasDrawController().getScreenOffset().clone(); controller.handleMouseDown(mouse(2, 100)); controller.handleMouseMove(mouse(2, 160)); controller.handleMouseUp(mouse(2, 160)); const after = getScreenCanvasDrawController().getScreenOffset(); return Math.hypot(after.x - before.x, after.y - before.y); }; it("기본(가운데) = 오른쪽 끌기 안 옮김 · 오른쪽 고름 = 옮김 · 놓으면 끝", () => { const controller = initApplication(); expect(rightDrag(controller)).toBe(0); savePrefs({ panButton: "right" }); expect(rightDrag(controller)).toBeGreaterThan(0); expect(getPanStartLocation()).toBeNull(); }); });