diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /spec/frontend/ref/components/ref_selector_spec.js | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) | |
download | gitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'spec/frontend/ref/components/ref_selector_spec.js')
-rw-r--r-- | spec/frontend/ref/components/ref_selector_spec.js | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js new file mode 100644 index 00000000000..2688e4b3428 --- /dev/null +++ b/spec/frontend/ref/components/ref_selector_spec.js @@ -0,0 +1,532 @@ +import Vuex from 'vuex'; +import { mount, createLocalVue } from '@vue/test-utils'; +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { GlLoadingIcon, GlSearchBoxByType, GlNewDropdownItem, GlIcon } from '@gitlab/ui'; +import { trimText } from 'helpers/text_helper'; +import { sprintf } from '~/locale'; +import RefSelector from '~/ref/components/ref_selector.vue'; +import { X_TOTAL_HEADER, DEFAULT_I18N } from '~/ref/constants'; +import createStore from '~/ref/stores/'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('Ref selector component', () => { + const fixtures = { + branches: getJSONFixture('api/branches/branches.json'), + tags: getJSONFixture('api/tags/tags.json'), + commit: getJSONFixture('api/commits/commit.json'), + }; + + const projectId = '8'; + + let wrapper; + let branchesApiCallSpy; + let tagsApiCallSpy; + let commitApiCallSpy; + + const createComponent = () => { + wrapper = mount(RefSelector, { + propsData: { + projectId, + value: '', + }, + listeners: { + // simulate a parent component v-model binding + input: selectedRef => { + wrapper.setProps({ value: selectedRef }); + }, + }, + stubs: { + GlSearchBoxByType: true, + }, + localVue, + store: createStore(), + }); + }; + + beforeEach(() => { + const mock = new MockAdapter(axios); + gon.api_version = 'v4'; + + branchesApiCallSpy = jest + .fn() + .mockReturnValue([200, fixtures.branches, { [X_TOTAL_HEADER]: '123' }]); + tagsApiCallSpy = jest.fn().mockReturnValue([200, fixtures.tags, { [X_TOTAL_HEADER]: '456' }]); + commitApiCallSpy = jest.fn().mockReturnValue([200, fixtures.commit]); + + mock + .onGet(`/api/v4/projects/${projectId}/repository/branches`) + .reply(config => branchesApiCallSpy(config)); + mock + .onGet(`/api/v4/projects/${projectId}/repository/tags`) + .reply(config => tagsApiCallSpy(config)); + mock + .onGet(new RegExp(`/api/v4/projects/${projectId}/repository/commits/.*`)) + .reply(config => commitApiCallSpy(config)); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + // + // Finders + // + const findButtonContent = () => wrapper.find('[data-testid="button-content"]'); + + const findNoResults = () => wrapper.find('[data-testid="no-results"]'); + + const findLoadingIcon = () => wrapper.find(GlLoadingIcon); + + const findBranchesSection = () => wrapper.find('[data-testid="branches-section"]'); + const findBranchDropdownItems = () => findBranchesSection().findAll(GlNewDropdownItem); + const findFirstBranchDropdownItem = () => findBranchDropdownItems().at(0); + + const findTagsSection = () => wrapper.find('[data-testid="tags-section"]'); + const findTagDropdownItems = () => findTagsSection().findAll(GlNewDropdownItem); + const findFirstTagDropdownItem = () => findTagDropdownItems().at(0); + + const findCommitsSection = () => wrapper.find('[data-testid="commits-section"]'); + const findCommitDropdownItems = () => findCommitsSection().findAll(GlNewDropdownItem); + const findFirstCommitDropdownItem = () => findCommitDropdownItems().at(0); + + // + // Expecters + // + const branchesSectionContainsErrorMessage = () => { + const branchesSection = findBranchesSection(); + + return branchesSection.text().includes(DEFAULT_I18N.branchesErrorMessage); + }; + + const tagsSectionContainsErrorMessage = () => { + const tagsSection = findTagsSection(); + + return tagsSection.text().includes(DEFAULT_I18N.tagsErrorMessage); + }; + + const commitsSectionContainsErrorMessage = () => { + const commitsSection = findCommitsSection(); + + return commitsSection.text().includes(DEFAULT_I18N.commitsErrorMessage); + }; + + // + // Convenience methods + // + const updateQuery = newQuery => { + wrapper.find(GlSearchBoxByType).vm.$emit('input', newQuery); + }; + + const selectFirstBranch = () => { + findFirstBranchDropdownItem().vm.$emit('click'); + }; + + const selectFirstTag = () => { + findFirstTagDropdownItem().vm.$emit('click'); + }; + + const selectFirstCommit = () => { + findFirstCommitDropdownItem().vm.$emit('click'); + }; + + const waitForRequests = ({ andClearMocks } = { andClearMocks: false }) => + axios.waitForAll().then(() => { + if (andClearMocks) { + branchesApiCallSpy.mockClear(); + tagsApiCallSpy.mockClear(); + commitApiCallSpy.mockClear(); + } + }); + + describe('initialization behavior', () => { + beforeEach(createComponent); + + it('initializes the dropdown with branches and tags when mounted', () => { + 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', () => { + expect(findLoadingIcon().exists()).toBe(true); + + return waitForRequests().then(() => { + expect(findLoadingIcon().exists()).toBe(false); + }); + }); + }); + + describe('post-initialization behavior', () => { + 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(() => { + branchesApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]); + tagsApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]); + commitApiCallSpy = jest.fn().mockReturnValue([404]); + + 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(() => { + createComponent(); + + return waitForRequests(); + }); + + it('renders the branches section in the dropdown', () => { + expect(findBranchesSection().exists()).toBe(true); + }); + + it('renders the "Branches" heading with a total number indicator', () => { + expect( + findBranchesSection() + .find('[data-testid="section-header"]') + .text(), + ).toBe('Branches 123'); + }); + + it("does not render an error message in the branches section's body", () => { + expect(branchesSectionContainsErrorMessage()).toBe(false); + }); + + it('renders each non-default branch as a selectable item', () => { + const dropdownItems = findBranchDropdownItems(); + + fixtures.branches.forEach((b, i) => { + if (!b.default) { + expect(dropdownItems.at(i).text()).toBe(b.name); + } + }); + }); + + it('renders the default branch as a selectable item with a "default" badge', () => { + const dropdownItems = findBranchDropdownItems(); + + const defaultBranch = fixtures.branches.find(b => b.default); + 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(() => { + branchesApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]); + + 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(() => { + branchesApiCallSpy = jest.fn().mockReturnValue([500]); + + createComponent(); + + return waitForRequests(); + }); + + it('renders the branches section in the dropdown', () => { + expect(findBranchesSection().exists()).toBe(true); + }); + + it("renders an error message in the branches section's body", () => { + expect(branchesSectionContainsErrorMessage()).toBe(true); + }); + }); + }); + + describe('tags', () => { + describe('when the tags search returns results', () => { + beforeEach(() => { + createComponent(); + + 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', () => { + expect( + findTagsSection() + .find('[data-testid="section-header"]') + .text(), + ).toBe('Tags 456'); + }); + + it("does not render an error message in the tags section's body", () => { + expect(tagsSectionContainsErrorMessage()).toBe(false); + }); + + it('renders each tag as a selectable item', () => { + const dropdownItems = findTagDropdownItems(); + + fixtures.tags.forEach((t, i) => { + expect(dropdownItems.at(i).text()).toBe(t.name); + }); + }); + }); + + describe('when the tags search returns no results', () => { + beforeEach(() => { + tagsApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]); + + 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(() => { + tagsApiCallSpy = jest.fn().mockReturnValue([500]); + + createComponent(); + + return waitForRequests(); + }); + + it('renders the tags section in the dropdown', () => { + expect(findTagsSection().exists()).toBe(true); + }); + + it("renders an error message in the tags section's body", () => { + expect(tagsSectionContainsErrorMessage()).toBe(true); + }); + }); + }); + + 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', () => { + expect( + findCommitsSection() + .find('[data-testid="section-header"]') + .text(), + ).toBe('Commits 1'); + }); + + it("does not render an error message in the comits section's body", () => { + expect(commitsSectionContainsErrorMessage()).toBe(false); + }); + + it('renders each commit as a selectable item with the short SHA and commit title', () => { + const dropdownItems = findCommitDropdownItems(); + + const { commit } = fixtures; + + expect(dropdownItems.at(0).text()).toBe(`${commit.short_id} ${commit.title}`); + }); + }); + + describe('when the commit search returns no results (i.e. a 404)', () => { + beforeEach(() => { + commitApiCallSpy = jest.fn().mockReturnValue([404]); + + 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(() => { + commitApiCallSpy = jest.fn().mockReturnValue([500]); + + createComponent(); + + updateQuery('abcd1234'); + + return waitForRequests(); + }); + + it('renders the commits section in the dropdown', () => { + expect(findCommitsSection().exists()).toBe(true); + }); + + it("renders an error message in the commits section's body", () => { + expect(commitsSectionContainsErrorMessage()).toBe(true); + }); + }); + }); + + describe('selection', () => { + beforeEach(() => { + createComponent(); + + updateQuery(fixtures.commit.short_id); + + return waitForRequests(); + }); + + it('renders a checkmark by the selected item', () => { + expect(findFirstBranchDropdownItem().find(GlIcon).element).toHaveClass( + 'gl-visibility-hidden', + ); + + selectFirstBranch(); + + return localVue.nextTick().then(() => { + expect(findFirstBranchDropdownItem().find(GlIcon).element).not.toHaveClass( + 'gl-visibility-hidden', + ); + }); + }); + + describe('when a branch is seleceted', () => { + it("displays the branch name in the dropdown's button", () => { + expect(findButtonContent().text()).toBe(DEFAULT_I18N.noRefSelected); + + selectFirstBranch(); + + return localVue.nextTick().then(() => { + expect(findButtonContent().text()).toBe(fixtures.branches[0].name); + }); + }); + + it("updates the v-model binding with the branch's name", () => { + expect(wrapper.vm.value).toEqual(''); + + selectFirstBranch(); + + expect(wrapper.vm.value).toEqual(fixtures.branches[0].name); + }); + }); + + describe('when a tag is seleceted', () => { + it("displays the tag name in the dropdown's button", () => { + expect(findButtonContent().text()).toBe(DEFAULT_I18N.noRefSelected); + + selectFirstTag(); + + return localVue.nextTick().then(() => { + expect(findButtonContent().text()).toBe(fixtures.tags[0].name); + }); + }); + + it("updates the v-model binding with the tag's name", () => { + expect(wrapper.vm.value).toEqual(''); + + selectFirstTag(); + + expect(wrapper.vm.value).toEqual(fixtures.tags[0].name); + }); + }); + + describe('when a commit is selected', () => { + it("displays the full SHA in the dropdown's button", () => { + expect(findButtonContent().text()).toBe(DEFAULT_I18N.noRefSelected); + + selectFirstCommit(); + + return localVue.nextTick().then(() => { + expect(findButtonContent().text()).toBe(fixtures.commit.id); + }); + }); + + it("updates the v-model binding with the commit's full SHA", () => { + expect(wrapper.vm.value).toEqual(''); + + selectFirstCommit(); + + expect(wrapper.vm.value).toEqual(fixtures.commit.id); + }); + }); + }); + }); +}); |