diff options
Diffstat (limited to 'spec/frontend/notifications/components/notifications_dropdown_spec.js')
-rw-r--r-- | spec/frontend/notifications/components/notifications_dropdown_spec.js | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/spec/frontend/notifications/components/notifications_dropdown_spec.js b/spec/frontend/notifications/components/notifications_dropdown_spec.js new file mode 100644 index 00000000000..2fe8e9c677e --- /dev/null +++ b/spec/frontend/notifications/components/notifications_dropdown_spec.js @@ -0,0 +1,240 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { shallowMount } from '@vue/test-utils'; +import { GlButtonGroup, GlButton, GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import waitForPromises from 'helpers/wait_for_promises'; +import httpStatus from '~/lib/utils/http_status'; +import NotificationsDropdown from '~/notifications/components/notifications_dropdown.vue'; +import NotificationsDropdownItem from '~/notifications/components/notifications_dropdown_item.vue'; + +const mockDropdownItems = ['global', 'watch', 'participating', 'mention', 'disabled']; +const mockToastShow = jest.fn(); + +describe('NotificationsDropdown', () => { + let wrapper; + let mockAxios; + + function createComponent(injectedProperties = {}) { + return shallowMount(NotificationsDropdown, { + stubs: { + GlButtonGroup, + GlDropdown, + GlDropdownItem, + NotificationsDropdownItem, + }, + directives: { + GlTooltip: createMockDirective(), + }, + provide: { + dropdownItems: mockDropdownItems, + initialNotificationLevel: 'global', + ...injectedProperties, + }, + mocks: { + $toast: { + show: mockToastShow, + }, + }, + }); + } + + const findButtonGroup = () => wrapper.find(GlButtonGroup); + const findButton = () => wrapper.find(GlButton); + const findDropdown = () => wrapper.find(GlDropdown); + const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`); + const findAllNotificationsDropdownItems = () => wrapper.findAll(NotificationsDropdownItem); + const findDropdownItemAt = (index) => + findAllNotificationsDropdownItems().at(index).find(GlDropdownItem); + + const clickDropdownItemAt = async (index) => { + const dropdownItem = findDropdownItemAt(index); + dropdownItem.vm.$emit('click'); + + await waitForPromises(); + }; + + beforeEach(() => { + gon.api_version = 'v4'; + mockAxios = new MockAdapter(axios); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + mockAxios.restore(); + }); + + describe('template', () => { + describe('when notification level is "custom"', () => { + beforeEach(() => { + wrapper = createComponent({ + initialNotificationLevel: 'custom', + }); + }); + + it('renders a button group', () => { + expect(findButtonGroup().exists()).toBe(true); + }); + + it('shows the button text when showLabel is true', () => { + wrapper = createComponent({ + initialNotificationLevel: 'custom', + showLabel: true, + }); + + expect(findButton().text()).toBe('Custom'); + }); + + it("doesn't show the button text when showLabel is false", () => { + wrapper = createComponent({ + initialNotificationLevel: 'custom', + showLabel: false, + }); + + expect(findButton().text()).toBe(''); + }); + }); + + describe('when notification level is not "custom"', () => { + beforeEach(() => { + wrapper = createComponent({ + initialNotificationLevel: 'global', + }); + }); + + it('does not render a button group', () => { + expect(findButtonGroup().exists()).toBe(false); + }); + + it('shows the button text when showLabel is true', () => { + wrapper = createComponent({ + showLabel: true, + }); + + expect(findDropdown().props('text')).toBe('Global'); + }); + + it("doesn't show the button text when showLabel is false", () => { + wrapper = createComponent({ + showLabel: false, + }); + + expect(findDropdown().props('text')).toBe(null); + }); + }); + + describe('button tooltip', () => { + const tooltipTitlePrefix = 'Notification setting'; + it.each` + level | title + ${'global'} | ${'Global'} + ${'watch'} | ${'Watch'} + ${'participating'} | ${'Participate'} + ${'mention'} | ${'On mention'} + ${'disabled'} | ${'Disabled'} + ${'custom'} | ${'Custom'} + `(`renders "${tooltipTitlePrefix} - $title" for "$level" level`, ({ level, title }) => { + wrapper = createComponent({ + initialNotificationLevel: level, + }); + + const tooltipElement = findByTestId('notificationButton'); + const tooltip = getBinding(tooltipElement.element, 'gl-tooltip'); + + expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`); + }); + }); + + describe('button icon', () => { + beforeEach(() => { + wrapper = createComponent({ + initialNotificationLevel: 'disabled', + }); + }); + + it('renders the "notifications-off" icon when notification level is "disabled"', () => { + expect(findDropdown().props('icon')).toBe('notifications-off'); + }); + + it('renders the "notifications" icon when notification level is not "disabled"', () => { + wrapper = createComponent(); + + expect(findDropdown().props('icon')).toBe('notifications'); + }); + }); + + describe('dropdown items', () => { + it.each` + dropdownIndex | level | title | description + ${0} | ${'global'} | ${'Global'} | ${'Use your global notification setting'} + ${1} | ${'watch'} | ${'Watch'} | ${'You will receive notifications for any activity'} + ${2} | ${'participating'} | ${'Participate'} | ${'You will only receive notifications for threads you have participated in'} + ${3} | ${'mention'} | ${'On mention'} | ${'You will receive notifications only for comments in which you were @mentioned'} + ${4} | ${'disabled'} | ${'Disabled'} | ${'You will not get any notifications via email'} + ${5} | ${'custom'} | ${'Custom'} | ${'You will only receive notifications for the events you choose'} + `('displays "$title" and "$description"', ({ dropdownIndex, title, description }) => { + wrapper = createComponent(); + + expect(findAllNotificationsDropdownItems().at(dropdownIndex).props('title')).toBe(title); + expect(findAllNotificationsDropdownItems().at(dropdownIndex).props('description')).toBe( + description, + ); + }); + }); + }); + + describe('when selecting an item', () => { + beforeEach(() => { + jest.spyOn(axios, 'put'); + }); + + it.each` + projectId | groupId | endpointUrl | endpointType | condition + ${1} | ${null} | ${'/api/v4/projects/1/notification_settings'} | ${'project notifications'} | ${'a projectId is given'} + ${null} | ${1} | ${'/api/v4/groups/1/notification_settings'} | ${'group notifications'} | ${'a groupId is given'} + ${null} | ${null} | ${'/api/v4/notification_settings'} | ${'global notifications'} | ${'when neither projectId nor groupId are given'} + `( + 'calls the $endpointType endpoint when $condition', + async ({ projectId, groupId, endpointUrl }) => { + wrapper = createComponent({ + projectId, + groupId, + }); + + await clickDropdownItemAt(1); + + expect(axios.put).toHaveBeenCalledWith(endpointUrl, { + level: 'watch', + }); + }, + ); + + it('updates the selectedNotificationLevel and marks the item with a checkmark', async () => { + mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.OK, {}); + wrapper = createComponent(); + + const dropdownItem = findDropdownItemAt(1); + + await clickDropdownItemAt(1); + + expect(wrapper.vm.selectedNotificationLevel).toBe('watch'); + expect(dropdownItem.props('isChecked')).toBe(true); + }); + + it("won't update the selectedNotificationLevel and shows a toast message when the request fails and ", async () => { + mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.NOT_FOUND, {}); + wrapper = createComponent(); + + await clickDropdownItemAt(1); + + expect(wrapper.vm.selectedNotificationLevel).toBe('global'); + expect( + mockToastShow, + ).toHaveBeenCalledWith( + 'An error occured while updating the notification settings. Please try again.', + { type: 'error' }, + ); + }); + }); +}); |