Commit df3db1df authored by Frédéric Caplette's avatar Frédéric Caplette Committed by Enrique Alcántara

Fix editor browser modal when creating new MR

The pipeline editor will prevent user with uncommitted
changes to leave the page with a browser alert. This
was incorrectlybeing shown after a user had commited and
had checked the option to create a new MR. This fix
now ensure that the update to the internal Vue components
has occurred before checking if we should stop the user
from navigating.

Changelog: fixed
parent d967565a
<script> <script>
import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { import {
COMMIT_ACTION_CREATE, COMMIT_ACTION_CREATE,
COMMIT_ACTION_UPDATE, COMMIT_ACTION_UPDATE,
COMMIT_FAILURE, COMMIT_FAILURE,
COMMIT_SUCCESS, COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT,
} from '../../constants'; } from '../../constants';
import commitCIFile from '../../graphql/mutations/commit_ci_file.mutation.graphql'; import commitCIFile from '../../graphql/mutations/commit_ci_file.mutation.graphql';
import updateCurrentBranchMutation from '../../graphql/mutations/client/update_current_branch.mutation.graphql'; import updateCurrentBranchMutation from '../../graphql/mutations/client/update_current_branch.mutation.graphql';
...@@ -15,9 +15,6 @@ import getCurrentBranch from '../../graphql/queries/client/current_branch.query. ...@@ -15,9 +15,6 @@ import getCurrentBranch from '../../graphql/queries/client/current_branch.query.
import CommitForm from './commit_form.vue'; import CommitForm from './commit_form.vue';
const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
const MR_TARGET_BRANCH = 'merge_request[target_branch]';
export default { export default {
alertTexts: { alertTexts: {
[COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'), [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
...@@ -29,7 +26,7 @@ export default { ...@@ -29,7 +26,7 @@ export default {
components: { components: {
CommitForm, CommitForm,
}, },
inject: ['projectFullPath', 'ciConfigPath', 'newMergeRequestPath'], inject: ['projectFullPath', 'ciConfigPath'],
props: { props: {
ciFileContent: { ciFileContent: {
type: String, type: String,
...@@ -74,16 +71,6 @@ export default { ...@@ -74,16 +71,6 @@ export default {
}, },
}, },
methods: { methods: {
redirectToNewMergeRequest(sourceBranch) {
const url = mergeUrlParams(
{
[MR_SOURCE_BRANCH]: sourceBranch,
[MR_TARGET_BRANCH]: this.currentBranch,
},
this.newMergeRequestPath,
);
redirectTo(url);
},
async onCommitSubmit({ message, targetBranch, openMergeRequest }) { async onCommitSubmit({ message, targetBranch, openMergeRequest }) {
this.isSaving = true; this.isSaving = true;
...@@ -112,12 +99,25 @@ export default { ...@@ -112,12 +99,25 @@ export default {
if (errors?.length) { if (errors?.length) {
this.$emit('showError', { type: COMMIT_FAILURE, reasons: errors }); this.$emit('showError', { type: COMMIT_FAILURE, reasons: errors });
} else if (openMergeRequest) {
this.redirectToNewMergeRequest(targetBranch);
} else { } else {
this.$emit('commit', { type: COMMIT_SUCCESS }); const commitBranch = targetBranch;
const params = openMergeRequest
? {
type: COMMIT_SUCCESS_WITH_REDIRECT,
params: {
sourceBranch: commitBranch,
targetBranch: this.currentBranch,
},
}
: { type: COMMIT_SUCCESS };
this.$emit('commit', {
...params,
});
this.updateLastCommitBranch(targetBranch); this.updateLastCommitBranch(targetBranch);
this.updateCurrentBranch(targetBranch); this.updateCurrentBranch(targetBranch);
if (this.currentBranch === targetBranch) { if (this.currentBranch === targetBranch) {
this.$emit('updateCommitSha'); this.$emit('updateCommitSha');
} }
......
...@@ -5,6 +5,7 @@ import { __, s__ } from '~/locale'; ...@@ -5,6 +5,7 @@ import { __, s__ } from '~/locale';
import { import {
COMMIT_FAILURE, COMMIT_FAILURE,
COMMIT_SUCCESS, COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT,
DEFAULT_FAILURE, DEFAULT_FAILURE,
DEFAULT_SUCCESS, DEFAULT_SUCCESS,
LOAD_FAILURE_UNKNOWN, LOAD_FAILURE_UNKNOWN,
...@@ -21,14 +22,18 @@ export default { ...@@ -21,14 +22,18 @@ export default {
GlAlert, GlAlert,
CodeSnippetAlert, CodeSnippetAlert,
}, },
errorTexts: {
errors: {
[COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'), [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
[DEFAULT_FAILURE]: __('Something went wrong on our end.'), [DEFAULT_FAILURE]: __('Something went wrong on our end.'),
[LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'), [LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'),
[PIPELINE_FAILURE]: s__('Pipelines|There was a problem with loading the pipeline data.'), [PIPELINE_FAILURE]: s__('Pipelines|There was a problem with loading the pipeline data.'),
}, },
successTexts: { success: {
[COMMIT_SUCCESS]: __('Your changes have been successfully committed.'), [COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
[COMMIT_SUCCESS_WITH_REDIRECT]: s__(
'Pipelines|Your changes have been successfully committed. Now redirecting to the new merge request page.',
),
[DEFAULT_SUCCESS]: __('Your action succeeded.'), [DEFAULT_SUCCESS]: __('Your action succeeded.'),
}, },
props: { props: {
...@@ -65,42 +70,20 @@ export default { ...@@ -65,42 +70,20 @@ export default {
}, },
computed: { computed: {
failure() { failure() {
switch (this.failureType) { const { errors } = this.$options;
case LOAD_FAILURE_UNKNOWN:
return { return {
text: this.$options.errorTexts[LOAD_FAILURE_UNKNOWN], text: errors[this.failureType] ?? errors[DEFAULT_FAILURE],
variant: 'danger', variant: 'danger',
}; };
case COMMIT_FAILURE:
return {
text: this.$options.errorTexts[COMMIT_FAILURE],
variant: 'danger',
};
case PIPELINE_FAILURE:
return {
text: this.$options.errorTexts[PIPELINE_FAILURE],
variant: 'danger',
};
default:
return {
text: this.$options.errorTexts[DEFAULT_FAILURE],
variant: 'danger',
};
}
}, },
success() { success() {
switch (this.successType) { const { success } = this.$options;
case COMMIT_SUCCESS:
return { return {
text: this.$options.successTexts[COMMIT_SUCCESS], text: success[this.successType] ?? success[DEFAULT_SUCCESS],
variant: 'info', variant: 'info',
}; };
default:
return {
text: this.$options.successTexts[DEFAULT_SUCCESS],
variant: 'info',
};
}
}, },
}, },
created() { created() {
......
...@@ -20,6 +20,7 @@ export const EDITOR_APP_VALID_STATUSES = [ ...@@ -20,6 +20,7 @@ export const EDITOR_APP_VALID_STATUSES = [
export const COMMIT_FAILURE = 'COMMIT_FAILURE'; export const COMMIT_FAILURE = 'COMMIT_FAILURE';
export const COMMIT_SUCCESS = 'COMMIT_SUCCESS'; export const COMMIT_SUCCESS = 'COMMIT_SUCCESS';
export const COMMIT_SUCCESS_WITH_REDIRECT = 'COMMIT_SUCCESS_WITH_REDIRECT';
export const DEFAULT_FAILURE = 'DEFAULT_FAILURE'; export const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
export const DEFAULT_SUCCESS = 'DEFAULT_SUCCESS'; export const DEFAULT_SUCCESS = 'DEFAULT_SUCCESS';
......
<script> <script>
import { GlLoadingIcon, GlModal } from '@gitlab/ui'; import { GlLoadingIcon, GlModal } from '@gitlab/ui';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import { queryToObject } from '~/lib/utils/url_utility'; import { mergeUrlParams, queryToObject, redirectTo } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils'; import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
...@@ -11,6 +11,7 @@ import PipelineEditorEmptyState from './components/ui/pipeline_editor_empty_stat ...@@ -11,6 +11,7 @@ import PipelineEditorEmptyState from './components/ui/pipeline_editor_empty_stat
import PipelineEditorMessages from './components/ui/pipeline_editor_messages.vue'; import PipelineEditorMessages from './components/ui/pipeline_editor_messages.vue';
import { import {
COMMIT_SHA_POLL_INTERVAL, COMMIT_SHA_POLL_INTERVAL,
COMMIT_SUCCESS_WITH_REDIRECT,
EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_EMPTY,
EDITOR_APP_STATUS_LOADING, EDITOR_APP_STATUS_LOADING,
EDITOR_APP_STATUS_LINT_UNAVAILABLE, EDITOR_APP_STATUS_LINT_UNAVAILABLE,
...@@ -27,6 +28,9 @@ import getTemplate from './graphql/queries/get_starter_template.query.graphql'; ...@@ -27,6 +28,9 @@ import getTemplate from './graphql/queries/get_starter_template.query.graphql';
import getLatestCommitShaQuery from './graphql/queries/latest_commit_sha.query.graphql'; import getLatestCommitShaQuery from './graphql/queries/latest_commit_sha.query.graphql';
import PipelineEditorHome from './pipeline_editor_home.vue'; import PipelineEditorHome from './pipeline_editor_home.vue';
const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
const MR_TARGET_BRANCH = 'merge_request[target_branch]';
export default { export default {
components: { components: {
ConfirmUnsavedChangesDialog, ConfirmUnsavedChangesDialog,
...@@ -36,14 +40,7 @@ export default { ...@@ -36,14 +40,7 @@ export default {
PipelineEditorHome, PipelineEditorHome,
PipelineEditorMessages, PipelineEditorMessages,
}, },
inject: { inject: ['ciConfigPath', 'newMergeRequestPath', 'projectFullPath'],
ciConfigPath: {
default: '',
},
projectFullPath: {
default: '',
},
},
data() { data() {
return { return {
ciConfigData: {}, ciConfigData: {},
...@@ -57,7 +54,7 @@ export default { ...@@ -57,7 +54,7 @@ export default {
lastCommittedContent: '', lastCommittedContent: '',
shouldSkipStartScreen: false, shouldSkipStartScreen: false,
showFailure: false, showFailure: false,
showResetComfirmationModal: false, showResetConfirmationModal: false,
showStartScreen: false, showStartScreen: false,
showSuccess: false, showSuccess: false,
starterTemplate: '', starterTemplate: '',
...@@ -271,17 +268,39 @@ export default { ...@@ -271,17 +268,39 @@ export default {
this.checkShouldSkipStartScreen(); this.checkShouldSkipStartScreen();
}, },
methods: { methods: {
checkShouldSkipStartScreen() {
const params = queryToObject(window.location.search);
this.shouldSkipStartScreen = Boolean(params?.add_new_config_file);
},
confirmReset() {
if (this.hasUnsavedChanges) {
this.showResetConfirmationModal = true;
}
},
hideFailure() { hideFailure() {
this.showFailure = false; this.showFailure = false;
}, },
hideSuccess() { hideSuccess() {
this.showSuccess = false; this.showSuccess = false;
}, },
confirmReset() { loadTemplateFromURL() {
if (this.hasUnsavedChanges) { const templateName = queryToObject(window.location.search)?.template;
this.showResetComfirmationModal = true;
if (templateName) {
this.starterTemplateName = templateName;
this.setNewEmptyCiConfigFile();
} }
}, },
redirectToNewMergeRequest(sourceBranch, targetBranch) {
const url = mergeUrlParams(
{
[MR_SOURCE_BRANCH]: sourceBranch,
[MR_TARGET_BRANCH]: targetBranch,
},
this.newMergeRequestPath,
);
redirectTo(url);
},
async refetchContent() { async refetchContent() {
this.$apollo.queries.initialCiFileContent.skip = false; this.$apollo.queries.initialCiFileContent.skip = false;
await this.$apollo.queries.initialCiFileContent.refetch(); await this.$apollo.queries.initialCiFileContent.refetch();
...@@ -298,7 +317,7 @@ export default { ...@@ -298,7 +317,7 @@ export default {
this.successType = type; this.successType = type;
}, },
resetContent() { resetContent() {
this.showResetComfirmationModal = false; this.showResetConfirmationModal = false;
this.currentCiFileContent = this.lastCommittedContent; this.currentCiFileContent = this.lastCommittedContent;
}, },
setAppStatus(appStatus) { setAppStatus(appStatus) {
...@@ -323,7 +342,7 @@ export default { ...@@ -323,7 +342,7 @@ export default {
this.isFetchingCommitSha = true; this.isFetchingCommitSha = true;
this.$apollo.queries.commitSha.refetch(); this.$apollo.queries.commitSha.refetch();
}, },
updateOnCommit({ type }) { async updateOnCommit({ type, params = {} }) {
this.reportSuccess(type); this.reportSuccess(type);
if (this.isNewCiConfigFile) { if (this.isNewCiConfigFile) {
...@@ -333,19 +352,17 @@ export default { ...@@ -333,19 +352,17 @@ export default {
// Keep track of the latest committed content to know // Keep track of the latest committed content to know
// if the user has made changes to the file that are unsaved. // if the user has made changes to the file that are unsaved.
this.lastCommittedContent = this.currentCiFileContent; this.lastCommittedContent = this.currentCiFileContent;
},
loadTemplateFromURL() {
const templateName = queryToObject(window.location.search)?.template;
if (templateName) { if (type === COMMIT_SUCCESS_WITH_REDIRECT) {
this.starterTemplateName = templateName; const { sourceBranch, targetBranch } = params;
this.setNewEmptyCiConfigFile(); // This force update does 2 things for us:
// 1. It make sure `hasUnsavedChanges` is updated so
// we don't show a modal when the user creates an MR
// 2. Ensure the commit success banner is visible.
await this.$forceUpdate();
this.redirectToNewMergeRequest(sourceBranch, targetBranch);
} }
}, },
checkShouldSkipStartScreen() {
const params = queryToObject(window.location.search);
this.shouldSkipStartScreen = Boolean(params?.add_new_config_file);
},
}, },
}; };
</script> </script>
...@@ -382,7 +399,7 @@ export default { ...@@ -382,7 +399,7 @@ export default {
@updateCommitSha="updateCommitSha" @updateCommitSha="updateCommitSha"
/> />
<gl-modal <gl-modal
v-model="showResetComfirmationModal" v-model="showResetConfirmationModal"
modal-id="reset-content" modal-id="reset-content"
:title="$options.i18n.resetModal.title" :title="$options.i18n.resetModal.title"
:action-cancel="$options.i18n.resetModal.actionCancel" :action-cancel="$options.i18n.resetModal.actionCancel"
......
...@@ -26486,6 +26486,9 @@ msgstr "" ...@@ -26486,6 +26486,9 @@ msgstr ""
msgid "Pipelines|Visualize" msgid "Pipelines|Visualize"
msgstr "" msgstr ""
msgid "Pipelines|Your changes have been successfully committed. Now redirecting to the new merge request page."
msgstr ""
msgid "Pipelines|detached" msgid "Pipelines|detached"
msgstr "" msgstr ""
......
...@@ -26,7 +26,7 @@ RSpec.describe 'Pipeline Editor', :js do ...@@ -26,7 +26,7 @@ RSpec.describe 'Pipeline Editor', :js do
expect(page).to have_content('Pipeline Editor') expect(page).to have_content('Pipeline Editor')
end end
context 'branch switcher' do describe 'Branch switcher' do
def switch_to_branch(branch) def switch_to_branch(branch)
find('[data-testid="branch-selector"]').click find('[data-testid="branch-selector"]').click
...@@ -64,7 +64,64 @@ RSpec.describe 'Pipeline Editor', :js do ...@@ -64,7 +64,64 @@ RSpec.describe 'Pipeline Editor', :js do
end end
end end
context 'Editor content' do describe 'Editor navigation' do
context 'when no change is made' do
it 'user can navigate away without a browser alert' do
expect(page).to have_content('Pipeline Editor')
click_link 'Pipelines'
expect(page).not_to have_content('Pipeline Editor')
end
end
context 'when a change is made' do
before do
click_button 'Collapse'
page.within('#source-editor-') do
find('textarea').send_keys '123'
# It takes some time after sending keys for the vue
# component to update
sleep 1
end
end
it 'user who tries to navigate away can cancel the action and keep their changes' do
click_link 'Pipelines'
page.driver.browser.switch_to.alert.dismiss
expect(page).to have_content('Pipeline Editor')
page.within('#source-editor-') do
expect(page).to have_content('Default Content123')
end
end
it 'user who tries to navigate away can confirm the action and discard their change' do
click_link 'Pipelines'
page.driver.browser.switch_to.alert.accept
expect(page).not_to have_content('Pipeline Editor')
end
it 'user who creates a MR is taken to the merge request page without warnings' do
expect(page).not_to have_content('New merge request')
find_field('Target Branch').set 'new_branch'
find_field('Start a new merge request with these changes').click
click_button 'Commit changes'
expect(page).not_to have_content('Pipeline Editor')
expect(page).to have_content('New merge request')
end
end
end
describe 'Editor content' do
it 'user can reset their CI configuration' do it 'user can reset their CI configuration' do
click_button 'Collapse' click_button 'Collapse'
......
...@@ -4,13 +4,13 @@ import { mount } from '@vue/test-utils'; ...@@ -4,13 +4,13 @@ import { mount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { objectToQuery, redirectTo } from '~/lib/utils/url_utility';
import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue'; import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue';
import { import {
COMMIT_ACTION_CREATE, COMMIT_ACTION_CREATE,
COMMIT_ACTION_UPDATE, COMMIT_ACTION_UPDATE,
COMMIT_SUCCESS, COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT,
} from '~/pipeline_editor/constants'; } from '~/pipeline_editor/constants';
import commitCreate from '~/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql'; import commitCreate from '~/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql';
import updatePipelineEtag from '~/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql'; import updatePipelineEtag from '~/pipeline_editor/graphql/mutations/client/update_pipeline_etag.mutation.graphql';
...@@ -24,16 +24,8 @@ import { ...@@ -24,16 +24,8 @@ import {
mockCommitMessage, mockCommitMessage,
mockDefaultBranch, mockDefaultBranch,
mockProjectFullPath, mockProjectFullPath,
mockNewMergeRequestPath,
} from '../../mock_data'; } from '../../mock_data';
jest.mock('~/lib/utils/url_utility', () => ({
redirectTo: jest.fn(),
refreshCurrentPage: jest.fn(),
objectToQuery: jest.requireActual('~/lib/utils/url_utility').objectToQuery,
mergeUrlParams: jest.requireActual('~/lib/utils/url_utility').mergeUrlParams,
}));
const mockVariables = { const mockVariables = {
action: COMMIT_ACTION_UPDATE, action: COMMIT_ACTION_UPDATE,
projectPath: mockProjectFullPath, projectPath: mockProjectFullPath,
...@@ -47,7 +39,6 @@ const mockVariables = { ...@@ -47,7 +39,6 @@ const mockVariables = {
const mockProvide = { const mockProvide = {
ciConfigPath: mockCiConfigPath, ciConfigPath: mockCiConfigPath,
projectFullPath: mockProjectFullPath, projectFullPath: mockProjectFullPath,
newMergeRequestPath: mockNewMergeRequestPath,
}; };
describe('Pipeline Editor | Commit section', () => { describe('Pipeline Editor | Commit section', () => {
...@@ -208,19 +199,21 @@ describe('Pipeline Editor | Commit section', () => { ...@@ -208,19 +199,21 @@ describe('Pipeline Editor | Commit section', () => {
beforeEach(async () => { beforeEach(async () => {
createComponentWithApollo(); createComponentWithApollo();
mockMutateCommitData.mockResolvedValue(mockCommitCreateResponse);
await submitCommit({ await submitCommit({
branch: newBranch, branch: newBranch,
openMergeRequest: true, openMergeRequest: true,
}); });
}); });
it('redirects to the merge request page with source and target branches', () => { it('emits a commit event with the right type, sourceBranch and targetBranch', () => {
const branchesQuery = objectToQuery({ expect(wrapper.emitted('commit')).toBeTruthy();
'merge_request[source_branch]': newBranch, expect(wrapper.emitted('commit')[0]).toMatchObject([
'merge_request[target_branch]': mockDefaultBranch, {
}); type: COMMIT_SUCCESS_WITH_REDIRECT,
params: { sourceBranch: newBranch, targetBranch: mockDefaultBranch },
expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`); },
]);
}); });
}); });
......
...@@ -8,6 +8,7 @@ import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_edi ...@@ -8,6 +8,7 @@ import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_edi
import { import {
COMMIT_FAILURE, COMMIT_FAILURE,
COMMIT_SUCCESS, COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT,
DEFAULT_FAILURE, DEFAULT_FAILURE,
DEFAULT_SUCCESS, DEFAULT_SUCCESS,
LOAD_FAILURE_UNKNOWN, LOAD_FAILURE_UNKNOWN,
...@@ -34,7 +35,13 @@ describe('Pipeline Editor messages', () => { ...@@ -34,7 +35,13 @@ describe('Pipeline Editor messages', () => {
it('shows a message for successful commit type', () => { it('shows a message for successful commit type', () => {
createComponent({ successType: COMMIT_SUCCESS, showSuccess: true }); createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[COMMIT_SUCCESS]); expect(findAlert().text()).toBe(wrapper.vm.$options.success[COMMIT_SUCCESS]);
});
it('shows a message for successful commit with redirect type', () => {
createComponent({ successType: COMMIT_SUCCESS_WITH_REDIRECT, showSuccess: true });
expect(findAlert().text()).toBe(wrapper.vm.$options.success[COMMIT_SUCCESS_WITH_REDIRECT]);
}); });
it('does not show alert when there is a successType but visibility is off', () => { it('does not show alert when there is a successType but visibility is off', () => {
...@@ -46,7 +53,7 @@ describe('Pipeline Editor messages', () => { ...@@ -46,7 +53,7 @@ describe('Pipeline Editor messages', () => {
it('shows a success alert with default copy if `showSuccess` is true and the `successType` is not valid,', () => { it('shows a success alert with default copy if `showSuccess` is true and the `successType` is not valid,', () => {
createComponent({ successType: 'random', showSuccess: true }); createComponent({ successType: 'random', showSuccess: true });
expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[DEFAULT_SUCCESS]); expect(findAlert().text()).toBe(wrapper.vm.$options.success[DEFAULT_SUCCESS]);
}); });
it('emit `hide-success` event when clicking on the dismiss button', async () => { it('emit `hide-success` event when clicking on the dismiss button', async () => {
...@@ -71,7 +78,7 @@ describe('Pipeline Editor messages', () => { ...@@ -71,7 +78,7 @@ describe('Pipeline Editor messages', () => {
`('shows a message for $message', ({ failureType, expectedFailureType }) => { `('shows a message for $message', ({ failureType, expectedFailureType }) => {
createComponent({ failureType, showFailure: true }); createComponent({ failureType, showFailure: true });
expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[expectedFailureType]); expect(findAlert().text()).toBe(wrapper.vm.$options.errors[expectedFailureType]);
}); });
it('show failure reasons when there are some', () => { it('show failure reasons when there are some', () => {
......
...@@ -5,6 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; ...@@ -5,6 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper'; import setWindowLocation from 'helpers/set_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { objectToQuery, redirectTo } from '~/lib/utils/url_utility';
import { resolvers } from '~/pipeline_editor/graphql/resolvers'; import { resolvers } from '~/pipeline_editor/graphql/resolvers';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue'; import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue'; import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
...@@ -13,7 +14,11 @@ import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_e ...@@ -13,7 +14,11 @@ import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_e
import ValidationSegment, { import ValidationSegment, {
i18n as validationSegmenti18n, i18n as validationSegmenti18n,
} from '~/pipeline_editor/components/header/validation_segment.vue'; } from '~/pipeline_editor/components/header/validation_segment.vue';
import { COMMIT_SUCCESS, COMMIT_FAILURE } from '~/pipeline_editor/constants'; import {
COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT,
COMMIT_FAILURE,
} from '~/pipeline_editor/constants';
import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql'; import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql'; import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql'; import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
...@@ -35,15 +40,22 @@ import { ...@@ -35,15 +40,22 @@ import {
mockDefaultBranch, mockDefaultBranch,
mockEmptyCommitShaResults, mockEmptyCommitShaResults,
mockNewCommitShaResults, mockNewCommitShaResults,
mockNewMergeRequestPath,
mockProjectFullPath, mockProjectFullPath,
} from './mock_data'; } from './mock_data';
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
redirectTo: jest.fn(),
}));
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
const mockProvide = { const mockProvide = {
ciConfigPath: mockCiConfigPath, ciConfigPath: mockCiConfigPath,
defaultBranch: mockDefaultBranch, defaultBranch: mockDefaultBranch,
newMergeRequestPath: mockNewMergeRequestPath,
projectFullPath: mockProjectFullPath, projectFullPath: mockProjectFullPath,
}; };
...@@ -311,6 +323,28 @@ describe('Pipeline editor app component', () => { ...@@ -311,6 +323,28 @@ describe('Pipeline editor app component', () => {
}); });
}); });
describe('when the commit succeeds with a redirect', () => {
const newBranch = 'new-branch';
beforeEach(async () => {
await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
findEditorHome().vm.$emit('commit', {
type: COMMIT_SUCCESS_WITH_REDIRECT,
params: { sourceBranch: newBranch, targetBranch: mockDefaultBranch },
});
});
it('redirects to the merge request page with source and target branches', () => {
const branchesQuery = objectToQuery({
'merge_request[source_branch]': newBranch,
'merge_request[target_branch]': mockDefaultBranch,
});
expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`);
});
});
describe('and the commit mutation fails', () => { describe('and the commit mutation fails', () => {
const commitFailedReasons = ['Commit failed']; const commitFailedReasons = ['Commit failed'];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment