diff options
Diffstat (limited to 'spec/frontend/branches')
-rw-r--r-- | spec/frontend/branches/components/delete_branch_button_spec.js | 96 | ||||
-rw-r--r-- | spec/frontend/branches/components/delete_branch_modal_spec.js | 157 |
2 files changed, 253 insertions, 0 deletions
diff --git a/spec/frontend/branches/components/delete_branch_button_spec.js b/spec/frontend/branches/components/delete_branch_button_spec.js new file mode 100644 index 00000000000..acbc83a9bdc --- /dev/null +++ b/spec/frontend/branches/components/delete_branch_button_spec.js @@ -0,0 +1,96 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import DeleteBranchButton from '~/branches/components/delete_branch_button.vue'; +import eventHub from '~/branches/event_hub'; + +let wrapper; +let findDeleteButton; + +const createComponent = (props = {}) => { + wrapper = shallowMount(DeleteBranchButton, { + propsData: { + branchName: 'test', + deletePath: '/path/to/branch', + defaultBranchName: 'main', + ...props, + }, + }); +}; + +describe('Delete branch button', () => { + let eventHubSpy; + + beforeEach(() => { + findDeleteButton = () => wrapper.findComponent(GlButton); + eventHubSpy = jest.spyOn(eventHub, '$emit'); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the button with default tooltip, style, and icon', () => { + createComponent(); + + expect(findDeleteButton().attributes()).toMatchObject({ + title: 'Delete branch', + variant: 'danger', + icon: 'remove', + }); + }); + + it('renders a different tooltip for a protected branch', () => { + createComponent({ isProtectedBranch: true }); + + 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', () => { + createComponent({ merged: true }); + + findDeleteButton().vm.$emit('click'); + + expect(eventHubSpy).toHaveBeenCalledWith('openModal', { + branchName: 'test', + defaultBranchName: 'main', + deletePath: '/path/to/branch', + isProtectedBranch: false, + merged: true, + }); + }); + + describe('#disabled', () => { + it('does not disable the button by default when mounted', () => { + createComponent(); + + 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, tooltip: 'The default branch cannot be deleted' }); + + expect(findDeleteButton().attributes()).toMatchObject({ + title: 'The default branch cannot be deleted', + disabled: 'true', + variant: 'default', + }); + }); + }); +}); diff --git a/spec/frontend/branches/components/delete_branch_modal_spec.js b/spec/frontend/branches/components/delete_branch_modal_spec.js new file mode 100644 index 00000000000..0c6111bda9e --- /dev/null +++ b/spec/frontend/branches/components/delete_branch_modal_spec.js @@ -0,0 +1,157 @@ +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 = 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 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(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 the delete button is clicked', () => { + const submitFormSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit'); + + 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'); + + 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 }); + }); + + 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('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 }); + + expect(findModalMessage().html()).not.toContain(expectedUnmergedWarning); + }); + }); +}); |