Files
headlessui/packages/@headlessui-react/src/test-utils/scenarios.tsx
T
Robin Malfait ae8c253c21 Fix typos (#3086)
* fix a bunch of typos

* fix typos in `@headlessui/vue`
2024-04-08 23:31:50 +02:00

308 lines
8.4 KiB
TypeScript

import { render, screen } from '@testing-library/react'
import React from 'react'
import { Description, Field, Fieldset, Label } from '..'
import {
assertActiveElement,
assertDisabledish,
assertLinkedWithDescription,
assertLinkedWithLabel,
getControl,
getDescriptions,
getLabel,
getLabels,
} from './accessibility-assertions'
import { click } from './interactions'
import { suppressConsoleLogs } from './suppress-console-logs'
export function commonControlScenarios(Control: React.ComponentType<any>) {
describe('Rendering composition', () => {
describe('Inside `Field`', () => {
it('should mark the control as disabled, if the `Field` is disabled', () => {
render(
<Field disabled>
<Control />
</Field>
)
assertDisabledish(getControl())
})
it('should link a control and a `Label` when inside a `Field`', () => {
render(
<Field>
<Label>My Label</Label>
<Control />
</Field>
)
assertLinkedWithLabel(getControl(), getLabels())
})
it('should link a control and multiple `Label` components when inside a `Field`', () => {
render(
<Field>
<Label>My Label #1</Label>
<Label>My Label #2</Label>
<Control />
</Field>
)
assertLinkedWithLabel(getControl(), getLabels())
})
it('should link a control and a `Description` when inside a `Field`', () => {
render(
<Field>
<Control />
<Description>My Description</Description>
</Field>
)
assertLinkedWithDescription(getControl(), getDescriptions())
})
it('should link a control and multiple `Description` components when inside a `Field`', () => {
render(
<Field>
<Control />
<Description>My Description #1</Description>
<Description>My Description #2</Description>
<Description>My Description #3</Description>
</Field>
)
assertLinkedWithDescription(getControl(), getDescriptions())
})
it('should link a control with a `Label` and a `Description` when inside a `Field`', () => {
render(
<Field>
<Label>My Label</Label>
<Control />
<Description>My Description</Description>
</Field>
)
assertLinkedWithDescription(getControl(), getDescriptions())
assertLinkedWithLabel(getControl(), getLabels())
})
})
})
describe('Mouse interactions', () => {
describe('Inside `Field`', () => {
it('should be possible to click a `Label`, and focus the control when in a `Field`', async () => {
render(
<Field>
<Label>My Label</Label>
<Control />
</Field>
)
assertActiveElement(document.body)
await click(getLabel())
assertActiveElement(getControl())
})
it('should not be possible to click a `Label`, if the `Label` has the `passive` prop', async () => {
render(
<Field>
<Label passive>My Label</Label>
<Control />
</Field>
)
assertActiveElement(document.body)
await click(getLabel())
assertActiveElement(document.body)
})
it('should not be possible to click a `Label` and focus the control, if the control is disabled', async () => {
render(
<Field>
<Label>My Label</Label>
<Control disabled />
</Field>
)
assertActiveElement(document.body)
await click(getLabel())
assertActiveElement(document.body)
})
it('should not be possible to click a `Label` and focus the control, if the `Field` is disabled', async () => {
render(
<Field disabled>
<Label>My Label</Label>
<Control />
</Field>
)
assertActiveElement(document.body)
await click(getLabel())
assertActiveElement(document.body)
})
})
describe('Inside `Fieldset`', () => {
it('should not be possible to click a `Label` and focus the control, if the `Fieldset` is disabled', async () => {
render(
<Fieldset disabled>
<Field>
<Label>My Label</Label>
<Control />
</Field>
</Fieldset>
)
assertActiveElement(document.body)
await click(getLabel())
assertActiveElement(document.body)
})
})
})
}
export function commonFormScenarios(
Control: React.ComponentType<any>,
{
performUserInteraction,
}: { performUserInteraction: (control: HTMLElement | null) => PromiseLike<void> }
) {
describe('Form compatibility', () => {
it('should render native (hidden) form elements for the control', () => {
render(
<form>
<Control name="foo" />
</form>
)
expect(document.querySelector('[name=foo]')).toBeInTheDocument()
})
it('should submit the form with all the data', async () => {
let formDataMock = jest.fn()
render(
<form
onSubmit={(e) => {
e.preventDefault()
formDataMock(new FormData(e.target as HTMLFormElement))
}}
>
<Control name="foo" />
<button>Submit</button>
</form>
)
// Submit form
await click(screen.getByText('Submit'))
// Ensure the form was submitted with the `foo` input present
expect(formDataMock.mock.calls[0][0].has('foo')).toBe(true)
})
it('should not submit the data if the control is disabled', async () => {
let submits = jest.fn()
function Example() {
return (
<form
onSubmit={(event) => {
event.preventDefault()
submits([...new FormData(event.currentTarget).entries()])
}}
>
<input type="hidden" name="foo" value="bar" />
<Control name="bar" disabled />
<button>Submit</button>
</form>
)
}
render(<Example />)
// Submit the form
await click(screen.getByText('Submit'))
// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})
it(
'should reset the control when the form is reset',
suppressConsoleLogs(async () => {
let formDataMock = jest.fn()
render(
<form
onSubmit={(e) => {
e.preventDefault()
formDataMock(new FormData(e.target as HTMLFormElement))
}}
>
<Field>
<Label>The Label</Label>
<Control name="foo" />
</Field>
<button>Submit</button>
<button type="reset">Reset</button>
</form>
)
// Submit the form to get the initial state of the form
await click(screen.getByText('Submit'))
let formState = Object.fromEntries(formDataMock.mock.calls[0][0])
// Make changes to the control
await performUserInteraction(getControl())
// Submit form
await click(screen.getByText('Submit'))
// Ensure the form was, and the values are different
let newFormState = Object.fromEntries(formDataMock.mock.calls[1][0])
expect(newFormState).not.toEqual(formState)
// Reset the form
await click(screen.getByText('Reset'))
// Ensure the form was reset
await click(screen.getByText('Submit'))
// Ensure the form state looks like the initial state
let resetFormState = Object.fromEntries(formDataMock.mock.calls[2][0])
expect(resetFormState).toEqual(formState)
})
)
})
}
export function commonRenderingScenarios(
Control: React.ComponentType<any>,
{ getElement }: { getElement: () => HTMLElement | null }
) {
describe('Rendering', () => {
it('should render a control', async () => {
render(<Control />)
expect(getElement()).toBeInTheDocument()
})
it('should have an `id` attached', () => {
render(<Control />)
expect(getElement()).toHaveAttribute('id')
})
it('should be possible to override the `id`', () => {
render(<Control id="foo" />)
expect(getElement()).toHaveAttribute('id', 'foo')
})
})
}