diff options
Diffstat (limited to 'spec/frontend/notes/components')
5 files changed, 183 insertions, 39 deletions
diff --git a/spec/frontend/notes/components/discussion_counter_spec.js b/spec/frontend/notes/components/discussion_counter_spec.js index affd6c1d1d2..d82590c7e9e 100644 --- a/spec/frontend/notes/components/discussion_counter_spec.js +++ b/spec/frontend/notes/components/discussion_counter_spec.js @@ -1,6 +1,6 @@ import Vuex from 'vuex'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlIcon } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; import notesModule from '~/notes/stores/modules'; import DiscussionCounter from '~/notes/components/discussion_counter.vue'; import { noteableDataMock, discussionMock, notesDataMock, userDataMock } from '../mock_data'; @@ -9,6 +9,7 @@ import * as types from '~/notes/stores/mutation_types'; describe('DiscussionCounter component', () => { let store; let wrapper; + let setExpandDiscussionsFn; const localVue = createLocalVue(); localVue.use(Vuex); @@ -16,6 +17,7 @@ describe('DiscussionCounter component', () => { beforeEach(() => { window.mrTabs = {}; const { state, getters, mutations, actions } = notesModule(); + setExpandDiscussionsFn = jest.fn().mockImplementation(actions.setExpandDiscussions); store = new Vuex.Store({ state: { @@ -24,7 +26,10 @@ describe('DiscussionCounter component', () => { }, getters, mutations, - actions, + actions: { + ...actions, + setExpandDiscussions: setExpandDiscussionsFn, + }, }); store.dispatch('setNoteableData', { ...noteableDataMock, @@ -84,7 +89,7 @@ describe('DiscussionCounter component', () => { wrapper = shallowMount(DiscussionCounter, { store, localVue }); expect(wrapper.find(`.is-active`).exists()).toBe(isActive); - expect(wrapper.findAll('[role="group"').length).toBe(groupLength); + expect(wrapper.findAll(GlButton)).toHaveLength(groupLength); }); }); @@ -103,23 +108,22 @@ describe('DiscussionCounter component', () => { it('calls button handler when clicked', () => { updateStoreWithExpanded(true); - wrapper.setMethods({ handleExpandDiscussions: jest.fn() }); - toggleAllButton.trigger('click'); + toggleAllButton.vm.$emit('click'); - expect(wrapper.vm.handleExpandDiscussions).toHaveBeenCalledTimes(1); + expect(setExpandDiscussionsFn).toHaveBeenCalledTimes(1); }); it('collapses all discussions if expanded', () => { updateStoreWithExpanded(true); expect(wrapper.vm.allExpanded).toBe(true); - expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-up'); + expect(toggleAllButton.props('icon')).toBe('angle-up'); - toggleAllButton.trigger('click'); + toggleAllButton.vm.$emit('click'); return wrapper.vm.$nextTick().then(() => { expect(wrapper.vm.allExpanded).toBe(false); - expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-down'); + expect(toggleAllButton.props('icon')).toBe('angle-down'); }); }); @@ -127,13 +131,13 @@ describe('DiscussionCounter component', () => { updateStoreWithExpanded(false); expect(wrapper.vm.allExpanded).toBe(false); - expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-down'); + expect(toggleAllButton.props('icon')).toBe('angle-down'); - toggleAllButton.trigger('click'); + toggleAllButton.vm.$emit('click'); return wrapper.vm.$nextTick().then(() => { expect(wrapper.vm.allExpanded).toBe(true); - expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-up'); + expect(toggleAllButton.props('icon')).toBe('angle-up'); }); }); }); diff --git a/spec/frontend/notes/components/discussion_filter_spec.js b/spec/frontend/notes/components/discussion_filter_spec.js index 91ff796b9de..9f3655c53b9 100644 --- a/spec/frontend/notes/components/discussion_filter_spec.js +++ b/spec/frontend/notes/components/discussion_filter_spec.js @@ -25,6 +25,8 @@ describe('DiscussionFilter component', () => { const filterDiscussion = jest.fn(); + const findFilter = filterType => wrapper.find(`.dropdown-item[data-filter-type="${filterType}"]`); + const mountComponent = () => { const discussions = [ { @@ -74,22 +76,22 @@ describe('DiscussionFilter component', () => { }); it('renders the all filters', () => { - expect(wrapper.findAll('.dropdown-menu li').length).toBe(discussionFiltersMock.length); + expect(wrapper.findAll('.discussion-filter-container .dropdown-item').length).toBe( + discussionFiltersMock.length, + ); }); it('renders the default selected item', () => { expect( wrapper - .find('#discussion-filter-dropdown') + .find('#discussion-filter-dropdown .dropdown-item') .text() .trim(), ).toBe(discussionFiltersMock[0].title); }); it('updates to the selected item', () => { - const filterItem = wrapper.find( - `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"] button`, - ); + const filterItem = findFilter(DISCUSSION_FILTER_TYPES.ALL); filterItem.trigger('click'); @@ -97,37 +99,37 @@ describe('DiscussionFilter component', () => { }); it('only updates when selected filter changes', () => { - wrapper - .find(`.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] button`) - .trigger('click'); + findFilter(DISCUSSION_FILTER_TYPES.ALL).trigger('click'); expect(filterDiscussion).not.toHaveBeenCalled(); }); + it('disables timeline view if it was enabled', () => { + store.state.isTimelineEnabled = true; + + findFilter(DISCUSSION_FILTER_TYPES.HISTORY).trigger('click'); + + expect(wrapper.vm.$store.state.isTimelineEnabled).toBe(false); + }); + it('disables commenting when "Show history only" filter is applied', () => { - const filterItem = wrapper.find( - `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"] button`, - ); - filterItem.trigger('click'); + findFilter(DISCUSSION_FILTER_TYPES.HISTORY).trigger('click'); expect(wrapper.vm.$store.state.commentsDisabled).toBe(true); }); it('enables commenting when "Show history only" filter is not applied', () => { - const filterItem = wrapper.find( - `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] button`, - ); - filterItem.trigger('click'); + findFilter(DISCUSSION_FILTER_TYPES.ALL).trigger('click'); expect(wrapper.vm.$store.state.commentsDisabled).toBe(false); }); it('renders a dropdown divider for the default filter', () => { const defaultFilter = wrapper.findAll( - `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] > *`, + `.discussion-filter-container .dropdown-item-wrapper > *`, ); - expect(defaultFilter.at(defaultFilter.length - 1).classes('dropdown-divider')).toBe(true); + expect(defaultFilter.at(1).classes('gl-new-dropdown-divider')).toBe(true); }); describe('Merge request tabs', () => { diff --git a/spec/frontend/notes/components/notes_app_spec.js b/spec/frontend/notes/components/notes_app_spec.js index c6034639a4a..e905a12919e 100644 --- a/spec/frontend/notes/components/notes_app_spec.js +++ b/spec/frontend/notes/components/notes_app_spec.js @@ -174,6 +174,23 @@ describe('note_app', () => { }); }); + describe('timeline view', () => { + beforeEach(() => { + setFixtures('<div class="js-discussions-count"></div>'); + + axiosMock.onAny().reply(mockData.getIndividualNoteResponse); + store.state.commentsDisabled = false; + store.state.isTimelineEnabled = true; + + wrapper = mountComponent(); + return waitForDiscussionsRequest(); + }); + + it('should not render comments form', () => { + expect(wrapper.find('.js-main-target-form').exists()).toBe(false); + }); + }); + describe('while fetching data', () => { beforeEach(() => { setFixtures('<div class="js-discussions-count"></div>'); diff --git a/spec/frontend/notes/components/sort_discussion_spec.js b/spec/frontend/notes/components/sort_discussion_spec.js index 575f1057db2..739e247735d 100644 --- a/spec/frontend/notes/components/sort_discussion_spec.js +++ b/spec/frontend/notes/components/sort_discussion_spec.js @@ -46,7 +46,7 @@ describe('Sort Discussion component', () => { it('calls setDiscussionSortDirection when update is emitted', () => { findLocalStorageSync().vm.$emit('input', ASC); - expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC); + expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { direction: ASC }); }); }); @@ -55,9 +55,11 @@ describe('Sort Discussion component', () => { it('calls the right actions', () => { createComponent(); - wrapper.find('.js-newest-first').trigger('click'); + wrapper.find('.js-newest-first').vm.$emit('click'); - expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', DESC); + expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { + direction: DESC, + }); expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', { property: DESC, }); @@ -67,7 +69,7 @@ describe('Sort Discussion component', () => { it('shows the "Oldest First" as the dropdown', () => { createComponent(); - expect(wrapper.find('.js-dropdown-text').text()).toBe('Oldest first'); + expect(wrapper.find('.js-dropdown-text').props('text')).toBe('Oldest first'); }); }); @@ -79,21 +81,23 @@ describe('Sort Discussion component', () => { describe('when the dropdown item is clicked', () => { it('calls the right actions', () => { - wrapper.find('.js-oldest-first').trigger('click'); + wrapper.find('.js-oldest-first').vm.$emit('click'); - expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC); + expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { + direction: ASC, + }); expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', { property: ASC, }); }); - it('applies the active class to the correct button in the dropdown', () => { - expect(wrapper.find('.js-newest-first').classes()).toContain('is-active'); + it('sets is-checked to true on the active button in the dropdown', () => { + expect(wrapper.find('.js-newest-first').props('isChecked')).toBe(true); }); }); it('shows the "Newest First" as the dropdown', () => { - expect(wrapper.find('.js-dropdown-text').text()).toBe('Newest first'); + expect(wrapper.find('.js-dropdown-text').props('text')).toBe('Newest first'); }); }); }); diff --git a/spec/frontend/notes/components/timeline_toggle_spec.js b/spec/frontend/notes/components/timeline_toggle_spec.js new file mode 100644 index 00000000000..b8df6fc7996 --- /dev/null +++ b/spec/frontend/notes/components/timeline_toggle_spec.js @@ -0,0 +1,117 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; +import Vuex from 'vuex'; +import TimelineToggle, { + timelineEnabledTooltip, + timelineDisabledTooltip, +} from '~/notes/components/timeline_toggle.vue'; +import createStore from '~/notes/stores'; +import { ASC, DESC } from '~/notes/constants'; +import { trackToggleTimelineView } from '~/notes/utils'; +import Tracking from '~/tracking'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('Timeline toggle', () => { + let wrapper; + let store; + const mockEvent = { currentTarget: { blur: jest.fn() } }; + + const createComponent = () => { + jest.spyOn(store, 'dispatch').mockImplementation(); + jest.spyOn(Tracking, 'event').mockImplementation(); + + wrapper = shallowMount(TimelineToggle, { + localVue, + store, + }); + }; + + const findGlButton = () => wrapper.find(GlButton); + + beforeEach(() => { + store = createStore(); + createComponent(); + }); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + store.dispatch.mockReset(); + mockEvent.currentTarget.blur.mockReset(); + Tracking.event.mockReset(); + }); + + describe('ON state', () => { + it('should update timeline flag in the store', () => { + store.state.isTimelineEnabled = false; + findGlButton().vm.$emit('click', mockEvent); + expect(store.dispatch).toHaveBeenCalledWith('setTimelineView', true); + }); + + it('should set sort direction to DESC if not set', () => { + store.state.isTimelineEnabled = true; + store.state.sortDirection = ASC; + findGlButton().vm.$emit('click', mockEvent); + expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { + direction: DESC, + persist: false, + }); + }); + + it('should set correct UI state', async () => { + store.state.isTimelineEnabled = true; + findGlButton().vm.$emit('click', mockEvent); + await wrapper.vm.$nextTick(); + expect(findGlButton().attributes('title')).toBe(timelineEnabledTooltip); + expect(findGlButton().attributes('selected')).toBe('true'); + expect(mockEvent.currentTarget.blur).toHaveBeenCalled(); + }); + + it('should track Snowplow event', async () => { + store.state.isTimelineEnabled = true; + await wrapper.vm.$nextTick(); + + findGlButton().trigger('click'); + + const { category, action, label, property, value } = trackToggleTimelineView(true); + expect(Tracking.event).toHaveBeenCalledWith(category, action, { label, property, value }); + }); + }); + + describe('OFF state', () => { + it('should update timeline flag in the store', () => { + store.state.isTimelineEnabled = true; + findGlButton().vm.$emit('click', mockEvent); + expect(store.dispatch).toHaveBeenCalledWith('setTimelineView', false); + }); + + it('should NOT update sort direction', () => { + store.state.isTimelineEnabled = false; + findGlButton().vm.$emit('click', mockEvent); + expect(store.dispatch).not.toHaveBeenCalledWith(); + }); + + it('should set correct UI state', async () => { + store.state.isTimelineEnabled = false; + findGlButton().vm.$emit('click', mockEvent); + await wrapper.vm.$nextTick(); + expect(findGlButton().attributes('title')).toBe(timelineDisabledTooltip); + expect(findGlButton().attributes('selected')).toBe(undefined); + expect(mockEvent.currentTarget.blur).toHaveBeenCalled(); + }); + + it('should track Snowplow event', async () => { + store.state.isTimelineEnabled = false; + await wrapper.vm.$nextTick(); + + findGlButton().trigger('click'); + + const { category, action, label, property, value } = trackToggleTimelineView(false); + expect(Tracking.event).toHaveBeenCalledWith(category, action, { label, property, value }); + }); + }); +}); |