QuickSearch.spec.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. import '#tests/graphql/builders/mocks.ts'
  3. import renderComponent from '#tests/support/components/renderComponent.ts'
  4. import { mockUserCurrent } from '#tests/support/mock-userCurrent.ts'
  5. import { waitForNextTick } from '#tests/support/utils.ts'
  6. import {
  7. EnumTicketStateColorCode,
  8. type Organization,
  9. } from '#shared/graphql/types.ts'
  10. import { convertToGraphQLId } from '#shared/graphql/utils.ts'
  11. import QuickSearch from '#desktop/components/QuickSearch/QuickSearch.vue'
  12. import { useRecentSearches } from '#desktop/composables/useRecentSearches.ts'
  13. import { mockUserCurrentRecentViewResetMutation } from '#desktop/entities/user/current/graphql/mutations/userCurrentRecentViewReset.mocks.ts'
  14. import { mockUserCurrentRecentViewListQuery } from '#desktop/entities/user/current/graphql/queries/userCurrentRecentViewList.mocks.ts'
  15. import { getUserCurrentRecentViewUpdatesSubscriptionHandler } from '#desktop/entities/user/current/graphql/subscriptions/userCurrentRecentViewUpdates.mocks.ts'
  16. import { mockQuickSearchQuery } from '../graphql/queries/quickSearch.mocks.ts'
  17. const renderQuickSearch = async (search: string = '') => {
  18. const wrapper = renderComponent(QuickSearch, {
  19. props: {
  20. collapsed: false,
  21. search,
  22. },
  23. router: true,
  24. store: true,
  25. dialog: true,
  26. })
  27. await waitForNextTick()
  28. return wrapper
  29. }
  30. mockUserCurrent()
  31. describe('QuickSearch', () => {
  32. const { addSearch, removeSearch, clearSearches } = useRecentSearches()
  33. beforeEach(() => {
  34. clearSearches()
  35. })
  36. describe('default state', () => {
  37. it('shows empty state message when no searches or recently viewed items exist', async () => {
  38. mockUserCurrentRecentViewListQuery({ userCurrentRecentViewList: [] })
  39. const wrapper = await renderQuickSearch()
  40. expect(
  41. wrapper.getByText(
  42. 'Start typing i.e. the name of a ticket, an organization or a user.',
  43. ),
  44. ).toBeInTheDocument()
  45. expect(
  46. wrapper.queryByRole('button', { name: 'Clear All' }),
  47. ).not.toBeInTheDocument()
  48. })
  49. })
  50. describe('recent searches', () => {
  51. beforeEach(() => {
  52. mockUserCurrentRecentViewListQuery({ userCurrentRecentViewList: [] })
  53. })
  54. it('displays recent searches when added', async () => {
  55. const wrapper = await renderQuickSearch()
  56. addSearch('Foobar')
  57. addSearch('Dummy')
  58. await waitForNextTick()
  59. expect(
  60. wrapper.getByRole('heading', { level: 3, name: 'Recent searches' }),
  61. ).toBeInTheDocument()
  62. expect(wrapper.getByText('Foobar')).toBeInTheDocument()
  63. expect(wrapper.getByText('Dummy')).toBeInTheDocument()
  64. expect(
  65. wrapper.getByRole('link', { name: 'Clear recent searches' }),
  66. ).toBeInTheDocument()
  67. })
  68. it('allows clearing all recent searches', async () => {
  69. const wrapper = await renderQuickSearch()
  70. addSearch('Foobar')
  71. addSearch('Dummy')
  72. await waitForNextTick()
  73. expect(
  74. wrapper.getByRole('link', { name: 'Clear recent searches' }),
  75. ).toBeInTheDocument()
  76. clearSearches()
  77. await waitForNextTick()
  78. expect(
  79. wrapper.getByText(
  80. 'Start typing i.e. the name of a ticket, an organization or a user.',
  81. ),
  82. ).toBeInTheDocument()
  83. expect(
  84. wrapper.queryByRole('link', { name: 'Clear recent searches' }),
  85. ).not.toBeInTheDocument()
  86. })
  87. it('allows removing individual recent searches', async () => {
  88. const wrapper = await renderQuickSearch()
  89. addSearch('Foobar')
  90. addSearch('Dummy')
  91. await waitForNextTick()
  92. let removeIcons = wrapper.getAllByIconName('x-lg')
  93. expect(removeIcons.length).toBe(2)
  94. removeSearch('Foobar')
  95. await waitForNextTick()
  96. removeIcons = wrapper.getAllByIconName('x-lg')
  97. expect(removeIcons.length).toBe(1)
  98. })
  99. })
  100. describe('recently viewed items', () => {
  101. const recentlyViewedItems = [
  102. {
  103. __typename: 'Ticket',
  104. id: convertToGraphQLId('Ticket', 1),
  105. title: 'Ticket 1',
  106. number: '1',
  107. stateColorCode: EnumTicketStateColorCode.Open,
  108. },
  109. {
  110. __typename: 'User',
  111. id: convertToGraphQLId('User', 1),
  112. internalId: 1,
  113. fullname: 'User 1',
  114. },
  115. {
  116. __typename: 'Organization',
  117. id: convertToGraphQLId('Organization', 1),
  118. internalId: 1,
  119. name: 'Organization 1',
  120. },
  121. ]
  122. it('displays recently viewed items', async () => {
  123. mockUserCurrentRecentViewListQuery({
  124. userCurrentRecentViewList: recentlyViewedItems as Organization[],
  125. })
  126. const wrapper = await renderQuickSearch()
  127. expect(
  128. wrapper.getByRole('heading', { level: 3, name: 'Recently viewed' }),
  129. ).toBeInTheDocument()
  130. expect(
  131. wrapper.getByRole('link', { name: 'check-circle-no Ticket 1' }),
  132. ).toBeInTheDocument()
  133. expect(wrapper.getByRole('link', { name: 'User 1' })).toBeInTheDocument()
  134. expect(
  135. wrapper.getByRole('link', { name: 'Organization 1' }),
  136. ).toBeInTheDocument()
  137. })
  138. it('allows clearing all recently viewed items', async () => {
  139. mockUserCurrentRecentViewListQuery({
  140. userCurrentRecentViewList: recentlyViewedItems as Organization[],
  141. })
  142. mockUserCurrentRecentViewResetMutation({
  143. userCurrentRecentViewReset: { success: true },
  144. })
  145. const wrapper = await renderQuickSearch()
  146. expect(
  147. wrapper.getByRole('link', { name: 'Clear recently viewed' }),
  148. ).toBeInTheDocument()
  149. mockUserCurrentRecentViewListQuery({ userCurrentRecentViewList: [] })
  150. await getUserCurrentRecentViewUpdatesSubscriptionHandler().trigger({
  151. userCurrentRecentViewUpdates: {
  152. recentViewsUpdated: true,
  153. },
  154. })
  155. await waitForNextTick()
  156. expect(
  157. wrapper.getByText(
  158. 'Start typing i.e. the name of a ticket, an organization or a user.',
  159. ),
  160. ).toBeInTheDocument()
  161. expect(
  162. wrapper.queryByRole('link', { name: 'Clear recently viewed' }),
  163. ).not.toBeInTheDocument()
  164. })
  165. it('display search results when typing', async () => {
  166. mockQuickSearchQuery({
  167. quickSearchUsers: {
  168. totalCount: 0,
  169. items: [],
  170. },
  171. quickSearchOrganizations: {
  172. totalCount: 0,
  173. items: [],
  174. },
  175. quickSearchTickets: {
  176. totalCount: 0,
  177. items: [],
  178. },
  179. })
  180. const wrapper = await renderQuickSearch('test')
  181. expect(
  182. await wrapper.findByText('No results for this query.'),
  183. ).toBeInTheDocument()
  184. })
  185. })
  186. })