config-provider.test.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import { defineComponent, nextTick, reactive, ref } from 'vue'
  2. import { mount } from '@vue/test-utils'
  3. import { afterEach, beforeEach, describe, expect, it } from 'vitest'
  4. import { useLocale } from '@element-plus/hooks'
  5. import Chinese from '@element-plus/locale/lang/zh-cn'
  6. import English from '@element-plus/locale/lang/en'
  7. import { ElButton, ElMessage } from '@element-plus/components'
  8. import { rAF } from '@element-plus/test-utils/tick'
  9. import {
  10. useGlobalComponentSettings,
  11. useGlobalConfig,
  12. } from '../src/hooks/use-global-config'
  13. import ConfigProvider from '../src/config-provider'
  14. import type { PropType } from 'vue'
  15. import type { VueWrapper } from '@vue/test-utils'
  16. import type { Language } from '@element-plus/locale'
  17. import type { ComponentSize } from '@element-plus/constants'
  18. import type { ConfigProviderProps } from '../src/config-provider-props'
  19. const TestComp = defineComponent({
  20. setup() {
  21. const { t } = useLocale()
  22. return () => <div>{t('el.popconfirm.confirmButtonText')}</div>
  23. },
  24. })
  25. describe('config-provider', () => {
  26. describe('locale-provider', () => {
  27. let wrapper: VueWrapper<any>
  28. beforeEach(() => {
  29. const currentLocale = ref<Language>(English)
  30. const oppositeLocale = ref<Language>(Chinese)
  31. const toEn = () => {
  32. currentLocale.value = English
  33. oppositeLocale.value = Chinese
  34. }
  35. const toZh = () => {
  36. currentLocale.value = Chinese
  37. oppositeLocale.value = English
  38. }
  39. wrapper = mount(() => (
  40. <>
  41. <ConfigProvider locale={currentLocale.value}>
  42. <TestComp class="current-locale" />
  43. <ConfigProvider locale={oppositeLocale.value}>
  44. <TestComp class="opposite-locale" />
  45. </ConfigProvider>
  46. </ConfigProvider>
  47. <button onClick={toEn} class="to-en">
  48. toEn
  49. </button>
  50. <button onClick={toZh} class="to-zh">
  51. toZh
  52. </button>
  53. </>
  54. ))
  55. })
  56. afterEach(() => {
  57. wrapper.unmount()
  58. })
  59. it('should provide locale properly', async () => {
  60. expect(wrapper.find('.current-locale').text()).toBe(
  61. English.el.popconfirm.confirmButtonText
  62. )
  63. expect(wrapper.find('.opposite-locale').text()).toBe(
  64. Chinese.el.popconfirm.confirmButtonText
  65. )
  66. })
  67. it('should reactively update the text on page', async () => {
  68. expect(wrapper.find('.current-locale').text()).toBe(
  69. English.el.popconfirm.confirmButtonText
  70. )
  71. expect(wrapper.find('.opposite-locale').text()).toBe(
  72. Chinese.el.popconfirm.confirmButtonText
  73. )
  74. await wrapper.find('.to-zh').trigger('click')
  75. expect(wrapper.find('.current-locale').text()).toBe(
  76. Chinese.el.popconfirm.confirmButtonText
  77. )
  78. expect(wrapper.find('.opposite-locale').text()).toBe(
  79. English.el.popconfirm.confirmButtonText
  80. )
  81. })
  82. })
  83. describe('button-config', () => {
  84. it('autoInsertSpace', async () => {
  85. const config = reactive({
  86. autoInsertSpace: true,
  87. })
  88. const wrapper = mount(() => (
  89. <>
  90. <ConfigProvider button={config}>
  91. <ElButton>中文</ElButton>
  92. </ConfigProvider>
  93. <button
  94. class="toggle"
  95. onClick={() => (config.autoInsertSpace = !config.autoInsertSpace)}
  96. >
  97. toggle
  98. </button>
  99. </>
  100. ))
  101. await nextTick()
  102. expect(
  103. wrapper.find('.el-button .el-button__text--expand').exists()
  104. ).toBeTruthy()
  105. await wrapper.find('.toggle').trigger('click')
  106. expect(
  107. wrapper.find('.el-button .el-button__text--expand').exists()
  108. ).toBeFalsy()
  109. })
  110. })
  111. describe('namespace-config', () => {
  112. it('reactive namespace', async () => {
  113. const namespace = ref()
  114. const wrapper = mount(() => (
  115. <ConfigProvider namespace={namespace.value}>
  116. <ElButton>test str</ElButton>
  117. </ConfigProvider>
  118. ))
  119. await nextTick()
  120. expect(wrapper.find('button').classes().join('')).toBe('el-button')
  121. namespace.value = 'ep'
  122. await nextTick()
  123. expect(wrapper.find('button').classes().join('')).toBe('ep-button')
  124. })
  125. })
  126. describe('message-config', () => {
  127. afterEach(() => {
  128. ElMessage.closeAll()
  129. })
  130. it('limit the number of messages displayed at the same time', async () => {
  131. const config = reactive({
  132. max: 3,
  133. })
  134. const open = () => {
  135. ElMessage('this is a message.')
  136. }
  137. const wrapper = mount(() => (
  138. <ConfigProvider message={config}>
  139. <ElButton onClick={open}>open</ElButton>
  140. </ConfigProvider>
  141. ))
  142. await nextTick()
  143. wrapper.find('.el-button').trigger('click')
  144. wrapper.find('.el-button').trigger('click')
  145. wrapper.find('.el-button').trigger('click')
  146. wrapper.find('.el-button').trigger('click')
  147. await nextTick()
  148. expect(document.querySelectorAll('.el-message').length).toBe(3)
  149. config.max = 10
  150. await nextTick()
  151. wrapper.find('.el-button').trigger('click')
  152. wrapper.find('.el-button').trigger('click')
  153. wrapper.find('.el-button').trigger('click')
  154. wrapper.find('.el-button').trigger('click')
  155. await nextTick()
  156. expect(document.querySelectorAll('.el-message').length).toBe(7)
  157. })
  158. it('multiple config-provider config override', async () => {
  159. const config = reactive({
  160. max: 3,
  161. })
  162. const overrideConfig = reactive({
  163. max: 1,
  164. })
  165. const open = () => {
  166. ElMessage('this is a message.')
  167. }
  168. const wrapper = mount(() => (
  169. <ConfigProvider message={config}>
  170. <ConfigProvider message={overrideConfig}>
  171. <ElButton onClick={open}>open</ElButton>
  172. </ConfigProvider>
  173. </ConfigProvider>
  174. ))
  175. await rAF()
  176. await wrapper.find('.el-button').trigger('click')
  177. await wrapper.find('.el-button').trigger('click')
  178. await wrapper.find('.el-button').trigger('click')
  179. await nextTick()
  180. expect(document.querySelectorAll('.el-message').length).toBe(1)
  181. })
  182. })
  183. describe('feature checking', () => {
  184. const TestComponent = defineComponent({
  185. props: {
  186. configKey: {
  187. type: String as PropType<keyof ConfigProviderProps>,
  188. required: true,
  189. },
  190. },
  191. setup(props) {
  192. const features = useGlobalConfig(props.configKey)
  193. return {
  194. [props.configKey]: features,
  195. }
  196. },
  197. render: () => <div />,
  198. })
  199. it.each([
  200. { feature: 'a11y', config: false },
  201. { feature: 'keyboardNavigation', config: false },
  202. { feature: 'experimentalFeatures', config: { someFeature: true } },
  203. ])(
  204. 'should inject config $feature to $config correctly',
  205. ({ feature, config }: { feature: string; config: any }) => {
  206. const props: Record<string, any> = {}
  207. props[feature] = config
  208. const wrapper = mount(() => (
  209. <ConfigProvider {...props}>
  210. <TestComponent configKey={feature as keyof ConfigProviderProps} />
  211. </ConfigProvider>
  212. ))
  213. expect(wrapper.findComponent(TestComponent).vm[feature]).toEqual(config)
  214. }
  215. )
  216. })
  217. describe('global component configs', () => {
  218. it('should use global configured settings', async () => {
  219. const namespace = 'test'
  220. const locale = Chinese
  221. const zIndex = 1000
  222. const block = 'button'
  223. const size = 'large'
  224. const receiverRef = ref()
  225. const fallback = ref('' as ComponentSize)
  226. const ReceiverComponent = defineComponent({
  227. setup() {
  228. receiverRef.value = useGlobalComponentSettings(block, fallback)
  229. },
  230. template: '<div></div>',
  231. })
  232. mount(() => (
  233. <ConfigProvider
  234. zIndex={zIndex}
  235. locale={locale}
  236. namespace={namespace}
  237. size={size}
  238. >
  239. <ReceiverComponent />
  240. </ConfigProvider>
  241. ))
  242. const vm = receiverRef.value
  243. expect(vm.ns.namespace).toBe(namespace)
  244. expect(vm.locale.locale).toBe(locale)
  245. expect(vm.zIndex.currentZIndex).toBeGreaterThanOrEqual(zIndex)
  246. expect(vm.size).toBe(size)
  247. fallback.value = 'small'
  248. await nextTick()
  249. expect(vm.size).toBe('small')
  250. })
  251. })
  252. })