diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /spec/frontend/projects | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'spec/frontend/projects')
16 files changed, 440 insertions, 124 deletions
diff --git a/spec/frontend/projects/commit/components/branches_dropdown_spec.js b/spec/frontend/projects/commit/components/branches_dropdown_spec.js index 7686c28c7fc..ab84c3768d0 100644 --- a/spec/frontend/projects/commit/components/branches_dropdown_spec.js +++ b/spec/frontend/projects/commit/components/branches_dropdown_spec.js @@ -15,7 +15,7 @@ describe('BranchesDropdown', () => { const createComponent = (term, state = { isFetching: false }) => { store = new Vuex.Store({ getters: { - joinedBranches: () => ['_master_', '_branch_1_', '_branch_2_'], + joinedBranches: () => ['_main_', '_branch_1_', '_branch_2_'], }, actions: { fetchBranches: spyFetchBranches, @@ -94,13 +94,13 @@ describe('BranchesDropdown', () => { it('renders all branches when search term is empty', () => { expect(findAllDropdownItems()).toHaveLength(3); - expect(findDropdownItemByIndex(0).text()).toBe('_master_'); + expect(findDropdownItemByIndex(0).text()).toBe('_main_'); expect(findDropdownItemByIndex(1).text()).toBe('_branch_1_'); expect(findDropdownItemByIndex(2).text()).toBe('_branch_2_'); }); it('should not be selected on the inactive branch', () => { - expect(wrapper.vm.isSelected('_master_')).toBe(false); + expect(wrapper.vm.isSelected('_main_')).toBe(false); }); }); diff --git a/spec/frontend/projects/commit/components/commit_comments_button_spec.js b/spec/frontend/projects/commit/components/commit_comments_button_spec.js new file mode 100644 index 00000000000..873270c5be1 --- /dev/null +++ b/spec/frontend/projects/commit/components/commit_comments_button_spec.js @@ -0,0 +1,42 @@ +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import CommitCommentsButton from '~/projects/commit/components/commit_comments_button.vue'; + +describe('CommitCommentsButton', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = extendedWrapper( + shallowMount(CommitCommentsButton, { + propsData: { + commentsCount: 1, + ...props, + }, + }), + ); + }; + + const tooltip = () => wrapper.findByTestId('comment-button-wrapper'); + + describe('Comment Button', () => { + it('has proper tooltip and button attributes for 1 comment', () => { + createComponent(); + + expect(tooltip().attributes('title')).toBe('1 comment on this commit'); + expect(tooltip().text()).toBe('1'); + }); + + it('has proper tooltip and button attributes for multiple comments', () => { + createComponent({ commentsCount: 2 }); + + expect(tooltip().attributes('title')).toBe('2 comments on this commit'); + expect(tooltip().text()).toBe('2'); + }); + + it('does not show when there are no comments', () => { + createComponent({ commentsCount: 0 }); + + expect(tooltip().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/projects/commit/components/commit_options_dropdown_spec.js b/spec/frontend/projects/commit/components/commit_options_dropdown_spec.js new file mode 100644 index 00000000000..70491405986 --- /dev/null +++ b/spec/frontend/projects/commit/components/commit_options_dropdown_spec.js @@ -0,0 +1,123 @@ +import { GlDropdownDivider, GlDropdownSectionHeader } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import CommitOptionsDropdown from '~/projects/commit/components/commit_options_dropdown.vue'; +import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '~/projects/commit/constants'; +import eventHub from '~/projects/commit/event_hub'; + +describe('BranchesDropdown', () => { + let wrapper; + const provide = { + newProjectTagPath: '_new_project_tag_path_', + emailPatchesPath: '_email_patches_path_', + plainDiffPath: '_plain_diff_path_', + }; + + const createComponent = (props = {}) => { + wrapper = extendedWrapper( + shallowMount(CommitOptionsDropdown, { + provide, + propsData: { + canRevert: true, + canCherryPick: true, + canTag: true, + canEmailPatches: true, + ...props, + }, + }), + ); + }; + + const findRevertLink = () => wrapper.findByTestId('revert-link'); + const findCherryPickLink = () => wrapper.findByTestId('cherry-pick-link'); + const findTagItem = () => wrapper.findByTestId('tag-link'); + const findEmailPatchesItem = () => wrapper.findByTestId('email-patches-link'); + const findPlainDiffItem = () => wrapper.findByTestId('plain-diff-link'); + const findDivider = () => wrapper.findComponent(GlDropdownDivider); + const findSectionHeader = () => wrapper.findComponent(GlDropdownSectionHeader); + + describe('Everything enabled', () => { + beforeEach(() => { + createComponent(); + }); + + it('has expected dropdown button text', () => { + expect(wrapper.attributes('text')).toBe('Options'); + }); + + it('has expected items', () => { + expect( + [ + findRevertLink().exists(), + findCherryPickLink().exists(), + findTagItem().exists(), + findDivider().exists(), + findSectionHeader().exists(), + findEmailPatchesItem().exists(), + findPlainDiffItem().exists(), + ].every((exists) => exists), + ).toBe(true); + }); + + it('has expected href links', () => { + expect(findTagItem().attributes('href')).toBe(provide.newProjectTagPath); + expect(findEmailPatchesItem().attributes('href')).toBe(provide.emailPatchesPath); + expect(findPlainDiffItem().attributes('href')).toBe(provide.plainDiffPath); + }); + }); + + describe('Different dropdown item permutations', () => { + it('does not have a revert option', () => { + createComponent({ canRevert: false }); + + expect(findRevertLink().exists()).toBe(false); + }); + + it('does not have a cherry-pick option', () => { + createComponent({ canCherryPick: false }); + + expect(findCherryPickLink().exists()).toBe(false); + }); + + it('does not have a tag option', () => { + createComponent({ canTag: false }); + + expect(findTagItem().exists()).toBe(false); + }); + + it('does not have a email patches options', () => { + createComponent({ canEmailPatches: false }); + + expect(findEmailPatchesItem().exists()).toBe(false); + }); + + it('only has the download items', () => { + createComponent({ canRevert: false, canCherryPick: false, canTag: false }); + + expect(findDivider().exists()).toBe(false); + expect(findEmailPatchesItem().exists()).toBe(true); + expect(findPlainDiffItem().exists()).toBe(true); + }); + }); + + describe('Modal triggering', () => { + let spy; + + beforeEach(() => { + spy = jest.spyOn(eventHub, '$emit'); + createComponent(); + }); + + it('emits openModal for revert', () => { + findRevertLink().vm.$emit('click'); + + expect(spy).toHaveBeenCalledWith(OPEN_REVERT_MODAL); + }); + + it('emits openModal for cherry-pick', () => { + findCherryPickLink().vm.$emit('click'); + + expect(spy).toHaveBeenCalledWith(OPEN_CHERRY_PICK_MODAL); + }); + }); +}); diff --git a/spec/frontend/projects/commit/components/form_modal_spec.js b/spec/frontend/projects/commit/components/form_modal_spec.js index 708644cb7ee..9688cb47799 100644 --- a/spec/frontend/projects/commit/components/form_modal_spec.js +++ b/spec/frontend/projects/commit/components/form_modal_spec.js @@ -17,15 +17,14 @@ describe('CommitFormModal', () => { let store; let axiosMock; - const createComponent = (method, state = {}, provide = {}) => { + const createComponent = (method, state = {}, provide = {}, propsData = {}) => { store = createStore({ ...mockData.mockModal, ...state }); wrapper = extendedWrapper( method(CommitFormModal, { provide: { ...provide, - glFeatures: { pickIntoProject: true }, }, - propsData: { ...mockData.modalPropsData }, + propsData: { ...mockData.modalPropsData, ...propsData }, store, attrs: { static: true, @@ -160,6 +159,12 @@ describe('CommitFormModal', () => { }); it('Changes the target_project_id input value', async () => { + createComponent( + shallowMount, + {}, + { glFeatures: { pickIntoProject: true } }, + { isCherryPick: true }, + ); findProjectsDropdown().vm.$emit('selectProject', '_changed_project_value_'); await wrapper.vm.$nextTick(); diff --git a/spec/frontend/projects/commit/components/form_trigger_spec.js b/spec/frontend/projects/commit/components/form_trigger_spec.js deleted file mode 100644 index 4503493c0a6..00000000000 --- a/spec/frontend/projects/commit/components/form_trigger_spec.js +++ /dev/null @@ -1,44 +0,0 @@ -import { GlLink } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import FormTrigger from '~/projects/commit/components/form_trigger.vue'; -import eventHub from '~/projects/commit/event_hub'; - -const displayText = '_display_text_'; - -const createComponent = () => { - return shallowMount(FormTrigger, { - provide: { displayText }, - propsData: { openModal: '_open_modal_' }, - }); -}; - -describe('FormTrigger', () => { - let wrapper; - let spy; - - beforeEach(() => { - spy = jest.spyOn(eventHub, '$emit'); - wrapper = createComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - const findLink = () => wrapper.find(GlLink); - - describe('displayText', () => { - it('includes the correct displayText for the link', () => { - expect(findLink().text()).toBe(displayText); - }); - }); - - describe('clicking the link', () => { - it('emits openModal', () => { - findLink().vm.$emit('click'); - - expect(spy).toHaveBeenCalledWith('_open_modal_'); - }); - }); -}); diff --git a/spec/frontend/projects/commit/mock_data.js b/spec/frontend/projects/commit/mock_data.js index e4dcb24c4c0..34e9c400af4 100644 --- a/spec/frontend/projects/commit/mock_data.js +++ b/spec/frontend/projects/commit/mock_data.js @@ -23,6 +23,6 @@ export default { modalId: '_modal_id_', openModal: '_open_modal_', }, - mockBranches: ['_branch_1', '_abc_', '_master_'], + mockBranches: ['_branch_1', '_abc_', '_main_'], mockProjects: ['_project_1', '_abc_', '_project_'], }; diff --git a/spec/frontend/projects/commit/store/mutations_spec.js b/spec/frontend/projects/commit/store/mutations_spec.js index 8989e769772..60abf0fddad 100644 --- a/spec/frontend/projects/commit/store/mutations_spec.js +++ b/spec/frontend/projects/commit/store/mutations_spec.js @@ -27,7 +27,7 @@ describe('Commit form modal mutations', () => { describe('CLEAR_MODAL', () => { it('should clear modal state ', () => { - stateCopy = { branch: '_master_', defaultBranch: '_default_branch_' }; + stateCopy = { branch: '_main_', defaultBranch: '_default_branch_' }; mutations[types.CLEAR_MODAL](stateCopy); @@ -47,7 +47,7 @@ describe('Commit form modal mutations', () => { describe('SET_BRANCH', () => { it('should set branch', () => { - stateCopy = { branch: '_master_' }; + stateCopy = { branch: '_main_' }; mutations[types.SET_BRANCH](stateCopy, '_changed_branch_'); @@ -57,7 +57,7 @@ describe('Commit form modal mutations', () => { describe('SET_SELECTED_BRANCH', () => { it('should set selectedBranch', () => { - stateCopy = { selectedBranch: '_master_' }; + stateCopy = { selectedBranch: '_main_' }; mutations[types.SET_SELECTED_BRANCH](stateCopy, '_changed_branch_'); diff --git a/spec/frontend/projects/commit_box/info/load_branches_spec.js b/spec/frontend/projects/commit_box/info/load_branches_spec.js index 8100200cbdd..9456e6ef5f5 100644 --- a/spec/frontend/projects/commit_box/info/load_branches_spec.js +++ b/spec/frontend/projects/commit_box/info/load_branches_spec.js @@ -1,66 +1,73 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; +import { setHTMLFixture } from 'helpers/fixtures'; import waitForPromises from 'helpers/wait_for_promises'; import { loadBranches } from '~/projects/commit_box/info/load_branches'; const mockCommitPath = '/commit/abcd/branches'; const mockBranchesRes = - '<a href="/-/commits/master">master</a><span><a href="/-/commits/my-branch">my-branch</a></span>'; + '<a href="/-/commits/main">main</a><span><a href="/-/commits/my-branch">my-branch</a></span>'; describe('~/projects/commit_box/info/load_branches', () => { let mock; - let el; + + const getElInnerHtml = () => document.querySelector('.js-commit-box-info').innerHTML; beforeEach(() => { + setHTMLFixture(` + <div class="js-commit-box-info" data-commit-path="${mockCommitPath}"> + <div class="commit-info branches"> + <span class="spinner"/> + </div> + </div>`); + mock = new MockAdapter(axios); mock.onGet(mockCommitPath).reply(200, mockBranchesRes); - - el = document.createElement('div'); - el.dataset.commitPath = mockCommitPath; - el.innerHTML = '<div class="commit-info branches"><span class="spinner"/></div>'; }); it('loads and renders branches info', async () => { - loadBranches(el); + loadBranches(); await waitForPromises(); - expect(el.innerHTML).toBe(`<div class="commit-info branches">${mockBranchesRes}</div>`); + expect(getElInnerHtml()).toMatchInterpolatedText( + `<div class="commit-info branches">${mockBranchesRes}</div>`, + ); }); it('does not load when no container is provided', async () => { - loadBranches(null); + loadBranches('.js-another-class'); await waitForPromises(); expect(mock.history.get).toHaveLength(0); }); - describe('when braches request returns unsafe content', () => { + describe('when branches request returns unsafe content', () => { beforeEach(() => { mock .onGet(mockCommitPath) - .reply(200, '<a onload="alert(\'xss!\');" href="/-/commits/master">master</a>'); + .reply(200, '<a onload="alert(\'xss!\');" href="/-/commits/main">main</a>'); }); it('displays sanitized html', async () => { - loadBranches(el); + loadBranches(); await waitForPromises(); - expect(el.innerHTML).toBe( - '<div class="commit-info branches"><a href="/-/commits/master">master</a></div>', + expect(getElInnerHtml()).toMatchInterpolatedText( + '<div class="commit-info branches"><a href="/-/commits/main">main</a></div>', ); }); }); - describe('when braches request fails', () => { + describe('when branches request fails', () => { beforeEach(() => { mock.onGet(mockCommitPath).reply(500, 'Error!'); }); it('attempts to load and renders an error', async () => { - loadBranches(el); + loadBranches(); await waitForPromises(); - expect(el.innerHTML).toBe( + expect(getElInnerHtml()).toMatchInterpolatedText( '<div class="commit-info branches">Failed to load branches. Please try again.</div>', ); }); diff --git a/spec/frontend/projects/compare/components/app_legacy_spec.js b/spec/frontend/projects/compare/components/app_legacy_spec.js index 4c7f0d5cccc..93e96c8b9f7 100644 --- a/spec/frontend/projects/compare/components/app_legacy_spec.js +++ b/spec/frontend/projects/compare/components/app_legacy_spec.js @@ -8,7 +8,7 @@ jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); const projectCompareIndexPath = 'some/path'; const refsProjectPath = 'some/refs/path'; const paramsFrom = 'master'; -const paramsTo = 'master'; +const paramsTo = 'some-other-branch'; describe('CompareApp component', () => { let wrapper; @@ -36,6 +36,9 @@ describe('CompareApp component', () => { createComponent(); }); + const findSourceDropdown = () => wrapper.find('[data-testid="sourceRevisionDropdown"]'); + const findTargetDropdown = () => wrapper.find('[data-testid="targetRevisionDropdown"]'); + it('renders component with prop', () => { expect(wrapper.props()).toEqual( expect.objectContaining({ @@ -62,12 +65,31 @@ describe('CompareApp component', () => { expect(wrapper.find('[data-testid="ellipsis"]').exists()).toBe(true); }); - it('render Source and Target BranchDropdown components', () => { - const branchDropdowns = wrapper.findAll(RevisionDropdown); + describe('Source and Target BranchDropdown components', () => { + const findAllBranchDropdowns = () => wrapper.findAll(RevisionDropdown); + + it('renders the components with the correct props', () => { + expect(findAllBranchDropdowns().length).toBe(2); + expect(findSourceDropdown().props('revisionText')).toBe('Source'); + expect(findTargetDropdown().props('revisionText')).toBe('Target'); + }); + + it('sets the revision when the "selectRevision" event is emitted', async () => { + findSourceDropdown().vm.$emit('selectRevision', { + direction: 'to', + revision: 'some-source-revision', + }); + + findTargetDropdown().vm.$emit('selectRevision', { + direction: 'from', + revision: 'some-target-revision', + }); + + await wrapper.vm.$nextTick(); - expect(branchDropdowns.length).toBe(2); - expect(branchDropdowns.at(0).props('revisionText')).toBe('Source'); - expect(branchDropdowns.at(1).props('revisionText')).toBe('Target'); + expect(findTargetDropdown().props('paramsBranch')).toBe('some-target-revision'); + expect(findSourceDropdown().props('paramsBranch')).toBe('some-source-revision'); + }); }); describe('compare button', () => { @@ -87,6 +109,27 @@ describe('CompareApp component', () => { }); }); + describe('swap revisions button', () => { + const findSwapRevisionsButton = () => wrapper.find('[data-testid="swapRevisionsButton"]'); + + it('renders the swap revisions button', () => { + expect(findSwapRevisionsButton().exists()).toBe(true); + }); + + it('has the correct text', () => { + expect(findSwapRevisionsButton().text()).toBe('Swap revisions'); + }); + + it('swaps revisions when clicked', async () => { + findSwapRevisionsButton().vm.$emit('click'); + + await wrapper.vm.$nextTick(); + + expect(findTargetDropdown().props('paramsBranch')).toBe(paramsTo); + expect(findSourceDropdown().props('paramsBranch')).toBe(paramsFrom); + }); + }); + describe('merge request buttons', () => { const findProjectMrButton = () => wrapper.find('[data-testid="projectMrButton"]'); const findCreateMrButton = () => wrapper.find('[data-testid="createMrButton"]'); diff --git a/spec/frontend/projects/compare/components/repo_dropdown_spec.js b/spec/frontend/projects/compare/components/repo_dropdown_spec.js index af76632515c..df8fea8fd32 100644 --- a/spec/frontend/projects/compare/components/repo_dropdown_spec.js +++ b/spec/frontend/projects/compare/components/repo_dropdown_spec.js @@ -1,4 +1,4 @@ -import { GlDropdown } from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import RepoDropdown from '~/projects/compare/components/repo_dropdown.vue'; @@ -69,12 +69,12 @@ describe('RepoDropdown component', () => { createComponent({ paramsName: 'from' }); }); - it('set hidden input of the first project', () => { - expect(findHiddenInput().attributes('value')).toBe(projectFromId); + it('set hidden input of the selected project', () => { + expect(findHiddenInput().attributes('value')).toBe(projectToId); }); - it('displays the first project name initially in the dropdown', () => { - expect(findGlDropdown().props('text')).toBe(projectFromName); + it('displays matching project name of the source revision initially in the dropdown', () => { + expect(findGlDropdown().props('text')).toBe(projectToName); }); it('updates the hiddin input value when onClick method is triggered', async () => { @@ -84,15 +84,13 @@ describe('RepoDropdown component', () => { expect(findHiddenInput().attributes('value')).toBe(repoId); }); - it('emits initial `changeTargetProject` event with target project', () => { - expect(wrapper.emitted('changeTargetProject')).toEqual([[projectFromName]]); - }); - it('emits `changeTargetProject` event when another target project is selected', async () => { - const newTargetProject = 'new-from-name'; - wrapper.vm.$emit('changeTargetProject', newTargetProject); + const index = 1; + const { projectsFrom } = defaultProvide; + findGlDropdown().findAll(GlDropdownItem).at(index).vm.$emit('click'); await wrapper.vm.$nextTick(); - expect(wrapper.emitted('changeTargetProject')[1]).toEqual([newTargetProject]); + + expect(wrapper.emitted('changeTargetProject')[0][0]).toEqual(projectsFrom[index].name); }); }); }); diff --git a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js index 270c89e674c..ca208395e82 100644 --- a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js +++ b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js @@ -1,4 +1,4 @@ -import { GlDropdown } from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; import createFlash from '~/flash'; @@ -29,6 +29,7 @@ describe('RevisionDropdown component', () => { beforeEach(() => { axiosMock = new AxiosMockAdapter(axios); + createComponent(); }); afterEach(() => { @@ -39,7 +40,6 @@ describe('RevisionDropdown component', () => { const findGlDropdown = () => wrapper.find(GlDropdown); it('sets hidden input', () => { - createComponent(); expect(wrapper.find('input[type="hidden"]').attributes('value')).toBe( defaultProps.paramsBranch, ); @@ -68,8 +68,6 @@ describe('RevisionDropdown component', () => { Tags: undefined, }); - createComponent(); - await axios.waitForAll(); expect(wrapper.vm.branches).toEqual([]); @@ -79,15 +77,12 @@ describe('RevisionDropdown component', () => { it('shows flash message on error', async () => { axiosMock.onGet('some/invalid/path').replyOnce(404); - createComponent(); - await wrapper.vm.fetchBranchesAndTags(); expect(createFlash).toHaveBeenCalled(); }); describe('GlDropdown component', () => { it('renders props', () => { - createComponent(); expect(wrapper.props()).toEqual(expect.objectContaining(defaultProps)); }); @@ -99,8 +94,22 @@ describe('RevisionDropdown component', () => { }); it('display params branch text', () => { - createComponent(); expect(findGlDropdown().props('text')).toBe(defaultProps.paramsBranch); }); + + it('emits a "selectRevision" event when a revision is selected', async () => { + const findGlDropdownItems = () => wrapper.findAll(GlDropdownItem); + const findFirstGlDropdownItem = () => findGlDropdownItems().at(0); + + wrapper.setData({ branches: ['some-branch'] }); + + await wrapper.vm.$nextTick(); + + findFirstGlDropdownItem().vm.$emit('click'); + + expect(wrapper.emitted()).toEqual({ + selectRevision: [[{ direction: 'from', revision: 'some-branch' }]], + }); + }); }); }); diff --git a/spec/frontend/projects/compare/components/revision_dropdown_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_spec.js index 69d3167c99c..aab9607ceae 100644 --- a/spec/frontend/projects/compare/components/revision_dropdown_spec.js +++ b/spec/frontend/projects/compare/components/revision_dropdown_spec.js @@ -1,4 +1,4 @@ -import { GlDropdown } from '@gitlab/ui'; +import { GlDropdown, GlSearchBoxByType } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; import createFlash from '~/flash'; @@ -23,6 +23,10 @@ describe('RevisionDropdown component', () => { ...defaultProps, ...props, }, + stubs: { + GlDropdown, + GlSearchBoxByType, + }, }); }; @@ -36,6 +40,7 @@ describe('RevisionDropdown component', () => { }); const findGlDropdown = () => wrapper.find(GlDropdown); + const findSearchBox = () => wrapper.find(GlSearchBoxByType); it('sets hidden input', () => { createComponent(); @@ -85,6 +90,40 @@ describe('RevisionDropdown component', () => { expect(axios.get).toHaveBeenLastCalledWith(newRefsProjectPath); }); + describe('search', () => { + it('shows flash message on error', async () => { + axiosMock.onGet('some/invalid/path').replyOnce(404); + + createComponent(); + + await wrapper.vm.searchBranchesAndTags(); + expect(createFlash).toHaveBeenCalled(); + }); + + it('makes request with search param', async () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: { + Branches: [], + Tags: [], + }, + }); + + const mockSearchTerm = 'foobar'; + createComponent(); + findSearchBox().vm.$emit('input', mockSearchTerm); + await axios.waitForAll(); + + expect(axios.get).toHaveBeenCalledWith( + defaultProps.refsProjectPath, + expect.objectContaining({ + params: { + search: mockSearchTerm, + }, + }), + ); + }); + }); + describe('GlDropdown component', () => { it('renders props', () => { createComponent(); diff --git a/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js index b4ae50341d4..204e7a7c394 100644 --- a/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js +++ b/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js @@ -1,5 +1,6 @@ import { GlBreadcrumb } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import { assignGitlabExperiment } from 'helpers/experimentation_helper'; import App from '~/projects/experiment_new_project_creation/components/app.vue'; import LegacyContainer from '~/projects/experiment_new_project_creation/components/legacy_container.vue'; import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue'; @@ -17,6 +18,57 @@ describe('Experimental new project creation app', () => { wrapper = null; }); + const findWelcomePage = () => wrapper.findComponent(WelcomePage); + const findPanel = (panelName) => + findWelcomePage() + .props() + .panels.find((p) => p.name === panelName); + const findPanelHeader = () => wrapper.find('h4'); + + describe('new_repo experiment', () => { + describe('when in the candidate variant', () => { + assignGitlabExperiment('new_repo', 'candidate'); + + it('has "repository" in the panel title', () => { + createComponent(); + + expect(findPanel('blank_project').title).toBe('Create blank project/repository'); + }); + + describe('when hash is not empty on load', () => { + beforeEach(() => { + window.location.hash = '#blank_project'; + createComponent(); + }); + + it('renders "project/repository"', () => { + expect(findPanelHeader().text()).toBe('Create blank project/repository'); + }); + }); + }); + + describe('when in the control variant', () => { + assignGitlabExperiment('new_repo', 'control'); + + it('has "project" in the panel title', () => { + createComponent(); + + expect(findPanel('blank_project').title).toBe('Create blank project'); + }); + + describe('when hash is not empty on load', () => { + beforeEach(() => { + window.location.hash = '#blank_project'; + createComponent(); + }); + + it('renders "project"', () => { + expect(findPanelHeader().text()).toBe('Create blank project'); + }); + }); + }); + }); + describe('with empty hash', () => { beforeEach(() => { createComponent(); diff --git a/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js index f26d1a6d2a3..9fd1230806e 100644 --- a/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js +++ b/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js @@ -1,8 +1,13 @@ import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; import { mockTracking } from 'helpers/tracking_helper'; +import { TRACKING_CONTEXT_SCHEMA } from '~/experimentation/constants'; +import { getExperimentData } from '~/experimentation/utils'; import NewProjectPushTipPopover from '~/projects/experiment_new_project_creation/components/new_project_push_tip_popover.vue'; import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue'; +jest.mock('~/experimentation/utils', () => ({ getExperimentData: jest.fn() })); + describe('Welcome page', () => { let wrapper; let trackingSpy; @@ -14,6 +19,7 @@ describe('Welcome page', () => { beforeEach(() => { trackingSpy = mockTracking('_category_', document, jest.spyOn); trackingSpy.mockImplementation(() => {}); + getExperimentData.mockReturnValue(undefined); }); afterEach(() => { @@ -22,14 +28,35 @@ describe('Welcome page', () => { wrapper = null; }); - it('tracks link clicks', () => { + it('tracks link clicks', async () => { createComponent({ panels: [{ name: 'test', href: '#' }] }); - wrapper.find('a').trigger('click'); + const link = wrapper.find('a'); + link.trigger('click'); + await nextTick(); return wrapper.vm.$nextTick().then(() => { expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', { label: 'test' }); }); }); + it('adds new_repo experiment data if in experiment', async () => { + const mockExperimentData = 'data'; + getExperimentData.mockReturnValue(mockExperimentData); + + createComponent({ panels: [{ name: 'test', href: '#' }] }); + const link = wrapper.find('a'); + link.trigger('click'); + await nextTick(); + return wrapper.vm.$nextTick().then(() => { + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', { + label: 'test', + context: { + data: mockExperimentData, + schema: TRACKING_CONTEXT_SCHEMA, + }, + }); + }); + }); + it('renders new project push tip popover', () => { createComponent({ panels: [{ name: 'test', href: '#' }] }); diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap index fc51825f15b..c37f6415898 100644 --- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap +++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap @@ -21,7 +21,11 @@ exports[`CiCdAnalyticsAreaChart matches the snapshot 1`] = ` option="[object Object]" thresholds="" width="0" - /> + > + <template /> + + <template /> + </glareachart-stub> </div> </div> `; diff --git a/spec/frontend/projects/pipelines/charts/components/app_spec.js b/spec/frontend/projects/pipelines/charts/components/app_spec.js index e8aace14db4..0cf05d4ac37 100644 --- a/spec/frontend/projects/pipelines/charts/components/app_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/app_spec.js @@ -10,6 +10,7 @@ import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_char jest.mock('~/lib/utils/url_utility'); const DeploymentFrequencyChartsStub = { name: 'DeploymentFrequencyCharts', render: () => {} }; +const LeadTimeChartsStub = { name: 'LeadTimeCharts', render: () => {} }; describe('ProjectsPipelinesChartsApp', () => { let wrapper; @@ -21,10 +22,11 @@ describe('ProjectsPipelinesChartsApp', () => { {}, { provide: { - shouldRenderDeploymentFrequencyCharts: false, + shouldRenderDoraCharts: true, }, stubs: { DeploymentFrequencyCharts: DeploymentFrequencyChartsStub, + LeadTimeCharts: LeadTimeChartsStub, }, }, mountOptions, @@ -32,37 +34,42 @@ describe('ProjectsPipelinesChartsApp', () => { ); } - beforeEach(() => { - createComponent(); - }); - afterEach(() => { wrapper.destroy(); - wrapper = null; }); const findGlTabs = () => wrapper.find(GlTabs); - const findAllGlTab = () => wrapper.findAll(GlTab); - const findGlTabAt = (i) => findAllGlTab().at(i); + const findAllGlTabs = () => wrapper.findAll(GlTab); + const findGlTabAtIndex = (index) => findAllGlTabs().at(index); + const findLeadTimeCharts = () => wrapper.find(LeadTimeChartsStub); const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub); const findPipelineCharts = () => wrapper.find(PipelineCharts); - it('renders the pipeline charts', () => { - expect(findPipelineCharts().exists()).toBe(true); - }); - - describe('when shouldRenderDeploymentFrequencyCharts is true', () => { + describe('when all charts are available', () => { beforeEach(() => { - createComponent({ provide: { shouldRenderDeploymentFrequencyCharts: true } }); + createComponent(); }); - it('renders the deployment frequency charts in a tab', () => { + it('renders tabs', () => { expect(findGlTabs().exists()).toBe(true); - expect(findGlTabAt(0).attributes('title')).toBe('Pipelines'); - expect(findGlTabAt(1).attributes('title')).toBe('Deployments'); + + expect(findGlTabAtIndex(0).attributes('title')).toBe('Pipelines'); + expect(findGlTabAtIndex(1).attributes('title')).toBe('Deployments'); + expect(findGlTabAtIndex(2).attributes('title')).toBe('Lead Time'); + }); + + it('renders the pipeline charts', () => { + expect(findPipelineCharts().exists()).toBe(true); + }); + + it('renders the deployment frequency charts', () => { expect(findDeploymentFrequencyCharts().exists()).toBe(true); }); + it('renders the lead time charts', () => { + expect(findLeadTimeCharts().exists()).toBe(true); + }); + it('sets the tab and url when a tab is clicked', async () => { let chartsPath; setWindowLocation(`${TEST_HOST}/gitlab-org/gitlab-test/-/pipelines/charts`); @@ -108,6 +115,7 @@ describe('ProjectsPipelinesChartsApp', () => { describe('when provided with a query param', () => { it.each` chart | tab + ${'lead-time'} | ${'2'} ${'deployments'} | ${'1'} ${'pipelines'} | ${'0'} ${'fake'} | ${'0'} @@ -118,7 +126,7 @@ describe('ProjectsPipelinesChartsApp', () => { expect(name).toBe('chart'); return chart ? [chart] : []; }); - createComponent({ provide: { shouldRenderDeploymentFrequencyCharts: true } }); + createComponent(); expect(findGlTabs().attributes('value')).toBe(tab); }); @@ -138,7 +146,7 @@ describe('ProjectsPipelinesChartsApp', () => { return []; }); - createComponent({ provide: { shouldRenderDeploymentFrequencyCharts: true } }); + createComponent(); expect(findGlTabs().attributes('value')).toBe('0'); @@ -155,14 +163,17 @@ describe('ProjectsPipelinesChartsApp', () => { }); }); - describe('when shouldRenderDeploymentFrequencyCharts is false', () => { + describe('when the dora charts are not available', () => { beforeEach(() => { - createComponent({ provide: { shouldRenderDeploymentFrequencyCharts: false } }); + createComponent({ provide: { shouldRenderDoraCharts: false } }); }); - it('does not render the deployment frequency charts in a tab', () => { + it('does not render tabs', () => { expect(findGlTabs().exists()).toBe(false); - expect(findDeploymentFrequencyCharts().exists()).toBe(false); + }); + + it('renders the pipeline charts', () => { + expect(findPipelineCharts().exists()).toBe(true); }); }); }); |