pagination.test.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import { nextTick, ref } from 'vue'
  2. import { mount } from '@vue/test-utils'
  3. import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
  4. import { CaretLeft, CaretRight } from '@element-plus/icons-vue'
  5. import Pagination from '../src/pagination'
  6. import selectDropdownVue from '../../select/src/select-dropdown.vue'
  7. import type { VueWrapper } from '@vue/test-utils'
  8. const assertElementsExistence = (
  9. wrapper: VueWrapper<any>,
  10. selectors: string[],
  11. existence: boolean
  12. ) => {
  13. selectors.forEach((selector) => {
  14. expect(wrapper.find(selector).exists()).toBe(existence)
  15. })
  16. }
  17. const assertCurrent = (wrapper: VueWrapper<any>, page: number) => {
  18. expect(wrapper.find('.el-pager li.is-active.number').text()).toBe(
  19. String(page)
  20. )
  21. }
  22. const assertPages = (wrapper: VueWrapper<any>, total: number) => {
  23. expect(wrapper.find('.el-pagination .el-pager li:last-child').text()).toBe(
  24. String(total)
  25. )
  26. }
  27. describe('Pagination', () => {
  28. describe('test invalid usages', () => {
  29. const cacheWarn = console.warn
  30. beforeEach(() => {
  31. console.warn = vi.fn()
  32. })
  33. afterEach(() => {
  34. console.warn = cacheWarn
  35. })
  36. test('both absence of total & pageCount is invalid', async () => {
  37. expect(console.warn).not.toHaveBeenCalled()
  38. const total = ref<number | undefined>(undefined)
  39. const wrapper = mount(() => <Pagination total={total.value}></Pagination>)
  40. expect(wrapper.find('.el-pagination').exists()).toBe(false)
  41. expect(console.warn).toHaveBeenCalled()
  42. total.value = 100
  43. await nextTick()
  44. expect(wrapper.find('.el-pagination').exists()).toBe(true)
  45. })
  46. test('current-page defined while absence of current-page listener is invalid', () => {
  47. expect(console.warn).not.toHaveBeenCalled()
  48. const wrapper = mount(() => (
  49. <Pagination total={100} currentPage={1}></Pagination>
  50. ))
  51. expect(wrapper.find('.el-pagination').exists()).toBe(false)
  52. expect(console.warn).toHaveBeenCalled()
  53. })
  54. test('layout with `sizes` restrictions(page-count)', () => {
  55. expect(console.warn).not.toHaveBeenCalled()
  56. const wrapper = mount(() => (
  57. <Pagination layout="sizes, pager" pageCount={10}></Pagination>
  58. ))
  59. expect(wrapper.find('.el-pagination').exists()).toBe(false)
  60. expect(console.warn).toHaveBeenCalled()
  61. })
  62. test('layout with `sizes` restrictions(page-size)', () => {
  63. expect(console.warn).not.toHaveBeenCalled()
  64. const wrapper = mount(() => (
  65. <Pagination layout="sizes, pager" pageSize={10}></Pagination>
  66. ))
  67. expect(wrapper.find('.el-pagination').exists()).toBe(false)
  68. expect(console.warn).toHaveBeenCalled()
  69. })
  70. })
  71. describe('test layout & layout reactive change', () => {
  72. const layoutRef = ref('')
  73. const wrapper = mount(() => (
  74. <Pagination total={100} layout={layoutRef.value}></Pagination>
  75. ))
  76. test('layout empty', async () => {
  77. await nextTick()
  78. expect(wrapper.find('.el-pagination').exists()).toBe(false)
  79. })
  80. const layoutSelectorPairs = [
  81. ['sizes', '.el-pagination__sizes'],
  82. ['prev', 'button.btn-prev'],
  83. ['pager', 'ul.el-pager'],
  84. ['next', 'button.btn-next'],
  85. ['jumper', '.el-pagination__jump'],
  86. ['total', '.el-pagination__total'],
  87. ]
  88. layoutSelectorPairs.forEach(([layout], idx) => {
  89. test(`layout with only '${layout}'`, async () => {
  90. layoutRef.value = layout
  91. await nextTick()
  92. for (const [i, layoutSelectorPair] of layoutSelectorPairs.entries()) {
  93. expect(wrapper.find(layoutSelectorPair[1]).exists()).toBe(i === idx)
  94. }
  95. })
  96. })
  97. test(`layout with '->, total'`, async () => {
  98. layoutRef.value = '->, total'
  99. await nextTick()
  100. assertElementsExistence(
  101. wrapper,
  102. ['.el-pagination__total', '.el-pagination__rightwrapper'],
  103. true
  104. )
  105. })
  106. test('layout with default layout prop', () => {
  107. const wrapper = mount(() => <Pagination total={100}></Pagination>)
  108. assertElementsExistence(
  109. wrapper,
  110. [
  111. '.el-pagination__rightwrapper',
  112. 'button.btn-prev',
  113. 'ul.el-pager',
  114. 'button.btn-next',
  115. '.el-pagination__jump',
  116. ],
  117. true
  118. )
  119. })
  120. test('test layout with slot', () => {
  121. const wrapper = mount(() => (
  122. <Pagination layout="slot, prev, pager, next" pageSize={25} total={100}>
  123. <span class="slot-test">slot test</span>
  124. </Pagination>
  125. ))
  126. expect(wrapper.find('.slot-test').exists()).toBe(true)
  127. })
  128. test('test small layout', () => {
  129. const wrapper = mount(() => (
  130. <Pagination total={100} small={true}></Pagination>
  131. ))
  132. expect(wrapper.vm.$el.classList.contains('el-pagination--small')).toBe(
  133. true
  134. )
  135. })
  136. test('test with background', async () => {
  137. const withBackground = ref(true)
  138. const wrapper = mount(() => (
  139. <Pagination total={100} background={withBackground.value}></Pagination>
  140. ))
  141. expect(wrapper.find('.is-background').exists()).toBe(true)
  142. withBackground.value = false
  143. await nextTick()
  144. expect(wrapper.find('.is-background').exists()).toBe(false)
  145. })
  146. test('test hide-on-single-page prop', async () => {
  147. const hideOnSinglePage = ref(false)
  148. const wrapper = mount(() => (
  149. <Pagination
  150. total={10} // deivded by default page-size(10), there will be only one page
  151. hideOnSinglePage={hideOnSinglePage.value}
  152. />
  153. ))
  154. expect(wrapper.find('.el-pagination').exists()).toBe(true)
  155. hideOnSinglePage.value = true
  156. await nextTick()
  157. expect(wrapper.find('.el-pagination').exists()).toBe(false)
  158. })
  159. test('test custom icon', async () => {
  160. const wrapper = mount(() => (
  161. <Pagination
  162. layout="prev, pager, next"
  163. total={1000}
  164. prev-icon={CaretLeft}
  165. next-icon={CaretRight}
  166. />
  167. ))
  168. const btnPrev = wrapper.findComponent(CaretLeft).element
  169. const caretLeftIcon = mount(CaretLeft).find('svg').element
  170. expect(btnPrev.innerHTML).toBe(caretLeftIcon.innerHTML)
  171. const nextPrev = wrapper.findComponent(CaretRight).element
  172. const caretRightIcon = mount(CaretRight).find('svg').element
  173. expect(nextPrev.innerHTML).toBe(caretRightIcon.innerHTML)
  174. })
  175. })
  176. describe('test pageSize & currentPage reactive change', () => {
  177. test(`test pageSize change`, async () => {
  178. const pageSize = ref(10)
  179. const wrapper = mount(() => (
  180. <Pagination layout="pager" total={100} pageSize={pageSize.value} />
  181. ))
  182. // total pages = Math.ceil(total / pageSize)
  183. assertPages(wrapper, 10)
  184. pageSize.value = 20
  185. await nextTick()
  186. assertPages(wrapper, 5)
  187. pageSize.value = 55
  188. await nextTick()
  189. assertPages(wrapper, 2)
  190. })
  191. test('test currentPage change', async () => {
  192. const pageSize = ref(10)
  193. const defaultCurrentPage = ref(2)
  194. const wrapper = mount(() => (
  195. <Pagination
  196. layout="prev, pager, next"
  197. total={100}
  198. pageSize={pageSize.value}
  199. defaultCurrentPage={defaultCurrentPage.value}
  200. />
  201. ))
  202. assertCurrent(wrapper, 2)
  203. defaultCurrentPage.value = 1
  204. assertCurrent(wrapper, 2) // still 2
  205. await wrapper.find('.el-pager li:last-child').trigger('click')
  206. assertCurrent(wrapper, 10)
  207. await wrapper.find('button.btn-prev').trigger('click')
  208. assertCurrent(wrapper, 9)
  209. await wrapper.find('button.btn-next').trigger('click')
  210. assertCurrent(wrapper, 10)
  211. pageSize.value = 50
  212. await nextTick()
  213. assertCurrent(wrapper, 2)
  214. })
  215. test('test pageCount change and side effect', async () => {
  216. const pageCount = ref(10)
  217. const wrapper = mount(() => (
  218. <Pagination layout="prev, pager, next" pageCount={pageCount.value} />
  219. ))
  220. assertPages(wrapper, 10)
  221. pageCount.value = 20
  222. await nextTick()
  223. assertPages(wrapper, 20)
  224. await wrapper.find('.el-pager li:last-child').trigger('click')
  225. assertCurrent(wrapper, 20)
  226. pageCount.value = 5
  227. await nextTick()
  228. // side effect, if currentPage is greater than pageCount
  229. // currentPage should change accordingly
  230. assertPages(wrapper, 5)
  231. assertCurrent(wrapper, 5)
  232. })
  233. test('test listener work', async () => {
  234. const pageSizeWatcher = vi.fn()
  235. const currentPageWatcher = vi.fn()
  236. const wrapper = mount(() => (
  237. <Pagination
  238. total={100}
  239. layout="prev, pager, next, sizes"
  240. onUpdate:current-page={currentPageWatcher}
  241. onUpdate:page-size={pageSizeWatcher}
  242. />
  243. ))
  244. await wrapper.find('.el-pager li:last-child').trigger('click')
  245. assertCurrent(wrapper, 10 /* Math.ceil(100/10) */)
  246. expect(currentPageWatcher).toHaveBeenCalled()
  247. await wrapper.find('.el-select').trigger('click')
  248. await wrapper
  249. .getComponent(selectDropdownVue)
  250. .find('li:nth-child(2)')
  251. .trigger('click')
  252. expect(pageSizeWatcher).toHaveBeenCalled()
  253. assertCurrent(wrapper, 5 /* Math.ceil(100/20) */)
  254. })
  255. })
  256. describe('test a11y supports', () => {
  257. test('test a11y attributes', async () => {
  258. const wrapper = mount(() => <Pagination total={100} />)
  259. expect(wrapper.find('.el-pagination').attributes('aria-label')).toBe(
  260. 'pagination'
  261. )
  262. expect(wrapper.find('.el-pagination').attributes('role')).toBe(
  263. 'pagination'
  264. )
  265. expect(
  266. wrapper.find('.el-pagination .btn-prev').attributes('aria-disabled')
  267. ).toBe('true')
  268. expect(
  269. wrapper.find('.el-pagination .btn-next').attributes('aria-disabled')
  270. ).toBe('false')
  271. expect(
  272. wrapper.find('.el-pager li:first-child').attributes('aria-current')
  273. ).toBe('true')
  274. expect(
  275. wrapper.find('.el-pager li:last-child').attributes('aria-current')
  276. ).toBe('false')
  277. await wrapper.find('.el-pager li:last-child').trigger('click')
  278. expect(
  279. wrapper.find('.el-pagination .btn-prev').attributes('aria-disabled')
  280. ).toBe('false')
  281. expect(
  282. wrapper.find('.el-pagination .btn-next').attributes('aria-disabled')
  283. ).toBe('true')
  284. expect(
  285. wrapper.find('.el-pager li:first-child').attributes('aria-current')
  286. ).toBe('false')
  287. expect(
  288. wrapper.find('.el-pager li:last-child').attributes('aria-current')
  289. ).toBe('true')
  290. })
  291. test('test tabindex interactive', async () => {
  292. const wrapper = mount(() => <Pagination total={100} />)
  293. await wrapper.find('.el-pager li:nth-child(2)').trigger('click')
  294. assertCurrent(wrapper, 2)
  295. await wrapper.find('.el-pager li:nth-child(3)').trigger('click', {
  296. key: 'Enter',
  297. })
  298. assertCurrent(wrapper, 3)
  299. // TODO getComputedStyle is not implemented in jsdom, so I duno how to assert style of psuedo-class
  300. /*
  301. * await wrapper.find('.el-pager li:nth-child(3)').trigger('keyup', {
  302. * key: 'Tab',
  303. * })
  304. * const style = window.getComputedStyle(wrapper.find('.el-pager li:nth-child(4)').element, ':focus-visible')
  305. * expect(style.outline).toBeTruthy()
  306. */
  307. })
  308. test('test tabindex disabled', async () => {
  309. const disabled = ref(true)
  310. const wrapper = mount(() => (
  311. <Pagination total={100} disabled={disabled.value}></Pagination>
  312. ))
  313. expect(
  314. wrapper.find('.el-pager li:first-child').attributes('tabindex')
  315. ).toBe('-1')
  316. disabled.value = false
  317. await nextTick()
  318. expect(
  319. wrapper.find('.el-pager li:first-child').attributes('tabindex')
  320. ).toBe('0')
  321. })
  322. })
  323. })