element-plus/packages/components/focus-trap/__tests__/focus-trap.spec.ts
jeremywu dd19cae2bc
refactor(components): popper composables (#5035)
* refactor(components): popper composables

- Refactor popper composables

* updates

* updates for tooltip

* Updates for popper. TODO: fix controlled tooltip animation

* Fix controlled mode popper animation issue

* Add new feature for customizing tooltip theme

* Fix popover and popconfirm error

* - Add Collection component for wrapping a collection of component
- Add FocusTrap component for trap focus for popups
- Add RovingFocus component for roving focus component type
- Adjust dropdown component based on these newly added components
- Add popper-trigger component for placing the trigger
- TODO: Finish current dropdown component, and all component's tests plus documents

* Refactor popper

* Complete organizing popper

* Almost finish dropdown

* Update popper tests

* update only-child test

* Finish focus trap component test

* Finish tooltip content test

* Finish tooltip trigger tests

* Finish tooltip tests

* finish tests for Collection and RovingFocusGroup

* Fix test cases for timeselect & select & popover

* Fix popover, popconfirm, menu bug and test cases

* Fix select-v2 test error caused by updating popper

* Fix date-picker test issue for updating popper

* fix test cases

* Fix eslint

* Rebase dev & fix tests

* Remove unused code
2022-01-04 09:15:15 +08:00

217 lines
6.2 KiB
TypeScript

import { h, nextTick, inject } from 'vue'
import { mount } from '@vue/test-utils'
import { EVENT_CODE } from '@element-plus/utils/aria'
import ElFocusTrap from '../src/focus-trap.vue'
import { FOCUS_TRAP_INJECTION_KEY } from '../src/tokens'
const AXIOM = 'rem is the best girl'
describe('<ElFocusTrap', () => {
const childKls = 'child-class'
const TrapChild = {
props: {
items: Number,
},
setup() {
const { focusTrapRef, onKeydown } = inject(
FOCUS_TRAP_INJECTION_KEY,
undefined
)!
return {
focusTrapRef,
onKeydown,
}
},
template: `<div ref="focusTrapRef" tabindex="0" class="${childKls}" @keydown="onKeydown">
<template v-if="!items">${AXIOM}</template>
<template v-else v-for="i in items">
<span class="item" tabindex="0">{{ i }}</span>
</template>
</div>`,
}
const createComponent = (props = {}, items = null) =>
mount(ElFocusTrap, {
props: {
trapped: true,
...props,
},
slots: {
default: () => h(TrapChild, { items }),
},
attachTo: document.body,
})
let wrapper: ReturnType<typeof createComponent>
const findFocusComponent = () => wrapper.findComponent(TrapChild as any)
const findDescendants = () => wrapper.findAll('.item')
afterEach(() => {
wrapper?.unmount()
document.body.innerHTML = ''
})
describe('render', () => {
it('should render correctly', async () => {
wrapper = createComponent()
await nextTick()
const child = findFocusComponent()
expect(child.exists()).toBe(true)
expect(child.text()).toBe(AXIOM)
expect(document.activeElement).toBe(child.element)
})
it('should be able to focus on the first descendant item', async () => {
wrapper = createComponent(undefined, 3)
await nextTick()
const descendants = findDescendants()
expect(descendants).toHaveLength(3)
expect(document.activeElement).toBe(descendants.at(0).element)
})
})
describe('events', () => {
it('should be able to dispatch focus on mount event', async () => {
const focusOnMount = jest.fn()
wrapper = createComponent({
onMountOnFocus: focusOnMount,
})
await nextTick()
expect(focusOnMount).toHaveBeenCalled()
})
it('should be able to dispatch focus on unmount', async () => {
const focusOnUnmount = jest.fn()
wrapper = createComponent({
onUnmountOnFocus: focusOnUnmount,
})
await nextTick()
const child = findFocusComponent()
expect(document.activeElement).toBe(child.element)
wrapper.unmount()
expect(focusOnUnmount).toHaveBeenCalled()
expect(document.activeElement).toBe(document.body)
})
})
describe('features', () => {
it('should be able to navigate via keyboard', async () => {
wrapper = createComponent(undefined, 3)
await nextTick()
const childComponent = findFocusComponent()
const items = findDescendants()
expect(document.activeElement).toBe(items.at(0).element)
/**
* NOTE:
* JSDOM does not support keyboard navigation simulation so that
* dispatching keyboard event with tab key is useless, we cannot test it
* it here, maybe turn to cypress for robust e2e test would be a better idea
*/
// when loop is off
await childComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0).element)
;(items.at(2).element as HTMLElement).focus()
expect(document.activeElement).toBe(items.at(2).element)
await childComponent.trigger('keydown', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(2).element)
// set loop to true so that tab can tabbing from last to first and back forth
await wrapper.setProps({
loop: true,
})
await childComponent.trigger('keydown', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0).element)
await childComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(2).element)
})
it('should not be able to navigate when no focusable element contained', async () => {
wrapper = createComponent()
await nextTick()
const focusComponent = findFocusComponent()
expect(document.activeElement).toBe(focusComponent.element)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(focusComponent.element)
})
it('should not be able to navigate by keyboard when not trapped', async () => {
wrapper = createComponent(
{
trapped: false,
},
3
)
await nextTick()
const focusComponent = findFocusComponent()
const items = findDescendants()
expect(document.activeElement).toBe(items.at(0).element)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0).element)
})
it('should not be able to navigate if the current layer is paused', async () => {
wrapper = createComponent(
{
loop: true,
},
3
)
await nextTick()
const focusComponent = findFocusComponent()
const items = findDescendants()
expect(document.activeElement).toBe(items.at(0).element)
await focusComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(2).element)
const newFocusTrap = createComponent()
await nextTick()
expect(document.activeElement).toBe(
newFocusTrap.find(`.${childKls}`).element
)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).not.toBe(items.at(0).element)
newFocusTrap.unmount()
expect(document.activeElement).toBe(items.at(2).element)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0).element)
})
})
})