123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- import { within } from '@testing-library/vue'
- import * as VueUse from '@vueuse/core'
- import { defineComponent, ref } from 'vue'
- import { visitView } from '#tests/support/components/visitView.ts'
- import { mockUserCurrent } from '#tests/support/mock-userCurrent.ts'
- import { waitForNextTick } from '#tests/support/utils.ts'
- import { mockUserCurrentAvatarAddMutation } from '#shared/entities/user/current/graphql/mutations/userCurrentAvatarAdd.mocks.ts'
- import {
- mockUserCurrentAvatarDeleteMutation,
- waitForUserCurrentAvatarDeleteMutationCalls,
- } from '#shared/entities/user/current/graphql/mutations/userCurrentAvatarDelete.mocks.ts'
- import {
- mockUserCurrentAvatarSelectMutation,
- waitForUserCurrentAvatarSelectMutationCalls,
- } from '../graphql/mutations/userCurrentAvatarSelect.mocks.ts'
- import { mockUserCurrentAvatarListQuery } from '../graphql/queries/userCurrentAvatarList.mocks.ts'
- vi.mock('vue-advanced-cropper', () => {
- const Cropper = defineComponent({
- emits: ['change'],
- mounted() {
- this.$emit('change', {
- canvas: {
- toDataURL() {
- return 'cropped image url'
- },
- },
- })
- },
- template: '<div></div>',
- })
- return {
- Cropper,
- }
- })
- describe('avatar personal settings', () => {
- beforeEach(() => {
- mockUserCurrent({
- firstname: 'John',
- lastname: 'Doe',
- })
- })
- it('shows all the avatars of the current user', async () => {
- mockUserCurrentAvatarListQuery({
- userCurrentAvatarList: [
- {
- default: true,
- initial: true,
- deletable: false,
- },
- {
- default: false,
- initial: false,
- deletable: true,
- },
- {
- default: false,
- initial: false,
- deletable: true,
- },
- ],
- })
- const view = await visitView('/personal-setting/avatar')
- const mainContent = within(view.getByRole('main'))
- const avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(3)
- expect(view.getByRole('button', { name: 'Upload' })).toBeInTheDocument()
- expect(view.getByRole('button', { name: 'Camera' })).toBeInTheDocument()
- })
- it('can select an avatar to be the new default one', async () => {
- mockUserCurrentAvatarListQuery({
- userCurrentAvatarList: [
- {
- default: true,
- initial: true,
- deletable: false,
- },
- {
- default: false,
- initial: false,
- deletable: true,
- },
- {
- default: false,
- initial: false,
- deletable: true,
- },
- ],
- })
- const view = await visitView('/personal-setting/avatar')
- const mainContent = within(view.getByRole('main'))
- let avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars[0].parentElement).toHaveClass('avatar-selected')
- mockUserCurrentAvatarSelectMutation({
- userCurrentAvatarSelect: {
- success: true,
- },
- })
- await view.events.click(avatars[1])
- const calls = await waitForUserCurrentAvatarSelectMutationCalls()
- expect(calls).toHaveLength(1)
- avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars[0]).not.toHaveClass('avatar-selected')
- expect(avatars[1]).toHaveClass('avatar-selected')
- })
- it('can delete an avatar', async () => {
- mockUserCurrentAvatarListQuery({
- userCurrentAvatarList: [
- {
- default: true,
- initial: true,
- deletable: false,
- },
- {
- default: false,
- initial: false,
- deletable: true,
- },
- ],
- })
- const view = await visitView('/personal-setting/avatar')
- const mainContent = within(view.getByRole('main'))
- let avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(2)
- const deleteButton = await view.findByRole('button', {
- name: 'Delete this avatar',
- })
- expect(deleteButton).toBeInTheDocument()
- mockUserCurrentAvatarDeleteMutation({
- userCurrentAvatarDelete: {
- success: true,
- },
- })
- await view.events.click(deleteButton)
- await waitForNextTick()
- expect(
- await view.findByRole('dialog', { name: 'Delete Object' }),
- ).toBeInTheDocument()
- await view.events.click(view.getByRole('button', { name: 'Delete Object' }))
- const calls = await waitForUserCurrentAvatarDeleteMutationCalls()
- expect(calls).toHaveLength(1)
- avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(1)
- expect(avatars[0]).toHaveTextContent('JD')
- })
- it('upload new avatar by file', async () => {
- mockUserCurrentAvatarListQuery({
- userCurrentAvatarList: [
- {
- default: true,
- initial: true,
- deletable: false,
- },
- ],
- })
- const view = await visitView('/personal-setting/avatar')
- const mainContent = within(view.getByRole('main'))
- let avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(1)
- expect(avatars[0].parentElement).toHaveClass('avatar-selected')
- const fileUploadButton = view.getByRole('button', {
- name: 'Upload',
- })
- expect(fileUploadButton).toBeInTheDocument()
- const file = new File([], 'test.jpg', { type: 'image/jpeg' })
- await view.events.upload(view.getByTestId('fileUploadInput'), file)
- await waitForNextTick()
- const flyout = await view.findByRole('complementary', {
- name: 'Crop Image',
- })
- expect(flyout).toBeInTheDocument()
- const flyoutContent = within(flyout)
- expect(
- await flyoutContent.findByTestId('common-avatar'),
- ).toBeInTheDocument()
- mockUserCurrentAvatarAddMutation({
- userCurrentAvatarAdd: {
- avatar: {
- default: true,
- initial: true,
- deletable: false,
- },
- },
- })
- await view.events.click(view.getByRole('button', { name: 'Save' }))
- avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(2)
- expect(avatars[0]).not.toHaveClass('avatar-selected')
- expect(avatars[1].parentElement).toHaveClass('avatar-selected')
- })
- describe('with camera flyout', () => {
- let mockPermissionState = 'granted'
- let originalMediaDevices: MediaDevices
- beforeAll(() => {
- originalMediaDevices = navigator.mediaDevices
- // Redefine mediaDevices to be writable
- Object.defineProperty(navigator, 'mediaDevices', {
- writable: true,
- value: {
- getUserMedia: vi.fn().mockResolvedValue({
- getTracks: () => [
- {
- kind: 'video',
- stop: vi.fn(),
- },
- ],
- }),
- },
- })
- vi.spyOn(VueUse, 'usePermission').mockImplementation(() => {
- // Return a mock ref object based on the permission you are testing
- // You can control the returned value based on the permissionName if needed
- return ref(
- mockPermissionState,
- ) as unknown as VueUse.UsePermissionReturnWithControls
- })
- })
- afterAll(() => {
- // Restore original mediaDevices
- Object.defineProperty(navigator, 'mediaDevices', {
- writable: true,
- value: originalMediaDevices,
- })
- })
- beforeEach(() => {
- mockUserCurrentAvatarListQuery({
- userCurrentAvatarList: [
- {
- default: true,
- initial: true,
- deletable: false,
- },
- ],
- })
- })
- it('upload new avatar by camera', async () => {
- const view = await visitView('/personal-setting/avatar')
- const mainContent = within(view.getByRole('main'))
- let avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(1)
- expect(avatars[0].parentElement).toHaveClass('avatar-selected')
- const cameraButton = view.getByRole('button', {
- name: 'Camera',
- })
- await view.events.click(cameraButton)
- const flyout = await view.findByRole('complementary', {
- name: 'Camera',
- })
- expect(flyout).toBeInTheDocument()
- expect(
- await view.findByLabelText(
- 'Use the camera to take a photo for the avatar.',
- ),
- ).toBeInTheDocument()
- const captureButton = view.getByRole('button', {
- name: 'Capture From Camera',
- })
- await view.events.click(captureButton)
- mockUserCurrentAvatarAddMutation({
- userCurrentAvatarAdd: {
- avatar: {
- default: true,
- initial: false,
- deletable: false,
- },
- },
- })
- await view.events.click(view.getByRole('button', { name: 'Save' }))
- avatars = await mainContent.findAllByTestId('common-avatar')
- expect(avatars).toHaveLength(2)
- expect(avatars[0]).not.toHaveClass('avatar-selected')
- expect(avatars[1]).toHaveClass('avatar-selected')
- })
- it('should show forbidden access for camera', async () => {
- mockPermissionState = 'denied'
- const view = await visitView('/personal-setting/avatar')
- const cameraButton = view.getByRole('button', {
- name: 'Camera',
- })
- await view.events.click(cameraButton)
- const flyout = await view.findByRole('complementary', {
- name: 'Camera',
- })
- expect(flyout).toBeInTheDocument()
- expect(
- view.getByText(
- 'Accessing your camera is forbidden. Please check your settings.',
- ),
- ).toBeInTheDocument()
- })
- })
- })
|