import React, { useState } from 'react'
import { render } from '@testing-library/react'
import { Switch } from './switch'
import {
SwitchState,
assertSwitch,
getSwitch,
assertActiveElement,
getSwitchLabel,
} from '../../test-utils/accessibility-assertions'
import { press, click, Keys } from '../../test-utils/interactions'
jest.mock('../../hooks/use-id')
describe('Safe guards', () => {
it('should be possible to render a Switch without crashing', () => {
render()
})
})
describe('Rendering', () => {
it('should be possible to render an (on) Switch using a render prop', () => {
render(
{({ checked }) => {checked ? 'On' : 'Off'}}
)
assertSwitch({ state: SwitchState.On, textContent: 'On' })
})
it('should be possible to render an (off) Switch using a render prop', () => {
render(
{({ checked }) => {checked ? 'On' : 'Off'}}
)
assertSwitch({ state: SwitchState.Off, textContent: 'Off' })
})
it('should be possible to render an (on) Switch using an `as` prop', () => {
render()
assertSwitch({ state: SwitchState.On, tag: 'span' })
})
it('should be possible to render an (off) Switch using an `as` prop', () => {
render()
assertSwitch({ state: SwitchState.Off, tag: 'span' })
})
it('should be possible to use the switch contents as the label', () => {
render(
Enable notifications
)
assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })
})
})
describe('Render composition', () => {
it('should be possible to render a Switch.Group, Switch and Switch.Label', () => {
render(
Enable notifications
)
assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })
})
it('should be possible to render a Switch.Group, Switch and Switch.Label (before the Switch)', () => {
render(
Label B
Label A
)
// Warning! Using aria-label or aria-labelledby will hide any descendant content from assistive
// technologies.
//
// Thus: Label A should not be part of the "label" in this case
assertSwitch({ state: SwitchState.Off, label: 'Label B' })
})
it('should be possible to render a Switch.Group, Switch and Switch.Label (after the Switch)', () => {
render(
Label A
Label B
)
// Warning! Using aria-label or aria-labelledby will hide any descendant content from assistive
// technologies.
//
// Thus: Label A should not be part of the "label" in this case
assertSwitch({ state: SwitchState.Off, label: 'Label B' })
})
it('should be possible to render a Switch.Group, Switch and Switch.Description (before the Switch)', async () => {
render(
This is an important feature
)
assertSwitch({ state: SwitchState.Off, description: 'This is an important feature' })
})
it('should be possible to render a Switch.Group, Switch and Switch.Description (after the Switch)', () => {
render(
This is an important feature
)
assertSwitch({ state: SwitchState.Off, description: 'This is an important feature' })
})
it('should be possible to render a Switch.Group, Switch, Switch.Label and Switch.Description', () => {
render(
Label AThis is an important feature
)
assertSwitch({
state: SwitchState.Off,
label: 'Label A',
description: 'This is an important feature',
})
})
})
describe('Keyboard interactions', () => {
describe('`Space` key', () => {
it('should be possible to toggle the Switch with Space', async () => {
let handleChange = jest.fn()
function Example() {
let [state, setState] = useState(false)
return (
{
setState(value)
handleChange(value)
}}
/>
)
}
render()
// Ensure checkbox is off
assertSwitch({ state: SwitchState.Off })
// Focus the switch
getSwitch()?.focus()
// Toggle
await press(Keys.Space)
// Ensure state is on
assertSwitch({ state: SwitchState.On })
// Toggle
await press(Keys.Space)
// Ensure state is off
assertSwitch({ state: SwitchState.Off })
})
})
describe('`Enter` key', () => {
it('should not be possible to use Enter to toggle the Switch', async () => {
let handleChange = jest.fn()
render()
// Ensure checkbox is off
assertSwitch({ state: SwitchState.Off })
// Focus the switch
getSwitch()?.focus()
// Try to toggle
await press(Keys.Enter)
expect(handleChange).not.toHaveBeenCalled()
})
})
describe('`Tab` key', () => {
it('should be possible to tab away from the Switch', async () => {
render(
)
// Ensure checkbox is off
assertSwitch({ state: SwitchState.Off })
// Focus the switch
getSwitch()?.focus()
// Expect the switch to be active
assertActiveElement(getSwitch())
// Toggle
await press(Keys.Tab)
// Expect the button to be active
assertActiveElement(document.getElementById('btn'))
})
})
})
describe('Mouse interactions', () => {
it('should be possible to toggle the Switch with a click', async () => {
let handleChange = jest.fn()
function Example() {
let [state, setState] = useState(false)
return (
{
setState(value)
handleChange(value)
}}
/>
)
}
render()
// Ensure checkbox is off
assertSwitch({ state: SwitchState.Off })
// Toggle
await click(getSwitch())
// Ensure state is on
assertSwitch({ state: SwitchState.On })
// Toggle
await click(getSwitch())
// Ensure state is off
assertSwitch({ state: SwitchState.Off })
})
it('should be possible to toggle the Switch with a click on the Label', async () => {
let handleChange = jest.fn()
function Example() {
let [state, setState] = useState(false)
return (
{
setState(value)
handleChange(value)
}}
/>
The label
)
}
render()
// Ensure checkbox is off
assertSwitch({ state: SwitchState.Off })
// Toggle
await click(getSwitchLabel())
// Ensure the switch is focused
assertActiveElement(getSwitch())
// Ensure state is on
assertSwitch({ state: SwitchState.On })
// Toggle
await click(getSwitchLabel())
// Ensure the switch is focused
assertActiveElement(getSwitch())
// Ensure state is off
assertSwitch({ state: SwitchState.Off })
})
it('should not be possible to toggle the Switch with a click on the Label (passive)', async () => {
let handleChange = jest.fn()
function Example() {
let [state, setState] = useState(false)
return (
{
setState(value)
handleChange(value)
}}
/>
The label
)
}
render()
// Ensure checkbox is off
assertSwitch({ state: SwitchState.Off })
// Toggle
await click(getSwitchLabel())
// Ensure state is still off
assertSwitch({ state: SwitchState.Off })
})
})