Files
supabase/apps/studio/state/shortcuts/formatShortcut.test.ts
Ali Waseem d67050c44e test(studio): add unit tests for shortcut primitives (#45162)
## Summary

Closes
[FE-3056](https://linear.app/supabase/issue/FE-3056/add-unit-tests-for-new-shortcut-primitives).

Backfills unit coverage for the shortcut primitives added in the recent
keyboard-shortcut overhaul — these shipped with no tests.

- **`state/shortcuts/formatShortcut.test.ts`** (16 tests) —
`hotkeyToKeys`: `Mod → Meta` mapping (case-sensitive, substring-safe),
single/multi-modifier combos, named keys, punctuation, edge cases.
- **`state/shortcuts/useShortcut.test.tsx`** (26 tests) — mocks
`useHotkeySequence`, `useRegisterCommands`, and `useIsShortcutEnabled`
to unit-test: sequence/callback wiring (single-step + multi-step
G-chords), full option-resolution priority (caller → registry →
fallback) for `enabled` / `timeout` / `ignoreInputs`, global-preference
AND gating, command-menu registration across all four enable
permutations, stable action identity, stale-closure protection via
`useLatest`, and badge rendering with/without the "then" separator.
- **`components/ui/Shortcut.test.tsx`** (12 tests) — mocks `useShortcut`
and `ShortcutTooltip` to verify prop forwarding (including rerender
behavior), tooltip positioning props, unset-prop hygiene, and the
`asChild` pass-through contract (clicks on the child do not fire
`onTrigger`).

54 tests total, all passing.

## Test plan

- [x] `pnpm --filter studio test state/shortcuts/formatShortcut.test.ts
state/shortcuts/useShortcut.test.tsx components/ui/Shortcut.test.tsx`
- [x] `tsc --noEmit` clean across the three test files

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Added comprehensive test coverage for the Shortcut component,
verifying rendering, hook integration, and tooltip behavior.
* Added test suite for hotkey key formatting, covering plain keys, named
keys, modifiers, and edge cases.
* Added test suite for the useShortcut hook, validating hotkey
sequences, enablement logic, command menu integration, and callback
handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-23 09:22:16 -06:00

100 lines
3.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { hotkeyToKeys } from './formatShortcut'
describe('hotkeyToKeys', () => {
describe('single-step hotkeys', () => {
it('returns a single letter key unchanged', () => {
expect(hotkeyToKeys('K')).toEqual(['K'])
})
it('preserves named keys', () => {
expect(hotkeyToKeys('Enter')).toEqual(['Enter'])
expect(hotkeyToKeys('Escape')).toEqual(['Escape'])
expect(hotkeyToKeys('Tab')).toEqual(['Tab'])
expect(hotkeyToKeys('ArrowUp')).toEqual(['ArrowUp'])
expect(hotkeyToKeys('ArrowDown')).toEqual(['ArrowDown'])
expect(hotkeyToKeys('ArrowLeft')).toEqual(['ArrowLeft'])
expect(hotkeyToKeys('ArrowRight')).toEqual(['ArrowRight'])
})
it('preserves punctuation keys', () => {
expect(hotkeyToKeys(',')).toEqual([','])
expect(hotkeyToKeys('.')).toEqual(['.'])
expect(hotkeyToKeys('/')).toEqual(['/'])
})
})
describe('Mod mapping', () => {
it('converts standalone Mod to Meta', () => {
expect(hotkeyToKeys('Mod')).toEqual(['Meta'])
})
it('converts Mod to Meta when combined with a single key', () => {
expect(hotkeyToKeys('Mod+K')).toEqual(['Meta', 'K'])
})
it('is case sensitive — only exact "Mod" is converted', () => {
expect(hotkeyToKeys('mod+K')).toEqual(['mod', 'K'])
expect(hotkeyToKeys('MOD+K')).toEqual(['MOD', 'K'])
})
it('does not rewrite keys that merely contain "Mod" as a substring', () => {
expect(hotkeyToKeys('Model')).toEqual(['Model'])
expect(hotkeyToKeys('Mod+Model')).toEqual(['Meta', 'Model'])
})
})
describe('modifier combos', () => {
it('handles Mod+Shift combos', () => {
expect(hotkeyToKeys('Mod+Shift+M')).toEqual(['Meta', 'Shift', 'M'])
expect(hotkeyToKeys('Mod+Shift+J')).toEqual(['Meta', 'Shift', 'J'])
expect(hotkeyToKeys('Mod+Shift+C')).toEqual(['Meta', 'Shift', 'C'])
})
it('preserves modifier order', () => {
expect(hotkeyToKeys('Shift+Mod+K')).toEqual(['Shift', 'Meta', 'K'])
expect(hotkeyToKeys('Alt+Mod+K')).toEqual(['Alt', 'Meta', 'K'])
})
it('leaves Ctrl, Alt, Shift, Meta literals untouched', () => {
expect(hotkeyToKeys('Ctrl+K')).toEqual(['Ctrl', 'K'])
expect(hotkeyToKeys('Alt+K')).toEqual(['Alt', 'K'])
expect(hotkeyToKeys('Shift+K')).toEqual(['Shift', 'K'])
expect(hotkeyToKeys('Meta+K')).toEqual(['Meta', 'K'])
})
it('handles three-modifier combos', () => {
expect(hotkeyToKeys('Mod+Alt+Shift+K')).toEqual(['Meta', 'Alt', 'Shift', 'K'])
})
})
describe('named-key combos from the registry', () => {
it('handles Mod+ArrowUp/Down/Left/Right', () => {
expect(hotkeyToKeys('Mod+ArrowUp')).toEqual(['Meta', 'ArrowUp'])
expect(hotkeyToKeys('Mod+ArrowDown')).toEqual(['Meta', 'ArrowDown'])
expect(hotkeyToKeys('Mod+ArrowLeft')).toEqual(['Meta', 'ArrowLeft'])
expect(hotkeyToKeys('Mod+ArrowRight')).toEqual(['Meta', 'ArrowRight'])
})
it('handles Mod+Enter and Mod+Escape', () => {
expect(hotkeyToKeys('Mod+Enter')).toEqual(['Meta', 'Enter'])
expect(hotkeyToKeys('Mod+Escape')).toEqual(['Meta', 'Escape'])
})
it('handles Mod+. (operation queue toggle / logs reset)', () => {
expect(hotkeyToKeys('Mod+.')).toEqual(['Meta', '.'])
})
})
describe('edge cases', () => {
it('returns a single-element array for empty input', () => {
expect(hotkeyToKeys('')).toEqual([''])
})
it('preserves empty segments from trailing plus', () => {
expect(hotkeyToKeys('Mod+')).toEqual(['Meta', ''])
})
})
})