51 lines
3.0 KiB
TypeScript
51 lines
3.0 KiB
TypeScript
// @vitest-environment happy-dom
|
|
import { afterEach, expect, it, vi } from 'vitest'
|
|
import { mount, type VueWrapper } from '@vue/test-utils'
|
|
import AppDialog from './AppDialog.vue'
|
|
const mounted: VueWrapper[] = []
|
|
afterEach(() => { mounted.splice(0).reverse().forEach(w => w.unmount()); document.body.innerHTML = ''; document.body.style.cssText = ''; document.documentElement.style.cssText = '' })
|
|
it('locks all scroll ancestors and restores focus and inline styles', async () => {
|
|
const opener = document.createElement('button'); document.body.append(opener); opener.focus()
|
|
const host = document.createElement('div'); host.style.setProperty('overflow', 'auto', 'important'); document.body.append(host)
|
|
const w = mount(AppDialog, { props:{label:'测试'}, slots:{default:'<section class="modal"><input autofocus /></section>'}, attachTo:host }); mounted.push(w)
|
|
expect(w.get('dialog').element.open).toBe(true)
|
|
expect(host.style.overflow).toBe('hidden')
|
|
expect(document.body.style.overflow).toBe('hidden')
|
|
await w.get('dialog').trigger('keydown', {key:'Escape'})
|
|
expect(w.emitted('close')).toHaveLength(1)
|
|
w.unmount(); mounted.pop()
|
|
expect(host.style.overflow).toBe('auto')
|
|
expect(host.style.getPropertyPriority('overflow')).toBe('important')
|
|
expect(document.body.style.overflow).toBe('')
|
|
expect(document.activeElement).toBe(opener)
|
|
})
|
|
it('retains scroll locks until the last nested dialog closes', () => {
|
|
const first = mount(AppDialog, {props:{label:'父弹窗'}, attachTo:document.body}); mounted.push(first)
|
|
const second = mount(AppDialog, {props:{label:'子弹窗'}, attachTo:document.body}); mounted.push(second)
|
|
first.unmount(); mounted.splice(0,1)
|
|
expect(document.body.style.overflow).toBe('hidden')
|
|
second.unmount(); mounted.pop()
|
|
expect(document.body.style.overflow).toBe('')
|
|
})
|
|
it('does not dismiss permission or busy dialogs through Escape or backdrop', async () => {
|
|
const w = mount(AppDialog, {props:{label:'权限确认',dismissible:false},attachTo:document.body}); mounted.push(w)
|
|
await w.get('dialog').trigger('keydown',{key:'Escape'})
|
|
await w.get('dialog').trigger('cancel')
|
|
await w.get('dialog').trigger('click')
|
|
expect(w.emitted('close')).toBeUndefined()
|
|
})
|
|
|
|
it('cycles Tab between the first and last visible controls', async () => {
|
|
const w = mount(AppDialog, {props:{label:'键盘'}, slots:{default:'<section class="modal"><input /><button>取消</button><button disabled>禁用</button></section>'},attachTo:document.body}); mounted.push(w)
|
|
const input = w.get('input').element
|
|
const button = w.get('button').element
|
|
const rects = [new DOMRect(0, 0, 50, 30)] as unknown as DOMRectList
|
|
const spies = [input, button].map(element => vi.spyOn(element, 'getClientRects').mockReturnValue(rects))
|
|
input.focus()
|
|
await w.get('dialog').trigger('keydown', {key:'Tab', shiftKey:true})
|
|
expect(document.activeElement).toBe(button)
|
|
await w.get('dialog').trigger('keydown', {key:'Tab'})
|
|
expect(document.activeElement).toBe(input)
|
|
spies.forEach(spy => spy.mockRestore())
|
|
})
|