summaryrefslogtreecommitdiff
path: root/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js')
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js413
1 files changed, 413 insertions, 0 deletions
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js
new file mode 100644
index 00000000000..865dc8fe8fb
--- /dev/null
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view_spec.js
@@ -0,0 +1,413 @@
+import {
+ GlIntersectionObserver,
+ GlButton,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlLink,
+} from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
+import DropdownContentsLabelsView from '~/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue';
+import LabelItem from '~/sidebar/components/labels/labels_select_vue/label_item.vue';
+
+import * as actions from '~/sidebar/components/labels/labels_select_vue/store/actions';
+import * as getters from '~/sidebar/components/labels/labels_select_vue/store/getters';
+import mutations from '~/sidebar/components/labels/labels_select_vue/store/mutations';
+import defaultState from '~/sidebar/components/labels/labels_select_vue/store/state';
+
+import { mockConfig, mockLabels, mockRegularLabel } from './mock_data';
+
+Vue.use(Vuex);
+
+describe('DropdownContentsLabelsView', () => {
+ let wrapper;
+
+ const createComponent = (initialState = mockConfig) => {
+ const store = new Vuex.Store({
+ getters,
+ mutations,
+ state: {
+ ...defaultState(),
+ footerCreateLabelTitle: 'Create label',
+ footerManageLabelTitle: 'Manage labels',
+ },
+ actions: {
+ ...actions,
+ fetchLabels: jest.fn(),
+ },
+ });
+
+ store.dispatch('setInitialState', initialState);
+ store.dispatch('receiveLabelsSuccess', mockLabels);
+
+ wrapper = shallowMount(DropdownContentsLabelsView, {
+ store,
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findDropdownContent = () => wrapper.find('[data-testid="dropdown-content"]');
+ const findDropdownTitle = () => wrapper.find('[data-testid="dropdown-title"]');
+ const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+
+ describe('computed', () => {
+ describe('visibleLabels', () => {
+ it('returns matching labels filtered with `searchKey`', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'bug',
+ });
+
+ expect(wrapper.vm.visibleLabels.length).toBe(1);
+ expect(wrapper.vm.visibleLabels[0].title).toBe('Bug');
+ });
+
+ it('returns matching labels with fuzzy filtering', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'bg',
+ });
+
+ expect(wrapper.vm.visibleLabels.length).toBe(2);
+ expect(wrapper.vm.visibleLabels[0].title).toBe('Bug');
+ expect(wrapper.vm.visibleLabels[1].title).toBe('Boog');
+ });
+
+ it('returns all labels when `searchKey` is empty', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: '',
+ });
+
+ expect(wrapper.vm.visibleLabels.length).toBe(mockLabels.length);
+ });
+ });
+
+ describe('showNoMatchingResultsMessage', () => {
+ it.each`
+ searchKey | labels | labelsDescription | returnValue
+ ${''} | ${[]} | ${'empty'} | ${false}
+ ${'bug'} | ${[]} | ${'empty'} | ${true}
+ ${''} | ${mockLabels} | ${'not empty'} | ${false}
+ ${'bug'} | ${mockLabels} | ${'not empty'} | ${false}
+ `(
+ 'returns $returnValue when searchKey is "$searchKey" and visibleLabels is $labelsDescription',
+ async ({ searchKey, labels, returnValue }) => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey,
+ });
+
+ wrapper.vm.$store.dispatch('receiveLabelsSuccess', labels);
+
+ await nextTick();
+
+ expect(wrapper.vm.showNoMatchingResultsMessage).toBe(returnValue);
+ },
+ );
+ });
+ });
+
+ describe('methods', () => {
+ const fakePreventDefault = jest.fn();
+
+ describe('isLabelSelected', () => {
+ it('returns true when provided `label` param is one of the selected labels', () => {
+ expect(wrapper.vm.isLabelSelected(mockRegularLabel)).toBe(true);
+ });
+
+ it('returns false when provided `label` param is not one of the selected labels', () => {
+ expect(wrapper.vm.isLabelSelected(mockLabels[1])).toBe(false);
+ });
+ });
+
+ describe('handleComponentAppear', () => {
+ it('calls `focusInput` on searchInput field', async () => {
+ wrapper.vm.$refs.searchInput.focusInput = jest.fn();
+
+ await wrapper.vm.handleComponentAppear();
+
+ expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled();
+ });
+ });
+
+ describe('handleComponentDisappear', () => {
+ it('calls action `receiveLabelsSuccess` with empty array', () => {
+ jest.spyOn(wrapper.vm, 'receiveLabelsSuccess');
+
+ wrapper.vm.handleComponentDisappear();
+
+ expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]);
+ });
+ });
+
+ describe('handleCreateLabelClick', () => {
+ it('calls actions `receiveLabelsSuccess` with empty array and `toggleDropdownContentsCreateView`', () => {
+ jest.spyOn(wrapper.vm, 'receiveLabelsSuccess');
+ jest.spyOn(wrapper.vm, 'toggleDropdownContentsCreateView');
+
+ wrapper.vm.handleCreateLabelClick();
+
+ expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]);
+ expect(wrapper.vm.toggleDropdownContentsCreateView).toHaveBeenCalled();
+ });
+ });
+
+ describe('handleKeyDown', () => {
+ it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: UP_KEY_CODE,
+ });
+
+ expect(wrapper.vm.currentHighlightItem).toBe(0);
+ });
+
+ it('increases `currentHighlightItem` value by 1 when Down arrow key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: DOWN_KEY_CODE,
+ });
+
+ expect(wrapper.vm.currentHighlightItem).toBe(2);
+ });
+
+ it('resets the search text when the Enter key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ searchKey: 'bug',
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: ENTER_KEY_CODE,
+ preventDefault: fakePreventDefault,
+ });
+
+ expect(wrapper.vm.searchKey).toBe('');
+ expect(fakePreventDefault).toHaveBeenCalled();
+ });
+
+ it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
+ jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 2,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: ENTER_KEY_CODE,
+ preventDefault: fakePreventDefault,
+ });
+
+ expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockLabels[2]]);
+ });
+
+ it('calls action `toggleDropdownContents` when Esc key is pressed', () => {
+ jest.spyOn(wrapper.vm, 'toggleDropdownContents').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: ESC_KEY_CODE,
+ });
+
+ expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled();
+ });
+
+ it('calls action `scrollIntoViewIfNeeded` in next tick when any key is pressed', async () => {
+ jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeeded').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 1,
+ });
+
+ wrapper.vm.handleKeyDown({
+ keyCode: DOWN_KEY_CODE,
+ });
+
+ await nextTick();
+ expect(wrapper.vm.scrollIntoViewIfNeeded).toHaveBeenCalled();
+ });
+ });
+
+ describe('handleLabelClick', () => {
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
+ });
+
+ it('calls action `updateSelectedLabels` with provided `label` param', () => {
+ wrapper.vm.handleLabelClick(mockRegularLabel);
+
+ expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockRegularLabel]);
+ });
+
+ it('calls action `toggleDropdownContents` when `state.allowMultiselect` is false', () => {
+ jest.spyOn(wrapper.vm, 'toggleDropdownContents');
+ wrapper.vm.$store.state.allowMultiselect = false;
+
+ wrapper.vm.handleLabelClick(mockRegularLabel);
+
+ expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders gl-intersection-observer as component root', () => {
+ expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true);
+ });
+
+ it('renders gl-loading-icon component when `labelsFetchInProgress` prop is true', async () => {
+ wrapper.vm.$store.dispatch('requestLabels');
+
+ await nextTick();
+ const loadingIconEl = findLoadingIcon();
+
+ expect(loadingIconEl.exists()).toBe(true);
+ expect(loadingIconEl.attributes('class')).toContain('labels-fetch-loading');
+ });
+
+ it('renders dropdown title element', () => {
+ const titleEl = findDropdownTitle();
+
+ expect(titleEl.exists()).toBe(true);
+ expect(titleEl.text()).toBe('Assign labels');
+ });
+
+ it('does not render dropdown title element when `state.variant` is "standalone"', () => {
+ createComponent({ ...mockConfig, variant: 'standalone' });
+ expect(findDropdownTitle().exists()).toBe(false);
+ });
+
+ it('renders dropdown title element when `state.variant` is "embedded"', () => {
+ createComponent({ ...mockConfig, variant: 'embedded' });
+ expect(findDropdownTitle().exists()).toBe(true);
+ });
+
+ it('renders dropdown close button element', () => {
+ const closeButtonEl = findDropdownTitle().findComponent(GlButton);
+
+ expect(closeButtonEl.exists()).toBe(true);
+ expect(closeButtonEl.props('icon')).toBe('close');
+ });
+
+ it('renders label search input element', () => {
+ const searchInputEl = wrapper.findComponent(GlSearchBoxByType);
+
+ expect(searchInputEl.exists()).toBe(true);
+ });
+
+ it('renders label elements for all labels', () => {
+ expect(wrapper.findAllComponents(LabelItem)).toHaveLength(mockLabels.length);
+ });
+
+ it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ currentHighlightItem: 0,
+ });
+
+ await nextTick();
+ const labelItemEl = findDropdownContent().findComponent(LabelItem);
+
+ expect(labelItemEl.attributes('highlight')).toBe('true');
+ });
+
+ it('renders element containing "No matching results" when `searchKey` does not match with any label', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ searchKey: 'abc',
+ });
+
+ await nextTick();
+ const noMatchEl = findDropdownContent().find('li');
+
+ expect(noMatchEl.isVisible()).toBe(true);
+ expect(noMatchEl.text()).toContain('No matching results');
+ });
+
+ it('renders empty content while loading', async () => {
+ wrapper.vm.$store.state.labelsFetchInProgress = true;
+
+ await nextTick();
+ const dropdownContent = findDropdownContent();
+ const loadingIcon = findLoadingIcon();
+
+ expect(dropdownContent.exists()).toBe(true);
+ expect(dropdownContent.isVisible()).toBe(true);
+ expect(loadingIcon.exists()).toBe(true);
+ expect(loadingIcon.isVisible()).toBe(true);
+ });
+
+ it('renders footer list items', () => {
+ const footerLinks = findDropdownFooter().findAllComponents(GlLink);
+ const createLabelLink = footerLinks.at(0);
+ const manageLabelsLink = footerLinks.at(1);
+
+ expect(createLabelLink.exists()).toBe(true);
+ expect(createLabelLink.text()).toBe('Create label');
+ expect(manageLabelsLink.exists()).toBe(true);
+ expect(manageLabelsLink.text()).toBe('Manage labels');
+ });
+
+ it('does not render "Create label" footer link when `state.allowLabelCreate` is `false`', async () => {
+ wrapper.vm.$store.state.allowLabelCreate = false;
+
+ await nextTick();
+ const createLabelLink = findDropdownFooter().findAllComponents(GlLink).at(0);
+
+ expect(createLabelLink.text()).not.toBe('Create label');
+ });
+
+ it('does not render footer list items when `state.variant` is "standalone"', () => {
+ createComponent({ ...mockConfig, variant: 'standalone' });
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('does not render footer list items when `allowLabelCreate` is false and `labelsManagePath` is null', () => {
+ createComponent({
+ ...mockConfig,
+ allowLabelCreate: false,
+ labelsManagePath: null,
+ });
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('renders footer list items when `state.variant` is "embedded"', () => {
+ expect(findDropdownFooter().exists()).toBe(true);
+ });
+ });
+});