diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-17 00:09:14 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-17 00:09:14 +0300 |
commit | 06b21ad63ca4f7d0cada7cc44b9cddbf17df2193 (patch) | |
tree | 9f79eb73e6335bc2bef652aaeb19691bc6ae44df /spec/frontend | |
parent | 204df35415f2b0ed86c83b31b1d276f52e07e577 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
10 files changed, 271 insertions, 79 deletions
diff --git a/spec/frontend/diffs/components/hidden_files_warning_spec.js b/spec/frontend/diffs/components/hidden_files_warning_spec.js index 3f1f23a40f5..bbd4f5faeec 100644 --- a/spec/frontend/diffs/components/hidden_files_warning_spec.js +++ b/spec/frontend/diffs/components/hidden_files_warning_spec.js @@ -1,4 +1,6 @@ -import { shallowMount } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; +import { __ } from '~/locale'; import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue'; const propsData = { @@ -12,7 +14,7 @@ describe('HiddenFilesWarning', () => { let wrapper; const createComponent = () => { - wrapper = shallowMount(HiddenFilesWarning, { + wrapper = mount(HiddenFilesWarning, { propsData, }); }; @@ -26,22 +28,20 @@ describe('HiddenFilesWarning', () => { }); it('has a correct plain diff URL', () => { - const plainDiffLink = wrapper.findAll('a').wrappers.filter((x) => x.text() === 'Plain diff')[0]; + const plainDiffLink = wrapper.findAllComponents(GlButton).at(0); expect(plainDiffLink.attributes('href')).toBe(propsData.plainDiffPath); }); it('has a correct email patch URL', () => { - const emailPatchLink = wrapper - .findAll('a') - .wrappers.filter((x) => x.text() === 'Email patch')[0]; + const emailPatchLink = wrapper.findAllComponents(GlButton).at(1); expect(emailPatchLink.attributes('href')).toBe(propsData.emailPatchPath); }); it('has a correct visible/total files text', () => { - const filesText = wrapper.find('strong'); - - expect(filesText.text()).toBe('5 of 10'); + expect(wrapper.text()).toContain( + __('To preserve performance only 5 of 10 files are displayed.'), + ); }); }); diff --git a/spec/frontend/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb index 1eb48c0ce2c..1eae854eca3 100644 --- a/spec/frontend/fixtures/merge_requests.rb +++ b/spec/frontend/fixtures/merge_requests.rb @@ -130,6 +130,25 @@ RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: expect(response).to be_successful end + describe GraphQL::Query, type: :request do + include ApiHelpers + include GraphqlHelpers + + context 'merge request in state readyToMerge query' do + base_input_path = 'vue_merge_request_widget/queries/states/' + base_output_path = 'graphql/merge_requests/states/' + query_name = 'ready_to_merge.query.graphql' + + it "#{base_output_path}#{query_name}.json" do + query = get_graphql_query_as_string("#{base_input_path}#{query_name}", ee: true) + + post_graphql(query, current_user: user, variables: { projectPath: project.full_path, iid: merge_request.iid.to_s }) + + expect_graphql_errors_to_be_empty + end + end + end + private def render_discussions_json(merge_request) diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js index 96c9baeb328..9a30fd5f5c3 100644 --- a/spec/frontend/ide/components/repo_editor_spec.js +++ b/spec/frontend/ide/components/repo_editor_spec.js @@ -169,12 +169,11 @@ describe('RepoEditor', () => { expect(findEditor().isVisible()).toBe(true); }); - it('renders only an edit tab', async () => { + it('renders no tabs', async () => { await createComponent(); const tabs = findTabs(); - expect(tabs).toHaveLength(1); - expect(tabs.at(0).text()).toBe('Edit'); + expect(tabs).toHaveLength(0); }); }); @@ -196,25 +195,48 @@ describe('RepoEditor', () => { mock.restore(); }); - it('renders an Edit and a Preview Tab', async () => { - await createComponent({ activeFile }); - const tabs = findTabs(); + describe('when files is markdown', () => { + let layoutSpy; - expect(tabs).toHaveLength(2); - expect(tabs.at(0).text()).toBe('Edit'); - expect(tabs.at(1).text()).toBe('Preview Markdown'); - }); + beforeEach(async () => { + await createComponent({ activeFile }); + layoutSpy = jest.spyOn(wrapper.vm.editor, 'layout'); + }); - it('renders markdown for tempFile', async () => { - // by default files created in the spec are temp: no need for explicitly sending the param - await createComponent({ activeFile }); + it('renders an Edit and a Preview Tab', () => { + const tabs = findTabs(); - findPreviewTab().trigger('click'); - await waitForPromises(); - expect(wrapper.find(ContentViewer).html()).toContain(defaultFileProps.content); + expect(tabs).toHaveLength(2); + expect(tabs.at(0).text()).toBe('Edit'); + expect(tabs.at(1).text()).toBe('Preview Markdown'); + }); + + it('renders markdown for tempFile', async () => { + findPreviewTab().trigger('click'); + await waitForPromises(); + expect(wrapper.find(ContentViewer).html()).toContain(defaultFileProps.content); + }); + + it('should not trigger layout', async () => { + expect(layoutSpy).not.toHaveBeenCalled(); + }); + + describe('when file changes to non-markdown file', () => { + beforeEach(async () => { + wrapper.setProps({ file: dummyFile.empty }); + }); + + it('should hide tabs', () => { + expect(findTabs()).toHaveLength(0); + }); + + it('should trigger refresh dimensions', async () => { + expect(layoutSpy).toHaveBeenCalledTimes(1); + }); + }); }); - it('shows no tabs when not in Edit mode', async () => { + it('when not in edit mode, shows no tabs', async () => { await createComponent({ state: { currentActivityView: leftSidebarViews.review.name, @@ -405,7 +427,7 @@ describe('RepoEditor', () => { it.each` mode | isVisible - ${'edit'} | ${true} + ${'edit'} | ${false} ${'review'} | ${false} ${'commit'} | ${false} `('tabs in $mode are $isVisible', async ({ mode, isVisible } = {}) => { diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js index 3fea08d5512..0be0bf89210 100644 --- a/spec/frontend/lib/utils/common_utils_spec.js +++ b/spec/frontend/lib/utils/common_utils_spec.js @@ -51,31 +51,6 @@ describe('common_utils', () => { }); }); - describe('parseUrl', () => { - it('returns an anchor tag with url', () => { - expect(commonUtils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url'); - }); - - it('url is escaped', () => { - // IE11 will return a relative pathname while other browsers will return a full pathname. - // parseUrl uses an anchor element for parsing an url. With relative urls, the anchor - // element will create an absolute url relative to the current execution context. - // The JavaScript test suite is executed at '/' which will lead to an absolute url - // starting with '/'. - expect(commonUtils.parseUrl('" test="asf"').pathname).toContain('/%22%20test=%22asf%22'); - }); - }); - - describe('parseUrlPathname', () => { - it('returns an absolute url when given an absolute url', () => { - expect(commonUtils.parseUrlPathname('/some/absolute/url')).toEqual('/some/absolute/url'); - }); - - it('returns an absolute url when given a relative url', () => { - expect(commonUtils.parseUrlPathname('some/relative/url')).toEqual('/some/relative/url'); - }); - }); - describe('handleLocationHash', () => { beforeEach(() => { jest.spyOn(window.document, 'getElementById'); diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js index c6edba19c56..7608cff4c9e 100644 --- a/spec/frontend/lib/utils/url_utility_spec.js +++ b/spec/frontend/lib/utils/url_utility_spec.js @@ -22,6 +22,27 @@ beforeEach(() => { }); describe('URL utility', () => { + describe('parseUrlPathname', () => { + it('returns an absolute url when given an absolute url', () => { + expect(urlUtils.parseUrlPathname('/some/absolute/url')).toBe('/some/absolute/url'); + }); + + it('returns an absolute url when given a relative url', () => { + expect(urlUtils.parseUrlPathname('some/relative/url')).toBe('/some/relative/url'); + }); + + it('returns an absolute url that includes the document.location path when given a relative url', () => { + // Change the location to see the `/mypath/` included in the result + setWindowLocation(`${TEST_HOST}/mypath/`); + + expect(urlUtils.parseUrlPathname('some/relative/url')).toBe('/mypath/some/relative/url'); + }); + + it('encodes certain character in the url', () => { + expect(urlUtils.parseUrlPathname('test="a b"')).toBe('/test=%22a%20b%22'); + }); + }); + describe('webIDEUrl', () => { afterEach(() => { gon.relative_url_root = ''; @@ -636,7 +657,7 @@ describe('URL utility', () => { `('returns "$expectation" with "$protocol" protocol', ({ protocol, expectation }) => { setWindowLocation(`${protocol}//example.com`); - expect(urlUtils.getWebSocketProtocol()).toEqual(expectation); + expect(urlUtils.getWebSocketProtocol()).toBe(expectation); }); }); @@ -646,7 +667,7 @@ describe('URL utility', () => { const path = '/lorem/ipsum?a=bc'; - expect(urlUtils.getWebSocketUrl(path)).toEqual('ws://example.com/lorem/ipsum?a=bc'); + expect(urlUtils.getWebSocketUrl(path)).toBe('ws://example.com/lorem/ipsum?a=bc'); }); }); @@ -696,7 +717,7 @@ describe('URL utility', () => { it('should return valid parameter', () => { setWindowLocation('?scope=all&p=2'); - expect(getParameterByName('p')).toEqual('2'); + expect(getParameterByName('p')).toBe('2'); expect(getParameterByName('scope')).toBe('all'); }); @@ -737,7 +758,7 @@ describe('URL utility', () => { it('converts search query object back into a search query', () => { const searchQueryObject = { one: '1', two: '2' }; - expect(urlUtils.objectToQuery(searchQueryObject)).toEqual('one=1&two=2'); + expect(urlUtils.objectToQuery(searchQueryObject)).toBe('one=1&two=2'); }); it('returns empty string when `params` is undefined, null or empty string', () => { @@ -833,15 +854,15 @@ describe('URL utility', () => { it('adds new params as query string', () => { const url = 'https://gitlab.com/test'; - expect( - urlUtils.setUrlParams({ group_id: 'gitlab-org', project_id: 'my-project' }, url), - ).toEqual('https://gitlab.com/test?group_id=gitlab-org&project_id=my-project'); + expect(urlUtils.setUrlParams({ group_id: 'gitlab-org', project_id: 'my-project' }, url)).toBe( + 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project', + ); }); it('updates an existing parameter', () => { const url = 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project'; - expect(urlUtils.setUrlParams({ project_id: 'gitlab-test' }, url)).toEqual( + expect(urlUtils.setUrlParams({ project_id: 'gitlab-test' }, url)).toBe( 'https://gitlab.com/test?group_id=gitlab-org&project_id=gitlab-test', ); }); @@ -849,7 +870,7 @@ describe('URL utility', () => { it("removes the project_id param when it's value is null", () => { const url = 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project'; - expect(urlUtils.setUrlParams({ project_id: null }, url)).toEqual( + expect(urlUtils.setUrlParams({ project_id: null }, url)).toBe( 'https://gitlab.com/test?group_id=gitlab-org', ); }); @@ -857,7 +878,7 @@ describe('URL utility', () => { it('adds parameters from arrays', () => { const url = 'https://gitlab.com/test'; - expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url)).toEqual( + expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url)).toBe( 'https://gitlab.com/test?labels=foo&labels=bar', ); }); @@ -865,13 +886,13 @@ describe('URL utility', () => { it('removes parameters from empty arrays', () => { const url = 'https://gitlab.com/test?labels=foo&labels=bar'; - expect(urlUtils.setUrlParams({ labels: [] }, url)).toEqual('https://gitlab.com/test'); + expect(urlUtils.setUrlParams({ labels: [] }, url)).toBe('https://gitlab.com/test'); }); it('removes parameters from empty arrays while keeping other parameters', () => { const url = 'https://gitlab.com/test?labels=foo&labels=bar&unrelated=unrelated'; - expect(urlUtils.setUrlParams({ labels: [] }, url)).toEqual( + expect(urlUtils.setUrlParams({ labels: [] }, url)).toBe( 'https://gitlab.com/test?unrelated=unrelated', ); }); @@ -879,7 +900,7 @@ describe('URL utility', () => { it('adds parameters from arrays when railsArraySyntax=true', () => { const url = 'https://gitlab.com/test'; - expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true)).toEqual( + expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true)).toBe( 'https://gitlab.com/test?labels%5B%5D=foo&labels%5B%5D=bar', ); }); @@ -887,7 +908,7 @@ describe('URL utility', () => { it('removes parameters from empty arrays when railsArraySyntax=true', () => { const url = 'https://gitlab.com/test?labels%5B%5D=foo&labels%5B%5D=bar'; - expect(urlUtils.setUrlParams({ labels: [] }, url, false, true)).toEqual( + expect(urlUtils.setUrlParams({ labels: [] }, url, false, true)).toBe( 'https://gitlab.com/test', ); }); @@ -895,7 +916,7 @@ describe('URL utility', () => { it('decodes URI when decodeURI=true', () => { const url = 'https://gitlab.com/test'; - expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true, true)).toEqual( + expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true, true)).toBe( 'https://gitlab.com/test?labels[]=foo&labels[]=bar', ); }); @@ -903,7 +924,7 @@ describe('URL utility', () => { it('removes all existing URL params and sets a new param when cleanParams=true', () => { const url = 'https://gitlab.com/test?group_id=gitlab-org&project_id=my-project'; - expect(urlUtils.setUrlParams({ foo: 'bar' }, url, true)).toEqual( + expect(urlUtils.setUrlParams({ foo: 'bar' }, url, true)).toBe( 'https://gitlab.com/test?foo=bar', ); }); diff --git a/spec/frontend/merge_request_tabs_spec.js b/spec/frontend/merge_request_tabs_spec.js index ced9b71125b..5c24a070342 100644 --- a/spec/frontend/merge_request_tabs_spec.js +++ b/spec/frontend/merge_request_tabs_spec.js @@ -4,6 +4,7 @@ import initMrPage from 'helpers/init_vue_mr_page_helper'; import axios from '~/lib/utils/axios_utils'; import MergeRequestTabs from '~/merge_request_tabs'; import '~/lib/utils/common_utils'; +import '~/lib/utils/url_utility'; jest.mock('~/lib/utils/webpack', () => ({ resetServiceWorkersPublicPath: jest.fn(), diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js index e25162f4da5..9680e273add 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js @@ -6,6 +6,7 @@ import { DELETE_TAG_ERROR_MESSAGE, DELETE_TAGS_SUCCESS_MESSAGE, DELETE_TAGS_ERROR_MESSAGE, + DETAILS_IMPORTING_ERROR_MESSAGE, ADMIN_GARBAGE_COLLECTION_TIP, } from '~/packages_and_registries/container_registry/explorer/constants'; @@ -76,6 +77,7 @@ describe('Delete alert', () => { }); }); }); + describe('error states', () => { describe.each` deleteAlertType | message @@ -105,6 +107,25 @@ describe('Delete alert', () => { }); }); + describe('importing repository error state', () => { + beforeEach(() => { + mountComponent({ + deleteAlertType: 'danger_importing', + containerRegistryImportingHelpPagePath: 'https://foobar', + }); + }); + + it('alert exist and text is appropriate', () => { + expect(findAlert().text()).toMatchInterpolatedText(DETAILS_IMPORTING_ERROR_MESSAGE); + }); + + it('alert body contains link', () => { + const alertLink = findLink(); + expect(alertLink.exists()).toBe(true); + expect(alertLink.attributes('href')).toBe('https://foobar'); + }); + }); + describe('dismissing alert', () => { it('GlAlert dismiss event triggers a change event', () => { mountComponent({ deleteAlertType: 'success_tags' }); diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js index f0c586fe7f4..fda1db4b7e1 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js @@ -239,6 +239,15 @@ export const graphQLDeleteImageRepositoryTagsMock = { }, }; +export const graphQLDeleteImageRepositoryTagImportingErrorMock = { + data: { + destroyContainerRepositoryTags: { + errors: ['repository importing'], + __typename: 'DestroyContainerRepositoryTagsPayload', + }, + }, +}; + export const dockerCommands = { dockerBuildCommand: 'foofoo', dockerPushCommand: 'barbar', diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js index c602b37c3b5..59ca47bee50 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js @@ -18,6 +18,7 @@ import { UNFINISHED_STATUS, DELETE_SCHEDULED, ALERT_DANGER_IMAGE, + ALERT_DANGER_IMPORTING, MISSING_OR_DELETED_IMAGE_BREADCRUMB, ROOT_IMAGE_TEXT, MISSING_OR_DELETED_IMAGE_TITLE, @@ -33,6 +34,7 @@ import Tracking from '~/tracking'; import { graphQLImageDetailsMock, graphQLDeleteImageRepositoryTagsMock, + graphQLDeleteImageRepositoryTagImportingErrorMock, containerRepositoryMock, graphQLEmptyImageDetailsMock, tagsMock, @@ -329,6 +331,7 @@ describe('Details Page', () => { const config = { isAdmin: true, garbageCollectionHelpPagePath: 'baz', + containerRegistryImportingHelpPagePath: 'https://foobar', }; const deleteAlertType = 'success_tag'; @@ -353,6 +356,35 @@ describe('Details Page', () => { expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType }); }); + + describe('importing repository error', () => { + let mutationResolver; + let tagsResolver; + + beforeEach(async () => { + mutationResolver = jest + .fn() + .mockResolvedValue(graphQLDeleteImageRepositoryTagImportingErrorMock); + tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock)); + + mountComponent({ mutationResolver, tagsResolver }); + await waitForApolloRequestRender(); + }); + + it('displays the proper alert', async () => { + findTagsList().vm.$emit('delete', [cleanTags[0]]); + await nextTick(); + + findDeleteModal().vm.$emit('confirmDelete'); + await waitForPromises(); + + expect(tagsResolver).toHaveBeenCalled(); + + const deleteAlert = findDeleteAlert(); + expect(deleteAlert.exists()).toBe(true); + expect(deleteAlert.props('deleteAlertType')).toBe(ALERT_DANGER_IMPORTING); + }); + }); }); describe('Partial Cleanup Alert', () => { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index 78585ed75bc..0e364eb6800 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -1,7 +1,12 @@ -import { shallowMount } from '@vue/test-utils'; +import { createLocalVue, shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; import { GlSprintf } from '@gitlab/ui'; +import VueApollo from 'vue-apollo'; +import produce from 'immer'; +import readyToMergeResponse from 'test_fixtures/graphql/merge_requests/states/ready_to_merge.query.graphql.json'; import waitForPromises from 'helpers/wait_for_promises'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql'; import simplePoll from '~/lib/utils/simple_poll'; import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue'; import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue'; @@ -19,9 +24,11 @@ jest.mock('~/commons/nav/user_merge_requests', () => ({ refreshUserMergeRequestCounts: jest.fn(), })); -const commitMessage = 'This is the commit message'; -const squashCommitMessage = 'This is the squash commit message'; -const commitMessageWithDescription = 'This is the commit message description'; +const commitMessage = readyToMergeResponse.data.project.mergeRequest.defaultMergeCommitMessage; +const squashCommitMessage = + readyToMergeResponse.data.project.mergeRequest.defaultSquashCommitMessage; +const commitMessageWithDescription = + readyToMergeResponse.data.project.mergeRequest.defaultMergeCommitMessageWithDescription; const createTestMr = (customConfig) => { const mr = { isPipelineActive: false, @@ -42,6 +49,8 @@ const createTestMr = (customConfig) => { commitMessage, squashCommitMessage, commitMessageWithDescription, + defaultMergeCommitMessage: commitMessage, + defaultSquashCommitMessage: squashCommitMessage, shouldRemoveSourceBranch: true, canRemoveSourceBranch: false, targetBranch: 'main', @@ -61,15 +70,25 @@ const createTestService = () => ({ merge: jest.fn(), poll: jest.fn().mockResolvedValue(), }); +const localVue = createLocalVue(); +localVue.use(VueApollo); let wrapper; +let readyToMergeResponseSpy; const findMergeButton = () => wrapper.find('[data-testid="merge-button"]'); const findPipelineFailedConfirmModal = () => wrapper.findComponent(MergeFailedPipelineConfirmationDialog); +const createReadyToMergeResponse = (customMr) => { + return produce(readyToMergeResponse, (draft) => { + Object.assign(draft.data.project.mergeRequest, customMr); + }); +}; + const createComponent = (customConfig = {}, mergeRequestWidgetGraphql = false) => { wrapper = shallowMount(ReadyToMerge, { + localVue, propsData: { mr: createTestMr(customConfig), service: createTestService(), @@ -82,10 +101,29 @@ const createComponent = (customConfig = {}, mergeRequestWidgetGraphql = false) = stubs: { CommitEdit, }, + apolloProvider: createMockApollo([[readyToMergeQuery, readyToMergeResponseSpy]]), }); }; +const findCheckboxElement = () => wrapper.find(SquashBeforeMerge); +const findCommitsHeaderElement = () => wrapper.find(CommitsHeader); +const findCommitEditElements = () => wrapper.findAll(CommitEdit); +const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown); +const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label'); +const findTipLink = () => wrapper.find(GlSprintf); +const findCommitEditWithInputId = (inputId) => + findCommitEditElements().wrappers.find((x) => x.props('inputId') === inputId); +const findMergeCommitMessage = () => findCommitEditWithInputId('merge-message-edit').props('value'); +const findSquashCommitMessage = () => + findCommitEditWithInputId('squash-message-edit').props('value'); + +const triggerApprovalUpdated = () => eventHub.$emit('ApprovalUpdated'); + describe('ReadyToMerge', () => { + beforeEach(() => { + readyToMergeResponseSpy = jest.fn().mockResolvedValueOnce(readyToMergeResponse); + }); + afterEach(() => { wrapper.destroy(); }); @@ -447,13 +485,6 @@ describe('ReadyToMerge', () => { }); describe('render children components', () => { - const findCheckboxElement = () => wrapper.find(SquashBeforeMerge); - const findCommitsHeaderElement = () => wrapper.find(CommitsHeader); - const findCommitEditElements = () => wrapper.findAll(CommitEdit); - const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown); - const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label'); - const findTipLink = () => wrapper.find(GlSprintf); - describe('squash checkbox', () => { it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => { createComponent({ @@ -772,4 +803,65 @@ describe('ReadyToMerge', () => { expect(findPipelineFailedConfirmModal().props()).toEqual({ visible: true }); }); }); + + describe('updating graphql data triggers commit message update when default changed', () => { + const UPDATED_MERGE_COMMIT_MESSAGE = 'New merge message from BE'; + const UPDATED_SQUASH_COMMIT_MESSAGE = 'New squash message from BE'; + const USER_COMMIT_MESSAGE = 'Merge message provided manually by user'; + + const createDefaultGqlComponent = () => + createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: true } }, true); + + beforeEach(() => { + readyToMergeResponseSpy = jest + .fn() + .mockResolvedValueOnce(createReadyToMergeResponse({ squash: true, squashOnMerge: true })) + .mockResolvedValue( + createReadyToMergeResponse({ + squash: true, + squashOnMerge: true, + defaultMergeCommitMessage: UPDATED_MERGE_COMMIT_MESSAGE, + defaultSquashCommitMessage: UPDATED_SQUASH_COMMIT_MESSAGE, + }), + ); + }); + + describe.each` + desc | finderFn | initialValue | updatedValue | inputId + ${'merge commit message'} | ${findMergeCommitMessage} | ${commitMessage} | ${UPDATED_MERGE_COMMIT_MESSAGE} | ${'#merge-message-edit'} + ${'squash commit message'} | ${findSquashCommitMessage} | ${squashCommitMessage} | ${UPDATED_SQUASH_COMMIT_MESSAGE} | ${'#squash-message-edit'} + `('with $desc', ({ finderFn, initialValue, updatedValue, inputId }) => { + it('should have initial value', async () => { + createDefaultGqlComponent(); + + await waitForPromises(); + + expect(finderFn()).toBe(initialValue); + }); + + it('should have updated value after graphql refetch', async () => { + createDefaultGqlComponent(); + await waitForPromises(); + + triggerApprovalUpdated(); + await waitForPromises(); + + expect(finderFn()).toBe(updatedValue); + }); + + it('should not update if user has touched', async () => { + createDefaultGqlComponent(); + await waitForPromises(); + + const input = wrapper.find(inputId); + input.element.value = USER_COMMIT_MESSAGE; + input.trigger('input'); + + triggerApprovalUpdated(); + await waitForPromises(); + + expect(finderFn()).toBe(USER_COMMIT_MESSAGE); + }); + }); + }); }); |