Files
Aislo/resources/tester/cad/ctrl-shortcut-hangul.test.ts
T
eomsangdonandClaude Sonnet 5.5 7cb9860500 fix(CAD): 되돌리기 · 다시 하기 한글 자판 대응 · M02 화면 ↶ ↷ 단추(214-1)
- Ctrl+Z · Y · Shift+Z · S · A 를 자판 자리(code)로 판정 — 한글 자판 「ㅋ」·「ㅛ」 · Shift 대문자 대응
- 한글 조합 중에도 Ctrl 단축키는 먹음
- 제목줄 없는 M02 화면(양식 편집 · 구조물 카드)은 우하단 탐색 막대에 되돌리기 · 다시 하기 단추
- 포인터가 CAD 위에 오면 부모 입력 칸이 아닐 때 CAD 로 초점
- 새 시험 resources/tester/cad/ctrl-shortcut-hangul.test.ts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-10 15:26:23 +09:00

114 lines
4.1 KiB
TypeScript

/**
* Ctrl 단축키 — 한글 자판에서도 되돌리기 · 다시 하기가 먹는다(PLAN 214-1).
* 한글 자판은 key 가 「ㅋ」·「ㅛ」 · Shift 는 대문자 — 자판 자리(code)로 판정한다.
* 실행: cd B07_DesignDetail/openwebcad && npx vitest run --dir ../../resources/tester/cad ctrl-shortcut-hangul
*/
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../../../B07_DesignDetail/openwebcad/src/state.ts", async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
undo: vi.fn(),
redo: vi.fn(),
}));
import { ctrlShortcut } from "../../../B07_DesignDetail/openwebcad/src/inputController/input-shortcut";
import { InputController } from "../../../B07_DesignDetail/openwebcad/src/inputController/input-controller";
import { redo, undo } from "../../../B07_DesignDetail/openwebcad/src/state";
const press = (controller: InputController, extra: Partial<KeyboardEvent>) => {
const preventDefault = vi.fn();
controller.handleKeyStroke({
key: "",
code: "",
keyCode: 0,
isComposing: false,
shiftKey: false,
ctrlKey: true,
altKey: false,
metaKey: false,
repeat: false,
target: null,
preventDefault,
stopPropagation: vi.fn(),
...extra,
} as unknown as KeyboardEvent);
return preventDefault;
};
describe("ctrlShortcut — 자판 자리(code)로 판정", () => {
const k = (code: string, extra = {}) =>
ctrlShortcut({ ctrlKey: true, altKey: false, shiftKey: false, code, ...extra });
it("KeyZ = 되돌리기 · Shift+KeyZ · KeyY = 다시 하기", () => {
expect(k("KeyZ")).toBe("undo");
expect(k("KeyZ", { shiftKey: true })).toBe("redo");
expect(k("KeyY")).toBe("redo");
});
it("KeyS = 저장 · KeyA = 전부 고르기 · Shift 면 아님", () => {
expect(k("KeyS")).toBe("save");
expect(k("KeyA")).toBe("selectAll");
expect(k("KeyS", { shiftKey: true })).toBeNull();
expect(k("KeyA", { shiftKey: true })).toBeNull();
});
it("Ctrl 없음 · Alt(AltGr) 함께 · 다른 자리는 아님", () => {
expect(k("KeyZ", { ctrlKey: false })).toBeNull();
expect(k("KeyZ", { altKey: true })).toBeNull();
expect(k("KeyX")).toBeNull();
});
});
describe("키 입력 — 한글 자판", () => {
beforeEach(() => {
vi.mocked(undo).mockClear();
vi.mocked(redo).mockClear();
});
it("Ctrl+「ㅋ」(KeyZ) = 되돌리기", () => {
const controller = new InputController();
const prevented = press(controller, { key: "ㅋ", code: "KeyZ" });
expect(undo).toHaveBeenCalledTimes(1);
expect(redo).not.toHaveBeenCalled();
expect(prevented).toHaveBeenCalled();
});
it("Ctrl+「ㅛ」(KeyY) = 다시 하기", () => {
const controller = new InputController();
press(controller, { key: "ㅛ", code: "KeyY" });
expect(redo).toHaveBeenCalledTimes(1);
expect(undo).not.toHaveBeenCalled();
});
it("Ctrl+Shift+「ㅋ」(KeyZ) = 다시 하기", () => {
const controller = new InputController();
press(controller, { key: "ㅋ", code: "KeyZ", shiftKey: true });
expect(redo).toHaveBeenCalledTimes(1);
expect(undo).not.toHaveBeenCalled();
});
it("영문 자판 Ctrl+Shift+Z(key 「Z」) = 다시 하기 · Ctrl+Z = 되돌리기", () => {
const controller = new InputController();
press(controller, { key: "Z", code: "KeyZ", shiftKey: true });
expect(redo).toHaveBeenCalledTimes(1);
press(controller, { key: "z", code: "KeyZ" });
expect(undo).toHaveBeenCalledTimes(1);
});
it("한글 조합 중(keyCode 229 · key Process)에도 Ctrl+Z 는 먹음", () => {
const controller = new InputController();
press(controller, { key: "Process", code: "KeyZ", keyCode: 229, isComposing: true });
expect(undo).toHaveBeenCalledTimes(1);
});
it("조합 중 Ctrl 없는 키는 여전히 IME 에 넘김", () => {
const controller = new InputController();
const prevented = press(controller, {
key: "Process",
code: "KeyZ",
keyCode: 229,
isComposing: true,
ctrlKey: false,
});
expect(undo).not.toHaveBeenCalled();
expect(prevented).not.toHaveBeenCalled();
});
});