From 4720346c2e10e1ff62a20b39dfc9866eb88858e6 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 24 Apr 2023 12:08:36 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../dropdown_contents_labels_view_spec.js | 292 +++++++++------------ 1 file changed, 129 insertions(+), 163 deletions(-) (limited to 'spec/frontend/sidebar') 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 index 7940518f1e8..1fa613d15d4 100644 --- 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 @@ -5,27 +5,31 @@ import { 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 { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; 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 { stubComponent } from 'helpers/stub_component'; 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'; +import { mockConfig, mockLabels } from './mock_data'; Vue.use(Vuex); describe('DropdownContentsLabelsView', () => { let wrapper; + let store; + + const focusInputMock = jest.fn(); + const updateSelectedLabelsMock = jest.fn(); + const toggleDropdownContentsMock = jest.fn(); - const createComponent = (initialState = mockConfig) => { - const store = new Vuex.Store({ + const createComponent = (initialState = mockConfig, mountFn = shallowMountExtended) => { + store = new Vuex.Store({ getters, mutations, state: { @@ -36,14 +40,20 @@ describe('DropdownContentsLabelsView', () => { actions: { ...actions, fetchLabels: jest.fn(), + updateSelectedLabels: updateSelectedLabelsMock, + toggleDropdownContents: toggleDropdownContentsMock, }, }); store.dispatch('setInitialState', initialState); - store.dispatch('receiveLabelsSuccess', mockLabels); - wrapper = shallowMount(DropdownContentsLabelsView, { + wrapper = mountFn(DropdownContentsLabelsView, { store, + stubs: { + GlSearchBoxByType: stubComponent(GlSearchBoxByType, { + methods: { focusInput: focusInputMock }, + }), + }, }); }; @@ -51,44 +61,54 @@ describe('DropdownContentsLabelsView', () => { createComponent(); }); - 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 findDropdownContent = () => wrapper.findByTestId('dropdown-content'); + const findDropdownTitle = () => wrapper.findByTestId('dropdown-title'); + const findDropdownFooter = () => wrapper.findByTestId('dropdown-footer'); + const findNoMatchingResults = () => wrapper.findByTestId('no-matching-results'); + const findCreateLabelLink = () => wrapper.findByTestId('create-label-link'); + const findLabelsList = () => wrapper.findByTestId('labels-list'); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType); + const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver); + const findLabelItems = () => wrapper.findAllComponents(LabelItem); + + const setCurrentHighlightItem = (value) => { + let initialValue = -1; + + while (initialValue < value) { + findLabelsList().trigger('keydown.down'); + initialValue += 1; + } + }; 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'); + beforeEach(() => { + createComponent(undefined, mountExtended); + store.dispatch('receiveLabelsSuccess', mockLabels); }); - 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', - }); + it('returns matching labels filtered with `searchKey`', async () => { + await findSearchBoxByType().vm.$emit('input', 'bug'); - expect(wrapper.vm.visibleLabels.length).toBe(2); - expect(wrapper.vm.visibleLabels[0].title).toBe('Bug'); - expect(wrapper.vm.visibleLabels[1].title).toBe('Boog'); + const labelItems = findLabelItems(); + expect(labelItems).toHaveLength(1); + expect(labelItems.at(0).text()).toBe('Bug'); }); - 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: '', - }); + it('returns matching labels with fuzzy filtering', async () => { + await findSearchBoxByType().vm.$emit('input', 'bg'); - expect(wrapper.vm.visibleLabels.length).toBe(mockLabels.length); + const labelItems = findLabelItems(); + expect(labelItems).toHaveLength(2); + expect(labelItems.at(0).text()).toBe('Bug'); + expect(labelItems.at(1).text()).toBe('Boog'); + }); + + it('returns all labels when `searchKey` is empty', async () => { + await findSearchBoxByType().vm.$emit('input', ''); + + expect(findLabelItems()).toHaveLength(mockLabels.length); }); }); @@ -102,17 +122,11 @@ describe('DropdownContentsLabelsView', () => { `( '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); + store.dispatch('receiveLabelsSuccess', labels); - await nextTick(); + await findSearchBoxByType().vm.$emit('input', searchKey); - expect(wrapper.vm.showNoMatchingResultsMessage).toBe(returnValue); + expect(findNoMatchingResults().isVisible()).toBe(returnValue); }, ); }); @@ -121,171 +135,134 @@ describe('DropdownContentsLabelsView', () => { 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(); + findIntersectionObserver().vm.$emit('appear'); - await wrapper.vm.handleComponentAppear(); + await nextTick(); - expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled(); + expect(focusInputMock).toHaveBeenCalled(); }); }); describe('handleComponentDisappear', () => { - it('calls action `receiveLabelsSuccess` with empty array', () => { - jest.spyOn(wrapper.vm, 'receiveLabelsSuccess'); + it('calls action `receiveLabelsSuccess` with empty array', async () => { + jest.spyOn(store, 'dispatch'); - wrapper.vm.handleComponentDisappear(); + await findIntersectionObserver().vm.$emit('disappear'); - expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]); + expect(store.dispatch).toHaveBeenCalledWith(expect.anything(), []); }); }); describe('handleCreateLabelClick', () => { - it('calls actions `receiveLabelsSuccess` with empty array and `toggleDropdownContentsCreateView`', () => { - jest.spyOn(wrapper.vm, 'receiveLabelsSuccess'); - jest.spyOn(wrapper.vm, 'toggleDropdownContentsCreateView'); + it('calls actions `receiveLabelsSuccess` with empty array and `toggleDropdownContentsCreateView`', async () => { + jest.spyOn(store, 'dispatch'); - wrapper.vm.handleCreateLabelClick(); + await findCreateLabelLink().vm.$emit('click'); - expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]); - expect(wrapper.vm.toggleDropdownContentsCreateView).toHaveBeenCalled(); + expect(store.dispatch).toHaveBeenCalledWith('receiveLabelsSuccess', []); + expect(store.dispatch).toHaveBeenCalledWith('toggleDropdownContentsCreateView'); }); }); 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); + beforeEach(() => { + createComponent(undefined, mountExtended); + store.dispatch('receiveLabelsSuccess', mockLabels); }); - 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, - }); + it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', async () => { + await setCurrentHighlightItem(1); + + expect(findLabelItems().at(1).classes()).toContain('is-focused'); - wrapper.vm.handleKeyDown({ - keyCode: DOWN_KEY_CODE, - }); + await findLabelsList().trigger('keydown.up'); - expect(wrapper.vm.currentHighlightItem).toBe(2); + expect(findLabelItems().at(1).classes()).not.toContain('is-focused'); }); - 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', - }); + it('increases `currentHighlightItem` value by 1 when Down arrow key is pressed', async () => { + expect(findLabelItems().at(0).classes()).not.toContain('is-focused'); - wrapper.vm.handleKeyDown({ - keyCode: ENTER_KEY_CODE, - preventDefault: fakePreventDefault, - }); + await findLabelsList().trigger('keydown.down'); - expect(wrapper.vm.searchKey).toBe(''); - expect(fakePreventDefault).toHaveBeenCalled(); + expect(findLabelItems().at(0).classes()).toContain('is-focused'); }); - 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, - }); + it('resets the search text when the Enter key is pressed', async () => { + await setCurrentHighlightItem(1); + await findSearchBoxByType().vm.$emit('input', 'bug'); + await findLabelsList().trigger('keydown.enter', { preventDefault: fakePreventDefault }); - expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockLabels[2]]); + expect(findSearchBoxByType().props('value')).toBe(''); + expect(fakePreventDefault).toHaveBeenCalled(); }); - 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, - }); + it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', async () => { + await setCurrentHighlightItem(2); + await findLabelsList().trigger('keydown.enter', { preventDefault: fakePreventDefault }); + + expect(updateSelectedLabelsMock).toHaveBeenCalledWith(expect.anything(), [mockLabels[2]]); + }); - wrapper.vm.handleKeyDown({ - keyCode: ESC_KEY_CODE, - }); + it('calls action `toggleDropdownContents` when Esc key is pressed', async () => { + await setCurrentHighlightItem(1); + await findLabelsList().trigger('keydown.esc'); - expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled(); + expect(toggleDropdownContentsMock).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, - }); + it('calls action `scrollIntoViewIfNeeded` in next tick when esc key is pressed', async () => { + const containerTop = 500; + const labelTop = 0; - wrapper.vm.handleKeyDown({ - keyCode: DOWN_KEY_CODE, - }); + jest + .spyOn(findDropdownContent().element, 'getBoundingClientRect') + .mockReturnValueOnce({ top: containerTop }); - await nextTick(); - expect(wrapper.vm.scrollIntoViewIfNeeded).toHaveBeenCalled(); + await setCurrentHighlightItem(1); + await findLabelsList().trigger('keydown.esc'); + + expect(findDropdownContent().element.scrollTop).toBe(labelTop - containerTop); }); }); describe('handleLabelClick', () => { beforeEach(() => { - jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation(); + createComponent(undefined, mountExtended); + store.dispatch('receiveLabelsSuccess', mockLabels); }); it('calls action `updateSelectedLabels` with provided `label` param', () => { - wrapper.vm.handleLabelClick(mockRegularLabel); + findLabelItems().at(0).findComponent(GlLink).vm.$emit('click'); - expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockRegularLabel]); + expect(updateSelectedLabelsMock).toHaveBeenCalledWith(expect.anything(), [ + { ...mockLabels[0], indeterminate: expect.anything(), set: expect.anything() }, + ]); }); it('calls action `toggleDropdownContents` when `state.allowMultiselect` is false', () => { - jest.spyOn(wrapper.vm, 'toggleDropdownContents'); - wrapper.vm.$store.state.allowMultiselect = false; + store.state.allowMultiselect = false; - wrapper.vm.handleLabelClick(mockRegularLabel); + findLabelItems().at(0).findComponent(GlLink).vm.$emit('click'); - expect(wrapper.vm.toggleDropdownContents).toHaveBeenCalled(); + expect(toggleDropdownContentsMock).toHaveBeenCalled(); }); }); }); describe('template', () => { + beforeEach(() => { + store.dispatch('receiveLabelsSuccess', mockLabels); + }); + 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'); + store.dispatch('requestLabels'); await nextTick(); const loadingIconEl = findLoadingIcon(); @@ -325,30 +302,19 @@ describe('DropdownContentsLabelsView', () => { }); it('renders label elements for all labels', () => { - expect(wrapper.findAllComponents(LabelItem)).toHaveLength(mockLabels.length); + expect(findLabelItems()).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 setCurrentHighlightItem(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(); + await findSearchBoxByType().vm.$emit('input', 'abc'); const noMatchEl = findDropdownContent().find('li'); expect(noMatchEl.isVisible()).toBe(true); @@ -356,7 +322,7 @@ describe('DropdownContentsLabelsView', () => { }); it('renders empty content while loading', async () => { - wrapper.vm.$store.state.labelsFetchInProgress = true; + store.state.labelsFetchInProgress = true; await nextTick(); const dropdownContent = findDropdownContent(); @@ -380,7 +346,7 @@ describe('DropdownContentsLabelsView', () => { }); it('does not render "Create label" footer link when `state.allowLabelCreate` is `false`', async () => { - wrapper.vm.$store.state.allowLabelCreate = false; + store.state.allowLabelCreate = false; await nextTick(); const createLabelLink = findDropdownFooter().findAllComponents(GlLink).at(0); -- cgit v1.2.3