gitlab-ce/spec/frontend/notifications/components/notifications_dropdown_spec.js

261 lines
9.3 KiB
JavaScript

import { GlButton, GlButtonGroup, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import waitForPromises from 'helpers/wait_for_promises';
import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import CustomNotificationsModal from '~/notifications/components/custom_notifications_modal.vue';
import NotificationsDropdown from '~/notifications/components/notifications_dropdown.vue';
const mockDropdownItems = ['global', 'watch', 'participating', 'mention', 'disabled'];
const mockToastShow = jest.fn();
describe('NotificationsDropdown', () => {
let wrapper;
let mockAxios;
function createComponent(injectedProperties = {}) {
return mountExtended(NotificationsDropdown, {
stubs: {
GlButtonGroup,
GlButton,
GlCollapsibleListbox,
CustomNotificationsModal,
},
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
},
provide: {
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
...injectedProperties,
},
mocks: {
$toast: {
show: mockToastShow,
},
},
});
}
const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
const findSplitIcon = () => wrapper.findByTestId('notification-split-icon');
const findAllNotificationsDropdownItems = () => wrapper.findAllComponents(GlListboxItem);
const findListboxItemAt = (index) => findAllNotificationsDropdownItems().at(index);
const findListboxItem = (value) => wrapper.findByTestId(`listbox-item-${value}`);
const findNotificationsModal = () => wrapper.findComponent(CustomNotificationsModal);
const tooltipTitlePrefix = 'Notification setting';
const clickListboxItem = async (value) => {
const dropdownItem = findListboxItem(value);
await dropdownItem.trigger('click');
await waitForPromises();
};
beforeEach(() => {
gon.api_version = 'v4';
mockAxios = new MockAdapter(axios);
});
afterEach(() => {
mockAxios.restore();
});
describe('template', () => {
describe('when notification level is "custom"', () => {
beforeEach(() => {
wrapper = createComponent({
initialNotificationLevel: 'custom',
});
});
it('renders split dropdown', () => {
expect(findDropdown().props().icon).toBe('');
expect(findSplitIcon().exists()).toBe(true);
});
it('shows the button text when showLabel is true', () => {
wrapper = createComponent({
initialNotificationLevel: 'custom',
showLabel: true,
});
expect(findDropdown().props().toggleText).toBe('Custom');
});
it('shows the tooltip text when showLabel is false', () => {
wrapper = createComponent({
initialNotificationLevel: 'custom',
showLabel: false,
});
expect(findDropdown().props().toggleText).toBe(`${tooltipTitlePrefix} - Custom`);
});
it('opens the modal when the user clicks the button', async () => {
jest.spyOn(axios, 'put');
mockAxios.onPut('/api/v4/notification_settings').reply(HTTP_STATUS_OK, {});
wrapper = createComponent({
initialNotificationLevel: 'custom',
});
await findSplitIcon().vm.$emit('click');
expect(findNotificationsModal().props().visible).toBe(true);
});
});
describe('when notification level is not "custom"', () => {
beforeEach(() => {
wrapper = createComponent({
initialNotificationLevel: 'global',
});
});
it('renders unified dropdown', () => {
expect(findDropdown().props().icon).toBe('notifications');
expect(findSplitIcon().exists()).toBe(false);
});
it('shows the button text when showLabel is true', () => {
wrapper = createComponent({
showLabel: true,
});
expect(findDropdown().props().toggleText).toBe('Global');
});
it('shows the tooltip text when showLabel is false', () => {
wrapper = createComponent({
showLabel: false,
});
expect(findDropdown().props().toggleText).toBe(`${tooltipTitlePrefix} - Global`);
});
});
describe('button tooltip', () => {
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 = wrapper.findByTestId('notification-dropdown');
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 most activity'}
${2} | ${'participating'} | ${'Participate'} | ${'You will only receive notifications for items 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 items you have participated in and the events you choose'}
`('displays "$title" and "$description"', ({ dropdownIndex, title, description }) => {
wrapper = createComponent();
expect(
findAllNotificationsDropdownItems().at(dropdownIndex).find('span.gl-font-bold').text(),
).toBe(title);
expect(
findAllNotificationsDropdownItems().at(dropdownIndex).find('span.gl-text-subtle').text(),
).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 clickListboxItem('watch');
expect(axios.put).toHaveBeenCalledWith(endpointUrl, {
level: 'watch',
});
},
);
it('updates the selectedNotificationLevel and marks the item as selected', async () => {
mockAxios.onPut('/api/v4/notification_settings').reply(HTTP_STATUS_OK, {});
wrapper = createComponent();
const dropdownItem = findListboxItemAt(1);
await clickListboxItem('watch');
expect(wrapper.vm.selectedNotificationLevel).toBe('watch');
expect(dropdownItem.props().isSelected).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(HTTP_STATUS_NOT_FOUND, {});
wrapper = createComponent();
await clickListboxItem('watch');
expect(wrapper.vm.selectedNotificationLevel).toBe('global');
expect(mockToastShow).toHaveBeenCalledWith(
'An error occurred while updating the notification settings. Please try again.',
);
});
it('opens the modal when the user clicks on the "Custom" dropdown item', async () => {
mockAxios.onPut('/api/v4/notification_settings').reply(HTTP_STATUS_OK, {});
wrapper = createComponent();
await clickListboxItem('custom');
expect(findNotificationsModal().props().visible).toBe(true);
});
});
});