diff options
Diffstat (limited to 'spec/frontend/vue_shared/components/sidebar')
9 files changed, 211 insertions, 77 deletions
diff --git a/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js b/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js index b99b1a66b79..3980033862e 100644 --- a/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/copyable_field_spec.js @@ -1,4 +1,4 @@ -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlLoadingIcon, GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue'; @@ -14,6 +14,9 @@ describe('SidebarCopyableField', () => { const createComponent = (propsData = defaultProps) => { wrapper = shallowMount(CopyableField, { propsData, + stubs: { + GlSprintf, + }, }); }; diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js index 60903933505..06ea88c09a0 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js @@ -54,7 +54,6 @@ describe('DropdownContentsLabelsView', () => { afterEach(() => { wrapper.destroy(); - wrapper = null; }); const findDropdownContent = () => wrapper.find('[data-testid="dropdown-content"]'); @@ -381,6 +380,15 @@ describe('DropdownContentsLabelsView', () => { 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); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js index 3f11095cb04..46ade5d5857 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js @@ -1,11 +1,14 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; +import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; import * as actions from '~/vue_shared/components/sidebar/labels_select_vue/store/actions'; import * as types from '~/vue_shared/components/sidebar/labels_select_vue/store/mutation_types'; import defaultState from '~/vue_shared/components/sidebar/labels_select_vue/store/state'; +jest.mock('~/flash'); + describe('LabelsSelect Actions', () => { let state; const mockInitialState = { @@ -91,10 +94,6 @@ describe('LabelsSelect Actions', () => { }); describe('receiveLabelsFailure', () => { - beforeEach(() => { - setFixtures('<div class="flash-container"></div>'); - }); - it('sets value `state.labelsFetchInProgress` to `false`', (done) => { testAction( actions.receiveLabelsFailure, @@ -109,9 +108,7 @@ describe('LabelsSelect Actions', () => { it('shows flash error', () => { actions.receiveLabelsFailure({ commit: () => {} }); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - 'Error fetching labels.', - ); + expect(createFlash).toHaveBeenCalledWith({ message: 'Error fetching labels.' }); }); }); @@ -186,10 +183,6 @@ describe('LabelsSelect Actions', () => { }); describe('receiveCreateLabelFailure', () => { - beforeEach(() => { - setFixtures('<div class="flash-container"></div>'); - }); - it('sets value `state.labelCreateInProgress` to `false`', (done) => { testAction( actions.receiveCreateLabelFailure, @@ -204,9 +197,7 @@ describe('LabelsSelect Actions', () => { it('shows flash error', () => { actions.receiveCreateLabelFailure({ commit: () => {} }); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - 'Error creating label.', - ); + expect(createFlash).toHaveBeenCalledWith({ message: 'Error creating label.' }); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js index ab266ac8aed..1d2a9c34599 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js @@ -153,7 +153,16 @@ describe('LabelsSelect Mutations', () => { }); describe(`${types.UPDATE_SELECTED_LABELS}`, () => { - const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; + let labels; + + beforeEach(() => { + labels = [ + { id: 1, title: 'scoped::test', set: true }, + { id: 2, set: false, title: 'scoped::one' }, + { id: 3, title: '' }, + { id: 4, title: '' }, + ]; + }); it('updates `state.labels` to include `touched` and `set` props based on provided `labels` param', () => { const updatedLabelIds = [2]; @@ -169,5 +178,23 @@ describe('LabelsSelect Mutations', () => { } }); }); + + describe('when label is scoped', () => { + it('unsets the currently selected scoped label and sets the current label', () => { + const state = { + labels, + }; + mutations[types.UPDATE_SELECTED_LABELS](state, { + labels: [{ id: 2, title: 'scoped::one' }], + }); + + expect(state.labels).toEqual([ + { id: 1, title: 'scoped::test', set: false }, + { id: 2, set: true, title: 'scoped::one', touched: true }, + { id: 3, title: '' }, + { id: 4, title: '' }, + ]); + }); + }); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js index 59f3268c000..b3ffee2d020 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js @@ -1,88 +1,97 @@ import { GlLabel } from '@gitlab/ui'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import Vuex from 'vuex'; +import { shallowMount } from '@vue/test-utils'; import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue'; -import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store'; - -import { mockConfig, mockRegularLabel, mockScopedLabel } from './mock_data'; - -const localVue = createLocalVue(); -localVue.use(Vuex); +import { mockRegularLabel, mockScopedLabel } from './mock_data'; describe('DropdownValue', () => { let wrapper; - const createComponent = (initialState = {}, slots = {}) => { - const store = new Vuex.Store(labelsSelectModule()); - - store.dispatch('setInitialState', { ...mockConfig, ...initialState }); + const findAllLabels = () => wrapper.findAllComponents(GlLabel); + const findRegularLabel = () => findAllLabels().at(0); + const findScopedLabel = () => findAllLabels().at(1); + const findWrapper = () => wrapper.find('[data-testid="value-wrapper"]'); + const findEmptyPlaceholder = () => wrapper.find('[data-testid="empty-placeholder"]'); + const createComponent = (props = {}, slots = {}) => { wrapper = shallowMount(DropdownValue, { - localVue, - store, slots, + propsData: { + selectedLabels: [mockRegularLabel, mockScopedLabel], + allowLabelRemove: true, + allowScopedLabels: true, + labelsFilterBasePath: '/gitlab-org/my-project/issues', + labelsFilterParam: 'label_name', + ...props, + }, }); }; afterEach(() => { wrapper.destroy(); - wrapper = null; }); - describe('methods', () => { - describe('labelFilterUrl', () => { - it('returns a label filter URL based on provided label param', () => { - createComponent(); - - expect(wrapper.vm.labelFilterUrl(mockRegularLabel)).toBe( - '/gitlab-org/my-project/issues?label_name[]=Foo%20Label', - ); - }); + describe('when there are no labels', () => { + beforeEach(() => { + createComponent( + { + selectedLabels: [], + }, + { + default: 'None', + }, + ); }); - describe('scopedLabel', () => { - beforeEach(() => { - createComponent(); - }); + it('does not apply `has-labels` class to the wrapping container', () => { + expect(findWrapper().classes()).not.toContain('has-labels'); + }); - it('returns `true` when provided label param is a scoped label', () => { - expect(wrapper.vm.scopedLabel(mockScopedLabel)).toBe(true); - }); + it('renders an empty placeholder', () => { + expect(findEmptyPlaceholder().exists()).toBe(true); + expect(findEmptyPlaceholder().text()).toBe('None'); + }); - it('returns `false` when provided label param is a regular label', () => { - expect(wrapper.vm.scopedLabel(mockRegularLabel)).toBe(false); - }); + it('does not render any labels', () => { + expect(findAllLabels().length).toBe(0); }); }); - describe('template', () => { - it('renders class `has-labels` on component container element when `selectedLabels` is not empty', () => { + describe('when there are labels', () => { + beforeEach(() => { createComponent(); + }); - expect(wrapper.attributes('class')).toContain('has-labels'); + it('applies `has-labels` class to the wrapping container', () => { + expect(findWrapper().classes()).toContain('has-labels'); }); - it('renders element containing `None` when `selectedLabels` is empty', () => { - createComponent( - { - selectedLabels: [], - }, - { - default: 'None', - }, - ); - const noneEl = wrapper.find('span.text-secondary'); + it('does not render an empty placeholder', () => { + expect(findEmptyPlaceholder().exists()).toBe(false); + }); - expect(noneEl.exists()).toBe(true); - expect(noneEl.text()).toBe('None'); + it('renders a list of two labels', () => { + expect(findAllLabels().length).toBe(2); }); - it('renders labels when `selectedLabels` is not empty', () => { - createComponent(); + it('passes correct props to the regular label', () => { + expect(findRegularLabel().props('target')).toBe( + '/gitlab-org/my-project/issues?label_name[]=Foo%20Label', + ); + expect(findRegularLabel().props('scoped')).toBe(false); + }); + + it('passes correct props to the scoped label', () => { + expect(findScopedLabel().props('target')).toBe( + '/gitlab-org/my-project/issues?label_name[]=Foo%3A%3ABar', + ); + expect(findScopedLabel().props('scoped')).toBe(true); + }); - expect(wrapper.findAll(GlLabel).length).toBe(2); + it('emits `onLabelRemove` event with the correct ID', () => { + findRegularLabel().vm.$emit('close'); + expect(wrapper.emitted('onLabelRemove')).toEqual([[mockRegularLabel.id]]); }); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js index ee1346c362f..66971446f47 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js @@ -34,6 +34,10 @@ describe('LabelsSelectRoot', () => { stubs: { 'dropdown-contents': DropdownContents, }, + provide: { + iid: '1', + projectPath: 'test', + }, }); }; diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js index 7ef4b769b6b..27de7de2411 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js @@ -1,11 +1,14 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; +import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; import * as actions from '~/vue_shared/components/sidebar/labels_select_widget/store/actions'; import * as types from '~/vue_shared/components/sidebar/labels_select_widget/store/mutation_types'; import defaultState from '~/vue_shared/components/sidebar/labels_select_widget/store/state'; +jest.mock('~/flash'); + describe('LabelsSelect Actions', () => { let state; const mockInitialState = { @@ -91,10 +94,6 @@ describe('LabelsSelect Actions', () => { }); describe('receiveLabelsFailure', () => { - beforeEach(() => { - setFixtures('<div class="flash-container"></div>'); - }); - it('sets value `state.labelsFetchInProgress` to `false`', (done) => { testAction( actions.receiveLabelsFailure, @@ -109,9 +108,7 @@ describe('LabelsSelect Actions', () => { it('shows flash error', () => { actions.receiveLabelsFailure({ commit: () => {} }); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - 'Error fetching labels.', - ); + expect(createFlash).toHaveBeenCalledWith({ message: 'Error fetching labels.' }); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js index acb275b5d90..9e965cb33e8 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js @@ -120,7 +120,16 @@ describe('LabelsSelect Mutations', () => { }); describe(`${types.UPDATE_SELECTED_LABELS}`, () => { - const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; + let labels; + + beforeEach(() => { + labels = [ + { id: 1, title: 'scoped::test', set: true }, + { id: 2, set: false, title: 'scoped::one' }, + { id: 3, title: '' }, + { id: 4, title: '' }, + ]; + }); it('updates `state.labels` to include `touched` and `set` props based on provided `labels` param', () => { const updatedLabelIds = [2]; @@ -136,5 +145,23 @@ describe('LabelsSelect Mutations', () => { } }); }); + + describe('when label is scoped', () => { + it('unsets the currently selected scoped label and sets the current label', () => { + const state = { + labels, + }; + mutations[types.UPDATE_SELECTED_LABELS](state, { + labels: [{ id: 2, title: 'scoped::one' }], + }); + + expect(state.labels).toEqual([ + { id: 1, title: 'scoped::test', set: false }, + { id: 2, set: true, title: 'scoped::one', touched: true }, + { id: 3, title: '' }, + { id: 4, title: '' }, + ]); + }); + }); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js b/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js new file mode 100644 index 00000000000..de3e1ccfb03 --- /dev/null +++ b/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js @@ -0,0 +1,68 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMount, mount } from '@vue/test-utils'; +import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue'; + +describe('Todo Button', () => { + let wrapper; + let dispatchEventSpy; + + const createComponent = (props = {}, mountFn = shallowMount) => { + wrapper = mountFn(TodoButton, { + propsData: { + ...props, + }, + }); + }; + + beforeEach(() => { + dispatchEventSpy = jest.spyOn(document, 'dispatchEvent'); + jest.spyOn(document, 'querySelector').mockReturnValue({ + innerText: 2, + }); + }); + + afterEach(() => { + wrapper.destroy(); + dispatchEventSpy = null; + jest.clearAllMocks(); + }); + + it('renders GlButton', () => { + createComponent(); + + expect(wrapper.find(GlButton).exists()).toBe(true); + }); + + it('emits click event when clicked', () => { + createComponent({}, mount); + wrapper.find(GlButton).trigger('click'); + + expect(wrapper.emitted().click).toBeTruthy(); + }); + + it('calls dispatchDocumentEvent to update global To-Do counter correctly', () => { + createComponent({}, mount); + wrapper.find(GlButton).trigger('click'); + const dispatchedEvent = dispatchEventSpy.mock.calls[0][0]; + + expect(dispatchEventSpy).toHaveBeenCalledTimes(1); + expect(dispatchedEvent.detail).toEqual({ count: 1 }); + expect(dispatchedEvent.type).toBe('todo:toggle'); + }); + + it.each` + label | isTodo + ${'Mark as done'} | ${true} + ${'Add a to do'} | ${false} + `('sets correct label when isTodo is $isTodo', ({ label, isTodo }) => { + createComponent({ isTodo }); + + expect(wrapper.find(GlButton).text()).toBe(label); + }); + + it('binds additional props to GlButton', () => { + createComponent({ loading: true }); + + expect(wrapper.find(GlButton).props('loading')).toBe(true); + }); +}); |