drawer.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. // @ts-nocheck
  2. import { nextTick } from 'vue'
  3. import { mount } from '@vue/test-utils'
  4. import { describe, expect, test, vi } from 'vitest'
  5. import { rAF } from '@element-plus/test-utils/tick'
  6. import Drawer from '../src/drawer.vue'
  7. import Button from '../../button/src/button.vue'
  8. const _mount = (template: string, data, otherObj?) =>
  9. mount({
  10. components: {
  11. [Drawer.name]: Drawer,
  12. [Button.name]: Button,
  13. },
  14. template,
  15. data,
  16. ...otherObj,
  17. })
  18. const title = 'Drawer Title'
  19. const content = 'content'
  20. describe('Drawer', () => {
  21. test('create', async () => {
  22. const wrapper = _mount(
  23. `
  24. <el-drawer :title="title" v-model="visible"></el-drawer>
  25. `,
  26. () => ({
  27. title,
  28. visible: true,
  29. })
  30. )
  31. await nextTick()
  32. await rAF()
  33. await nextTick()
  34. const wrapperEl = wrapper.find('.el-overlay').element as HTMLDivElement
  35. const headerEl = wrapper.find('.el-drawer__header').element
  36. await nextTick()
  37. expect(wrapperEl.style.display).not.toEqual('none')
  38. expect(headerEl.textContent).toEqual(title)
  39. })
  40. test('render correct content', async () => {
  41. const wrapper = _mount(
  42. `
  43. <el-drawer :title='title' v-model='visible'>
  44. <span>this is a sentence</span>
  45. <el-button @click='dialogVisible = false'>cancel</el-button>
  46. <el-button type='primary' @click='dialogVisible = false'>confirm</el-button>
  47. </el-drawer>
  48. `,
  49. () => ({
  50. title,
  51. visible: true,
  52. })
  53. )
  54. await nextTick()
  55. await rAF()
  56. await nextTick()
  57. expect(wrapper.find('.el-drawer__body span').element.textContent).toEqual(
  58. 'this is a sentence'
  59. )
  60. const footerBtns = wrapper.findAll('.el-button')
  61. expect(footerBtns.length).toEqual(2)
  62. expect(footerBtns[0].find('span').element.textContent).toEqual('cancel')
  63. expect(footerBtns[1].find('span').element.textContent).toEqual('confirm')
  64. })
  65. test('should append to body, when append-to-body flag is true', async () => {
  66. const wrapper = _mount(
  67. `
  68. <el-drawer ref='d' :title='title' v-model='visible' :append-to-body='true'>
  69. <span> content </span>
  70. </el-drawer>
  71. `,
  72. () => ({
  73. title,
  74. visible: false,
  75. })
  76. )
  77. const vm = wrapper.vm as any
  78. vm.visible = true
  79. await nextTick()
  80. await rAF()
  81. await nextTick()
  82. expect(document.querySelector('.el-overlay')?.parentNode).toEqual(
  83. document.body
  84. )
  85. })
  86. test('should open and close drawer properly', async () => {
  87. const onClose = vi.fn()
  88. const onClosed = vi.fn()
  89. const onOpened = vi.fn()
  90. const wrapper = _mount(
  91. `
  92. <el-drawer :title='title' v-model='visible' @closed="onClosed" @close="onClose" @opened="onOpened">
  93. <span>${content}</span>
  94. </el-drawer>
  95. `,
  96. () => ({
  97. title,
  98. visible: false,
  99. }),
  100. {
  101. methods: {
  102. onOpened,
  103. onClose,
  104. onClosed,
  105. },
  106. }
  107. )
  108. const vm = wrapper.vm as any
  109. await nextTick()
  110. await rAF()
  111. await nextTick()
  112. expect(onOpened).not.toHaveBeenCalled()
  113. const drawerEl = wrapper.find('.el-overlay').element as HTMLDivElement
  114. expect(drawerEl.style.display).toEqual('none')
  115. vm.visible = true
  116. await nextTick()
  117. await rAF()
  118. expect(drawerEl.style.display).not.toEqual('none')
  119. expect(onOpened).toHaveBeenCalled()
  120. // vm.visible = false
  121. // await nextTick()
  122. // await rAF()
  123. // await nextTick()
  124. // expect(onClose).toHaveBeenCalled()
  125. })
  126. test('should destroy every child after drawer was closed when destroy-on-close flag is true', async () => {
  127. const wrapper = _mount(
  128. `
  129. <el-drawer :title='title' v-model='visible' :append-to-body='false' :destroy-on-close='true' ref='drawer'>
  130. <span>${content}</span>
  131. </el-drawer>
  132. `,
  133. () => ({
  134. title,
  135. visible: true,
  136. })
  137. )
  138. const vm = wrapper.vm as any
  139. await nextTick()
  140. await rAF()
  141. await nextTick()
  142. expect(wrapper.find('.el-drawer__body span').element.textContent).toEqual(
  143. content
  144. )
  145. vm.$refs.drawer.handleClose()
  146. await nextTick()
  147. await rAF()
  148. await nextTick()
  149. expect(wrapper.find('.el-drawer__body').exists()).toBe(false)
  150. })
  151. test('should close dialog by clicking the close button', async () => {
  152. const wrapper = _mount(
  153. `
  154. <el-drawer :title='title' v-model='visible' :append-to-body='false' :destroy-on-close='true' ref='drawer'>
  155. <span>${content}</span>
  156. </el-drawer>
  157. `,
  158. () => ({
  159. title,
  160. visible: true,
  161. })
  162. )
  163. await nextTick()
  164. await rAF()
  165. await nextTick()
  166. const vm = wrapper.vm as any
  167. await wrapper.find('.el-drawer__close-btn').trigger('click')
  168. await nextTick()
  169. await rAF()
  170. await nextTick()
  171. expect(vm.visible).toEqual(false)
  172. })
  173. test('should invoke before-close', async () => {
  174. const beforeClose = vi.fn()
  175. const wrapper = _mount(
  176. `
  177. <el-drawer
  178. :before-close='beforeClose'
  179. :title='title'
  180. v-model='visible'
  181. :append-to-body='true'
  182. :destroy-on-close='true'
  183. ref='drawer'
  184. >
  185. <span>${content}</span>
  186. </el-drawer>
  187. `,
  188. () => ({
  189. title,
  190. visible: true,
  191. beforeClose,
  192. })
  193. )
  194. const vm = wrapper.vm as any
  195. vm.$refs.drawer.handleClose()
  196. expect(beforeClose).toHaveBeenCalled()
  197. })
  198. test('should not show close button when show-close flag is false', async () => {
  199. const wrapper = _mount(
  200. `
  201. <el-drawer :title='title' v-model='visible' ref='drawer' :show-close='false'>
  202. <span>${content}</span>
  203. </el-drawer>
  204. `,
  205. () => ({
  206. title,
  207. visible: true,
  208. })
  209. )
  210. expect(wrapper.find('.el-drawer__close-btn').exists()).toBe(false)
  211. })
  212. test('should have custom classes when custom classes were given', async () => {
  213. const classes = 'some-custom-class'
  214. const wrapper = _mount(
  215. `
  216. <el-drawer :title='title' v-model='visible' ref='drawer' custom-class='${classes}'>
  217. <span>${content}</span>
  218. </el-drawer>
  219. `,
  220. () => ({
  221. title,
  222. visible: true,
  223. })
  224. )
  225. expect(wrapper.find(`.${classes}`).exists()).toBe(true)
  226. })
  227. test('drawer header should have slot props', async () => {
  228. const wrapper = _mount(
  229. `
  230. <el-drawer v-model='visible' ref='drawer'>
  231. <template #header="{ titleId, titleClass, close }">
  232. <button :data-title-id="titleId" :data-title-class="titleClass" @click="close" />
  233. </template>
  234. </el-drawer>
  235. `,
  236. () => ({
  237. visible: true,
  238. })
  239. )
  240. await nextTick()
  241. const drawer = wrapper.findComponent({ ref: 'drawer' })
  242. const headerButton = wrapper.find('button')
  243. expect(headerButton.attributes()['data-title-id']).toBeTruthy()
  244. expect(headerButton.attributes()['data-title-class']).toBe(
  245. 'el-drawer__title'
  246. )
  247. expect(drawer.emitted().close).toBeFalsy()
  248. headerButton.trigger('click')
  249. await nextTick()
  250. expect(drawer.emitted()).toHaveProperty('close')
  251. })
  252. test('should not render header when withHeader attribute is false', async () => {
  253. const wrapper = _mount(
  254. `
  255. <el-drawer :title='title' v-model='visible' ref='drawer' :with-header='false'>
  256. <span>${content}</span>
  257. </el-drawer>
  258. `,
  259. () => ({
  260. title,
  261. visible: true,
  262. })
  263. )
  264. expect(wrapper.find('.el-drawer__header').exists()).toBe(false)
  265. })
  266. describe('directions', () => {
  267. const renderer = (direction: string) => {
  268. return _mount(
  269. `
  270. <el-drawer :title='title' v-model='visible' direction='${direction}'>
  271. <span>${content}</span>
  272. </el-drawer>
  273. `,
  274. () => ({
  275. title,
  276. visible: true,
  277. })
  278. )
  279. }
  280. test('should render from left to right', async () => {
  281. expect(renderer('ltr').find('.ltr').exists()).toBe(true)
  282. })
  283. test('should render from right to left', async () => {
  284. expect(renderer('rtl').find('.rtl').exists()).toBe(true)
  285. })
  286. test('should render from top to bottom', async () => {
  287. expect(renderer('ttb').find('.ttb').exists()).toBe(true)
  288. })
  289. test('should render from bottom to top', async () => {
  290. expect(renderer('btt').find('.btt').exists()).toBe(true)
  291. })
  292. })
  293. test('events', async () => {
  294. const open = vi.fn()
  295. const opened = vi.fn()
  296. const close = vi.fn()
  297. const closed = vi.fn()
  298. const wrapper = _mount(
  299. `
  300. <el-drawer
  301. :title='title'
  302. v-model='visible'
  303. ref="drawer"
  304. @open="open"
  305. @opened="opened"
  306. @close="close"
  307. @closed="closed">
  308. <span>${content}</span>
  309. </el-drawer>
  310. `,
  311. () => ({
  312. title,
  313. visible: false,
  314. }),
  315. {
  316. methods: {
  317. close,
  318. closed,
  319. open,
  320. opened,
  321. },
  322. }
  323. )
  324. const vm = wrapper.vm as any
  325. const drawer = wrapper.vm.$refs.drawer as any
  326. vm.visible = true
  327. await nextTick()
  328. await nextTick()
  329. expect(open).toHaveBeenCalled()
  330. drawer.afterEnter()
  331. expect(opened).toHaveBeenCalled()
  332. expect(close).not.toHaveBeenCalled()
  333. expect(closed).not.toHaveBeenCalled()
  334. vm.visible = false
  335. await nextTick()
  336. expect(close).toHaveBeenCalled()
  337. drawer.afterLeave()
  338. expect(closed).toHaveBeenCalled()
  339. })
  340. describe('size', () => {
  341. const renderer = (size: string, isVertical: boolean) =>
  342. _mount(
  343. `
  344. <el-drawer :title='title' v-model='visible' direction='${
  345. isVertical ? 'ltr' : 'ttb'
  346. }' size='${size}'>
  347. <span>${content}</span>
  348. </el-drawer>
  349. `,
  350. () => ({
  351. visible: true,
  352. title,
  353. })
  354. )
  355. test('should effect height when drawer is vertical', async () => {
  356. const drawerEl = renderer('50%', true).find('.el-drawer')
  357. .element as HTMLDivElement
  358. expect(drawerEl.style.width).toEqual('50%')
  359. })
  360. test('should effect width when drawer is horizontal', async () => {
  361. const drawerEl = renderer('50%', false).find('.el-drawer')
  362. .element as HTMLDivElement
  363. expect(drawerEl.style.height).toEqual('50%')
  364. })
  365. })
  366. describe('accessibility', () => {
  367. test('title attribute should set aria-label', async () => {
  368. const wrapper = _mount(
  369. `
  370. <el-drawer
  371. :title='title'
  372. v-model='visible'
  373. ref="drawer">
  374. </el-drawer>
  375. `,
  376. () => ({
  377. title,
  378. visible: true,
  379. })
  380. )
  381. await nextTick()
  382. const drawerDialog = wrapper.find('[role="dialog"]')
  383. expect(drawerDialog.attributes()['aria-label']).toBe(title)
  384. expect(drawerDialog.attributes()['aria-labelledby']).toBeFalsy()
  385. })
  386. test('missing title attribute should point to header slot content', async () => {
  387. const wrapper = _mount(
  388. `
  389. <el-drawer
  390. v-model='visible'
  391. ref="drawer">
  392. <template #header="{ titleId, titleClass }">
  393. <h5 :id="titleId" :class="titleClass" />
  394. </template>
  395. </el-drawer>
  396. `,
  397. () => ({
  398. visible: true,
  399. })
  400. )
  401. await nextTick()
  402. const drawerDialog = wrapper.find('[role="dialog"]')
  403. const drawerTitle = wrapper.find('.el-drawer__title')
  404. expect(drawerDialog.attributes()['aria-label']).toBeFalsy()
  405. expect(drawerDialog.attributes()['aria-labelledby']).toBe(
  406. drawerTitle.attributes().id
  407. )
  408. })
  409. test('aria-describedby should point to modal body', async () => {
  410. const wrapper = _mount(
  411. `
  412. <el-drawer
  413. v-model='visible'
  414. ref="drawer">
  415. <span>${content}</span>
  416. </el-drawer>
  417. `,
  418. () => ({
  419. visible: true,
  420. })
  421. )
  422. await nextTick()
  423. const drawerDialog = wrapper.find('[role="dialog"]')
  424. const drawerBody = wrapper.find('.el-drawer__body')
  425. expect(drawerDialog.attributes()['aria-describedby']).toBe(
  426. drawerBody.attributes().id
  427. )
  428. })
  429. })
  430. })