personal-setting-avatar.spec.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { defineComponent } from 'vue'
  3. import type { ExtendedRenderResult } from '#tests/support/components/index.ts'
  4. import { visitView } from '#tests/support/components/visitView.ts'
  5. import { mockGraphQLApi } from '#tests/support/mock-graphql-api.ts'
  6. import { mockUserCurrent } from '#tests/support/mock-userCurrent.ts'
  7. import { UserCurrentAvatarAddDocument } from '#shared/entities/user/current/graphql/mutations/userCurrentAvatarAdd.api.ts'
  8. import { UserCurrentAvatarDeleteDocument } from '#shared/entities/user/current/graphql/mutations/userCurrentAvatarDelete.api.ts'
  9. import { UserCurrentAvatarActiveDocument } from '../graphql/queries/userCurrentAvatarActive.api.ts'
  10. vi.mock('vue-advanced-cropper', () => {
  11. const Cropper = defineComponent({
  12. emits: ['change'],
  13. mounted() {
  14. this.$emit('change', {
  15. canvas: {
  16. toDataURL() {
  17. return 'cropped image url'
  18. },
  19. },
  20. })
  21. },
  22. template: '<div></div>',
  23. })
  24. return {
  25. Cropper,
  26. }
  27. })
  28. const mockAvatarImage =
  29. 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
  30. const getAvatarObject = (deletable: boolean) => {
  31. return {
  32. id: 'Z2lkOi8vemFtbWFkL0F2YXRhci8yNA',
  33. default: true,
  34. deletable,
  35. initial: false,
  36. imageFull: mockAvatarImage,
  37. imageResize: mockAvatarImage,
  38. imageHash: '123456',
  39. createdAt: '2022-07-12T06:54:45Z',
  40. updatedAt: '2022-07-12T06:54:45Z',
  41. }
  42. }
  43. const mockActiveAvatar = async (deletable = true) => {
  44. mockGraphQLApi(UserCurrentAvatarActiveDocument).willResolve({
  45. userCurrentAvatarActive: getAvatarObject(deletable),
  46. })
  47. }
  48. const mockAddAvatar = async () => {
  49. mockGraphQLApi(UserCurrentAvatarAddDocument).willResolve({
  50. userCurrentAvatarAdd: {
  51. avatar: getAvatarObject(true),
  52. errors: null,
  53. },
  54. })
  55. }
  56. const mockDeleteAvatar = async () => {
  57. mockGraphQLApi(UserCurrentAvatarDeleteDocument).willResolve({
  58. userCurrentAvatarDelete: {
  59. success: true,
  60. errors: null,
  61. },
  62. })
  63. }
  64. const checkShownAvatar = async (view: ExtendedRenderResult, image: string) => {
  65. const waitForAvatar = await view.findByTestId('common-avatar')
  66. expect(waitForAvatar).toHaveStyle({
  67. 'background-image': image,
  68. })
  69. }
  70. const uploadFile = async (view: ExtendedRenderResult, testFlag: string) => {
  71. expect(view.queryByText('Save')).not.toBeInTheDocument()
  72. const file = new File([], 'test.jpg', { type: 'image/jpeg' })
  73. await view.events.upload(view.getByTestId(testFlag), file)
  74. const saveButton = await view.findByText('Save')
  75. expect(saveButton).toBeVisible()
  76. await view.events.click(saveButton)
  77. }
  78. const removeAvatar = async (view: ExtendedRenderResult) => {
  79. await view.events.click(view.getByText('Delete'))
  80. await view.findByText('Delete avatar')
  81. await view.events.click(view.getByText('Delete avatar'))
  82. await checkShownAvatar(view, '')
  83. }
  84. describe('editing avatar', () => {
  85. beforeEach(() => {
  86. mockUserCurrent({
  87. firstname: 'John',
  88. lastname: 'Doe',
  89. })
  90. })
  91. afterEach(() => {
  92. vi.spyOn(console, 'log').mockRestore()
  93. })
  94. it('shows the avatar', async () => {
  95. mockActiveAvatar()
  96. const view = await visitView('/account/avatar')
  97. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  98. })
  99. it('can remove avatar', async () => {
  100. mockActiveAvatar()
  101. mockDeleteAvatar()
  102. const view = await visitView('/account/avatar')
  103. await view.findByText('Delete')
  104. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  105. await removeAvatar(view)
  106. const avatar = await view.findByTestId('common-avatar')
  107. expect(avatar).toHaveTextContent('JD')
  108. })
  109. it('can not remove undeletable avatars', async () => {
  110. mockActiveAvatar(false)
  111. mockDeleteAvatar()
  112. const view = await visitView('/account/avatar')
  113. await view.findByTestId('common-avatar')
  114. const deleteButton = await view.findByRole('button', { name: 'Delete' })
  115. expect(deleteButton).toHaveAttribute('disabled')
  116. })
  117. it('can upload image from camera', async () => {
  118. mockAddAvatar()
  119. mockActiveAvatar()
  120. const view = await visitView('/account/avatar')
  121. await uploadFile(view, 'fileCameraInput')
  122. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  123. })
  124. it('can upload image from gallery', async () => {
  125. mockAddAvatar()
  126. mockActiveAvatar()
  127. const view = await visitView('/account/avatar')
  128. await uploadFile(view, 'fileGalleryInput')
  129. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  130. })
  131. it('even after deleting I can upload an image', async () => {
  132. mockAddAvatar()
  133. mockActiveAvatar()
  134. mockDeleteAvatar()
  135. const view = await visitView('/account/avatar')
  136. await view.findByTestId('common-avatar')
  137. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  138. await removeAvatar(view)
  139. await uploadFile(view, 'fileGalleryInput')
  140. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  141. })
  142. it('after selecting image I can cancel the cropping', async () => {
  143. mockActiveAvatar()
  144. const view = await visitView('/account/avatar')
  145. const file = new File([], 'test.jpg', { type: 'image/jpeg' })
  146. await view.events.upload(view.getByTestId('fileGalleryInput'), file)
  147. const cancelButton = await view.findByText('Cancel')
  148. expect(cancelButton).toBeInTheDocument()
  149. await view.events.click(view.getByText('Cancel'))
  150. await checkShownAvatar(view, `url(${mockAvatarImage})`)
  151. })
  152. })