// @vitest-environment jsdom
/**
* The bar's own search field, pinned: given `onSearch` it draws a button with
* the words and the shortcut, a click and Ctrl+K or Cmd+K anywhere call it,
* a `search` of the caller's own still takes the centre, and without either
* the bar draws nothing there and leaves the keys alone.
*/
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { AppShell } from './AppShell'
import { TopBar } from './TopBar'
afterEach(cleanup)
const press = (init: KeyboardEventInit) => {
const event = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init })
window.dispatchEvent(event)
return event
}
describe('TopBar search', () => {
it('draws a search field that opens the search', async () => {
const onSearch = vi.fn()
render()
const field = screen.getByRole('button', { name: /Search Peek\.\.\./ })
expect(field.textContent).toMatch(/Ctrl\+K|Cmd\+K/)
await userEvent.click(field)
expect(onSearch).toHaveBeenCalledTimes(1)
})
it('leaves focus where it was on a click', async () => {
const onSearch = vi.fn()
render(
<>
>,
)
const composer = screen.getByRole('textbox', { name: 'composer' })
composer.focus()
await userEvent.click(screen.getByRole('button', { name: /Search…/ }))
expect(onSearch).toHaveBeenCalledTimes(1)
expect(document.activeElement).toBe(composer)
})
it('is reached with Tab and pressed with Enter', async () => {
const onSearch = vi.fn()
render()
await userEvent.tab()
expect(document.activeElement).toBe(screen.getByRole('button', { name: /Search…/ }))
await userEvent.keyboard('{Enter}')
expect(onSearch).toHaveBeenCalledTimes(1)
})
it('says "Search…" when given no words', () => {
render( {}} />)
expect(screen.getByRole('button', { name: /Search…/ })).toBeTruthy()
})
it.each([
['Ctrl+K', { ctrlKey: true, key: 'k' }],
['Cmd+K', { metaKey: true, key: 'k' }],
['Ctrl+Shift+K', { ctrlKey: true, shiftKey: true, key: 'K' }],
])('opens the search on %s anywhere', (_name, init) => {
const onSearch = vi.fn()
render()
const event = press(init)
expect(onSearch).toHaveBeenCalledTimes(1)
expect(event.defaultPrevented).toBe(true)
})
it('calls the latest handler', () => {
const first = vi.fn()
const second = vi.fn()
const { rerender } = render()
rerender()
press({ ctrlKey: true, key: 'k' })
expect(first).not.toHaveBeenCalled()
expect(second).toHaveBeenCalledTimes(1)
})
it('leaves K alone without a modifier, and the keys alone without onSearch', () => {
const onSearch = vi.fn()
const { unmount } = render()
press({ key: 'k' })
expect(onSearch).not.toHaveBeenCalled()
unmount()
render()
expect(press({ ctrlKey: true, key: 'k' }).defaultPrevented).toBe(false)
expect(screen.queryByRole('button')).toBeNull()
})
it('stops listening once it is gone', () => {
const onSearch = vi.fn()
const { unmount } = render()
unmount()
press({ ctrlKey: true, key: 'k' })
expect(onSearch).not.toHaveBeenCalled()
})
it("draws the caller's own centre instead, and still answers the keys", () => {
const onSearch = vi.fn()
render(own} />)
expect(screen.getByText('own')).toBeTruthy()
expect(screen.queryByRole('button')).toBeNull()
press({ ctrlKey: true, key: 'k' })
expect(onSearch).toHaveBeenCalledTimes(1)
})
it('reaches the bar through AppShell', async () => {
const onSearch = vi.fn()
render(
} onSearch={onSearch} searchPlaceholder="Search Peek...">
page
,
)
await userEvent.click(screen.getByRole('button', { name: /Search Peek\.\.\./ }))
expect(onSearch).toHaveBeenCalledTimes(1)
})
})