tickets-view.spec.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { waitFor } from '@testing-library/vue'
  3. import { stringifyQuery } from 'vue-router'
  4. import { visitView } from '#tests/support/components/visitView.ts'
  5. import { mockApplicationConfig } from '#tests/support/mock-applicationConfig.ts'
  6. import { mockPermissions } from '#tests/support/mock-permissions.ts'
  7. import { mockTicketOverviews } from '#tests/support/mocks/ticket-overviews.ts'
  8. import { waitForNextTick } from '#tests/support/utils.ts'
  9. import { EnumOrderDirection } from '#shared/graphql/types.ts'
  10. import { mockTicketsByOverview, ticketDefault } from './mocks/overview.ts'
  11. beforeEach(() => {
  12. mockTicketOverviews()
  13. })
  14. it('see default list when opening page', async () => {
  15. mockPermissions(['ticket.agent'])
  16. mockTicketsByOverview([])
  17. const view = await visitView('/tickets/view')
  18. const plusSign = view.getByIconName('add')
  19. expect(plusSign, 'can create a new ticket from here').toBeInTheDocument()
  20. expect(view.getLinkFromElement(plusSign)).toHaveAttribute(
  21. 'href',
  22. '/mobile/tickets/create',
  23. )
  24. await waitForNextTick(true)
  25. expect(
  26. await view.findByTestId('overview'),
  27. 'has default overview',
  28. ).toHaveTextContent('Overview 1')
  29. expect(
  30. await view.findByTestId('column'),
  31. 'has default column',
  32. ).toHaveTextContent('Created at')
  33. expect(
  34. view.getByIconName('arrow-down'),
  35. 'descending by default',
  36. ).not.toHaveClass('rotate-180')
  37. expect(
  38. await view.findByText('No entries'),
  39. 'see a message when nothing is found',
  40. ).toBeInTheDocument()
  41. expect(window.location.pathname).toBe('/mobile/tickets/view/overview_1')
  42. })
  43. it('can filter by overview type', async () => {
  44. const ticketsMock = mockTicketsByOverview()
  45. const view = await visitView('/tickets/view')
  46. const overview = await view.findByTestId('overview')
  47. await view.events.click(overview)
  48. await view.events.click(view.getByText('Overview 1'))
  49. expect(ticketsMock.spies.resolve).toHaveBeenCalledWith(
  50. expect.objectContaining({
  51. orderBy: 'created_at',
  52. orderDirection: EnumOrderDirection.Descending,
  53. overviewId: '1',
  54. }),
  55. )
  56. const ticketItem = view.getByText('Ticket 1')
  57. expect(ticketItem).toBeInTheDocument()
  58. expect(view.getLinkFromElement(ticketItem)).toHaveAttribute(
  59. 'href',
  60. '/mobile/tickets/1',
  61. )
  62. })
  63. it('can filter by columns and direction', async () => {
  64. const ticketsMock = mockTicketsByOverview()
  65. const view = await visitView('/tickets/view')
  66. const columnSelector = await view.findByTestId('column')
  67. await view.events.click(columnSelector)
  68. await view.events.click(view.getByText('Updated at'))
  69. await view.events.click(view.getByText('ascending'))
  70. expect(ticketsMock.spies.resolve).toHaveBeenCalledWith(
  71. expect.objectContaining({
  72. orderBy: 'updated_at',
  73. orderDirection: EnumOrderDirection.Ascending,
  74. overviewId: '1',
  75. }),
  76. )
  77. expect(view.getByText('Ticket 1')).toBeInTheDocument()
  78. })
  79. it('can filter by type and columns and direction', async () => {
  80. const ticketsMock = mockTicketsByOverview()
  81. const view = await visitView('/tickets/view')
  82. const overview = await view.findByTestId('overview')
  83. await view.events.click(overview)
  84. await view.events.click(view.getByText('Overview 1'))
  85. const columnSelector = view.getByTestId('column')
  86. await view.events.click(columnSelector)
  87. await view.events.click(view.getByText('Updated at'))
  88. await view.events.click(view.getByText('ascending'))
  89. expect(ticketsMock.spies.resolve).toHaveBeenCalledWith(
  90. expect.objectContaining({
  91. overviewId: '1',
  92. orderBy: 'updated_at',
  93. orderDirection: EnumOrderDirection.Ascending,
  94. }),
  95. )
  96. expect(view.getByText('Ticket 1')).toBeInTheDocument()
  97. })
  98. it('takes filter from query', async () => {
  99. const ticketsMock = mockTicketsByOverview()
  100. const query = stringifyQuery({
  101. column: 'number',
  102. direction: EnumOrderDirection.Ascending,
  103. })
  104. const view = await visitView(`/tickets/view?${query}`)
  105. await view.findByTestId('overview')
  106. await waitFor(() => {
  107. expect(ticketsMock.spies.resolve).toHaveBeenCalledWith(
  108. expect.objectContaining({
  109. overviewId: '1',
  110. orderBy: 'number',
  111. orderDirection: EnumOrderDirection.Ascending,
  112. }),
  113. )
  114. })
  115. })
  116. // TODO 2023-05-08 Sheremet V.A. rewrite test to run in Vitest browser mode
  117. describe.skip('paginating ticket list', () => {
  118. const emulateScroll = async (scroll: number) => {
  119. document.documentElement.scrollTop = scroll
  120. document.dispatchEvent(
  121. new Event('scroll', { bubbles: true, cancelable: true }),
  122. )
  123. await waitForNextTick()
  124. }
  125. it("doesn't load more, when there is nothing to load", async () => {
  126. const ticketOverviewsApi = mockTicketsByOverview([ticketDefault()], {
  127. hasNextPage: false,
  128. endCursor: 'cursor',
  129. })
  130. mockApplicationConfig({
  131. ui_ticket_overview_ticket_limit: 2000,
  132. })
  133. const view = await visitView(`/tickets/view`)
  134. await waitFor(() => view.getByText('Ticket 1'))
  135. expect(
  136. view.queryByRole('button', { name: 'load 10 more' }),
  137. ).not.toBeInTheDocument()
  138. await emulateScroll(1000)
  139. expect(ticketOverviewsApi.spies.resolve).not.toHaveBeenCalledWith(
  140. expect.objectContaining({
  141. cursor: 'cursor',
  142. }),
  143. )
  144. })
  145. it('load more button loads more tickets', async () => {
  146. const ticketOverviewsApi = mockTicketsByOverview([ticketDefault()], {
  147. hasNextPage: true,
  148. endCursor: 'cursor',
  149. })
  150. mockApplicationConfig({
  151. ui_ticket_overview_ticket_limit: 2000,
  152. })
  153. const view = await visitView(`/tickets/view`)
  154. await waitFor(() => view.getByText('Ticket 1'))
  155. const loadMoreButton = view.getByRole('button', { name: 'load 10 more' })
  156. expect(loadMoreButton).toBeInTheDocument()
  157. await view.events.click(loadMoreButton)
  158. expect(ticketOverviewsApi.spies.resolve).toHaveBeenCalledWith(
  159. expect.objectContaining({
  160. cursor: 'cursor',
  161. }),
  162. )
  163. // page now has 2 links to tickets
  164. // the last link before pressing "load more" has the focus
  165. expect(view.getAllByRole('link', { name: /Ticket 1/ })[0]).toHaveFocus()
  166. })
  167. it('pagination loads additional list', async () => {
  168. const ticketOverviewsApi = mockTicketsByOverview([ticketDefault()], {
  169. hasNextPage: true,
  170. endCursor: 'cursor',
  171. })
  172. mockApplicationConfig({
  173. ui_ticket_overview_ticket_limit: 2000,
  174. })
  175. const view = await visitView(`/tickets/view`)
  176. await waitFor(() => view.getByText('Ticket 1'))
  177. await emulateScroll(1000)
  178. expect(ticketOverviewsApi.spies.resolve).toHaveBeenCalledWith(
  179. expect.objectContaining({
  180. cursor: 'cursor',
  181. }),
  182. )
  183. })
  184. it("pagination doesn't load if it is already loading more", async () => {
  185. const ticketOverviewsApi = mockTicketsByOverview([ticketDefault()], {
  186. hasNextPage: true,
  187. endCursor: 'cursor',
  188. })
  189. const view = await visitView(`/tickets/view`)
  190. await waitFor(() => view.getByTestId('overview'))
  191. await emulateScroll(1000)
  192. expect(ticketOverviewsApi.spies.resolve).not.toHaveBeenCalledWith(
  193. expect.objectContaining({
  194. cursor: 'cursor',
  195. }),
  196. )
  197. })
  198. })