notification.test.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import { nextTick } from 'vue'
  2. import { mount } from '@vue/test-utils'
  3. import { describe, expect, test, vi } from 'vitest'
  4. import { TypeComponentsMap } from '@element-plus/utils'
  5. import { EVENT_CODE } from '@element-plus/constants'
  6. import { notificationTypes } from '../src/notification'
  7. import Notification from '../src/notification.vue'
  8. import type { VNode } from 'vue'
  9. import type { VueWrapper } from '@vue/test-utils'
  10. import type { SpyInstance } from 'vitest'
  11. import type {
  12. NotificationInstance,
  13. NotificationProps,
  14. } from '../src/notification'
  15. const AXIOM = 'Rem is the best girl'
  16. const onClose = vi.fn()
  17. const _mount = ({
  18. props,
  19. slots,
  20. }: {
  21. props?: Partial<NotificationProps>
  22. slots?: Record<'default', () => string | VNode>
  23. }) => mount(<Notification {...{ onClose, ...props }} v-slots={slots} />)
  24. describe('Notification.vue', () => {
  25. describe('render', () => {
  26. test('basic render test', () => {
  27. const wrapper = _mount({
  28. slots: {
  29. default: () => AXIOM,
  30. },
  31. })
  32. expect(wrapper.text()).toEqual(AXIOM)
  33. expect(wrapper.vm.visible).toBe(true)
  34. expect(wrapper.vm.iconComponent).toBeUndefined()
  35. expect(wrapper.vm.horizontalClass).toBe('right')
  36. expect(wrapper.vm.positionStyle).toEqual(
  37. expect.objectContaining({
  38. top: '0px',
  39. })
  40. )
  41. })
  42. test('should be able to render VNode', () => {
  43. const wrapper = _mount({
  44. slots: {
  45. default: () => <span class="text-node">{AXIOM}</span>,
  46. },
  47. })
  48. expect(wrapper.find('.text-node').exists()).toBe(true)
  49. })
  50. test('should be able to render raw HTML tag with dangerouslyUseHTMLString flag', () => {
  51. const tagClass = 'test-class'
  52. const HTMLWrapper = _mount({
  53. props: {
  54. dangerouslyUseHTMLString: true,
  55. message: `<strong class=${tagClass}>${AXIOM}</strong>`,
  56. },
  57. })
  58. expect(HTMLWrapper.find(`.${tagClass}`).exists()).toBe(true)
  59. })
  60. test('should not be able to render raw HTML tag without dangerouslyUseHTMLString flag', () => {
  61. const tagClass = 'test-class'
  62. const HTMLWrapper = _mount({
  63. props: {
  64. dangerouslyUseHTMLString: false,
  65. message: `<strong class=${tagClass}>${AXIOM}</strong>`,
  66. },
  67. })
  68. expect(HTMLWrapper.find(`.${tagClass}`).exists()).toBe(false)
  69. })
  70. test('should be able to render z-index style with zIndex flag', async () => {
  71. const wrapper = _mount({})
  72. await nextTick()
  73. expect(wrapper.vm.positionStyle).toEqual(
  74. expect.objectContaining({
  75. top: '0px',
  76. })
  77. )
  78. })
  79. })
  80. describe('Notification.type', () => {
  81. test('should be able to render typed notification', () => {
  82. let wrapper: VueWrapper<NotificationInstance>
  83. for (const type of notificationTypes) {
  84. wrapper = _mount({
  85. props: {
  86. type,
  87. },
  88. })
  89. expect(wrapper.findComponent(TypeComponentsMap[type]).exists()).toBe(
  90. true
  91. )
  92. }
  93. })
  94. test('should not be able to render invalid type icon', () => {
  95. vi.spyOn(console, 'warn').mockImplementation(() => vi.fn)
  96. const type = 'some-type'
  97. const wrapper = _mount({
  98. props: {
  99. // @ts-expect-error
  100. type,
  101. },
  102. })
  103. expect(wrapper.find('.el-notification__icon').exists()).toBe(false)
  104. expect(console.warn).toHaveBeenCalled()
  105. ;(console.warn as any as SpyInstance).mockRestore()
  106. })
  107. })
  108. describe('event handlers', () => {
  109. test('it should be able to close the notification by clicking close button', async () => {
  110. const onClose = vi.fn()
  111. const wrapper = _mount({
  112. slots: {
  113. default: () => AXIOM,
  114. },
  115. props: { onClose },
  116. })
  117. await nextTick()
  118. const closeBtn = wrapper.find('.el-notification__closeBtn')
  119. expect(closeBtn.exists()).toBe(true)
  120. await closeBtn.trigger('click')
  121. expect(onClose).toHaveBeenCalled()
  122. })
  123. test('should be able to close after duration', async () => {
  124. vi.useFakeTimers()
  125. const duration = 100
  126. const wrapper = _mount({
  127. props: {
  128. duration,
  129. },
  130. })
  131. vi.runAllTimers()
  132. vi.useRealTimers()
  133. expect(wrapper.vm.visible).toBe(false)
  134. })
  135. test('should be able to prevent close itself when hover over', async () => {
  136. vi.useFakeTimers()
  137. const duration = 100
  138. const wrapper = _mount({
  139. props: {
  140. duration,
  141. },
  142. })
  143. vi.advanceTimersByTime(50)
  144. await wrapper.find('[role=alert]').trigger('mouseenter')
  145. vi.advanceTimersByTime(5000)
  146. expect(wrapper.vm.visible).toBe(true)
  147. await wrapper.find('[role=alert]').trigger('mouseleave')
  148. // expect(wrapper.vm.timer).not.toBe(null)
  149. expect(wrapper.vm.visible).toBe(true)
  150. // expect(wrapper.vm.closed).toBe(false)
  151. vi.runAllTimers()
  152. expect(wrapper.vm.visible).toBe(false)
  153. // expect(wrapper.vm.timer).toBe(null)
  154. // expect(wrapper.vm.closed).toBe(true)
  155. vi.useRealTimers()
  156. })
  157. test('should not be able to close when duration is set to 0', async () => {
  158. vi.useFakeTimers()
  159. const duration = 0
  160. const wrapper = _mount({
  161. props: {
  162. duration,
  163. },
  164. })
  165. vi.runAllTimers()
  166. expect(wrapper.vm.visible).toBe(true)
  167. vi.useRealTimers()
  168. })
  169. test('should be able to handle click event', async () => {
  170. const onClick = vi.fn()
  171. const wrapper = _mount({
  172. props: {
  173. duration: 0,
  174. onClick,
  175. },
  176. })
  177. await wrapper.trigger('click')
  178. expect(onClick).toHaveBeenCalledTimes(1)
  179. })
  180. test('should be able to delete timer when press delete', async () => {
  181. vi.useFakeTimers()
  182. const wrapper = _mount({
  183. slots: {
  184. default: () => AXIOM,
  185. },
  186. })
  187. const event = new KeyboardEvent('keydown', {
  188. code: EVENT_CODE.backspace,
  189. bubbles: true,
  190. })
  191. document.dispatchEvent(event)
  192. vi.runAllTimers()
  193. expect(wrapper.vm.visible).toBe(true)
  194. vi.useRealTimers()
  195. })
  196. test('should be able to close the notification immediately when press esc', async () => {
  197. vi.useFakeTimers()
  198. const wrapper = _mount({
  199. props: {
  200. duration: 0,
  201. },
  202. slots: {
  203. default: () => AXIOM,
  204. },
  205. })
  206. // Same as above
  207. const event = new KeyboardEvent('keydown', {
  208. code: EVENT_CODE.esc,
  209. })
  210. document.dispatchEvent(event)
  211. vi.runAllTimers()
  212. expect(wrapper.vm.visible).toBe(false)
  213. vi.useRealTimers()
  214. })
  215. })
  216. })