diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
commit | d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch) | |
tree | 2341ef426af70ad1e289c38036737e04b0aa5007 /spec/frontend/issues_list | |
parent | d6e514dd13db8947884cd58fe2a9c2a063400a9b (diff) |
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'spec/frontend/issues_list')
3 files changed, 190 insertions, 11 deletions
diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index 8d79a5eed35..6b443062f12 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -24,6 +24,7 @@ import IssuableByEmail from '~/issuable/components/issuable_by_email.vue'; import IssuableList from '~/issuable_list/components/issuable_list_root.vue'; import { IssuableListTabs, IssuableStates } from '~/issuable_list/constants'; import IssuesListApp from '~/issues_list/components/issues_list_app.vue'; +import NewIssueDropdown from '~/issues_list/components/new_issue_dropdown.vue'; import { CREATED_DESC, DUE_DATE_OVERDUE, @@ -65,6 +66,7 @@ describe('IssuesListApp component', () => { exportCsvPath: 'export/csv/path', fullPath: 'path/to/project', hasAnyIssues: true, + hasAnyProjects: true, hasBlockedIssuesFeature: true, hasIssueWeightsFeature: true, hasIterationsFeature: true, @@ -93,6 +95,7 @@ describe('IssuesListApp component', () => { const findGlEmptyState = () => wrapper.findComponent(GlEmptyState); const findGlLink = () => wrapper.findComponent(GlLink); const findIssuableList = () => wrapper.findComponent(IssuableList); + const findNewIssueDropdown = () => wrapper.findComponent(NewIssueDropdown); const mountComponent = ({ provide = {}, @@ -190,10 +193,7 @@ describe('IssuesListApp component', () => { beforeEach(() => { setWindowLocation(search); - wrapper = mountComponent({ - provide: { isSignedIn: true }, - mountFn: mount, - }); + wrapper = mountComponent({ provide: { isSignedIn: true }, mountFn: mount }); jest.runOnlyPendingTimers(); }); @@ -208,7 +208,7 @@ describe('IssuesListApp component', () => { describe('when user is not signed in', () => { it('does not render', () => { - wrapper = mountComponent({ provide: { isSignedIn: false } }); + wrapper = mountComponent({ provide: { isSignedIn: false }, mountFn: mount }); expect(findCsvImportExportButtons().exists()).toBe(false); }); @@ -216,7 +216,7 @@ describe('IssuesListApp component', () => { describe('when in a group context', () => { it('does not render', () => { - wrapper = mountComponent({ provide: { isProject: false } }); + wrapper = mountComponent({ provide: { isProject: false }, mountFn: mount }); expect(findCsvImportExportButtons().exists()).toBe(false); }); @@ -231,7 +231,7 @@ describe('IssuesListApp component', () => { }); it('does not render when user does not have permissions', () => { - wrapper = mountComponent({ provide: { canBulkUpdate: false } }); + wrapper = mountComponent({ provide: { canBulkUpdate: false }, mountFn: mount }); expect(findGlButtons().filter((button) => button.text() === 'Edit issues')).toHaveLength(0); }); @@ -258,11 +258,25 @@ describe('IssuesListApp component', () => { }); it('does not render when user does not have permissions', () => { - wrapper = mountComponent({ provide: { showNewIssueLink: false } }); + wrapper = mountComponent({ provide: { showNewIssueLink: false }, mountFn: mount }); expect(findGlButtons().filter((button) => button.text() === 'New issue')).toHaveLength(0); }); }); + + describe('new issue split dropdown', () => { + it('does not render in a project context', () => { + wrapper = mountComponent({ provide: { isProject: true }, mountFn: mount }); + + expect(findNewIssueDropdown().exists()).toBe(false); + }); + + it('renders in a group context', () => { + wrapper = mountComponent({ provide: { isProject: false }, mountFn: mount }); + + expect(findNewIssueDropdown().exists()).toBe(true); + }); + }); }); describe('initial url params', () => { @@ -506,7 +520,7 @@ describe('IssuesListApp component', () => { beforeEach(() => { wrapper = mountComponent({ provide: { - groupEpicsPath: '', + groupPath: '', }, }); }); @@ -522,7 +536,7 @@ describe('IssuesListApp component', () => { beforeEach(() => { wrapper = mountComponent({ provide: { - groupEpicsPath: '', + groupPath: '', }, }); }); @@ -550,7 +564,7 @@ describe('IssuesListApp component', () => { provide: { isSignedIn: true, projectIterationsPath: 'project/iterations/path', - groupEpicsPath: 'group/epics/path', + groupPath: 'group/path', hasIssueWeightsFeature: true, }, }); diff --git a/spec/frontend/issues_list/components/new_issue_dropdown_spec.js b/spec/frontend/issues_list/components/new_issue_dropdown_spec.js new file mode 100644 index 00000000000..1fcaa99cf5a --- /dev/null +++ b/spec/frontend/issues_list/components/new_issue_dropdown_spec.js @@ -0,0 +1,131 @@ +import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; +import { createLocalVue, mount, shallowMount } from '@vue/test-utils'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import NewIssueDropdown from '~/issues_list/components/new_issue_dropdown.vue'; +import searchProjectsQuery from '~/issues_list/queries/search_projects.query.graphql'; +import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; +import { + emptySearchProjectsQueryResponse, + project1, + project2, + searchProjectsQueryResponse, +} from '../mock_data'; + +describe('NewIssueDropdown component', () => { + let wrapper; + + const localVue = createLocalVue(); + localVue.use(VueApollo); + + const mountComponent = ({ + search = '', + queryResponse = searchProjectsQueryResponse, + mountFn = shallowMount, + } = {}) => { + const requestHandlers = [[searchProjectsQuery, jest.fn().mockResolvedValue(queryResponse)]]; + const apolloProvider = createMockApollo(requestHandlers); + + return mountFn(NewIssueDropdown, { + localVue, + apolloProvider, + provide: { + fullPath: 'mushroom-kingdom', + }, + data() { + return { search }; + }, + }); + }; + + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findInput = () => wrapper.findComponent(GlSearchBoxByType); + const showDropdown = async () => { + findDropdown().vm.$emit('shown'); + await wrapper.vm.$apollo.queries.projects.refetch(); + jest.runOnlyPendingTimers(); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders a split dropdown', () => { + wrapper = mountComponent(); + + expect(findDropdown().props('split')).toBe(true); + }); + + it('renders a label for the dropdown toggle button', () => { + wrapper = mountComponent(); + + expect(findDropdown().attributes('toggle-text')).toBe(NewIssueDropdown.i18n.toggleButtonLabel); + }); + + it('focuses on input when dropdown is shown', async () => { + wrapper = mountComponent({ mountFn: mount }); + + const inputSpy = jest.spyOn(findInput().vm, 'focusInput'); + + await showDropdown(); + + expect(inputSpy).toHaveBeenCalledTimes(1); + }); + + it('renders expected dropdown items', async () => { + wrapper = mountComponent({ mountFn: mount }); + + await showDropdown(); + + const listItems = wrapper.findAll('li'); + + expect(listItems.at(0).text()).toBe(project1.nameWithNamespace); + expect(listItems.at(1).text()).toBe(project2.nameWithNamespace); + }); + + it('renders `No matches found` when there are no matches', async () => { + wrapper = mountComponent({ + search: 'no matches', + queryResponse: emptySearchProjectsQueryResponse, + mountFn: mount, + }); + + await showDropdown(); + + expect(wrapper.find('li').text()).toBe(NewIssueDropdown.i18n.noMatchesFound); + }); + + describe('when no project is selected', () => { + beforeEach(() => { + wrapper = mountComponent(); + }); + + it('dropdown button is not a link', () => { + expect(findDropdown().attributes('split-href')).toBeUndefined(); + }); + + it('displays default text on the dropdown button', () => { + expect(findDropdown().props('text')).toBe(NewIssueDropdown.i18n.defaultDropdownText); + }); + }); + + describe('when a project is selected', () => { + beforeEach(async () => { + wrapper = mountComponent({ mountFn: mount }); + + await showDropdown(); + + wrapper.findComponent(GlDropdownItem).vm.$emit('click', project1); + }); + + it('dropdown button is a link', () => { + const href = joinPaths(project1.webUrl, DASH_SCOPE, 'issues/new'); + + expect(findDropdown().attributes('split-href')).toBe(href); + }); + + it('displays project name on the dropdown button', () => { + expect(findDropdown().props('text')).toBe(`New issue in ${project1.name}`); + }); + }); +}); diff --git a/spec/frontend/issues_list/mock_data.js b/spec/frontend/issues_list/mock_data.js index 720f9cac986..3be256d8094 100644 --- a/spec/frontend/issues_list/mock_data.js +++ b/spec/frontend/issues_list/mock_data.js @@ -221,3 +221,37 @@ export const urlParamsWithSpecialValues = { epic_id: 'None', weight: 'None', }; + +export const project1 = { + id: 'gid://gitlab/Group/26', + name: 'Super Mario Project', + nameWithNamespace: 'Mushroom Kingdom / Super Mario Project', + webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/super-mario-project', +}; + +export const project2 = { + id: 'gid://gitlab/Group/59', + name: 'Mario Kart Project', + nameWithNamespace: 'Mushroom Kingdom / Mario Kart Project', + webUrl: 'https://127.0.0.1:3000/mushroom-kingdom/mario-kart-project', +}; + +export const searchProjectsQueryResponse = { + data: { + group: { + projects: { + nodes: [project1, project2], + }, + }, + }, +}; + +export const emptySearchProjectsQueryResponse = { + data: { + group: { + projects: { + nodes: [], + }, + }, + }, +}; |