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: '
',
};
// 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');
beforeEach(() => {
gon.api_version = apiVersion;
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
it('renders HTML label when hasHtmlLabel is true', () => {
const testid = 'html-label';
createComponent({
props: {
label: ``,
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}
${'block'} | ${false}
`('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('does not include shared projects if withShared prop is false', async () => {
createComponent({
props: {
withShared: false,
},
});
openListbox();
await waitForPromises();
expect(mock.history.get[0].params.with_shared).toBe(false);
});
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);
});
});