message.test.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { h, nextTick } from 'vue'
  2. import { describe, expect, test, vi } from 'vitest'
  3. import makeMount from '@element-plus/test-utils/make-mount'
  4. import { rAF } from '@element-plus/test-utils/tick'
  5. import { TypeComponentsMap } from '@element-plus/utils'
  6. import { EVENT_CODE } from '@element-plus/constants'
  7. import Message from '../src/message.vue'
  8. import type { CSSProperties, Component, ComponentPublicInstance } from 'vue'
  9. const AXIOM = 'Rem is the best girl'
  10. type MessageInstance = ComponentPublicInstance<{
  11. visible: boolean
  12. iconComponent: string | Component
  13. customStyle: CSSProperties
  14. }>
  15. const onClose = vi.fn()
  16. const _mount = makeMount(Message, {
  17. props: {
  18. onClose,
  19. },
  20. })
  21. describe('Message.vue', () => {
  22. describe('render', () => {
  23. test('basic render test', () => {
  24. const wrapper = _mount({
  25. slots: {
  26. default: AXIOM,
  27. },
  28. })
  29. const vm = wrapper.vm as MessageInstance
  30. expect(wrapper.text()).toEqual(AXIOM)
  31. expect(vm.visible).toBe(true)
  32. expect(vm.iconComponent).toBe(TypeComponentsMap['info'])
  33. expect(vm.customStyle).toEqual({ top: '16px', zIndex: 2001 })
  34. })
  35. test('should be able to render VNode', () => {
  36. const wrapper = _mount({
  37. slots: {
  38. default: h('span', { class: 'text-node' }, AXIOM),
  39. },
  40. })
  41. expect(wrapper.find('.text-node').exists()).toBe(true)
  42. })
  43. test('should be able to render raw HTML with dangerouslyUseHTMLString prop', () => {
  44. const tagClass = 'test-class'
  45. const wrapper = _mount({
  46. props: {
  47. dangerouslyUseHTMLString: true,
  48. message: `<string class="${tagClass}"'>${AXIOM}</strong>`,
  49. },
  50. })
  51. expect(wrapper.find(`.${tagClass}`).exists()).toBe(true)
  52. })
  53. test('should not be able to render raw HTML without dangerouslyUseHTMLString prop', () => {
  54. const tagClass = 'test-class'
  55. const wrapper = _mount({
  56. props: {
  57. dangerouslyUseHTMLString: false,
  58. message: `<string class="${tagClass}"'>${AXIOM}</strong>`,
  59. },
  60. })
  61. expect(wrapper.find(`.${tagClass}`).exists()).toBe(false)
  62. })
  63. })
  64. describe('Message.type', () => {
  65. test('should be able to render typed messages', () => {
  66. for (const type of ['success', 'warning', 'info', 'error'] as const) {
  67. const wrapper = _mount({ props: { type } })
  68. expect(wrapper.findComponent(TypeComponentsMap[type]).exists()).toBe(
  69. true
  70. )
  71. }
  72. })
  73. test('should not be able to render invalid type icon', () => {
  74. const consoleWarn = console.warn
  75. console.warn = vi.fn()
  76. const type = 'some-type'
  77. const wrapper = _mount({ props: { type } })
  78. for (const component of Object.values(TypeComponentsMap)) {
  79. expect(wrapper.findComponent(component).exists()).toBe(false)
  80. }
  81. console.warn = consoleWarn
  82. })
  83. })
  84. describe('event handlers', () => {
  85. test('it should be able to close the message by clicking close button', async () => {
  86. const onClose = vi.fn()
  87. const wrapper = _mount({
  88. slots: { default: AXIOM },
  89. props: {
  90. onClose,
  91. showClose: true,
  92. },
  93. })
  94. const closeBtn = wrapper.find('.el-message__closeBtn')
  95. expect(closeBtn.exists()).toBe(true)
  96. await closeBtn.trigger('click')
  97. expect((wrapper.vm as MessageInstance).visible).toBe(false)
  98. })
  99. test('it should close after duration', async () => {
  100. vi.useFakeTimers()
  101. const duration = 1000
  102. const wrapper = _mount({ props: { duration } })
  103. const vm = wrapper.vm as MessageInstance
  104. await nextTick()
  105. expect(vm.visible).toBe(true)
  106. vi.runAllTimers()
  107. await nextTick()
  108. expect(vm.visible).toBe(false)
  109. vi.useRealTimers()
  110. })
  111. test('it should prevent close when hovered', async () => {
  112. vi.useFakeTimers()
  113. const duration = 1000
  114. const wrapper = _mount({ props: { duration } })
  115. const vm = wrapper.vm as MessageInstance
  116. vi.advanceTimersByTime(50)
  117. expect(vm.visible).toBe(true)
  118. await wrapper.find('[role="alert"]').trigger('mouseenter')
  119. vi.runAllTimers()
  120. expect(vm.visible).toBe(true)
  121. await wrapper.find('[role="alert"]').trigger('mouseleave')
  122. expect(vm.visible).toBe(true)
  123. vi.runAllTimers()
  124. expect(vm.visible).toBe(false)
  125. vi.useRealTimers()
  126. })
  127. test('it should not close when duration is set to 0', () => {
  128. vi.useFakeTimers()
  129. const duration = 0
  130. const wrapper = _mount({ props: { duration } })
  131. const vm = wrapper.vm as MessageInstance
  132. expect(vm.visible).toBe(true)
  133. vi.runAllTimers()
  134. expect(vm.visible).toBe(true)
  135. vi.useRealTimers()
  136. })
  137. test('it should close when esc is pressed', async () => {
  138. const wrapper = _mount({ slots: { default: AXIOM } })
  139. const event = new KeyboardEvent('keydown', {
  140. code: EVENT_CODE.esc,
  141. })
  142. document.dispatchEvent(event)
  143. expect((wrapper.vm as MessageInstance).visible).toBe(false)
  144. })
  145. test('it should call close after transition ends', async () => {
  146. const onClose = vi.fn()
  147. const wrapper = _mount({
  148. slots: { default: AXIOM },
  149. props: { onClose },
  150. })
  151. await rAF()
  152. const vm = wrapper.vm as MessageInstance
  153. vm.visible = false
  154. await rAF()
  155. expect(onClose).toHaveBeenCalledTimes(1)
  156. })
  157. })
  158. })