searching.spec.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. import {
  3. getGraphQLMockCalls,
  4. mockGraphQLResult,
  5. } from '#tests/graphql/builders/mocks.ts'
  6. import { getTestRouter } from '#tests/support/components/renderComponent.ts'
  7. import { visitView } from '#tests/support/components/visitView.ts'
  8. import { setupView } from '#tests/support/mock-user.ts'
  9. import { type SearchQuery } from '#shared/graphql/types.ts'
  10. import { convertToGraphQLId } from '#shared/graphql/utils.ts'
  11. import { SearchDocument } from '../graphql/queries/searchOverview.api.ts'
  12. describe('visiting search page', () => {
  13. beforeEach(() => {
  14. setupView('agent')
  15. })
  16. it('doesnt search if no type is selected', async () => {
  17. const view = await visitView('/search', { mockApollo: false })
  18. const searchInput = view.getByPlaceholderText('Search…')
  19. await view.events.type(searchInput, 'search')
  20. expect(getGraphQLMockCalls(SearchDocument)).toHaveLength(0)
  21. })
  22. it('allows searching', async () => {
  23. const view = await visitView('/search', { mockApollo: false })
  24. const mocker = mockGraphQLResult<SearchQuery>(SearchDocument, {
  25. search: {
  26. items: [],
  27. totalCount: 0,
  28. },
  29. })
  30. const searchInput = view.getByPlaceholderText('Search…')
  31. expect(searchInput).toHaveFocus()
  32. // don't show until query is entered
  33. expect(view.queryByTestId('selectTypesSection')).not.toBeInTheDocument()
  34. await view.events.type(searchInput, 'search')
  35. // show types when query is entered
  36. expect(view.getByTestId('selectTypesSection')).toBeInTheDocument()
  37. await view.events.click(view.getByText('Users with "search"'))
  38. // focus shifted to tab with the same type
  39. expect(view.getByRole('tab', { name: 'Users' })).toHaveFocus()
  40. const mockCalls = await mocker.waitForCalls()
  41. expect(mockCalls).toHaveLength(1)
  42. expect(mockCalls[0].variables).toEqual({
  43. onlyIn: 'User',
  44. search: 'search',
  45. limit: 30,
  46. })
  47. expect(view.container).toHaveTextContent('No entries')
  48. await view.events.click(view.getByText('Organizations'))
  49. expect(mockCalls).toHaveLength(2)
  50. expect(mockCalls[1].variables).toEqual({
  51. onlyIn: 'Organization',
  52. search: 'search',
  53. limit: 30,
  54. })
  55. expect(view.getByRole('tab', { name: 'Organizations' })).toHaveFocus()
  56. })
  57. it('renders correctly if queries are passed down', async () => {
  58. const view = await visitView('/search/invalid?search=search', {
  59. mockApollo: false,
  60. })
  61. expect(view.getByPlaceholderText('Search…')).toHaveDisplayValue('search')
  62. expect(view.getByTestId('selectTypesSection')).toBeInTheDocument()
  63. })
  64. it('opens with type, if there is only single type', async () => {
  65. // customer can only search for tickets
  66. setupView('customer')
  67. await visitView('/search', { mockApollo: false })
  68. expect(getTestRouter().currentRoute.value.fullPath).toBe('/search/ticket')
  69. })
  70. })
  71. describe('avatars', () => {
  72. it('renders user as inactive', async () => {
  73. setupView('agent')
  74. mockGraphQLResult<SearchQuery>(SearchDocument, {
  75. search: {
  76. __typename: 'SearchResult',
  77. totalCount: 2,
  78. items: [
  79. {
  80. __typename: 'User',
  81. id: convertToGraphQLId('User', 100),
  82. internalId: 100,
  83. updatedAt: new Date().toISOString(),
  84. active: false,
  85. vip: true,
  86. firstname: 'Max',
  87. lastname: 'Mustermann',
  88. },
  89. {
  90. __typename: 'User',
  91. id: convertToGraphQLId('User', 200),
  92. internalId: 200,
  93. updatedAt: new Date().toISOString(),
  94. outOfOffice: true,
  95. active: true,
  96. vip: false,
  97. image: 'jon.png',
  98. firstname: 'Jon',
  99. lastname: 'Doe',
  100. },
  101. ],
  102. },
  103. })
  104. const view = await visitView('/search/user?search=max', {
  105. mockApollo: false,
  106. })
  107. expect(
  108. await view.findByLabelText('Avatar (Max Mustermann) (VIP)'),
  109. ).toBeAvatarElement({
  110. active: false,
  111. vip: true,
  112. type: 'user',
  113. })
  114. expect(view.getByLabelText('Avatar (Jon Doe)')).toBeAvatarElement({
  115. active: true,
  116. vip: false,
  117. outOfOffice: true,
  118. image: 'jon.png',
  119. type: 'user',
  120. })
  121. })
  122. })
  123. test('correctly redirects from hash-based routes', async () => {
  124. setupView('agent')
  125. mockGraphQLResult<SearchQuery>(SearchDocument, {
  126. search: {
  127. items: [],
  128. totalCount: 0,
  129. },
  130. })
  131. await visitView('/#search/string', { mockApollo: false })
  132. const router = getTestRouter()
  133. const route = router.currentRoute.value
  134. expect(route.name).toBe('SearchOverview')
  135. expect(route.params).toEqual({ type: 'ticket' })
  136. expect(route.query).toEqual({ search: 'string' })
  137. })