Commit 20b5215f authored by Jackie Fraser's avatar Jackie Fraser Committed by Nathan Friend

Convert root emits to bvModal show and hide in delete branch modal

parent 7d5e46b2
<script>
import { GlButton, GlFormInput, GlModal, GlSprintf, GlAlert } from '@gitlab/ui';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import csrf from '~/lib/utils/csrf';
import { sprintf, s__ } from '~/locale';
import eventHub from '../event_hub';
......@@ -16,7 +15,6 @@ export default {
},
data() {
return {
visible: false,
isProtectedBranch: false,
branchName: '',
defaultBranchName: '',
......@@ -69,9 +67,10 @@ export default {
},
},
mounted() {
eventHub.$on('openModal', (options) => {
this.openModal(options);
});
eventHub.$on('openModal', this.openModal);
},
destroyed() {
eventHub.$off('openModal', this.openModal);
},
methods: {
openModal({ isProtectedBranch, branchName, defaultBranchName, deletePath, merged }) {
......@@ -81,13 +80,13 @@ export default {
this.deletePath = deletePath;
this.merged = merged;
this.$root.$emit(BV_SHOW_MODAL, this.modalId);
this.$refs.modal.show();
},
submitForm() {
this.$refs.form.submit();
},
closeModal() {
this.$root.$emit(BV_HIDE_MODAL, this.modalId);
this.$refs.modal.hide();
},
},
i18n: {
......@@ -117,7 +116,7 @@ export default {
</script>
<template>
<gl-modal :visible="visible" size="sm" :modal-id="modalId" :title="title">
<gl-modal ref="modal" size="sm" :modal-id="modalId" :title="title">
<gl-alert class="gl-mb-5" variant="danger" :dismissible="false">
<div data-testid="modal-message">
<gl-sprintf :message="message">
......@@ -175,7 +174,7 @@ export default {
<template #modal-footer>
<div class="gl-display-flex gl-flex-direction-row gl-justify-content-end gl-flex-wrap gl-m-0">
<gl-button @click="closeModal">
<gl-button data-testid="delete-branch-cancel-button" @click="closeModal">
{{ $options.i18n.cancelButtonText }}
</gl-button>
<div class="gl-mr-3"></div>
......@@ -184,7 +183,7 @@ export default {
:disabled="deleteButtonDisabled"
variant="danger"
data-qa-selector="delete_branch_confirmation_button"
data-testid="delete_branch_confirmation_button"
data-testid="delete-branch-confirmation-button"
@click="submitForm"
>{{ buttonText }}</gl-button
>
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Delete branch modal Deleting a protected branch (for owner or maintainer) renders the modal correctly 1`] = `
"<div visible=\\"visible\\">
<gl-alert-stub title=\\"\\" dismisslabel=\\"Dismiss\\" variant=\\"danger\\" primarybuttonlink=\\"\\" primarybuttontext=\\"\\" secondarybuttonlink=\\"\\" secondarybuttontext=\\"\\" class=\\"gl-mb-5\\">
<div data-testid=\\"modal-message\\">
<gl-sprintf-stub message=\\"You're about to permanently delete the protected branch %{strongStart}test_modal.%{strongEnd}\\"></gl-sprintf-stub>
<p class=\\"gl-mb-0 gl-mt-4\\">
This branch hasn’t been merged into default. To avoid data loss, consider merging this branch before deleting it.
</p>
</div>
</gl-alert-stub>
<form action=\\"/path/to/branch\\" method=\\"post\\">
<div class=\\"gl-mt-4\\">
<p>
<gl-sprintf-stub message=\\"Once you confirm and press %{strongStart}Yes, delete protected branch,%{strongEnd} it cannot be undone or recovered.\\"></gl-sprintf-stub>
</p>
<p>
<gl-sprintf-stub message=\\"Please type the following to confirm:\\"></gl-sprintf-stub> <code class=\\"gl-white-space-pre-wrap\\"> test_modal </code>
<b-form-input-stub name=\\"delete_branch_input\\" value=\\"\\" autocomplete=\\"off\\" debounce=\\"0\\" type=\\"text\\" aria-labelledby=\\"input-label\\" class=\\"gl-form-input gl-mt-4\\"></b-form-input-stub>
</p>
</div> <input type=\\"hidden\\" name=\\"_method\\" value=\\"delete\\"> <input type=\\"hidden\\" name=\\"authenticity_token\\">
</form>
<div class=\\"gl-display-flex gl-flex-direction-row gl-justify-content-end gl-flex-wrap gl-m-0\\">
<b-button-stub size=\\"md\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" class=\\"gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">
Cancel, keep branch
</span></b-button-stub>
<div class=\\"gl-mr-3\\"></div>
<b-button-stub disabled=\\"true\\" size=\\"md\\" variant=\\"danger\\" type=\\"button\\" tag=\\"button\\" data-qa-selector=\\"delete_branch_confirmation_button\\" data-testid=\\"delete_branch_confirmation_button\\" class=\\"gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">Yes, delete protected branch</span></b-button-stub>
</div>
</div>"
`;
exports[`Delete branch modal Deleting a regular branch renders the modal correctly 1`] = `
"<div visible=\\"visible\\">
<gl-alert-stub title=\\"\\" dismisslabel=\\"Dismiss\\" variant=\\"danger\\" primarybuttonlink=\\"\\" primarybuttontext=\\"\\" secondarybuttonlink=\\"\\" secondarybuttontext=\\"\\" class=\\"gl-mb-5\\">
<div data-testid=\\"modal-message\\">
<gl-sprintf-stub message=\\"You're about to permanently delete the branch %{strongStart}test_modal.%{strongEnd}\\"></gl-sprintf-stub>
<p class=\\"gl-mb-0 gl-mt-4\\">
This branch hasn’t been merged into default. To avoid data loss, consider merging this branch before deleting it.
</p>
</div>
</gl-alert-stub>
<form action=\\"/path/to/branch\\" method=\\"post\\">
<div>
<p class=\\"gl-mt-4\\">
<gl-sprintf-stub message=\\"Deleting the %{strongStart}test_modal%{strongEnd} branch cannot be undone. Are you sure?\\"></gl-sprintf-stub>
</p>
</div> <input type=\\"hidden\\" name=\\"_method\\" value=\\"delete\\"> <input type=\\"hidden\\" name=\\"authenticity_token\\">
</form>
<div class=\\"gl-display-flex gl-flex-direction-row gl-justify-content-end gl-flex-wrap gl-m-0\\">
<b-button-stub size=\\"md\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" class=\\"gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">
Cancel, keep branch
</span></b-button-stub>
<div class=\\"gl-mr-3\\"></div>
<b-button-stub size=\\"md\\" variant=\\"danger\\" type=\\"button\\" tag=\\"button\\" data-qa-selector=\\"delete_branch_confirmation_button\\" data-testid=\\"delete_branch_confirmation_button\\" class=\\"gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">Yes, delete branch</span></b-button-stub>
</div>
</div>"
`;
......@@ -29,7 +29,7 @@ describe('Delete branch button', () => {
wrapper.destroy();
});
it('renders the button with correct tooltip, style, and icon', () => {
it('renders the button with default tooltip, style, and icon', () => {
createComponent();
expect(findDeleteButton().attributes()).toMatchObject({
......@@ -42,7 +42,20 @@ describe('Delete branch button', () => {
it('renders a different tooltip for a protected branch', () => {
createComponent({ isProtectedBranch: true });
expect(findDeleteButton().attributes('title')).toBe('Delete protected branch');
expect(findDeleteButton().attributes()).toMatchObject({
title: 'Delete protected branch',
variant: 'danger',
icon: 'remove',
});
});
it('renders a different protected tooltip when it is both protected and disabled', () => {
createComponent({ isProtectedBranch: true, disabled: true });
expect(findDeleteButton().attributes()).toMatchObject({
title: 'Only a project maintainer or owner can delete a protected branch',
variant: 'default',
});
});
it('emits the data to eventHub when button is clicked', () => {
......@@ -63,14 +76,21 @@ describe('Delete branch button', () => {
it('does not disable the button by default when mounted', () => {
createComponent();
expect(findDeleteButton().attributes('disabled')).not.toBe('true');
expect(findDeleteButton().attributes()).toMatchObject({
title: 'Delete branch',
variant: 'danger',
});
});
// Used for unallowed users and for the default branch.
it('disables the button when mounted for a disabled modal', () => {
createComponent({ disabled: true });
createComponent({ disabled: true, tooltip: 'The default branch cannot be deleted' });
expect(findDeleteButton().attributes('disabled')).toBe('true');
expect(findDeleteButton().attributes()).toMatchObject({
title: 'The default branch cannot be deleted',
disabled: 'true',
variant: 'default',
});
});
});
});
import { GlButton, GlModal, GlFormInput } from '@gitlab/ui';
import { GlButton, GlModal, GlFormInput, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import DeleteBranchModal from '~/branches/components/delete_branch_modal.vue';
import eventHub from '~/branches/event_hub';
let wrapper;
const branchName = 'test_modal';
const defaultBranchName = 'default';
const deletePath = '/path/to/branch';
const merged = false;
const isProtectedBranch = false;
const createComponent = (data = {}) => {
wrapper = shallowMount(DeleteBranchModal, {
data() {
return {
branchName,
deletePath: '/path/to/branch',
defaultBranchName: 'default',
...data,
};
},
attrs: {
visible: true,
},
stubs: {
GlModal: stubComponent(GlModal, {
template:
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
}),
GlButton,
GlFormInput,
},
});
wrapper = extendedWrapper(
shallowMount(DeleteBranchModal, {
data() {
return {
branchName,
deletePath,
defaultBranchName,
merged,
isProtectedBranch,
...data,
};
},
stubs: {
GlModal: stubComponent(GlModal, {
template:
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
}),
GlButton,
GlFormInput,
GlSprintf,
},
}),
);
};
const findDeleteButton = () => wrapper.find('[data-testid="delete_branch_confirmation_button"]');
const findModal = () => wrapper.findComponent(GlModal);
const findModalMessage = () => wrapper.findByTestId('modal-message');
const findDeleteButton = () => wrapper.findByTestId('delete-branch-confirmation-button');
const findCancelButton = () => wrapper.findByTestId('delete-branch-cancel-button');
const findFormInput = () => wrapper.findComponent(GlFormInput);
const findForm = () => wrapper.find('form');
describe('Delete branch modal', () => {
const expectedUnmergedWarning =
'This branch hasn’t been merged into default. To avoid data loss, consider merging this branch before deleting it.';
afterEach(() => {
wrapper.destroy();
});
describe('Deleting a regular branch', () => {
const expectedTitle = 'Delete branch. Are you ABSOLUTELY SURE?';
const expectedWarning = "You're about to permanently delete the branch test_modal.";
const expectedMessage = `${expectedWarning} ${expectedUnmergedWarning}`;
beforeEach(() => {
createComponent();
});
it('renders the modal correctly', () => {
expect(wrapper.html()).toMatchSnapshot();
expect(findModal().props('title')).toBe(expectedTitle);
expect(findModalMessage().text()).toMatchInterpolatedText(expectedMessage);
expect(findCancelButton().text()).toBe('Cancel, keep branch');
expect(findDeleteButton().text()).toBe('Yes, delete branch');
expect(findForm().attributes('action')).toBe(deletePath);
});
it('submits the form when clicked', () => {
it('submits the form when the delete button is clicked', () => {
const submitFormSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
return wrapper.vm.$nextTick().then(() => {
findDeleteButton().trigger('click');
findDeleteButton().trigger('click');
expect(findForm().attributes('action')).toBe(deletePath);
expect(submitFormSpy).toHaveBeenCalled();
});
it('calls show on the modal when a `openModal` event is received through the event hub', async () => {
const showSpy = jest.spyOn(wrapper.vm.$refs.modal, 'show');
expect(submitFormSpy).toHaveBeenCalled();
eventHub.$emit('openModal', {
isProtectedBranch,
branchName,
defaultBranchName,
deletePath,
merged,
});
expect(showSpy).toHaveBeenCalled();
});
it('calls hide on the modal when cancel button is clicked', () => {
const closeModalSpy = jest.spyOn(wrapper.vm.$refs.modal, 'hide');
findCancelButton().trigger('click');
expect(closeModalSpy).toHaveBeenCalled();
});
});
describe('Deleting a protected branch (for owner or maintainer)', () => {
const expectedTitleProtected = 'Delete protected branch. Are you ABSOLUTELY SURE?';
const expectedWarningProtected =
"You're about to permanently delete the protected branch test_modal.";
const expectedMessageProtected = `${expectedWarningProtected} ${expectedUnmergedWarning}`;
const expectedConfirmationText =
'Once you confirm and press Yes, delete protected branch, it cannot be undone or recovered. Please type the following to confirm: test_modal';
beforeEach(() => {
createComponent({ isProtectedBranch: true });
});
it('renders the modal correctly', () => {
expect(wrapper.html()).toMatchSnapshot();
describe('rendering the modal correctly for a protected branch', () => {
it('sets the modal title for a protected branch', () => {
expect(findModal().props('title')).toBe(expectedTitleProtected);
});
it('renders the correct text in the modal message', () => {
expect(findModalMessage().text()).toMatchInterpolatedText(expectedMessageProtected);
});
it('renders the protected branch name confirmation form with expected text and action', () => {
expect(findForm().text()).toMatchInterpolatedText(expectedConfirmationText);
expect(findForm().attributes('action')).toBe(deletePath);
});
it('renders the buttons with the correct button text', () => {
expect(findCancelButton().text()).toBe('Cancel, keep branch');
expect(findDeleteButton().text()).toBe('Yes, delete protected branch');
});
});
it('disables the delete button when branch name input is unconfirmed', () => {
expect(findDeleteButton().attributes('disabled')).toBe('true');
it('opens with the delete button disabled and enables it when branch name is confirmed', async () => {
expect(findDeleteButton().props('disabled')).toBe(true);
findFormInput().vm.$emit('input', branchName);
await waitForPromises();
expect(findDeleteButton().props('disabled')).not.toBe(true);
});
});
describe('Deleting a merged branch', () => {
it('does not include the unmerged branch warning when merged is true', () => {
createComponent({ merged: true });
it('enables the delete button when branch name input is confirmed', () => {
return wrapper.vm
.$nextTick()
.then(() => {
findFormInput().vm.$emit('input', branchName);
})
.then(() => {
expect(findDeleteButton()).not.toBeDisabled();
});
expect(findModalMessage().html()).not.toContain(expectedUnmergedWarning);
});
});
});
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