2023-04-23 21:23:45 +05:30
|
|
|
import { GlLoadingIcon, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
|
2022-04-04 11:22:00 +05:30
|
|
|
import Vue, { nextTick } from 'vue';
|
2020-07-28 23:09:34 +05:30
|
|
|
import axios from 'axios';
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
2021-04-17 20:07:23 +05:30
|
|
|
import { merge, last } from 'lodash';
|
2021-03-11 19:13:27 +05:30
|
|
|
import Vuex from 'vuex';
|
2023-05-27 22:25:52 +05:30
|
|
|
import tags from 'test_fixtures/api/tags/tags.json';
|
2021-11-18 22:05:49 +05:30
|
|
|
import commit from 'test_fixtures/api/commits/commit.json';
|
|
|
|
import branches from 'test_fixtures/api/branches/branches.json';
|
2023-04-23 21:23:45 +05:30
|
|
|
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
2020-07-28 23:09:34 +05:30
|
|
|
import { trimText } from 'helpers/text_helper';
|
2023-04-23 21:23:45 +05:30
|
|
|
import {
|
|
|
|
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
|
|
|
HTTP_STATUS_NOT_FOUND,
|
|
|
|
HTTP_STATUS_OK,
|
|
|
|
} from '~/lib/utils/http_status';
|
2021-03-11 19:13:27 +05:30
|
|
|
import { sprintf } from '~/locale';
|
2020-07-28 23:09:34 +05:30
|
|
|
import RefSelector from '~/ref/components/ref_selector.vue';
|
2021-04-17 20:07:23 +05:30
|
|
|
import {
|
|
|
|
X_TOTAL_HEADER,
|
|
|
|
DEFAULT_I18N,
|
|
|
|
REF_TYPE_BRANCHES,
|
|
|
|
REF_TYPE_TAGS,
|
|
|
|
REF_TYPE_COMMITS,
|
|
|
|
} from '~/ref/constants';
|
2020-07-28 23:09:34 +05:30
|
|
|
import createStore from '~/ref/stores/';
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
Vue.use(Vuex);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
describe('Ref selector component', () => {
|
2021-11-18 22:05:49 +05:30
|
|
|
const fixtures = { branches, tags, commit };
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
const projectId = '8';
|
2023-05-27 22:25:52 +05:30
|
|
|
const totalBranchesCount = 123;
|
|
|
|
const totalTagsCount = 456;
|
2023-06-20 00:43:36 +05:30
|
|
|
const queryParams = { sort: 'updated_desc' };
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
let wrapper;
|
|
|
|
let branchesApiCallSpy;
|
|
|
|
let tagsApiCallSpy;
|
|
|
|
let commitApiCallSpy;
|
2021-04-17 20:07:23 +05:30
|
|
|
let requestSpies;
|
|
|
|
|
2023-03-17 16:20:25 +05:30
|
|
|
const createComponent = (mountOverrides = {}, propsData = {}) => {
|
2023-04-23 21:23:45 +05:30
|
|
|
wrapper = mountExtended(
|
2021-04-17 20:07:23 +05:30
|
|
|
RefSelector,
|
|
|
|
merge(
|
|
|
|
{
|
|
|
|
propsData: {
|
|
|
|
projectId,
|
|
|
|
value: '',
|
2023-03-17 16:20:25 +05:30
|
|
|
...propsData,
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
|
|
|
listeners: {
|
|
|
|
// simulate a parent component v-model binding
|
|
|
|
input: (selectedRef) => {
|
|
|
|
wrapper.setProps({ value: selectedRef });
|
|
|
|
},
|
|
|
|
},
|
|
|
|
store: createStore(),
|
2020-07-28 23:09:34 +05:30
|
|
|
},
|
2021-04-17 20:07:23 +05:30
|
|
|
mountOverrides,
|
|
|
|
),
|
|
|
|
);
|
2020-07-28 23:09:34 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
const mock = new MockAdapter(axios);
|
|
|
|
gon.api_version = 'v4';
|
|
|
|
|
|
|
|
branchesApiCallSpy = jest
|
|
|
|
.fn()
|
2023-05-27 22:25:52 +05:30
|
|
|
.mockReturnValue([
|
|
|
|
HTTP_STATUS_OK,
|
|
|
|
fixtures.branches,
|
|
|
|
{ [X_TOTAL_HEADER]: totalBranchesCount },
|
|
|
|
]);
|
2023-04-23 21:23:45 +05:30
|
|
|
tagsApiCallSpy = jest
|
|
|
|
.fn()
|
2023-05-27 22:25:52 +05:30
|
|
|
.mockReturnValue([HTTP_STATUS_OK, fixtures.tags, { [X_TOTAL_HEADER]: totalTagsCount }]);
|
2023-04-23 21:23:45 +05:30
|
|
|
commitApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_OK, fixtures.commit]);
|
2021-04-17 20:07:23 +05:30
|
|
|
requestSpies = { branchesApiCallSpy, tagsApiCallSpy, commitApiCallSpy };
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
mock
|
|
|
|
.onGet(`/api/v4/projects/${projectId}/repository/branches`)
|
2021-03-08 18:12:59 +05:30
|
|
|
.reply((config) => branchesApiCallSpy(config));
|
2020-07-28 23:09:34 +05:30
|
|
|
mock
|
|
|
|
.onGet(`/api/v4/projects/${projectId}/repository/tags`)
|
2021-03-08 18:12:59 +05:30
|
|
|
.reply((config) => tagsApiCallSpy(config));
|
2020-07-28 23:09:34 +05:30
|
|
|
mock
|
|
|
|
.onGet(new RegExp(`/api/v4/projects/${projectId}/repository/commits/.*`))
|
2021-03-08 18:12:59 +05:30
|
|
|
.reply((config) => commitApiCallSpy(config));
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
//
|
|
|
|
// Finders
|
|
|
|
//
|
2023-04-23 21:23:45 +05:30
|
|
|
const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
|
|
|
|
|
|
|
|
const findButtonToggle = () => wrapper.findByTestId('base-dropdown-toggle');
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
const findNoResults = () => wrapper.findByTestId('listbox-no-results-text');
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
const findListBoxSection = (section) => {
|
|
|
|
const foundSections = wrapper
|
|
|
|
.findAll('[role="group"]')
|
|
|
|
.filter((ul) => ul.text().includes(section));
|
|
|
|
return foundSections.length > 0 ? foundSections.at(0) : foundSections;
|
|
|
|
};
|
|
|
|
|
|
|
|
const findErrorListWrapper = () => wrapper.findByTestId('red-selector-error-list');
|
2020-11-24 15:15:51 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
const findBranchesSection = () => findListBoxSection('Branches');
|
|
|
|
const findBranchDropdownItems = () => wrapper.findAllComponents(GlListboxItem);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
const findTagsSection = () => findListBoxSection('Tags');
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
const findCommitsSection = () => findListBoxSection('Commits');
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
const findHiddenInputField = () => wrapper.findByTestId('selected-ref-form-field');
|
2022-11-25 23:54:43 +05:30
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
//
|
|
|
|
// Expecters
|
|
|
|
//
|
2023-04-23 21:23:45 +05:30
|
|
|
const sectionContainsErrorMessage = (message) => {
|
|
|
|
const errorSection = findErrorListWrapper();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
return errorSection ? errorSection.text().includes(message) : false;
|
2020-07-28 23:09:34 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// Convenience methods
|
|
|
|
//
|
2021-03-08 18:12:59 +05:30
|
|
|
const updateQuery = (newQuery) => {
|
2023-04-23 21:23:45 +05:30
|
|
|
findListbox().vm.$emit('search', newQuery);
|
2020-07-28 23:09:34 +05:30
|
|
|
};
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
const selectFirstBranch = async () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
findListbox().vm.$emit('select', fixtures.branches[0].name);
|
2022-04-04 11:22:00 +05:30
|
|
|
await nextTick();
|
2020-07-28 23:09:34 +05:30
|
|
|
};
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
const selectFirstTag = async () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
findListbox().vm.$emit('select', fixtures.tags[0].name);
|
2022-04-04 11:22:00 +05:30
|
|
|
await nextTick();
|
2020-07-28 23:09:34 +05:30
|
|
|
};
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
const selectFirstCommit = async () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
findListbox().vm.$emit('select', fixtures.commit.id);
|
2022-04-04 11:22:00 +05:30
|
|
|
await nextTick();
|
2020-07-28 23:09:34 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
const waitForRequests = ({ andClearMocks } = { andClearMocks: false }) =>
|
|
|
|
axios.waitForAll().then(() => {
|
|
|
|
if (andClearMocks) {
|
|
|
|
branchesApiCallSpy.mockClear();
|
|
|
|
tagsApiCallSpy.mockClear();
|
|
|
|
commitApiCallSpy.mockClear();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('initialization behavior', () => {
|
|
|
|
it('initializes the dropdown with branches and tags when mounted', () => {
|
2022-08-13 15:12:31 +05:30
|
|
|
createComponent();
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(branchesApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(tagsApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(commitApiCallSpy).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('shows a spinner while network requests are in progress', () => {
|
2022-08-13 15:12:31 +05:30
|
|
|
createComponent();
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
expect(findLoadingIcon().exists()).toBe(true);
|
|
|
|
|
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(findLoadingIcon().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
2022-11-25 23:54:43 +05:30
|
|
|
|
|
|
|
describe('when name property is provided', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
it('renders an form input hidden field', () => {
|
2022-11-25 23:54:43 +05:30
|
|
|
const name = 'default_tag';
|
|
|
|
|
|
|
|
createComponent({ propsData: { name } });
|
|
|
|
|
|
|
|
expect(findHiddenInputField().attributes().name).toBe(name);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when name property is not provided', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
it('renders an form input hidden field', () => {
|
2022-11-25 23:54:43 +05:30
|
|
|
createComponent();
|
|
|
|
|
|
|
|
expect(findHiddenInputField().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
describe('post-initialization behavior', () => {
|
2020-10-24 23:57:45 +05:30
|
|
|
describe('when the parent component provides an `id` binding', () => {
|
|
|
|
const id = 'git-ref';
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2021-04-17 20:07:23 +05:30
|
|
|
createComponent({ attrs: { id } });
|
2020-10-24 23:57:45 +05:30
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
2020-11-24 15:15:51 +05:30
|
|
|
it('adds the provided ID to the GlDropdown instance', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findListbox().attributes().id).toBe(id);
|
2020-10-24 23:57:45 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when a ref is pre-selected', () => {
|
|
|
|
const preselectedRef = fixtures.branches[0].name;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2022-11-25 23:54:43 +05:30
|
|
|
createComponent({ propsData: { value: preselectedRef, name: 'selectedRef' } });
|
2020-10-24 23:57:45 +05:30
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the pre-selected ref name', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(preselectedRef);
|
2020-10-24 23:57:45 +05:30
|
|
|
});
|
2022-11-25 23:54:43 +05:30
|
|
|
|
|
|
|
it('binds hidden input field to the pre-selected ref', () => {
|
|
|
|
expect(findHiddenInputField().attributes().value).toBe(preselectedRef);
|
|
|
|
});
|
2020-10-24 23:57:45 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the selected ref is updated by the parent component', () => {
|
|
|
|
const updatedRef = fixtures.branches[0].name;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
it('renders the updated ref name', async () => {
|
2020-10-24 23:57:45 +05:30
|
|
|
wrapper.setProps({ value: updatedRef });
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
await nextTick();
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(updatedRef);
|
2020-10-24 23:57:45 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
describe('when the search query is updated', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests({ andClearMocks: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('requeries the endpoints when the search query is updated', () => {
|
|
|
|
updateQuery('v1.2.3');
|
|
|
|
|
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(branchesApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(tagsApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it("does not make a call to the commit endpoint if the query doesn't look like a SHA", () => {
|
|
|
|
updateQuery('not a sha');
|
|
|
|
|
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(commitApiCallSpy).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('searches for a commit if the query could potentially be a SHA', () => {
|
|
|
|
updateQuery('abcdef');
|
|
|
|
|
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(commitApiCallSpy).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when no results are found', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
branchesApiCallSpy = jest
|
|
|
|
.fn()
|
|
|
|
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
|
|
|
tagsApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
|
|
|
commitApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_NOT_FOUND]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the search query is empty', () => {
|
|
|
|
it('renders a "no results" message', () => {
|
|
|
|
expect(findNoResults().text()).toBe(DEFAULT_I18N.noResults);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the search query is not empty', () => {
|
|
|
|
const query = 'hello';
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
updateQuery(query);
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders a "no results" message that includes the search query', () => {
|
|
|
|
expect(findNoResults().text()).toBe(sprintf(DEFAULT_I18N.noResultsWithQuery, { query }));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('branches', () => {
|
|
|
|
describe('when the branches search returns results', () => {
|
|
|
|
beforeEach(() => {
|
2023-07-09 08:55:56 +05:30
|
|
|
createComponent({}, { useSymbolicRefNames: true });
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the branches section in the dropdown', () => {
|
|
|
|
expect(findBranchesSection().exists()).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("does not render an error message in the branches section's body", () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findErrorListWrapper().exists()).toBe(false);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the default branch as a selectable item with a "default" badge', () => {
|
|
|
|
const dropdownItems = findBranchDropdownItems();
|
|
|
|
|
2021-03-08 18:12:59 +05:30
|
|
|
const defaultBranch = fixtures.branches.find((b) => b.default);
|
2020-07-28 23:09:34 +05:30
|
|
|
const defaultBranchIndex = fixtures.branches.indexOf(defaultBranch);
|
|
|
|
|
|
|
|
expect(trimText(dropdownItems.at(defaultBranchIndex).text())).toBe(
|
|
|
|
`${defaultBranch.name} default`,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the branches search returns no results', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
branchesApiCallSpy = jest
|
|
|
|
.fn()
|
|
|
|
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render the branches section in the dropdown', () => {
|
|
|
|
expect(findBranchesSection().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the branches search returns an error', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
branchesApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_INTERNAL_SERVER_ERROR]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the branches section in the dropdown', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findBranchesSection().exists()).toBe(false);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it("renders an error message in the branches section's body", () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(sectionContainsErrorMessage(DEFAULT_I18N.branchesErrorMessage)).toBe(true);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('tags', () => {
|
|
|
|
describe('when the tags search returns results', () => {
|
|
|
|
beforeEach(() => {
|
2023-07-09 08:55:56 +05:30
|
|
|
createComponent({}, { useSymbolicRefNames: true });
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the tags section in the dropdown', () => {
|
|
|
|
expect(findTagsSection().exists()).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the "Tags" heading with a total number indicator', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findTagsSection().find('[role="presentation"]').text()).toMatchInterpolatedText(
|
|
|
|
`Tags ${fixtures.tags.length}`,
|
|
|
|
);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it("does not render an error message in the tags section's body", () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findErrorListWrapper().exists()).toBe(false);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the tags search returns no results', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
tagsApiCallSpy = jest
|
|
|
|
.fn()
|
|
|
|
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render the tags section in the dropdown', () => {
|
|
|
|
expect(findTagsSection().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the tags search returns an error', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
tagsApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_INTERNAL_SERVER_ERROR]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the tags section in the dropdown', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findTagsSection().exists()).toBe(false);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it("renders an error message in the tags section's body", () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(sectionContainsErrorMessage(DEFAULT_I18N.tagsErrorMessage)).toBe(true);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('commits', () => {
|
|
|
|
describe('when the commit search returns results', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
updateQuery('abcd1234');
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the commit section in the dropdown', () => {
|
|
|
|
expect(findCommitsSection().exists()).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the "Commits" heading with a total number indicator', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findCommitsSection().find('[role="presentation"]').text()).toMatchInterpolatedText(
|
|
|
|
`Commits 1`,
|
|
|
|
);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
it("does not render an error message in the commits section's body", () => {
|
|
|
|
expect(findErrorListWrapper().exists()).toBe(false);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the commit search returns no results (i.e. a 404)', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
commitApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_NOT_FOUND]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
updateQuery('abcd1234');
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render the commits section in the dropdown', () => {
|
|
|
|
expect(findCommitsSection().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the commit search returns an error (other than a 404)', () => {
|
|
|
|
beforeEach(() => {
|
2023-04-23 21:23:45 +05:30
|
|
|
commitApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_INTERNAL_SERVER_ERROR]);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
updateQuery('abcd1234');
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the commits section in the dropdown', () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findCommitsSection().exists()).toBe(false);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it("renders an error message in the commits section's body", () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(sectionContainsErrorMessage(DEFAULT_I18N.commitsErrorMessage)).toBe(true);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('selection', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
updateQuery(fixtures.commit.short_id);
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
describe('when a branch is selected', () => {
|
2021-02-22 17:27:13 +05:30
|
|
|
it("displays the branch name in the dropdown's button", async () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(DEFAULT_I18N.noRefSelected);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
await selectFirstBranch();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(fixtures.branches[0].name);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
it("updates the v-model binding with the branch's name", async () => {
|
2020-07-28 23:09:34 +05:30
|
|
|
expect(wrapper.vm.value).toEqual('');
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
await selectFirstBranch();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
expect(wrapper.vm.value).toEqual(fixtures.branches[0].name);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when a tag is seleceted', () => {
|
2021-02-22 17:27:13 +05:30
|
|
|
it("displays the tag name in the dropdown's button", async () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(DEFAULT_I18N.noRefSelected);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
await selectFirstTag();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(fixtures.tags[0].name);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
it("updates the v-model binding with the tag's name", async () => {
|
2020-07-28 23:09:34 +05:30
|
|
|
expect(wrapper.vm.value).toEqual('');
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
await selectFirstTag();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
expect(wrapper.vm.value).toEqual(fixtures.tags[0].name);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when a commit is selected', () => {
|
2021-02-22 17:27:13 +05:30
|
|
|
it("displays the full SHA in the dropdown's button", async () => {
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(DEFAULT_I18N.noRefSelected);
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
await selectFirstCommit();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
expect(findButtonToggle().text()).toBe(fixtures.commit.id);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
it("updates the v-model binding with the commit's full SHA", async () => {
|
2020-07-28 23:09:34 +05:30
|
|
|
expect(wrapper.vm.value).toEqual('');
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
await selectFirstCommit();
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
expect(wrapper.vm.value).toEqual(fixtures.commit.id);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2021-04-17 20:07:23 +05:30
|
|
|
|
|
|
|
describe('with non-default ref types', () => {
|
|
|
|
it.each`
|
|
|
|
enabledRefTypes | reqsCalled | reqsNotCalled
|
|
|
|
${[REF_TYPE_BRANCHES]} | ${['branchesApiCallSpy']} | ${['tagsApiCallSpy', 'commitApiCallSpy']}
|
|
|
|
${[REF_TYPE_TAGS]} | ${['tagsApiCallSpy']} | ${['branchesApiCallSpy', 'commitApiCallSpy']}
|
|
|
|
${[REF_TYPE_COMMITS]} | ${[]} | ${['branchesApiCallSpy', 'tagsApiCallSpy', 'commitApiCallSpy']}
|
|
|
|
${[REF_TYPE_TAGS, REF_TYPE_COMMITS]} | ${['tagsApiCallSpy']} | ${['branchesApiCallSpy', 'commitApiCallSpy']}
|
|
|
|
`(
|
|
|
|
'only calls $reqsCalled requests when $enabledRefTypes are enabled',
|
|
|
|
async ({ enabledRefTypes, reqsCalled, reqsNotCalled }) => {
|
|
|
|
createComponent({ propsData: { enabledRefTypes } });
|
|
|
|
|
|
|
|
await waitForRequests();
|
|
|
|
|
|
|
|
reqsCalled.forEach((req) => expect(requestSpies[req]).toHaveBeenCalledTimes(1));
|
|
|
|
reqsNotCalled.forEach((req) => expect(requestSpies[req]).not.toHaveBeenCalled());
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
it('only calls commitApiCallSpy when REF_TYPE_COMMITS is enabled', async () => {
|
|
|
|
createComponent({ propsData: { enabledRefTypes: [REF_TYPE_COMMITS] } });
|
|
|
|
updateQuery('abcd1234');
|
|
|
|
|
|
|
|
await waitForRequests();
|
|
|
|
|
|
|
|
expect(commitApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(branchesApiCallSpy).not.toHaveBeenCalled();
|
|
|
|
expect(tagsApiCallSpy).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('triggers another search if enabled ref types change', async () => {
|
|
|
|
createComponent({ propsData: { enabledRefTypes: [REF_TYPE_BRANCHES] } });
|
|
|
|
await waitForRequests();
|
|
|
|
|
|
|
|
expect(branchesApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(tagsApiCallSpy).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
wrapper.setProps({
|
|
|
|
enabledRefTypes: [REF_TYPE_BRANCHES, REF_TYPE_TAGS],
|
|
|
|
});
|
|
|
|
await waitForRequests();
|
|
|
|
|
|
|
|
expect(branchesApiCallSpy).toHaveBeenCalledTimes(2);
|
|
|
|
expect(tagsApiCallSpy).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
enabledRefType | findVisibleSection | findHiddenSections
|
|
|
|
${REF_TYPE_BRANCHES} | ${findBranchesSection} | ${[findTagsSection, findCommitsSection]}
|
|
|
|
${REF_TYPE_TAGS} | ${findTagsSection} | ${[findBranchesSection, findCommitsSection]}
|
|
|
|
${REF_TYPE_COMMITS} | ${findCommitsSection} | ${[findBranchesSection, findTagsSection]}
|
|
|
|
`(
|
|
|
|
'hides section headers if a single ref type is enabled',
|
|
|
|
async ({ enabledRefType, findVisibleSection, findHiddenSections }) => {
|
|
|
|
createComponent({ propsData: { enabledRefTypes: [enabledRefType] } });
|
|
|
|
updateQuery('abcd1234');
|
|
|
|
await waitForRequests();
|
|
|
|
|
|
|
|
expect(findVisibleSection().exists()).toBe(true);
|
|
|
|
expect(findVisibleSection().find('[data-testid="section-header"]').exists()).toBe(false);
|
|
|
|
findHiddenSections.forEach((findHiddenSection) =>
|
|
|
|
expect(findHiddenSection().exists()).toBe(false),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('validation state', () => {
|
|
|
|
const invalidClass = 'gl-inset-border-1-red-500!';
|
2023-04-23 21:23:45 +05:30
|
|
|
const isInvalidClassApplied = () => findListbox().props('toggleClass')[0][invalidClass];
|
2021-04-17 20:07:23 +05:30
|
|
|
|
|
|
|
describe('valid state', () => {
|
|
|
|
describe('when the state prop is not provided', () => {
|
|
|
|
it('does not render a red border', () => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
expect(isInvalidClassApplied()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the state prop is true', () => {
|
|
|
|
it('does not render a red border', () => {
|
|
|
|
createComponent({ propsData: { state: true } });
|
|
|
|
|
|
|
|
expect(isInvalidClassApplied()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('invalid state', () => {
|
|
|
|
it('renders the dropdown with a red border if the state prop is false', () => {
|
|
|
|
createComponent({ propsData: { state: false } });
|
|
|
|
|
|
|
|
expect(isInvalidClassApplied()).toBe(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('footer slot', () => {
|
|
|
|
const footerContent = 'This is the footer content';
|
|
|
|
const createFooter = jest.fn().mockImplementation(function createMockFooter() {
|
|
|
|
return this.$createElement('div', { attrs: { 'data-testid': 'footer-content' } }, [
|
|
|
|
footerContent,
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent({
|
|
|
|
scopedSlots: { footer: createFooter },
|
|
|
|
});
|
|
|
|
|
|
|
|
updateQuery('abcd1234');
|
|
|
|
|
|
|
|
return waitForRequests();
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
createFooter.mockClear();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('allows custom content to be shown at the bottom of the dropdown using the footer slot', () => {
|
|
|
|
expect(wrapper.find(`[data-testid="footer-content"]`).text()).toBe(footerContent);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('passes the expected slot props', () => {
|
|
|
|
// The createFooter function gets called every time one of the scoped properties
|
|
|
|
// is updated. For the sake of this test, we'll just test the last call, which
|
|
|
|
// represents the final state of the slot props.
|
|
|
|
const lastCallProps = last(createFooter.mock.calls)[0];
|
2023-05-27 22:25:52 +05:30
|
|
|
expect(lastCallProps.isLoading).toBe(false);
|
|
|
|
expect(lastCallProps.query).toBe('abcd1234');
|
|
|
|
|
|
|
|
const branchesList = fixtures.branches.map((branch) => {
|
|
|
|
return {
|
|
|
|
default: branch.default,
|
|
|
|
name: branch.name,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const commitsList = [
|
|
|
|
{
|
|
|
|
name: fixtures.commit.short_id,
|
|
|
|
subtitle: fixtures.commit.title,
|
|
|
|
value: fixtures.commit.id,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const tagsList = fixtures.tags.map((tag) => {
|
|
|
|
return {
|
|
|
|
name: tag.name,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const expectedMatches = {
|
|
|
|
branches: {
|
|
|
|
list: branchesList,
|
|
|
|
totalCount: totalBranchesCount,
|
|
|
|
},
|
|
|
|
commits: {
|
|
|
|
list: commitsList,
|
|
|
|
totalCount: 1,
|
|
|
|
},
|
|
|
|
tags: {
|
|
|
|
list: tagsList,
|
|
|
|
totalCount: totalTagsCount,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
expect(lastCallProps.matches).toMatchObject(expectedMatches);
|
2021-04-17 20:07:23 +05:30
|
|
|
});
|
|
|
|
});
|
2023-06-20 00:43:36 +05:30
|
|
|
describe('when queryParam prop is present', () => {
|
|
|
|
it('passes params to a branches API call', () => {
|
|
|
|
createComponent({ propsData: { queryParams } });
|
|
|
|
|
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(branchesApiCallSpy).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({ params: { per_page: 20, search: '', sort: queryParams.sort } }),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not pass params to tags API call', () => {
|
|
|
|
createComponent({ propsData: { queryParams } });
|
|
|
|
|
|
|
|
return waitForRequests().then(() => {
|
|
|
|
expect(tagsApiCallSpy).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({ params: { per_page: 20, search: '' } }),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|