personal-setting-notifications.spec.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { visitView } from '#tests/support/components/visitView.ts'
  3. import { mockPermissions } from '#tests/support/mock-permissions.ts'
  4. import { mockUserCurrent } from '#tests/support/mock-userCurrent.ts'
  5. import { waitForNextTick } from '#tests/support/utils.ts'
  6. import { mockFormUpdaterQuery } from '#shared/components/Form/graphql/queries/formUpdater.mocks.ts'
  7. import { mockCurrentUserQuery } from '#shared/graphql/queries/currentUser.mocks.ts'
  8. import {
  9. EnumFormUpdaterId,
  10. EnumNotificationSoundFile,
  11. } from '#shared/graphql/types.ts'
  12. import { convertToGraphQLId } from '#shared/graphql/utils.ts'
  13. import { waitForUserCurrentNotificationPreferencesResetMutationCalls } from '#desktop/pages/personal-setting/graphql/mutations/userCurrentNotificationPreferencesReset.mocks.ts'
  14. import {
  15. mockUserCurrentNotificationPreferencesUpdateMutation,
  16. waitForUserCurrentNotificationPreferencesUpdateMutationCalls,
  17. } from '#desktop/pages/personal-setting/graphql/mutations/userCurrentNotificationPreferencesUpdate.mocks.ts'
  18. const mockPersonalSettings = (withGroups = true) => {
  19. return {
  20. personalSettings: {
  21. notificationConfig: {
  22. groupIds: withGroups ? [1, 2] : undefined,
  23. matrix: {
  24. create: {
  25. criteria: {
  26. ownedByMe: true,
  27. ownedByNobody: true,
  28. subscribed: true,
  29. no: false,
  30. },
  31. channel: { email: true, online: true },
  32. },
  33. update: {
  34. criteria: {
  35. ownedByMe: true,
  36. ownedByNobody: true,
  37. subscribed: true,
  38. no: false,
  39. },
  40. channel: { email: true, online: true },
  41. },
  42. reminderReached: {
  43. criteria: {
  44. ownedByMe: true,
  45. ownedByNobody: false,
  46. subscribed: false,
  47. no: false,
  48. },
  49. channel: { email: true, online: true },
  50. },
  51. escalation: {
  52. criteria: {
  53. ownedByMe: true,
  54. ownedByNobody: false,
  55. subscribed: false,
  56. no: false,
  57. },
  58. channel: { email: true, online: true },
  59. },
  60. },
  61. },
  62. notificationSound: {
  63. enabled: true,
  64. file: EnumNotificationSoundFile.Xylo,
  65. },
  66. },
  67. }
  68. }
  69. const mockUser = () => ({
  70. firstname: 'John',
  71. lastname: 'Doe',
  72. })
  73. describe('personal notifications settings', () => {
  74. beforeEach(() => {
  75. mockPermissions(['user_preferences.notifications'])
  76. mockFormUpdaterQuery({
  77. formUpdater: {
  78. fields: {
  79. EnumFormUpdaterId:
  80. EnumFormUpdaterId.FormUpdaterUpdaterUserNotifications,
  81. group_ids: {
  82. options: [
  83. {
  84. label: 'Testers Group',
  85. value: 1,
  86. },
  87. {
  88. label: 'Developers Group',
  89. value: 2,
  90. },
  91. ],
  92. },
  93. },
  94. },
  95. })
  96. })
  97. it('renders view correctly', async () => {
  98. mockUserCurrent({
  99. ...mockUser(),
  100. ...mockPersonalSettings(),
  101. })
  102. await waitForNextTick()
  103. const view = await visitView('personal-setting/notifications')
  104. // TABLE
  105. expect(view.getByText('New ticket')).toBeInTheDocument()
  106. expect(view.getByText('Ticket update')).toBeInTheDocument()
  107. expect(view.getByText('Ticket reminder reached')).toBeInTheDocument()
  108. expect(view.getByText('Ticket escalation')).toBeInTheDocument()
  109. expect(view.getByText('Name')).toBeInTheDocument()
  110. expect(view.getByText('My tickets')).toBeInTheDocument()
  111. expect(view.getByText('Not assigned')).toBeInTheDocument()
  112. expect(view.getByText('All tickets')).toBeInTheDocument()
  113. expect(view.getByText('Also notify via email')).toBeInTheDocument()
  114. expect(view.getByText('Developers Group')).toBeInTheDocument()
  115. expect(view.getByText('Testers Group')).toBeInTheDocument()
  116. const checkboxes = view.getAllByRole('checkbox')
  117. expect(checkboxes).toHaveLength(20)
  118. // Fields
  119. expect(view.getByText('Notification sound')).toBeInTheDocument()
  120. expect(
  121. view.getByText('Limit notifications to specific groups'),
  122. ).toBeInTheDocument()
  123. expect(
  124. view.getByText('Play user interface sound effects'),
  125. ).toBeInTheDocument()
  126. })
  127. it("doesn't render user groups if groups are not provided", async () => {
  128. mockUserCurrent({
  129. ...mockUser(),
  130. ...mockPersonalSettings(false),
  131. })
  132. const view = await visitView('personal-setting/notifications')
  133. expect(view.queryByText('User groups')).not.toBeInTheDocument()
  134. })
  135. it('resets values to default', async () => {
  136. mockUserCurrent({
  137. ...mockUser(),
  138. ...mockPersonalSettings(),
  139. })
  140. const playSound = vi.fn()
  141. // Set up a mock for playing sound effects in a test environment
  142. // This is necessary because the audio API is not available in the test environment
  143. window.HTMLMediaElement.prototype.play = () => playSound()
  144. const view = await visitView('personal-setting/notifications')
  145. await view.events.click(view.getByLabelText('Notification sound'))
  146. await view.events.click(view.getByText('Plop'))
  147. const checkboxes = view.getAllByTestId('checkbox-label')
  148. await view.events.click(checkboxes.at(-1)!)
  149. expect(
  150. (view.getByLabelText('Notification sound') as HTMLInputElement).value,
  151. ).toEqual('Plop')
  152. await view.events.click(
  153. view.getByRole('button', { name: 'Reset to Default Settings' }),
  154. )
  155. expect(
  156. await view.findByRole('dialog', { name: 'Confirmation' }),
  157. ).toBeInTheDocument()
  158. expect(
  159. view.getByText(
  160. 'Are you sure? Your notifications settings will be reset to default.',
  161. ),
  162. ).toBeInTheDocument()
  163. await view.events.click(view.getByRole('button', { name: 'Yes' }))
  164. const mocks =
  165. await waitForUserCurrentNotificationPreferencesResetMutationCalls()
  166. expect(mocks.at(-1)?.variables).toEqual({})
  167. mockCurrentUserQuery({
  168. currentUser: {
  169. ...mockUser(),
  170. ...mockPersonalSettings(),
  171. },
  172. })
  173. await waitForNextTick()
  174. expect(playSound).toHaveBeenCalled()
  175. expect(checkboxes.at(-1)).toBeEnabled()
  176. })
  177. it('submits notification form successfully', async () => {
  178. mockUserCurrent({
  179. ...mockUser(),
  180. ...mockPersonalSettings(),
  181. })
  182. const view = await visitView('personal-setting/notifications')
  183. const checkboxes = view.getAllByTestId('checkbox-label')
  184. await view.events.click(checkboxes.at(-1)!)
  185. mockUserCurrentNotificationPreferencesUpdateMutation({
  186. userCurrentNotificationPreferencesUpdate: {
  187. user: {
  188. ...mockUser(),
  189. ...mockPersonalSettings(),
  190. },
  191. errors: null,
  192. },
  193. })
  194. await view.events.click(
  195. view.getByRole('button', { name: 'Save Notifications' }),
  196. )
  197. const previousMockedData = mockPersonalSettings()
  198. // Convert group ids to GraphQL ids to match payload format
  199. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  200. // @ts-expect-error
  201. previousMockedData.personalSettings.notificationConfig.groupIds =
  202. previousMockedData.personalSettings.notificationConfig?.groupIds?.map(
  203. (id) => convertToGraphQLId('Group', id),
  204. )
  205. const mocks =
  206. await waitForUserCurrentNotificationPreferencesUpdateMutationCalls()
  207. // Last checkbox in table got updated
  208. expect(mocks.at(-1)?.variables).not.toEqual(previousMockedData)
  209. })
  210. })