summaryrefslogtreecommitdiff
path: root/spec/frontend/boards
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/boards')
-rw-r--r--spec/frontend/boards/board_card_inner_spec.js62
-rw-r--r--spec/frontend/boards/board_list_helper.js9
-rw-r--r--spec/frontend/boards/board_list_spec.js25
-rw-r--r--spec/frontend/boards/components/board_add_new_column_trigger_spec.js4
-rw-r--r--spec/frontend/boards/components/board_blocked_icon_spec.js4
-rw-r--r--spec/frontend/boards/components/board_card_spec.js6
-rw-r--r--spec/frontend/boards/components/board_filtered_search_spec.js4
-rw-r--r--spec/frontend/boards/components/board_form_spec.js2
-rw-r--r--spec/frontend/boards/components/board_list_header_spec.js8
-rw-r--r--spec/frontend/boards/components/board_new_issue_spec.js49
-rw-r--r--spec/frontend/boards/components/board_new_item_spec.js37
-rw-r--r--spec/frontend/boards/components/board_settings_sidebar_spec.js28
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js195
-rw-r--r--spec/frontend/boards/components/sidebar/board_editable_item_spec.js20
-rw-r--r--spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js11
-rw-r--r--spec/frontend/boards/mock_data.js80
-rw-r--r--spec/frontend/boards/project_select_spec.js4
-rw-r--r--spec/frontend/boards/stores/actions_spec.js4
18 files changed, 332 insertions, 220 deletions
diff --git a/spec/frontend/boards/board_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js
index e0446811f64..677978d31ca 100644
--- a/spec/frontend/boards/board_card_inner_spec.js
+++ b/spec/frontend/boards/board_card_inner_spec.js
@@ -1,15 +1,13 @@
import { GlLabel, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
import { range } from 'lodash';
import Vuex from 'vuex';
-import setWindowLocation from 'helpers/set_window_location_helper';
+import { nextTick } from 'vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardBlockedIcon from '~/boards/components/board_blocked_icon.vue';
import BoardCardInner from '~/boards/components/board_card_inner.vue';
import { issuableTypes } from '~/boards/constants';
-import eventHub from '~/boards/eventhub';
import defaultStore from '~/boards/stores';
-import { updateHistory } from '~/lib/utils/url_utility';
import { mockLabelList, mockIssue, mockIssueFullPath } from './mock_data';
jest.mock('~/lib/utils/url_utility');
@@ -53,6 +51,7 @@ describe('Board card component', () => {
state: {
...defaultStore.state,
issuableType: issuableTypes.issue,
+ isShowingLabels: true,
},
getters: {
isGroupBoard: () => true,
@@ -261,7 +260,7 @@ describe('Board card component', () => {
],
});
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.board-card-assignee img').attributes('src')).toBe(
'test_image_from_avatar_url?width=24',
@@ -376,7 +375,7 @@ describe('Board card component', () => {
},
});
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.board-card-assignee .avatar-counter').text().trim()).toEqual('99+');
});
@@ -399,58 +398,17 @@ describe('Board card component', () => {
it('does not render label if label does not have an ID', async () => {
wrapper.setProps({ item: { ...issue, labels: [label1, { title: 'closed' }] } });
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.findAll(GlLabel).length).toBe(1);
expect(wrapper.text()).not.toContain('closed');
});
- });
-
- describe('filterByLabel method', () => {
- beforeEach(() => {
- wrapper.setProps({
- updateFilters: true,
- });
- });
-
- describe('when selected label is not in the filter', () => {
- beforeEach(() => {
- jest.spyOn(wrapper.vm, 'performSearch').mockImplementation(() => {});
- setWindowLocation('?');
- wrapper.vm.filterByLabel(label1);
- });
-
- it('calls updateHistory', () => {
- expect(updateHistory).toHaveBeenCalledTimes(1);
- });
-
- it('dispatches performSearch vuex action', () => {
- expect(wrapper.vm.performSearch).toHaveBeenCalledTimes(1);
- });
-
- it('emits updateTokens event', () => {
- expect(eventHub.$emit).toHaveBeenCalledTimes(1);
- expect(eventHub.$emit).toHaveBeenCalledWith('updateTokens');
- });
- });
-
- describe('when selected label is already in the filter', () => {
- beforeEach(() => {
- jest.spyOn(wrapper.vm, 'performSearch').mockImplementation(() => {});
- setWindowLocation('?label_name[]=testing%20123');
- wrapper.vm.filterByLabel(label1);
- });
-
- it('does not call updateHistory', () => {
- expect(updateHistory).not.toHaveBeenCalled();
- });
- it('does not dispatch performSearch vuex action', () => {
- expect(wrapper.vm.performSearch).not.toHaveBeenCalled();
- });
-
- it('does not emit updateTokens event', () => {
- expect(eventHub.$emit).not.toHaveBeenCalled();
+ describe('when label params arent set', () => {
+ it('passes the right target to GlLabel', () => {
+ expect(wrapper.findAll(GlLabel).at(0).props('target')).toEqual(
+ '?label_name[]=testing%20123',
+ );
});
});
});
diff --git a/spec/frontend/boards/board_list_helper.js b/spec/frontend/boards/board_list_helper.js
index d0f14bd37c1..04192489817 100644
--- a/spec/frontend/boards/board_list_helper.js
+++ b/spec/frontend/boards/board_list_helper.js
@@ -1,4 +1,5 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
@@ -33,9 +34,8 @@ export default function createComponent({
},
issuesCount,
} = {}) {
- const localVue = createLocalVue();
- localVue.use(VueApollo);
- localVue.use(Vuex);
+ Vue.use(VueApollo);
+ Vue.use(Vuex);
const fakeApollo = createMockApollo([
[listQuery, jest.fn().mockResolvedValue(boardListQueryResponse(issuesCount))],
@@ -85,7 +85,6 @@ export default function createComponent({
const component = shallowMount(BoardList, {
apolloProvider: fakeApollo,
- localVue,
store,
propsData: {
disabled: false,
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index 1981ed5ab7f..fd9d2b6823d 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -1,6 +1,8 @@
import Draggable from 'vuedraggable';
+import { nextTick } from 'vue';
import { DraggableItemTypes } from 'ee_else_ce/boards/constants';
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
+import waitForPromises from 'helpers/wait_for_promises';
import createComponent from 'jest/boards/board_list_helper';
import BoardCard from '~/boards/components/board_card.vue';
import eventHub from '~/boards/eventhub';
@@ -64,14 +66,14 @@ describe('Board list component', () => {
it('shows new issue form', async () => {
wrapper.vm.toggleForm();
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.board-new-issue-form').exists()).toBe(true);
});
it('shows new issue form after eventhub event', async () => {
eventHub.$emit(`toggle-issue-form-${wrapper.vm.list.id}`);
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.board-new-issue-form').exists()).toBe(true);
});
@@ -85,7 +87,7 @@ describe('Board list component', () => {
it('shows count list item', async () => {
wrapper.vm.showCount = true;
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.board-list-count').exists()).toBe(true);
expect(wrapper.find('.board-list-count').text()).toBe('Showing all issues');
@@ -94,7 +96,7 @@ describe('Board list component', () => {
it('sets data attribute with invalid id', async () => {
wrapper.vm.showCount = true;
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.board-list-count').attributes('data-issue-id')).toBe('-1');
});
});
@@ -104,10 +106,6 @@ describe('Board list component', () => {
fetchItemsForList: jest.fn(),
};
- beforeEach(() => {
- wrapper = createComponent();
- });
-
it('does not load issues if already loading', () => {
wrapper = createComponent({
actions,
@@ -126,20 +124,23 @@ describe('Board list component', () => {
},
});
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(findIssueCountLoadingIcon().exists()).toBe(true);
});
it('shows how many more issues to load', async () => {
- // wrapper.vm.showCount = true;
wrapper = createComponent({
data: {
showCount: true,
},
});
- await wrapper.vm.$nextTick();
+ await nextTick();
+ await waitForPromises();
+ await nextTick();
+ await nextTick();
+
expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues');
});
});
@@ -155,7 +156,7 @@ describe('Board list component', () => {
it('sets background to bg-danger-100', async () => {
wrapper.setProps({ list: { issuesCount: 4, maxIssueCount: 3 } });
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find('.bg-danger-100').exists()).toBe(true);
});
});
diff --git a/spec/frontend/boards/components/board_add_new_column_trigger_spec.js b/spec/frontend/boards/components/board_add_new_column_trigger_spec.js
index c35f2463f69..7dd02bf1d35 100644
--- a/spec/frontend/boards/components/board_add_new_column_trigger_spec.js
+++ b/spec/frontend/boards/components/board_add_new_column_trigger_spec.js
@@ -1,5 +1,5 @@
import { GlButton } from '@gitlab/ui';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
@@ -49,7 +49,7 @@ describe('BoardAddNewColumnTrigger', () => {
it('shows the tooltip', async () => {
wrapper.find(GlButton).vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
const tooltip = findTooltipText();
diff --git a/spec/frontend/boards/components/board_blocked_icon_spec.js b/spec/frontend/boards/components/board_blocked_icon_spec.js
index 7b04942f056..7a5c49bd488 100644
--- a/spec/frontend/boards/components/board_blocked_icon_spec.js
+++ b/spec/frontend/boards/components/board_blocked_icon_spec.js
@@ -1,6 +1,6 @@
import { GlIcon, GlLink, GlPopover, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
@@ -39,7 +39,7 @@ describe('BoardBlockedIcon', () => {
const mouseenter = async () => {
findGlIcon().vm.$emit('mouseenter');
- await wrapper.vm.$nextTick();
+ await nextTick();
await waitForApollo();
};
diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js
index 3af173aa18c..aad89cf8261 100644
--- a/spec/frontend/boards/components/board_card_spec.js
+++ b/spec/frontend/boards/components/board_card_spec.js
@@ -1,6 +1,6 @@
import { GlLabel } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import BoardCard from '~/boards/components/board_card.vue';
@@ -65,12 +65,12 @@ describe('Board card', () => {
const selectCard = async () => {
wrapper.trigger('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
};
const multiSelectCard = async () => {
wrapper.trigger('click', { ctrlKey: true });
- await wrapper.vm.$nextTick();
+ await nextTick();
};
beforeEach(() => {
diff --git a/spec/frontend/boards/components/board_filtered_search_spec.js b/spec/frontend/boards/components/board_filtered_search_spec.js
index a8398a138ba..85ba703a6ee 100644
--- a/spec/frontend/boards/components/board_filtered_search_spec.js
+++ b/spec/frontend/boards/components/board_filtered_search_spec.js
@@ -120,7 +120,7 @@ describe('BoardFilteredSearch', () => {
{ type: 'author', value: { data: 'root', operator: '=' } },
{ type: 'assignee', value: { data: 'root', operator: '=' } },
{ type: 'label', value: { data: 'label', operator: '=' } },
- { type: 'label', value: { data: 'label2', operator: '=' } },
+ { type: 'label', value: { data: 'label&2', operator: '=' } },
{ type: 'milestone', value: { data: 'New Milestone', operator: '=' } },
{ type: 'type', value: { data: 'INCIDENT', operator: '=' } },
{ type: 'weight', value: { data: '2', operator: '=' } },
@@ -134,7 +134,7 @@ describe('BoardFilteredSearch', () => {
title: '',
replace: true,
url:
- 'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&assignee_username=root&milestone_title=New+Milestone&iteration_id=3341&types=INCIDENT&weight=2&release_tag=v1.0.0',
+ 'http://test.host/?author_username=root&label_name[]=label&label_name[]=label%262&assignee_username=root&milestone_title=New%20Milestone&iteration_id=3341&types=INCIDENT&weight=2&release_tag=v1.0.0',
});
});
diff --git a/spec/frontend/boards/components/board_form_spec.js b/spec/frontend/boards/components/board_form_spec.js
index 692fd3ec555..5678da2a246 100644
--- a/spec/frontend/boards/components/board_form_spec.js
+++ b/spec/frontend/boards/components/board_form_spec.js
@@ -130,7 +130,7 @@ describe('BoardForm', () => {
it('passes correct primary action text and variant', () => {
expect(findModalActionPrimary().text).toBe('Create board');
- expect(findModalActionPrimary().attributes[0].variant).toBe('success');
+ expect(findModalActionPrimary().attributes[0].variant).toBe('confirm');
});
it('does not render delete confirmation message', () => {
diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js
index 8cc0ad5f30c..14870ec76a2 100644
--- a/spec/frontend/boards/components/board_list_header_spec.js
+++ b/spec/frontend/boards/components/board_list_header_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -148,7 +148,7 @@ describe('Board List Header Component', () => {
findCaret().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(toggleListCollapsedSpy).toHaveBeenCalledTimes(1);
});
@@ -156,7 +156,7 @@ describe('Board List Header Component', () => {
createComponent({ withLocalStorage: false, currentUserId: 1 });
findCaret().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(updateListSpy).toHaveBeenCalledTimes(1);
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(null);
@@ -168,7 +168,7 @@ describe('Board List Header Component', () => {
});
findCaret().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(updateListSpy).not.toHaveBeenCalled();
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(String(isCollapsed()));
diff --git a/spec/frontend/boards/components/board_new_issue_spec.js b/spec/frontend/boards/components/board_new_issue_spec.js
index 57ccebf3676..8b0100d069a 100644
--- a/spec/frontend/boards/components/board_new_issue_spec.js
+++ b/spec/frontend/boards/components/board_new_issue_spec.js
@@ -1,15 +1,14 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import BoardNewIssue from '~/boards/components/board_new_issue.vue';
import BoardNewItem from '~/boards/components/board_new_item.vue';
import ProjectSelect from '~/boards/components/project_select.vue';
import eventHub from '~/boards/eventhub';
-import { mockList, mockGroupProjects } from '../mock_data';
+import { mockList, mockGroupProjects, mockIssue, mockIssue2 } from '../mock_data';
-const localVue = createLocalVue();
-
-localVue.use(Vuex);
+Vue.use(Vuex);
const addListNewIssuesSpy = jest.fn().mockResolvedValue();
const mockActions = { addListNewIssue: addListNewIssuesSpy };
@@ -17,10 +16,9 @@ const mockActions = { addListNewIssue: addListNewIssuesSpy };
const createComponent = ({
state = { selectedProject: mockGroupProjects[0], fullPath: mockGroupProjects[0].fullPath },
actions = mockActions,
- getters = { isGroupBoard: () => true, isProjectBoard: () => false },
+ getters = { isGroupBoard: () => true, getBoardItemsByList: () => () => [] },
} = {}) =>
shallowMount(BoardNewIssue, {
- localVue,
store: new Vuex.Store({
state,
actions,
@@ -47,7 +45,7 @@ describe('Issue boards new issue form', () => {
beforeEach(async () => {
wrapper = createComponent();
- await wrapper.vm.$nextTick();
+ await nextTick();
});
afterEach(() => {
@@ -68,7 +66,7 @@ describe('Issue boards new issue form', () => {
it('calls addListNewIssue action when `board-new-item` emits form-submit event', async () => {
findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' });
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(addListNewIssuesSpy).toHaveBeenCalledWith(expect.any(Object), {
list: mockList,
issueInput: {
@@ -77,15 +75,44 @@ describe('Issue boards new issue form', () => {
assigneeIds: [],
milestoneId: undefined,
projectPath: mockGroupProjects[0].fullPath,
+ moveAfterId: undefined,
},
});
});
+ describe('when list has an existing issues', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ getters: {
+ isGroupBoard: () => true,
+ getBoardItemsByList: () => () => [mockIssue, mockIssue2],
+ },
+ });
+ });
+
+ it('it uses the first issue ID as moveAfterId', async () => {
+ findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' });
+
+ await nextTick();
+ expect(addListNewIssuesSpy).toHaveBeenCalledWith(expect.any(Object), {
+ list: mockList,
+ issueInput: {
+ title: 'Foo',
+ labelIds: [],
+ assigneeIds: [],
+ milestoneId: undefined,
+ projectPath: mockGroupProjects[0].fullPath,
+ moveAfterId: mockIssue.id,
+ },
+ });
+ });
+ });
+
it('emits event `toggle-issue-form` with current list Id suffix on eventHub when `board-new-item` emits form-cancel event', async () => {
jest.spyOn(eventHub, '$emit').mockImplementation();
findBoardNewItem().vm.$emit('form-cancel');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(eventHub.$emit).toHaveBeenCalledWith(`toggle-issue-form-${mockList.id}`);
});
@@ -101,7 +128,7 @@ describe('Issue boards new issue form', () => {
describe('when in project issue board', () => {
beforeEach(() => {
wrapper = createComponent({
- getters: { isGroupBoard: () => false, isProjectBoard: () => true },
+ getters: { isGroupBoard: () => false },
});
});
diff --git a/spec/frontend/boards/components/board_new_item_spec.js b/spec/frontend/boards/components/board_new_item_spec.js
index 0151d9c1c14..86cebc8a719 100644
--- a/spec/frontend/boards/components/board_new_item_spec.js
+++ b/spec/frontend/boards/components/board_new_item_spec.js
@@ -1,4 +1,5 @@
import { GlForm, GlFormInput, GlButton } from '@gitlab/ui';
+import { nextTick } from 'vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardNewItem from '~/boards/components/board_new_item.vue';
@@ -39,6 +40,27 @@ describe('BoardNewItem', () => {
});
describe('template', () => {
+ describe('when the user provides a valid input', () => {
+ it('finds an enabled create button', async () => {
+ expect(wrapper.findByTestId('create-button').props('disabled')).toBe(true);
+
+ wrapper.find(GlFormInput).vm.$emit('input', 'hello');
+ await nextTick();
+
+ expect(wrapper.findByTestId('create-button').props('disabled')).toBe(false);
+ });
+ });
+
+ describe('when the user types in a string with only spaces', () => {
+ it('disables the Create Issue button', async () => {
+ wrapper.find(GlFormInput).vm.$emit('input', ' ');
+
+ await nextTick();
+
+ expect(wrapper.findByTestId('create-button').props('disabled')).toBe(true);
+ });
+ });
+
it('renders gl-form component', () => {
expect(wrapper.findComponent(GlForm).exists()).toBe(true);
});
@@ -80,6 +102,19 @@ describe('BoardNewItem', () => {
]);
});
+ it('emits `form-submit` event with trimmed title', async () => {
+ titleInput().setValue(' Foo ');
+
+ await glForm().trigger('submit');
+
+ expect(wrapper.emitted('form-submit')[0]).toEqual([
+ {
+ title: 'Foo',
+ list: mockList,
+ },
+ ]);
+ });
+
it('emits `scroll-board-list-` event with list.id on eventHub when `submit` is triggered on gl-form', async () => {
jest.spyOn(eventHub, '$emit').mockImplementation();
await glForm().trigger('submit');
@@ -90,7 +125,7 @@ describe('BoardNewItem', () => {
it('emits `form-cancel` event and clears title value when `reset` is triggered on gl-form', async () => {
titleInput().setValue('Foo');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(titleInput().element.value).toBe('Foo');
await glForm().trigger('reset');
diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js
index 46dd109ffb1..7f40c426b30 100644
--- a/spec/frontend/boards/components/board_settings_sidebar_spec.js
+++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js
@@ -1,8 +1,9 @@
-import { GlDrawer, GlLabel } from '@gitlab/ui';
+import { GlDrawer, GlLabel, GlModal, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { MountingPortal } from 'portal-vue';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { stubComponent } from 'helpers/stub_component';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
@@ -20,8 +21,7 @@ describe('BoardSettingsSidebar', () => {
const labelTitle = mockLabelList.label.title;
const labelColor = mockLabelList.label.color;
const listId = mockLabelList.id;
-
- const findRemoveButton = () => wrapper.findByTestId('remove-list');
+ const modalID = 'board-settings-sidebar-modal';
const createComponent = ({
canAdminList = false,
@@ -46,6 +46,9 @@ describe('BoardSettingsSidebar', () => {
canAdminList,
scopedLabelsAvailable: false,
},
+ directives: {
+ GlModal: createMockDirective(),
+ },
stubs: {
GlDrawer: stubComponent(GlDrawer, {
template: '<div><slot name="header"></slot><slot></slot></div>',
@@ -56,6 +59,8 @@ describe('BoardSettingsSidebar', () => {
};
const findLabel = () => wrapper.find(GlLabel);
const findDrawer = () => wrapper.find(GlDrawer);
+ const findModal = () => wrapper.find(GlModal);
+ const findRemoveButton = () => wrapper.find(GlButton);
afterEach(() => {
jest.restoreAllMocks();
@@ -86,7 +91,7 @@ describe('BoardSettingsSidebar', () => {
findDrawer().vm.$emit('close');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find(GlDrawer).exists()).toBe(false);
});
@@ -96,7 +101,7 @@ describe('BoardSettingsSidebar', () => {
sidebarEventHub.$emit('sidebar.closeAll');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.find(GlDrawer).exists()).toBe(false);
});
@@ -161,5 +166,16 @@ describe('BoardSettingsSidebar', () => {
expect(findRemoveButton().exists()).toBe(true);
});
+
+ it('has the correct ID on the button', () => {
+ createComponent({ canAdminList: true, activeId: listId, list: mockLabelList });
+ const binding = getBinding(findRemoveButton().element, 'gl-modal');
+ expect(binding.value).toBe(modalID);
+ });
+
+ it('has the correct ID on the modal', () => {
+ createComponent({ canAdminList: true, activeId: listId, list: mockLabelList });
+ expect(findModal().props('modalId')).toBe(modalID);
+ });
});
});
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 9cf7c5774bf..26a5bf34595 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -1,43 +1,40 @@
import { GlDropdown, GlLoadingIcon, GlDropdownSectionHeader } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
import { TEST_HOST } from 'spec/test_constants';
import BoardsSelector from '~/boards/components/boards_selector.vue';
+import { BoardType } from '~/boards/constants';
import groupBoardQuery from '~/boards/graphql/group_board.query.graphql';
import projectBoardQuery from '~/boards/graphql/project_board.query.graphql';
+import groupBoardsQuery from '~/boards/graphql/group_boards.query.graphql';
+import projectBoardsQuery from '~/boards/graphql/project_boards.query.graphql';
+import groupRecentBoardsQuery from '~/boards/graphql/group_recent_boards.query.graphql';
+import projectRecentBoardsQuery from '~/boards/graphql/project_recent_boards.query.graphql';
import defaultStore from '~/boards/stores';
-import axios from '~/lib/utils/axios_utils';
import createMockApollo from 'helpers/mock_apollo_helper';
-import { mockGroupBoardResponse, mockProjectBoardResponse } from '../mock_data';
+import {
+ mockGroupBoardResponse,
+ mockProjectBoardResponse,
+ mockGroupAllBoardsResponse,
+ mockProjectAllBoardsResponse,
+ mockGroupRecentBoardsResponse,
+ mockProjectRecentBoardsResponse,
+ mockSmallProjectAllBoardsResponse,
+ mockEmptyProjectRecentBoardsResponse,
+ boards,
+ recentIssueBoards,
+} from '../mock_data';
const throttleDuration = 1;
Vue.use(VueApollo);
-function boardGenerator(n) {
- return new Array(n).fill().map((board, index) => {
- const id = `${index}`;
- const name = `board${id}`;
-
- return {
- id,
- name,
- };
- });
-}
-
describe('BoardsSelector', () => {
let wrapper;
- let allBoardsResponse;
- let recentBoardsResponse;
- let mock;
let fakeApollo;
let store;
- const boards = boardGenerator(20);
- const recentBoards = boardGenerator(5);
const createStore = ({ isGroupBoard = false, isProjectBoard = false } = {}) => {
store = new Vuex.Store({
@@ -63,17 +60,43 @@ describe('BoardsSelector', () => {
};
const getDropdownItems = () => wrapper.findAll('.js-dropdown-item');
- const getDropdownHeaders = () => wrapper.findAll(GlDropdownSectionHeader);
- const getLoadingIcon = () => wrapper.find(GlLoadingIcon);
- const findDropdown = () => wrapper.find(GlDropdown);
+ const getDropdownHeaders = () => wrapper.findAllComponents(GlDropdownSectionHeader);
+ const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
const projectBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockProjectBoardResponse);
const groupBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupBoardResponse);
- const createComponent = () => {
+ const projectBoardsQueryHandlerSuccess = jest
+ .fn()
+ .mockResolvedValue(mockProjectAllBoardsResponse);
+ const groupBoardsQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupAllBoardsResponse);
+
+ const projectRecentBoardsQueryHandlerSuccess = jest
+ .fn()
+ .mockResolvedValue(mockProjectRecentBoardsResponse);
+ const groupRecentBoardsQueryHandlerSuccess = jest
+ .fn()
+ .mockResolvedValue(mockGroupRecentBoardsResponse);
+
+ const smallBoardsQueryHandlerSuccess = jest
+ .fn()
+ .mockResolvedValue(mockSmallProjectAllBoardsResponse);
+ const emptyRecentBoardsQueryHandlerSuccess = jest
+ .fn()
+ .mockResolvedValue(mockEmptyProjectRecentBoardsResponse);
+
+ const createComponent = ({
+ projectBoardsQueryHandler = projectBoardsQueryHandlerSuccess,
+ projectRecentBoardsQueryHandler = projectRecentBoardsQueryHandlerSuccess,
+ } = {}) => {
fakeApollo = createMockApollo([
[projectBoardQuery, projectBoardQueryHandlerSuccess],
[groupBoardQuery, groupBoardQueryHandlerSuccess],
+ [projectBoardsQuery, projectBoardsQueryHandler],
+ [groupBoardsQuery, groupBoardsQueryHandlerSuccess],
+ [projectRecentBoardsQuery, projectRecentBoardsQueryHandler],
+ [groupRecentBoardsQuery, groupRecentBoardsQueryHandlerSuccess],
]);
wrapper = mount(BoardsSelector, {
@@ -91,67 +114,34 @@ describe('BoardsSelector', () => {
attachTo: document.body,
provide: {
fullPath: '',
- recentBoardsEndpoint: `${TEST_HOST}/recent`,
},
});
-
- wrapper.vm.$apollo.addSmartQuery = jest.fn((_, options) => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- [options.loadingKey]: true,
- });
- });
};
afterEach(() => {
wrapper.destroy();
- wrapper = null;
- mock.restore();
+ fakeApollo = null;
});
- describe('fetching all boards', () => {
+ describe('template', () => {
beforeEach(() => {
- mock = new MockAdapter(axios);
-
- allBoardsResponse = Promise.resolve({
- data: {
- group: {
- boards: {
- edges: boards.map((board) => ({ node: board })),
- },
- },
- },
- });
- recentBoardsResponse = Promise.resolve({
- data: recentBoards,
- });
-
- createStore();
+ createStore({ isProjectBoard: true });
createComponent();
-
- mock.onGet(`${TEST_HOST}/recent`).replyOnce(200, recentBoards);
});
describe('loading', () => {
- beforeEach(async () => {
- // Wait for current board to be loaded
- await nextTick();
-
- // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
- findDropdown().vm.$emit('show');
- });
-
// we are testing loading state, so don't resolve responses until after the tests
afterEach(async () => {
- await Promise.all([allBoardsResponse, recentBoardsResponse]);
await nextTick();
});
it('shows loading spinner', () => {
+ // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
+ findDropdown().vm.$emit('show');
+
+ expect(getLoadingIcon().exists()).toBe(true);
expect(getDropdownHeaders()).toHaveLength(0);
expect(getDropdownItems()).toHaveLength(0);
- expect(getLoadingIcon().exists()).toBe(true);
});
});
@@ -163,16 +153,13 @@ describe('BoardsSelector', () => {
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
findDropdown().vm.$emit('show');
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- await wrapper.setData({
- loadingBoards: false,
- loadingRecentBoards: false,
- });
- await Promise.all([allBoardsResponse, recentBoardsResponse]);
await nextTick();
});
+ it('fetches all issue boards', () => {
+ expect(projectBoardsQueryHandlerSuccess).toHaveBeenCalled();
+ });
+
it('hides loading spinner', async () => {
await nextTick();
expect(getLoadingIcon().exists()).toBe(false);
@@ -180,22 +167,17 @@ describe('BoardsSelector', () => {
describe('filtering', () => {
beforeEach(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({
- boards,
- });
-
await nextTick();
});
it('shows all boards without filtering', () => {
- expect(getDropdownItems()).toHaveLength(boards.length + recentBoards.length);
+ expect(getDropdownItems()).toHaveLength(boards.length + recentIssueBoards.length);
});
it('shows only matching boards when filtering', async () => {
const filterTerm = 'board1';
- const expectedCount = boards.filter((board) => board.name.includes(filterTerm)).length;
+ const expectedCount = boards.filter((board) => board.node.name.includes(filterTerm))
+ .length;
fillSearchBox(filterTerm);
@@ -214,32 +196,21 @@ describe('BoardsSelector', () => {
describe('recent boards section', () => {
it('shows only when boards are greater than 10', 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({
- boards,
- });
-
await nextTick();
+ expect(projectRecentBoardsQueryHandlerSuccess).toHaveBeenCalled();
expect(getDropdownHeaders()).toHaveLength(2);
});
it('does not show when boards are less than 10', 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({
- boards: boards.slice(0, 5),
- });
+ createComponent({ projectBoardsQueryHandler: smallBoardsQueryHandlerSuccess });
await nextTick();
expect(getDropdownHeaders()).toHaveLength(0);
});
- it('does not show when recentBoards api returns empty array', 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({
- recentBoards: [],
+ it('does not show when recentIssueBoards api returns empty array', async () => {
+ createComponent({
+ projectRecentBoardsQueryHandler: emptyRecentBoardsQueryHandlerSuccess,
});
await nextTick();
@@ -256,15 +227,39 @@ describe('BoardsSelector', () => {
});
});
+ describe('fetching all boards', () => {
+ it.each`
+ boardType | queryHandler | notCalledHandler
+ ${BoardType.group} | ${groupBoardsQueryHandlerSuccess} | ${projectBoardsQueryHandlerSuccess}
+ ${BoardType.project} | ${projectBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess}
+ `('fetches $boardType boards', async ({ boardType, queryHandler, notCalledHandler }) => {
+ createStore({
+ isProjectBoard: boardType === BoardType.project,
+ isGroupBoard: boardType === BoardType.group,
+ });
+ createComponent();
+
+ await nextTick();
+
+ // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
+ findDropdown().vm.$emit('show');
+
+ await nextTick();
+
+ expect(queryHandler).toHaveBeenCalled();
+ expect(notCalledHandler).not.toHaveBeenCalled();
+ });
+ });
+
describe('fetching current board', () => {
it.each`
- boardType | queryHandler | notCalledHandler
- ${'group'} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess}
- ${'project'} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess}
+ boardType | queryHandler | notCalledHandler
+ ${BoardType.group} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess}
+ ${BoardType.project} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess}
`('fetches $boardType board', async ({ boardType, queryHandler, notCalledHandler }) => {
createStore({
- isProjectBoard: boardType === 'project',
- isGroupBoard: boardType === 'group',
+ isProjectBoard: boardType === BoardType.project,
+ isGroupBoard: boardType === BoardType.group,
});
createComponent();
diff --git a/spec/frontend/boards/components/sidebar/board_editable_item_spec.js b/spec/frontend/boards/components/sidebar/board_editable_item_spec.js
index 12e9a9ba365..0c76c711b3a 100644
--- a/spec/frontend/boards/components/sidebar/board_editable_item_spec.js
+++ b/spec/frontend/boards/components/sidebar/board_editable_item_spec.js
@@ -1,5 +1,6 @@
import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
import BoardSidebarItem from '~/boards/components/sidebar/board_editable_item.vue';
describe('boards sidebar remove issue', () => {
@@ -79,17 +80,16 @@ describe('boards sidebar remove issue', () => {
createComponent({ canUpdate: true, slots });
findEditButton().vm.$emit('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(findCollapsed().isVisible()).toBe(false);
- expect(findExpanded().isVisible()).toBe(true);
- });
+ await nextTick();
+ expect(findCollapsed().isVisible()).toBe(false);
+ expect(findExpanded().isVisible()).toBe(true);
});
it('hides the header while editing if `toggleHeader` is true', async () => {
createComponent({ canUpdate: true, props: { toggleHeader: true } });
findEditButton().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(findEditButton().isVisible()).toBe(false);
expect(findTitle().isVisible()).toBe(false);
@@ -101,14 +101,14 @@ describe('boards sidebar remove issue', () => {
beforeEach(async () => {
createComponent({ canUpdate: true });
findEditButton().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
});
it('hides expanded section and displays collapsed section', async () => {
expect(findExpanded().isVisible()).toBe(true);
document.body.click();
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(findCollapsed().isVisible()).toBe(true);
expect(findExpanded().isVisible()).toBe(false);
@@ -117,7 +117,7 @@ describe('boards sidebar remove issue', () => {
it('emits events', async () => {
document.body.click();
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.emitted().close).toHaveLength(1);
expect(wrapper.emitted()['off-click']).toHaveLength(1);
@@ -129,7 +129,7 @@ describe('boards sidebar remove issue', () => {
findEditButton().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(wrapper.emitted().open.length).toBe(1);
});
@@ -139,7 +139,7 @@ describe('boards sidebar remove issue', () => {
findEditButton().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
wrapper.vm.collapse({ emitEvent: false });
diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js
index 4a8eda298f2..5364d929c38 100644
--- a/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js
+++ b/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js
@@ -1,5 +1,6 @@
import { GlAlert, GlFormInput, GlForm } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
import { createStore } from '~/boards/stores';
@@ -75,7 +76,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
});
findFormInput().vm.$emit('input', TEST_TITLE);
findForm().vm.$emit('submit', { preventDefault: () => {} });
- await wrapper.vm.$nextTick();
+ await nextTick();
});
it('collapses sidebar and renders new title', () => {
@@ -98,7 +99,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
jest.spyOn(wrapper.vm, 'setActiveItemTitle').mockImplementation(() => {});
findFormInput().vm.$emit('input', '');
findForm().vm.$emit('submit', { preventDefault: () => {} });
- await wrapper.vm.$nextTick();
+ await nextTick();
});
it('commits change to the server', () => {
@@ -113,7 +114,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
wrapper.vm.$refs.sidebarItem.expand();
findFormInput().vm.$emit('input', TEST_TITLE);
findEditableItem().vm.$emit('off-click');
- await wrapper.vm.$nextTick();
+ await nextTick();
});
it('does not collapses sidebar and shows alert', () => {
@@ -148,7 +149,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
});
findFormInput().vm.$emit('input', TEST_TITLE);
findCancelButton().vm.$emit('click');
- await wrapper.vm.$nextTick();
+ await nextTick();
});
it('collapses sidebar and render former title', () => {
@@ -168,7 +169,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
jest.spyOn(wrapper.vm, 'setError').mockImplementation(() => {});
findFormInput().vm.$emit('input', 'Invalid title');
findForm().vm.$emit('submit', { preventDefault: () => {} });
- await wrapper.vm.$nextTick();
+ await nextTick();
});
it('collapses sidebar and renders former item title', () => {
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index a081a60166b..24096fddea6 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -29,6 +29,85 @@ export const listObj = {
},
};
+function boardGenerator(n) {
+ return new Array(n).fill().map((board, index) => {
+ const id = `${index}`;
+ const name = `board${id}`;
+
+ return {
+ node: {
+ id,
+ name,
+ weight: 0,
+ __typename: 'Board',
+ },
+ };
+ });
+}
+
+export const boards = boardGenerator(20);
+export const recentIssueBoards = boardGenerator(5);
+
+export const mockSmallProjectAllBoardsResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/114',
+ boards: { edges: boardGenerator(3) },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockEmptyProjectRecentBoardsResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/114',
+ recentIssueBoards: { edges: [] },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockGroupAllBoardsResponse = {
+ data: {
+ group: {
+ id: 'gid://gitlab/Group/114',
+ boards: { edges: boards },
+ __typename: 'Group',
+ },
+ },
+};
+
+export const mockProjectAllBoardsResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/1',
+ boards: { edges: boards },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockGroupRecentBoardsResponse = {
+ data: {
+ group: {
+ id: 'gid://gitlab/Group/114',
+ recentIssueBoards: { edges: recentIssueBoards },
+ __typename: 'Group',
+ },
+ },
+};
+
+export const mockProjectRecentBoardsResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/1',
+ recentIssueBoards: { edges: recentIssueBoards },
+ __typename: 'Project',
+ },
+ },
+};
+
export const mockGroupBoardResponse = {
data: {
workspace: {
@@ -612,6 +691,7 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, isSignedI
title: __('Milestone'),
symbol: '%',
type: 'milestone',
+ shouldSkipSort: true,
token: MilestoneToken,
unique: true,
fetchMilestones,
diff --git a/spec/frontend/boards/project_select_spec.js b/spec/frontend/boards/project_select_spec.js
index de823094630..05dc7d28eaa 100644
--- a/spec/frontend/boards/project_select_spec.js
+++ b/spec/frontend/boards/project_select_spec.js
@@ -1,6 +1,6 @@
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import ProjectSelect from '~/boards/components/project_select.vue';
import defaultState from '~/boards/stores/state';
@@ -88,7 +88,7 @@ describe('ProjectSelect component', () => {
expect(findGlDropdownLoadingIcon().exists()).toBe(true);
- await wrapper.vm.$nextTick();
+ await nextTick();
expect(findGlDropdownLoadingIcon().exists()).toBe(false);
});
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 7c842d71688..0eca0cb3ee5 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -315,14 +315,14 @@ describe('fetchMilestones', () => {
'project',
{
query: projectBoardMilestones,
- variables: { fullPath: 'gitlab-org/gitlab', state: 'active' },
+ variables: { fullPath: 'gitlab-org/gitlab' },
},
],
[
'group',
{
query: groupBoardMilestones,
- variables: { fullPath: 'gitlab-org/gitlab', state: 'active' },
+ variables: { fullPath: 'gitlab-org/gitlab' },
},
],
])(