- A00_Common/key_shortcut.ts 새로 — 영문이면 글자 · 아니면 자판 자리(code) · CAD 와 페이지가 같이 씀 - CAD input-shortcut.ts 는 공용을 다시 내보냄 · tsconfig.app.json 에 공용 파일 포함 - B05 종단 Ctrl+Z · 노선 편집 Ctrl+Z · Y · M02 흐름도 Ctrl+Z · 카드 Ctrl+S · 나무 Ctrl+C · V · 시트 Ctrl+글자 - 시트 키는 한글 조합 중에도 Ctrl+글자 판정 - 새 시험 ctrl-shortcut-hangul · ctrl-shortcut-sheet Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
150 lines
5.8 KiB
TypeScript
150 lines
5.8 KiB
TypeScript
/**
|
|
* Ctrl 단축키 — 한글 자판에서도 먹는다(PLAN 214-1 CAD · 214-2 페이지 공용 `A00_Common/key_shortcut.ts`).
|
|
* 한글 자판은 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();
|
|
});
|
|
});
|
|
|
|
// ---- 공용 판정 · 페이지(214-2) — 같은 함수를 B05 · M02 · 시트 키가 씀 ----
|
|
import { isCtrlKey, keyLetter } from "../../../A00_Common/key_shortcut";
|
|
|
|
describe("keyLetter · isCtrlKey — 페이지 공용", () => {
|
|
it("한글 자판 key 「ㅋ」 + KeyZ = z · 영문 · 대문자도 소문자 · 숫자", () => {
|
|
expect(keyLetter({ key: "ㅋ", code: "KeyZ" })).toBe("z");
|
|
expect(keyLetter({ key: "ㅊ", code: "KeyC" })).toBe("c");
|
|
expect(keyLetter({ key: "ㅍ", code: "KeyV" })).toBe("v");
|
|
expect(keyLetter({ key: "ㄴ", code: "KeyS" })).toBe("s");
|
|
expect(keyLetter({ key: "Z", code: "KeyZ" })).toBe("z");
|
|
expect(keyLetter({ key: "1", code: "Digit1" })).toBe("1");
|
|
expect(keyLetter({ key: "Process", code: "Digit1" })).toBe("1");
|
|
});
|
|
it("다른 자판 배열(key 가 영문)은 key 를 따름 · 글자 아닌 키는 빈 글", () => {
|
|
expect(keyLetter({ key: "z", code: "KeyY" })).toBe("z"); // 독일식 자리 바뀜
|
|
expect(keyLetter({ key: "Enter", code: "Enter" })).toBe("");
|
|
expect(keyLetter({ key: "Escape", code: "Escape" })).toBe("");
|
|
});
|
|
it("code 없는 합성 사건은 key 로", () => {
|
|
expect(keyLetter({ key: "Z", code: "" })).toBe("z");
|
|
});
|
|
it("isCtrlKey — Ctrl · ⌘ 만 · 글자 맞아야", () => {
|
|
const e = (extra: object) => ({
|
|
key: "ㅋ",
|
|
code: "KeyZ",
|
|
ctrlKey: false,
|
|
metaKey: false,
|
|
...extra,
|
|
});
|
|
expect(isCtrlKey(e({ ctrlKey: true }), "z")).toBe(true);
|
|
expect(isCtrlKey(e({ metaKey: true }), "z")).toBe(true);
|
|
expect(isCtrlKey(e({}), "z")).toBe(false);
|
|
expect(isCtrlKey(e({ ctrlKey: true }), "y")).toBe(false);
|
|
});
|
|
});
|