diff options
Diffstat (limited to 'spec/frontend/vue_shared/components/entity_select/project_select_spec.js')
-rw-r--r-- | spec/frontend/vue_shared/components/entity_select/project_select_spec.js | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js new file mode 100644 index 00000000000..57dce032d30 --- /dev/null +++ b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js @@ -0,0 +1,248 @@ +import { GlCollapsibleListbox } from '@gitlab/ui'; +import MockAdapter from 'axios-mock-adapter'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK, HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status'; +import ProjectSelect from '~/vue_shared/components/entity_select/project_select.vue'; +import EntitySelect from '~/vue_shared/components/entity_select/entity_select.vue'; +import { + PROJECT_TOGGLE_TEXT, + PROJECT_HEADER_TEXT, + FETCH_PROJECTS_ERROR, + FETCH_PROJECT_ERROR, +} from '~/vue_shared/components/entity_select/constants'; +import waitForPromises from 'helpers/wait_for_promises'; + +describe('ProjectSelect', () => { + let wrapper; + let mock; + + // Stubs + const GlAlert = { + template: '<div><slot /></div>', + }; + + // Props + const label = 'label'; + const inputName = 'inputName'; + const inputId = 'inputId'; + const groupId = '22'; + const userId = '1'; + + // Mocks + const apiVersion = 'v4'; + const projectMock = { + name_with_namespace: 'selectedProject', + id: '1', + }; + const projectsEndpoint = `/api/${apiVersion}/projects.json`; + const groupProjectEndpoint = `/api/${apiVersion}/groups/${groupId}/projects.json`; + const userProjectEndpoint = `/api/${apiVersion}/users/${userId}/projects`; + const projectEndpoint = `/api/${apiVersion}/projects/${projectMock.id}`; + + // Finders + const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); + const findEntitySelect = () => wrapper.findComponent(EntitySelect); + const findAlert = () => wrapper.findComponent(GlAlert); + + // Helpers + const createComponent = ({ props = {} } = {}) => { + wrapper = mountExtended(ProjectSelect, { + propsData: { + label, + inputName, + inputId, + groupId, + ...props, + }, + stubs: { + GlAlert, + EntitySelect, + }, + }); + }; + const openListbox = () => findListbox().vm.$emit('shown'); + + beforeAll(() => { + gon.api_version = apiVersion; + }); + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + it('renders HTML label when hasHtmlLabel is true', () => { + const testid = 'html-label'; + createComponent({ + props: { + label: `<div data-testid="${testid}" />`, + hasHtmlLabel: true, + }, + }); + + expect(wrapper.findByTestId(testid).exists()).toBe(true); + }); + + describe('entity_select props', () => { + beforeEach(() => { + createComponent(); + }); + + it.each` + prop | expectedValue + ${'label'} | ${label} + ${'inputName'} | ${inputName} + ${'inputId'} | ${inputId} + ${'defaultToggleText'} | ${PROJECT_TOGGLE_TEXT} + ${'headerText'} | ${PROJECT_HEADER_TEXT} + ${'clearable'} | ${true} + `('passes the $prop prop to entity-select', ({ prop, expectedValue }) => { + expect(findEntitySelect().props(prop)).toBe(expectedValue); + }); + }); + + describe('on mount', () => { + it('fetches projects when the listbox is opened', async () => { + createComponent(); + await waitForPromises(); + + expect(mock.history.get).toHaveLength(0); + + openListbox(); + await waitForPromises(); + + expect(mock.history.get).toHaveLength(1); + expect(mock.history.get[0].url).toBe(groupProjectEndpoint); + expect(mock.history.get[0].params).toEqual({ + include_subgroups: false, + order_by: 'similarity', + per_page: 20, + search: '', + simple: true, + with_shared: true, + }); + }); + + it('includes projects from subgroups if includeSubgroups is true', async () => { + createComponent({ + props: { + includeSubgroups: true, + }, + }); + openListbox(); + await waitForPromises(); + + expect(mock.history.get[0].params.include_subgroups).toBe(true); + }); + + it('fetches projects globally if no group ID is provided', async () => { + createComponent({ + props: { + groupId: null, + }, + }); + openListbox(); + await waitForPromises(); + + expect(mock.history.get[0].url).toBe(projectsEndpoint); + expect(mock.history.get[0].params).toEqual({ + membership: false, + order_by: 'similarity', + per_page: 20, + search: '', + simple: true, + }); + }); + + it('restricts search to owned projects if membership is true', async () => { + createComponent({ + props: { + groupId: null, + membership: true, + }, + }); + openListbox(); + await waitForPromises(); + + expect(mock.history.get[0].params.membership).toBe(true); + }); + + it("fetches the user's projects if a user ID is provided", async () => { + createComponent({ + props: { + groupId: null, + userId, + }, + }); + openListbox(); + await waitForPromises(); + + expect(mock.history.get[0].url).toBe(userProjectEndpoint); + expect(mock.history.get[0].params).toEqual({ + per_page: 20, + search: '', + with_shared: true, + include_subgroups: false, + }); + }); + + it.each([null, groupId])( + 'fetches with the provided sort key when groupId is %s', + async (groupIdProp) => { + const orderBy = 'last_activity_at'; + createComponent({ + props: { + groupId: groupIdProp, + orderBy, + }, + }); + openListbox(); + await waitForPromises(); + + expect(mock.history.get[0].params.order_by).toBe(orderBy); + }, + ); + + describe('with an initial selection', () => { + it("fetches the initially selected value's name", async () => { + mock.onGet(projectEndpoint).reply(HTTP_STATUS_OK, projectMock); + createComponent({ props: { initialSelection: projectMock.id } }); + await waitForPromises(); + + expect(mock.history.get).toHaveLength(1); + expect(findListbox().props('toggleText')).toBe(projectMock.name_with_namespace); + }); + + it('show an error if fetching the individual project fails', async () => { + mock + .onGet(groupProjectEndpoint) + .reply(HTTP_STATUS_OK, [{ full_name: 'notTheSelectedProject', id: '2' }]); + mock.onGet(projectEndpoint).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); + createComponent({ props: { initialSelection: projectMock.id } }); + + expect(findAlert().exists()).toBe(false); + + await waitForPromises(); + + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(FETCH_PROJECT_ERROR); + }); + }); + }); + + it('shows an error when fetching projects fails', async () => { + mock.onGet(groupProjectEndpoint).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); + createComponent(); + openListbox(); + expect(findAlert().exists()).toBe(false); + + await waitForPromises(); + + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(FETCH_PROJECTS_ERROR); + }); +}); |