import { render } from "@testing-library/react"
import { useHotkeys } from "react-hotkeys-hook"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { ShortcutHandler } from "../../components/shortcut-handler"
import { ShortcutDefinition } from "../../services/shortcuts-registry"
// Mock react-hotkeys-hook
vi.mock("react-hotkeys-hook", () => ({
useHotkeys: vi.fn(),
}))
describe("ShortcutHandler", () => {
const mockUseHotkeys = vi.mocked(useHotkeys)
beforeEach(() => {
vi.clearAllMocks()
})
describe("Component Rendering", () => {
it("should render null", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
}
const { container } = render()
expect(container.firstChild).toBeNull()
})
})
describe("Hotkey Registration", () => {
it("should register hotkey for single key combination", () => {
const mockAction = vi.fn()
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
action: mockAction,
enabled: true,
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
"cmd+t",
mockAction,
expect.objectContaining({
enableOnFormTags: false,
preventDefault: true,
enabled: true,
}),
expect.arrayContaining([true, mockAction, true, "cmd+t"]),
)
})
it("should register hotkeys for multiple key combinations as comma-separated string", () => {
const mockAction = vi.fn()
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t", "ctrl+t", "alt+t"],
action: mockAction,
}
render()
// Should be called once with comma-separated keys
expect(mockUseHotkeys).toHaveBeenCalledTimes(1)
expect(mockUseHotkeys).toHaveBeenCalledWith(
"cmd+t, ctrl+t, alt+t",
mockAction,
expect.objectContaining({
enableOnFormTags: false,
preventDefault: true,
}),
expect.arrayContaining([true, mockAction, undefined, "cmd+t, ctrl+t, alt+t"]),
)
})
it("should use empty function when no action provided", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
// No action provided
}
render()
const registeredAction = mockUseHotkeys.mock.calls[0][1]
expect(typeof registeredAction).toBe("function")
// Should not throw when called
expect(() => registeredAction({} as any, {})).not.toThrow()
})
})
describe("Enabled/Disabled State", () => {
it("should enable hotkey when both enabled and shortcut.enabled are true", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
enabled: true,
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
expect.any(String),
expect.any(Function),
expect.objectContaining({
enableOnFormTags: false,
enabled: true,
}),
expect.any(Array),
)
})
it("should disable hotkey when enabled prop is false", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
enabled: true,
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
expect.any(String),
expect.any(Function),
expect.objectContaining({
enableOnFormTags: false,
enabled: false,
}),
expect.any(Array),
)
})
it("should disable hotkey when shortcut.enabled is false", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
enabled: false,
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
expect.any(String),
expect.any(Function),
expect.objectContaining({
enableOnFormTags: false,
enabled: false,
}),
expect.any(Array),
)
})
it("should treat undefined shortcut.enabled as enabled", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
// enabled is undefined
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
expect.any(String),
expect.any(Function),
expect.objectContaining({
enableOnFormTags: false,
enabled: true, // undefined !== false, so it should be true
}),
expect.any(Array),
)
})
})
describe("Options Handling", () => {
it("should merge custom options with defaults", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
options: {
preventDefault: false,
enableOnFormTags: false,
enabled: false,
},
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
expect.any(String),
expect.any(Function),
expect.objectContaining({
enableOnFormTags: false,
preventDefault: true,
enabled: false, // Should respect the combined enabled state
// Custom options should be spread in
...shortcut.options,
}),
expect.any(Array),
)
})
it("should use default options when none provided", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
}
render()
expect(mockUseHotkeys).toHaveBeenCalledWith(
expect.any(String),
expect.any(Function),
expect.objectContaining({
enableOnFormTags: false,
preventDefault: true,
enabled: true, // undefined !== false, so it should be true
}),
expect.any(Array),
)
})
})
describe("Dependencies Array", () => {
it("should include correct dependencies", () => {
const mockAction = vi.fn()
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
action: mockAction,
enabled: true,
}
render()
const dependencies = mockUseHotkeys.mock.calls[0][3]
expect(dependencies).toEqual([true, mockAction, true, "cmd+t"])
})
it("should update dependencies when props change", () => {
const mockAction = vi.fn()
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: ["cmd+t"],
action: mockAction,
enabled: true,
}
const { rerender } = render()
mockUseHotkeys.mockClear()
// Change enabled prop
rerender()
const dependencies = mockUseHotkeys.mock.calls[0][3]
expect(dependencies).toEqual([false, mockAction, true, "cmd+t"])
})
})
describe("Edge Cases", () => {
it("should handle empty keys array", () => {
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys: [],
}
render()
// Should call useHotkeys with empty string
expect(mockUseHotkeys).toHaveBeenCalledWith("", expect.any(Function), expect.any(Object), expect.any(Array))
})
it("should handle shortcut with many key combinations", () => {
const keys = Array.from({ length: 10 }, (_, i) => `cmd+${i}`)
const shortcut: ShortcutDefinition = {
id: "test",
name: "Test",
category: "test",
keys,
}
render()
// Should be called once with comma-separated keys
expect(mockUseHotkeys).toHaveBeenCalledTimes(1)
expect(mockUseHotkeys).toHaveBeenCalledWith(
keys.join(", "),
expect.any(Function),
expect.any(Object),
expect.any(Array),
)
})
})
})