diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 12:08:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 12:08:42 +0300 |
commit | b76ae638462ab0f673e5915986070518dd3f9ad3 (patch) | |
tree | bdab0533383b52873be0ec0eb4d3c66598ff8b91 /spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js | |
parent | 434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff) |
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js')
-rw-r--r-- | spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js | 434 |
1 files changed, 145 insertions, 289 deletions
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js index 51301387c99..8bd944a3d54 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js @@ -1,357 +1,213 @@ -import { GlIntersectionObserver, GlLoadingIcon, GlSearchBoxByType, GlLink } from '@gitlab/ui'; +import { GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import Vuex from 'vuex'; -import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes'; +import { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; +import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants'; import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue'; +import projectLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql'; import LabelItem from '~/vue_shared/components/sidebar/labels_select_widget/label_item.vue'; +import { mockConfig, labelsQueryResponse } from './mock_data'; -import * as actions from '~/vue_shared/components/sidebar/labels_select_widget/store/actions'; -import * as getters from '~/vue_shared/components/sidebar/labels_select_widget/store/getters'; -import mutations from '~/vue_shared/components/sidebar/labels_select_widget/store/mutations'; -import defaultState from '~/vue_shared/components/sidebar/labels_select_widget/store/state'; - -import { mockConfig, mockLabels, mockRegularLabel } from './mock_data'; +jest.mock('~/flash'); const localVue = createLocalVue(); -localVue.use(Vuex); +localVue.use(VueApollo); + +const selectedLabels = [ + { + id: 28, + title: 'Bug', + description: 'Label for bugs', + color: '#FF0000', + textColor: '#FFFFFF', + }, +]; 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(), - }, - }); + const successfulQueryHandler = jest.fn().mockResolvedValue(labelsQueryResponse); - store.dispatch('setInitialState', initialState); - store.dispatch('receiveLabelsSuccess', mockLabels); + const createComponent = ({ + initialState = mockConfig, + queryHandler = successfulQueryHandler, + injected = {}, + } = {}) => { + const mockApollo = createMockApollo([[projectLabelsQuery, queryHandler]]); wrapper = shallowMount(DropdownContentsLabelsView, { localVue, - store, + apolloProvider: mockApollo, + provide: { + projectPath: 'test', + iid: 1, + allowLabelCreate: true, + labelsManagePath: '/gitlab-org/my-project/-/labels', + variant: DropdownVariant.Sidebar, + ...injected, + }, + propsData: { + ...initialState, + selectedLabels, + }, + stubs: { + GlSearchBoxByType, + }, }); }; - beforeEach(() => { - createComponent(); - }); - afterEach(() => { wrapper.destroy(); - wrapper = null; }); - const findDropdownContent = () => wrapper.find('[data-testid="dropdown-content"]'); + const findSearchInput = () => wrapper.findComponent(GlSearchBoxByType); + const findLabels = () => wrapper.findAllComponents(LabelItem); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + + const findLabelsList = () => wrapper.find('[data-testid="labels-list"]'); + const findDropdownWrapper = () => wrapper.find('[data-testid="dropdown-wrapper"]'); const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]'); - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); - - describe('computed', () => { - describe('visibleLabels', () => { - it('returns matching labels filtered with `searchKey`', () => { - 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', () => { - 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', () => { - wrapper.setData({ - searchKey: '', - }); - - expect(wrapper.vm.visibleLabels.length).toBe(mockLabels.length); - }); - }); + const findNoResultsMessage = () => wrapper.find('[data-testid="no-results"]'); + const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]'); - 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 }) => { - wrapper.setData({ - searchKey, - }); - - wrapper.vm.$store.dispatch('receiveLabelsSuccess', labels); - - await wrapper.vm.$nextTick(); - - expect(wrapper.vm.showNoMatchingResultsMessage).toBe(returnValue); - }, - ); + describe('when loading labels', () => { + it('renders disabled search input field', async () => { + createComponent(); + expect(findSearchInput().props('disabled')).toBe(true); }); - }); - - describe('methods', () => { - 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[2])).toBe(false); - }); + it('renders loading icon', async () => { + createComponent(); + expect(findLoadingIcon().exists()).toBe(true); }); - 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(); - }); + it('does not render labels list', async () => { + createComponent(); + expect(findLabelsList().exists()).toBe(false); }); + }); - describe('handleComponentDisappear', () => { - it('calls action `receiveLabelsSuccess` with empty array', () => { - jest.spyOn(wrapper.vm, 'receiveLabelsSuccess'); - - wrapper.vm.handleComponentDisappear(); - - expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]); - }); + describe('when labels are loaded', () => { + beforeEach(async () => { + createComponent(); + await waitForPromises(); }); - 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(); - }); + it('renders enabled search input field', async () => { + expect(findSearchInput().props('disabled')).toBe(false); }); - describe('handleKeyDown', () => { - it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', () => { - 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', () => { - 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', () => { - wrapper.setData({ - currentHighlightItem: 1, - searchKey: 'bug', - }); - - wrapper.vm.handleKeyDown({ - keyCode: ENTER_KEY_CODE, - }); - - expect(wrapper.vm.searchKey).toBe(''); - }); - - it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => { - jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation(); - wrapper.setData({ - currentHighlightItem: 1, - }); - - wrapper.vm.handleKeyDown({ - keyCode: ENTER_KEY_CODE, - }); - - expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([ - { - ...mockLabels[1], - set: true, - }, - ]); - }); - - it('calls action `toggleDropdownContents` when Esc key is pressed', () => { - jest.spyOn(wrapper.vm, 'toggleDropdownContents').mockImplementation(); - 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', () => { - jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeeded').mockImplementation(); - wrapper.setData({ - currentHighlightItem: 1, - }); - - wrapper.vm.handleKeyDown({ - keyCode: DOWN_KEY_CODE, - }); - - return wrapper.vm.$nextTick(() => { - expect(wrapper.vm.scrollIntoViewIfNeeded).toHaveBeenCalled(); - }); - }); + it('does not render loading icon', async () => { + expect(findLoadingIcon().exists()).toBe(false); }); - 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('renders labels list', async () => { + expect(findLabelsList().exists()).toBe(true); + expect(findLabels()).toHaveLength(2); + }); - it('calls action `toggleDropdownContents` when `state.allowMultiselect` is false', () => { - jest.spyOn(wrapper.vm, 'toggleDropdownContents'); - wrapper.vm.$store.state.allowMultiselect = false; + it('changes highlighted label correctly on pressing down button', async () => { + expect(findLabels().at(0).attributes('highlight')).toBeUndefined(); - wrapper.vm.handleLabelClick(mockRegularLabel); + await findDropdownWrapper().trigger('keydown.down'); + expect(findLabels().at(0).attributes('highlight')).toBe('true'); - expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled(); - }); + await findDropdownWrapper().trigger('keydown.down'); + expect(findLabels().at(1).attributes('highlight')).toBe('true'); + expect(findLabels().at(0).attributes('highlight')).toBeUndefined(); }); - }); - describe('template', () => { - it('renders gl-intersection-observer as component root', () => { - expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true); - }); + it('changes highlighted label correctly on pressing up button', async () => { + await findDropdownWrapper().trigger('keydown.down'); + await findDropdownWrapper().trigger('keydown.down'); + expect(findLabels().at(1).attributes('highlight')).toBe('true'); - it('renders gl-loading-icon component when `labelsFetchInProgress` prop is true', () => { - wrapper.vm.$store.dispatch('requestLabels'); + await findDropdownWrapper().trigger('keydown.up'); + expect(findLabels().at(0).attributes('highlight')).toBe('true'); + }); - return wrapper.vm.$nextTick(() => { - const loadingIconEl = findLoadingIcon(); + it('changes label selected state when Enter is pressed', async () => { + expect(findLabels().at(0).attributes('islabelset')).toBeUndefined(); + await findDropdownWrapper().trigger('keydown.down'); + await findDropdownWrapper().trigger('keydown.enter'); - expect(loadingIconEl.exists()).toBe(true); - expect(loadingIconEl.attributes('class')).toContain('labels-fetch-loading'); - }); + expect(findLabels().at(0).attributes('islabelset')).toBe('true'); }); - it('renders label search input element', () => { - const searchInputEl = wrapper.find(GlSearchBoxByType); + it('emits `closeDropdown event` when Esc button is pressed', () => { + findDropdownWrapper().trigger('keydown.esc'); - expect(searchInputEl.exists()).toBe(true); + expect(wrapper.emitted('closeDropdown')).toEqual([[selectedLabels]]); }); + }); - it('renders label elements for all labels', () => { - expect(wrapper.findAll(LabelItem)).toHaveLength(mockLabels.length); + it('when search returns 0 results', async () => { + createComponent({ + queryHandler: jest.fn().mockResolvedValue({ + data: { + workspace: { + labels: { + nodes: [], + }, + }, + }, + }), }); + findSearchInput().vm.$emit('input', '123'); + await waitForPromises(); + await nextTick(); - it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', () => { - wrapper.setData({ - currentHighlightItem: 0, - }); + expect(findNoResultsMessage().isVisible()).toBe(true); + }); - return wrapper.vm.$nextTick(() => { - const labelItemEl = findDropdownContent().find(LabelItem); + it('calls `createFlash` when fetching labels failed', async () => { + createComponent({ queryHandler: jest.fn().mockRejectedValue('Houston, we have a problem!') }); + jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS); + await waitForPromises(); + expect(createFlash).toHaveBeenCalled(); + }); - expect(labelItemEl.attributes('highlight')).toBe('true'); - }); - }); + it('does not render footer on standalone dropdown', () => { + createComponent({ injected: { variant: DropdownVariant.Standalone } }); - it('renders element containing "No matching results" when `searchKey` does not match with any label', () => { - wrapper.setData({ - searchKey: 'abc', - }); + expect(findDropdownFooter().exists()).toBe(false); + }); - return wrapper.vm.$nextTick(() => { - const noMatchEl = findDropdownContent().find('li'); + it('renders footer on sidebar dropdown', () => { + createComponent(); - expect(noMatchEl.isVisible()).toBe(true); - expect(noMatchEl.text()).toContain('No matching results'); - }); - }); + expect(findDropdownFooter().exists()).toBe(true); + }); - it('renders empty content while loading', () => { - wrapper.vm.$store.state.labelsFetchInProgress = true; + it('renders footer on embedded dropdown', () => { + createComponent({ injected: { variant: DropdownVariant.Embedded } }); - return wrapper.vm.$nextTick(() => { - const dropdownContent = findDropdownContent(); - const loadingIcon = findLoadingIcon(); + expect(findDropdownFooter().exists()).toBe(true); + }); - expect(dropdownContent.exists()).toBe(true); - expect(dropdownContent.isVisible()).toBe(true); - expect(loadingIcon.exists()).toBe(true); - expect(loadingIcon.isVisible()).toBe(true); - }); - }); + it('does not render create label button if `allowLabelCreate` is false', () => { + createComponent({ injected: { allowLabelCreate: false } }); - it('renders footer list items', () => { - const footerLinks = findDropdownFooter().findAll(GlLink); - const createLabelLink = footerLinks.at(0); - const manageLabelsLink = footerLinks.at(1); + expect(findCreateLabelButton().exists()).toBe(false); + }); - expect(createLabelLink.exists()).toBe(true); - expect(createLabelLink.text()).toBe('Create label'); - expect(manageLabelsLink.exists()).toBe(true); - expect(manageLabelsLink.text()).toBe('Manage labels'); + describe('when `allowLabelCreate` is true', () => { + beforeEach(() => { + createComponent(); }); - it('does not render "Create label" footer link when `state.allowLabelCreate` is `false`', () => { - wrapper.vm.$store.state.allowLabelCreate = false; - - return wrapper.vm.$nextTick(() => { - const createLabelLink = findDropdownFooter().findAll(GlLink).at(0); - - expect(createLabelLink.text()).not.toBe('Create label'); - }); + it('renders create label button', () => { + expect(findCreateLabelButton().exists()).toBe(true); }); - it('does not render footer list items when `state.variant` is "standalone"', () => { - createComponent({ ...mockConfig, variant: 'standalone' }); - expect(findDropdownFooter().exists()).toBe(false); - }); + it('emits `toggleDropdownContentsCreateView` event on create label button click', () => { + findCreateLabelButton().vm.$emit('click'); - it('renders footer list items when `state.variant` is "embedded"', () => { - expect(findDropdownFooter().exists()).toBe(true); + expect(wrapper.emitted('toggleDropdownContentsCreateView')).toEqual([[]]); }); }); }); |