Skip to content

Commit 5a9bc4a

Browse files
committed
feat: add e2e and unit for kbd shortcuts disabled
1 parent bff7be6 commit 5a9bc4a

3 files changed

Lines changed: 110 additions & 9 deletions

File tree

app/components/AppHeader.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { NavigationConfig, NavigationConfigWithGroups } from '~/types'
44
import { isEditableElement } from '~/utils/input'
55
import { NPMX_DOCS_SITE } from '#shared/utils/constants'
66
7+
const keyboardShortcuts = useKeyboardShortcuts()
8+
79
withDefaults(
810
defineProps<{
911
showLogo?: boolean
@@ -175,7 +177,7 @@ function handleSearchFocus() {
175177
176178
onKeyStroke(
177179
e => {
178-
if (isEditableElement(e.target)) {
180+
if (!keyboardShortcuts.value || isEditableElement(e.target)) {
179181
return
180182
}
181183

test/e2e/interactions.spec.ts

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,52 @@ test.describe('Keyboard Shortcuts', () => {
223223
await page.keyboard.press('ControlOrMeta+Shift+,')
224224
await expect(page).toHaveURL(/\/settings/)
225225
})
226+
})
226227

227-
test('"," does not navigate when search input is focused', async ({ page, goto }) => {
228-
await goto('/compare', { waitUntil: 'hydration' })
228+
test.describe('Keyboard Shortcuts disabled', () => {
229+
test.beforeEach(async ({ page }) => {
230+
await page.addInitScript(() => {
231+
localStorage.setItem('npmx-settings', JSON.stringify({ keyboardShortcuts: false }))
232+
})
233+
})
229234

230-
const searchInput = page.locator('#header-search')
231-
await searchInput.focus()
232-
await expect(searchInput).toBeFocused()
235+
test('"," (header) does not navigate to /settings when shortcuts are disabled', async ({
236+
page,
237+
goto,
238+
}) => {
239+
await goto('/compare', { waitUntil: 'hydration' })
233240

234241
await page.keyboard.press(',')
235242

236-
// Should still be on compare, not navigated to settings
237243
await expect(page).toHaveURL(/\/compare/)
238-
// The ',' should have been typed into the input
239-
await expect(searchInput).toHaveValue(',')
244+
})
245+
246+
test('"/" (global) does not focus search input when shortcuts are disabled', async ({
247+
page,
248+
goto,
249+
}) => {
250+
await goto('/search?q=vue', { waitUntil: 'hydration' })
251+
252+
await expect(page.locator('text=/found \\d+|showing \\d+/i').first()).toBeVisible({
253+
timeout: 15000,
254+
})
255+
256+
// Focus a non-input element so "/" would normally steal focus to search
257+
await page.locator('[data-result-index="0"]').first().focus()
258+
259+
await page.keyboard.press('/')
260+
261+
await expect(page.locator('input[type="search"]')).not.toBeFocused()
262+
})
263+
264+
test('"d" (package) does not navigate to docs when shortcuts are disabled', async ({
265+
page,
266+
goto,
267+
}) => {
268+
await goto('/package/vue', { waitUntil: 'hydration' })
269+
270+
await page.keyboard.press('d')
271+
272+
await expect(page).toHaveURL(/\/package\/vue$/)
240273
})
241274
})
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
2+
3+
describe('useSettings - keyboardShortcuts', () => {
4+
beforeEach(() => {
5+
localStorage.clear()
6+
})
7+
8+
afterEach(() => {
9+
// Reset the singleton so the next test gets a fresh instance
10+
localStorage.clear()
11+
})
12+
13+
describe('default value', () => {
14+
it('should default keyboardShortcuts to true', () => {
15+
const { settings } = useSettings()
16+
expect(settings.value.keyboardShortcuts).toBe(true)
17+
})
18+
})
19+
20+
describe('useKeyboardShortcuts composable', () => {
21+
it('should return true by default', () => {
22+
const enabled = useKeyboardShortcuts()
23+
expect(enabled.value).toBe(true)
24+
})
25+
26+
it('should reflect changes made via settings', () => {
27+
const { settings } = useSettings()
28+
const enabled = useKeyboardShortcuts()
29+
30+
settings.value.keyboardShortcuts = false
31+
expect(enabled.value).toBe(false)
32+
33+
settings.value.keyboardShortcuts = true
34+
expect(enabled.value).toBe(true)
35+
})
36+
37+
it('should be reactive', () => {
38+
const { settings } = useSettings()
39+
const enabled = useKeyboardShortcuts()
40+
41+
expect(enabled.value).toBe(true)
42+
43+
settings.value.keyboardShortcuts = false
44+
expect(enabled.value).toBe(false)
45+
})
46+
})
47+
48+
describe('persistence', () => {
49+
it('should persist keyboardShortcuts=false to localStorage', () => {
50+
const { settings } = useSettings()
51+
settings.value.keyboardShortcuts = false
52+
53+
const stored = JSON.parse(localStorage.getItem('npmx-settings') ?? '{}')
54+
expect(stored.keyboardShortcuts).toBe(false)
55+
})
56+
57+
it('should persist keyboardShortcuts=true to localStorage', () => {
58+
const { settings } = useSettings()
59+
settings.value.keyboardShortcuts = false
60+
settings.value.keyboardShortcuts = true
61+
62+
const stored = JSON.parse(localStorage.getItem('npmx-settings') ?? '{}')
63+
expect(stored.keyboardShortcuts).toBe(true)
64+
})
65+
})
66+
})

0 commit comments

Comments
 (0)