// @vitest-environment jsdom /** * What the Select page claims, pinned. * * This component had no component test at all — only `Select.fit.test.ts`, * eight assertions about the pure geometry it grew and then shared with the * Menu shell. That geometry is Floating UI's now, so the file is deleted and * its subject is measured in a browser instead (jsdom lays nothing out, so a * jsdom test that claimed to check placement would be checking nothing). * What is testable here is behaviour, and it never was. * * Ship asserts on this component in `web/src/components/ui/ui.test.tsx`; the * shapes it uses are repeated here deliberately, so a break shows up in this * repository rather than in Ship's adoption PR. */ import { afterEach, describe, expect, it, vi } from 'vitest' import { cleanup, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { useState } from 'react' import { Select } from './Select' afterEach(cleanup) const STATUSES = [ { value: 'todo', label: 'Todo' }, { value: 'in_progress', label: 'In Progress' }, { value: 'done', label: 'Done' }, ] function Controlled({ initial = 'todo', ...rest }: { initial?: string } & Partial>) { const [value, setValue] = useState(initial) return ) const trigger = screen.getByRole('combobox', { name: 'Status' }) expect(trigger.getAttribute('aria-disabled')).toBe('true') expect(trigger.hasAttribute('disabled')).toBe(false) await user.click(trigger) await user.keyboard('{ArrowDown}{Enter}') expect(screen.queryByRole('listbox')).toBeNull() expect(onChange).not.toHaveBeenCalled() await user.tab() await user.tab({ shift: true }) expect(document.activeElement).toBe(trigger) expect((await screen.findByRole('tooltip')).textContent).toBe('Read only: you are a guest') }) // F2: as on Button, a reason wins over plain `disabled` — or the reason is never seen. it('disabled with a reason: the reason wins', async () => { const user = userEvent.setup() render() const trigger = screen.getByRole('combobox', { name: 'Status' }) trigger.focus() await user.keyboard('{ArrowDown}') expect(await focusedList()).toBeTruthy() await user.keyboard('{ArrowDown}{Enter}') expect(onChange).toHaveBeenCalledWith('in_progress') expect(document.activeElement).toBe(trigger) }) /** * The list is unmounted from the page's point of view when it closes: Base UI * leaves the popup element in the DOM but hides the positioner around it with * the `hidden` attribute, so it is out of the accessibility tree and * `queryByRole` cannot see it. Ship asserts exactly this — pinned here so the * assertion is known to hold before Ship ever runs it. */ it('closes on Escape, and the listbox is no longer findable', async () => { const user = userEvent.setup() render() await user.click(screen.getByRole('combobox', { name: 'Status' })) expect(await screen.findByRole('listbox')).toBeTruthy() await user.keyboard('{Escape}') await waitFor(() => expect(screen.queryByRole('listbox')).toBeNull()) }) it('closes on a press outside', async () => { const user = userEvent.setup() render( <> , ) await user.click(screen.getByRole('combobox', { name: 'Status' })) expect(await screen.findByRole('listbox')).toBeTruthy() await user.click(screen.getByRole('button', { name: 'Elsewhere' })) await waitFor(() => expect(screen.queryByRole('listbox')).toBeNull()) }) it('picking an option reports it and closes', async () => { const user = userEvent.setup() const onChange = vi.fn() render(