diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-21 06:10:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-21 06:10:13 +0300 |
commit | 3c0d15f2f194a4e08b2700b0a75c305d89dd7816 (patch) | |
tree | 9e987dcacb631d6159ba44909933d5f31103ed0e /spec/frontend | |
parent | a558e386749c579a70cca6463926092926627388 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r-- | spec/frontend/ide/utils_spec.js | 11 | ||||
-rw-r--r-- | spec/frontend/lib/utils/file_utility_spec.js | 13 | ||||
-rw-r--r-- | spec/frontend/profile/edit/components/profile_edit_app_spec.js | 111 | ||||
-rw-r--r-- | spec/frontend/profile/edit/components/user_avatar_spec.js | 139 |
4 files changed, 263 insertions, 11 deletions
diff --git a/spec/frontend/ide/utils_spec.js b/spec/frontend/ide/utils_spec.js index 4efc0ac6028..dd3c6862ea4 100644 --- a/spec/frontend/ide/utils_spec.js +++ b/spec/frontend/ide/utils_spec.js @@ -8,7 +8,6 @@ import { trimTrailingWhitespace, getPathParents, getPathParent, - readFileAsDataURL, addNumericSuffix, } from '~/ide/utils'; @@ -267,16 +266,6 @@ describe('WebIDE utils', () => { }); }); - describe('readFileAsDataURL', () => { - it('reads a file and returns its output as a data url', () => { - const file = new File(['foo'], 'foo.png', { type: 'image/png' }); - - return readFileAsDataURL(file).then((contents) => { - expect(contents).toBe('data:image/png;base64,Zm9v'); - }); - }); - }); - /* * hello-2425 -> hello-2425 * hello.md -> hello-1.md diff --git a/spec/frontend/lib/utils/file_utility_spec.js b/spec/frontend/lib/utils/file_utility_spec.js new file mode 100644 index 00000000000..386deafe712 --- /dev/null +++ b/spec/frontend/lib/utils/file_utility_spec.js @@ -0,0 +1,13 @@ +import { readFileAsDataURL } from '~/lib/utils/file_utility'; + +describe('File utilities', () => { + describe('readFileAsDataURL', () => { + it('reads a file and returns its output as a data url', () => { + const file = new File(['foo'], 'foo.png', { type: 'image/png' }); + + return readFileAsDataURL(file).then((contents) => { + expect(contents).toBe('data:image/png;base64,Zm9v'); + }); + }); + }); +}); diff --git a/spec/frontend/profile/edit/components/profile_edit_app_spec.js b/spec/frontend/profile/edit/components/profile_edit_app_spec.js new file mode 100644 index 00000000000..614b5ad1a4d --- /dev/null +++ b/spec/frontend/profile/edit/components/profile_edit_app_spec.js @@ -0,0 +1,111 @@ +import { GlButton, GlForm } from '@gitlab/ui'; +import MockAdapter from 'axios-mock-adapter'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; + +import { readFileAsDataURL } from '~/lib/utils/file_utility'; +import axios from '~/lib/utils/axios_utils'; +import ProfileEditApp from '~/profile/edit/components/profile_edit_app.vue'; +import UserAvatar from '~/profile/edit/components/user_avatar.vue'; +import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/alert'; + +jest.mock('~/alert'); +jest.mock('~/lib/utils/file_utility', () => ({ + readFileAsDataURL: jest.fn().mockResolvedValue(), +})); + +describe('Profile Edit App', () => { + let wrapper; + let mockAxios; + + const mockAvatarBlob = new Blob([''], { type: 'image/png' }); + + const mockAvatarFile = new File([mockAvatarBlob], 'avatar.png', { type: mockAvatarBlob.type }); + + const stubbedProfilePath = '/profile/edit'; + const stubbedUserPath = '/user/test'; + const successMessage = 'Profile was successfully updated.'; + + const createComponent = () => { + wrapper = shallowMountExtended(ProfileEditApp, { + propsData: { + profilePath: stubbedProfilePath, + userPath: stubbedUserPath, + }, + }); + }; + + beforeEach(() => { + mockAxios = new MockAdapter(axios); + + createComponent(); + }); + + const findForm = () => wrapper.findComponent(GlForm); + const findButtons = () => wrapper.findAllComponents(GlButton); + const findAvatar = () => wrapper.findComponent(UserAvatar); + const submitForm = () => findForm().vm.$emit('submit', new Event('submit')); + const setAvatar = () => findAvatar().vm.$emit('blob-change', mockAvatarFile); + + it('renders the form for users to interact with', () => { + const form = findForm(); + const buttons = findButtons(); + + expect(form.exists()).toBe(true); + expect(buttons).toHaveLength(2); + + expect(wrapper.findByTestId('cancel-edit-button').attributes('href')).toBe(stubbedUserPath); + }); + + describe('when form submit request is successful', () => { + it('shows success alert', async () => { + mockAxios.onPut(stubbedProfilePath).reply(200, { + message: successMessage, + }); + + submitForm(); + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ message: successMessage, variant: VARIANT_INFO }); + }); + + it('syncs header avatars', async () => { + mockAxios.onPut(stubbedProfilePath).reply(200, { + message: successMessage, + }); + + setAvatar(); + submitForm(); + + await waitForPromises(); + + expect(readFileAsDataURL).toHaveBeenCalledWith(mockAvatarFile); + }); + }); + + describe('when form submit request is not successful', () => { + it('shows error alert', async () => { + mockAxios.onPut(stubbedProfilePath).reply(500); + + submitForm(); + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith( + expect.objectContaining({ variant: VARIANT_DANGER }), + ); + }); + }); + + it('submits API request with avatar file', async () => { + mockAxios.onPut(stubbedProfilePath).reply(200); + + setAvatar(); + submitForm(); + + await waitForPromises(); + + const axiosRequestData = mockAxios.history.put[0].data; + + expect(axiosRequestData.get('user[avatar]')).toEqual(mockAvatarFile); + }); +}); diff --git a/spec/frontend/profile/edit/components/user_avatar_spec.js b/spec/frontend/profile/edit/components/user_avatar_spec.js new file mode 100644 index 00000000000..caa3356b49f --- /dev/null +++ b/spec/frontend/profile/edit/components/user_avatar_spec.js @@ -0,0 +1,139 @@ +import { nextTick } from 'vue'; +import jQuery from 'jquery'; +import { GlAvatar, GlAvatarLink, GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { avatarI18n } from '~/profile/edit/constants'; +import { loadCSSFile } from '~/lib/utils/css_utils'; + +import UserAvatar from '~/profile/edit/components/user_avatar.vue'; + +const glCropDataMock = jest.fn().mockImplementation(() => ({ + getBlob: jest.fn(), +})); + +const jQueryMock = { + glCrop: jest.fn().mockReturnValue({ + data: glCropDataMock, + }), +}; + +jest.mock(`~/lib/utils/css_utils`); +jest.mock('jquery'); + +describe('Edit User Avatar', () => { + let wrapper; + + beforeEach(() => { + jQuery.mockImplementation(() => jQueryMock); + }); + + const defaultProvides = { + avatarUrl: '/-/profile/avatarUrl', + brandProfileImageGuidelines: '', + cropperCssPath: '', + hasAvatar: true, + gravatarEnabled: true, + gravatarLink: { + hostname: 'gravatar.com', + url: 'gravatar.com', + }, + profileAvatarPath: '/profile/avatar', + }; + + const createComponent = (provides = {}) => { + wrapper = shallowMountExtended(UserAvatar, { + provide: { + ...defaultProvides, + ...provides, + }, + }); + }; + + const findAvatar = () => wrapper.findComponent(GlAvatar); + const findAvatarLink = () => wrapper.findComponent(GlAvatarLink); + const findHelpText = () => wrapper.findComponent(GlSprintf).attributes('message'); + const findRemoveAvatarButton = () => wrapper.findByTestId('remove-avatar-button'); + + describe('renders correctly', () => { + it('under default condition', async () => { + createComponent(); + await nextTick(); + + expect(jQueryMock.glCrop).toHaveBeenCalledWith({ + filename: '.js-avatar-filename', + previewImage: '.avatar-image .gl-avatar', + modalCrop: '.modal-profile-crop', + pickImageEl: '.js-choose-user-avatar-button', + uploadImageBtn: '.js-upload-user-avatar', + modalCropImg: '.modal-profile-crop-image', + onBlobChange: expect.any(Function), + }); + expect(glCropDataMock).toHaveBeenCalledWith('glcrop'); + expect(loadCSSFile).toHaveBeenCalledWith(defaultProvides.cropperCssPath); + const avatar = findAvatar(); + + expect(avatar.exists()).toBe(true); + expect(avatar.attributes('src')).toBe(defaultProvides.avatarUrl); + expect(findAvatarLink().attributes('href')).toBe(defaultProvides.avatarUrl); + + const removeAvatarButton = findRemoveAvatarButton(); + expect(removeAvatarButton.exists()).toBe(true); + expect(removeAvatarButton.attributes('href')).toBe(defaultProvides.profileAvatarPath); + }); + + describe('when user has avatar', () => { + describe('while gravatar is enabled', () => { + it('shows help text for change or remove avatar', () => { + createComponent({ + gravatarEnabled: true, + }); + + expect(findHelpText()).toBe(avatarI18n.changeOrRemoveAvatar); + }); + }); + describe('while gravatar is disabled', () => { + it('shows help text for change avatar', () => { + createComponent({ + gravatarEnabled: false, + }); + + expect(findHelpText()).toBe(avatarI18n.changeAvatar); + }); + }); + }); + + describe('when user does not have an avatar', () => { + describe('while gravatar is enabled', () => { + it('shows help text for upload or change avatar', () => { + createComponent({ + gravatarEnabled: true, + hasAvatar: false, + }); + expect(findHelpText()).toBe(avatarI18n.uploadOrChangeAvatar); + }); + }); + + describe('while gravatar is disabled', () => { + it('shows help text for upload avatar', () => { + createComponent({ + gravatarEnabled: false, + hasAvatar: false, + }); + expect(findHelpText()).toBe(avatarI18n.uploadAvatar); + expect(findRemoveAvatarButton().exists()).toBe(false); + }); + }); + }); + }); + + it('can render profile image guidelines', () => { + const brandProfileImageGuidelines = 'brandProfileImageGuidelines'; + createComponent({ + brandProfileImageGuidelines, + }); + + expect(wrapper.findByTestId('brand-profile-image-guidelines').text()).toBe( + brandProfileImageGuidelines, + ); + }); +}); |