Commit e006d7b3 authored by Jackie Fraser's avatar Jackie Fraser Committed by Nathan Friend

Add delete branch modals behind feature flag

Replaces the bootstrap modal for the delete branch button and
confirmation.

Changelog: added
parent cb611101
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
export default {
name: 'DeleteBranchButton',
components: { GlButton },
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
branchName: {
type: String,
required: false,
default: '',
},
defaultBranchName: {
type: String,
required: false,
default: '',
},
deletePath: {
type: String,
required: false,
default: '',
},
tooltip: {
type: String,
required: false,
default: s__('Branches|Delete branch'),
},
disabled: {
type: Boolean,
required: false,
default: false,
},
isProtectedBranch: {
type: Boolean,
required: false,
default: false,
},
merged: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
variant() {
if (this.disabled) {
return 'default';
}
return 'danger';
},
title() {
if (this.isProtectedBranch && this.disabled) {
return s__('Branches|Only a project maintainer or owner can delete a protected branch');
} else if (this.isProtectedBranch) {
return s__('Branches|Delete protected branch');
}
return this.tooltip;
},
},
methods: {
openModal() {
eventHub.$emit('openModal', {
branchName: this.branchName,
defaultBranchName: this.defaultBranchName,
deletePath: this.deletePath,
isProtectedBranch: this.isProtectedBranch,
merged: this.merged,
});
},
},
};
</script>
<template>
<gl-button
v-gl-tooltip.hover
icon="remove"
class="js-delete-branch-button"
data-qa-selector="delete_branch_button"
:disabled="disabled"
:variant="variant"
:title="title"
:aria-label="title"
@click="openModal"
/>
</template>
<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';
export default {
csrf,
components: {
GlModal,
GlButton,
GlFormInput,
GlSprintf,
GlAlert,
},
data() {
return {
visible: false,
isProtectedBranch: false,
branchName: '',
defaultBranchName: '',
deletePath: '',
merged: false,
enteredBranchName: '',
modalId: 'delete-branch-modal',
};
},
computed: {
title() {
const modalTitle = this.isProtectedBranch
? this.$options.i18n.modalTitleProtectedBranch
: this.$options.i18n.modalTitle;
return sprintf(modalTitle, { branchName: this.branchName });
},
message() {
const modalMessage = this.isProtectedBranch
? this.$options.i18n.modalMessageProtectedBranch
: this.$options.i18n.modalMessage;
return sprintf(modalMessage, { branchName: this.branchName });
},
unmergedWarning() {
return sprintf(this.$options.i18n.unmergedWarning, {
defaultBranchName: this.defaultBranchName,
});
},
undoneWarning() {
return sprintf(this.$options.i18n.undoneWarning, {
buttonText: this.buttonText,
});
},
confirmationText() {
return sprintf(this.$options.i18n.confirmationText, {
branchName: this.branchName,
});
},
buttonText() {
return this.isProtectedBranch
? this.$options.i18n.deleteButtonTextProtectedBranch
: this.$options.i18n.deleteButtonText;
},
branchNameConfirmed() {
return this.enteredBranchName === this.branchName;
},
deleteButtonDisabled() {
return this.isProtectedBranch && !this.branchNameConfirmed;
},
},
mounted() {
eventHub.$on('openModal', (options) => {
this.openModal(options);
});
},
methods: {
openModal({ isProtectedBranch, branchName, defaultBranchName, deletePath, merged }) {
this.isProtectedBranch = isProtectedBranch;
this.branchName = branchName;
this.defaultBranchName = defaultBranchName;
this.deletePath = deletePath;
this.merged = merged;
this.$root.$emit(BV_SHOW_MODAL, this.modalId);
},
submitForm() {
this.$refs.form.submit();
},
closeModal() {
this.$root.$emit(BV_HIDE_MODAL, this.modalId);
},
},
i18n: {
modalTitle: s__('Branches|Delete branch. Are you ABSOLUTELY SURE?'),
modalTitleProtectedBranch: s__('Branches|Delete protected branch. Are you ABSOLUTELY SURE?'),
modalMessage: s__(
"Branches|You're about to permanently delete the branch %{strongStart}%{branchName}.%{strongEnd}",
),
modalMessageProtectedBranch: s__(
"Branches|You're about to permanently delete the protected branch %{strongStart}%{branchName}.%{strongEnd}",
),
unmergedWarning: s__(
'Branches|This branch hasn’t been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it.',
),
undoneWarning: s__(
'Branches|Once you confirm and press %{strongStart}%{buttonText},%{strongEnd} it cannot be undone or recovered.',
),
cancelButtonText: s__('Branches|Cancel, keep branch'),
confirmationText: s__(
'Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?',
),
confirmationTextProtectedBranch: s__('Branches|Please type the following to confirm:'),
deleteButtonText: s__('Branches|Yes, delete branch'),
deleteButtonTextProtectedBranch: s__('Branches|Yes, delete protected branch'),
},
};
</script>
<template>
<gl-modal :visible="visible" 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">
<template #strong="{ content }">
<strong> {{ content }} </strong>
</template>
</gl-sprintf>
<p v-if="!merged" class="gl-mb-0 gl-mt-4">
{{ unmergedWarning }}
</p>
</div>
</gl-alert>
<form ref="form" :action="deletePath" method="post">
<div v-if="isProtectedBranch" class="gl-mt-4">
<p>
<gl-sprintf :message="undoneWarning">
<template #strong="{ content }">
<strong> {{ content }} </strong>
</template>
</gl-sprintf>
</p>
<p>
<gl-sprintf :message="$options.i18n.confirmationTextProtectedBranch">
<template #strong="{ content }">
{{ content }}
</template>
</gl-sprintf>
<code class="gl-white-space-pre-wrap"> {{ branchName }} </code>
<gl-form-input
v-model="enteredBranchName"
name="delete_branch_input"
type="text"
class="gl-mt-4"
aria-labelledby="input-label"
autocomplete="off"
/>
</p>
</div>
<div v-else>
<p class="gl-mt-4">
<gl-sprintf :message="confirmationText">
<template #strong="{ content }">
<strong>
{{ content }}
</strong>
</template>
</gl-sprintf>
</p>
</div>
<input ref="method" type="hidden" name="_method" value="delete" />
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
</form>
<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">
{{ $options.i18n.cancelButtonText }}
</gl-button>
<div class="gl-mr-3"></div>
<gl-button
ref="deleteBranchButton"
:disabled="deleteButtonDisabled"
variant="danger"
data-qa-selector="delete_branch_confirmation_button"
data-testid="delete_branch_confirmation_button"
@click="submitForm"
>{{ buttonText }}</gl-button
>
</div>
</template>
</gl-modal>
</template>
import createEventHub from '~/helpers/event_hub_factory';
export default createEventHub();
import Vue from 'vue';
import DeleteBranchButton from '~/branches/components/delete_branch_button.vue';
import { parseBoolean } from '~/lib/utils/common_utils';
export default function initDeleteBranchButton(el) {
if (!el) {
return false;
}
const {
branchName,
defaultBranchName,
deletePath,
tooltip,
disabled,
isProtectedBranch,
merged,
} = el.dataset;
return new Vue({
el,
render: (createElement) =>
createElement(DeleteBranchButton, {
props: {
branchName,
defaultBranchName,
deletePath,
tooltip,
disabled: parseBoolean(disabled),
isProtectedBranch: parseBoolean(isProtectedBranch),
merged: parseBoolean(merged),
},
}),
});
}
import Vue from 'vue';
import DeleteBranchModal from '~/branches/components/delete_branch_modal.vue';
export default function initDeleteBranchModal() {
const el = document.querySelector('.js-delete-branch-modal');
if (!el) {
return false;
}
return new Vue({
el,
render(createComponent) {
return createComponent(DeleteBranchModal);
},
});
}
......@@ -3,6 +3,8 @@ import AjaxLoadingSpinner from '~/branches/ajax_loading_spinner';
import BranchSortDropdown from '~/branches/branch_sort_dropdown';
import DeleteModal from '~/branches/branches_delete_modal';
import initDiverganceGraph from '~/branches/divergence_graph';
import initDeleteBranchButton from '~/branches/init_delete_branch_button';
import initDeleteBranchModal from '~/branches/init_delete_branch_modal';
AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new
......@@ -14,3 +16,9 @@ const { divergingCountsEndpoint, defaultBranch } = document.querySelector(
initDiverganceGraph(divergingCountsEndpoint, defaultBranch);
BranchSortDropdown();
initDeprecatedRemoveRowBehavior();
document
.querySelectorAll('.js-delete-branch-button')
.forEach((elem) => initDeleteBranchButton(elem));
initDeleteBranchModal();
......@@ -48,7 +48,10 @@
= render 'projects/buttons/download', project: @project, ref: branch.name, pipeline: @refs_pipelines[branch.name], class: 'gl-vertical-align-top'
- if can?(current_user, :push_code, @project)
- if Feature.enabled?(:delete_branch_confirmation_modals, @project, default_enabled: :yaml)
= render 'projects/branches/delete_branch_modal_button', project: @project, branch: branch, merged: merged
- elsif can?(current_user, :push_code, @project)
- if branch.name == @project.repository.root_ref
- delete_default_branch_tooltip = s_('Branches|The default branch cannot be deleted')
%span.gl-display-inline-block.has-tooltip{ title: delete_default_branch_tooltip }
......
- if branch.name == @project.repository.root_ref
.js-delete-branch-button{ data: { tooltip: s_('Branches|The default branch cannot be deleted'),
disabled: true.to_s } }
- elsif protected_branch?(@project, branch)
- if can?(current_user, :push_to_delete_protected_branch, @project)
.js-delete-branch-button{ data: { branch_name: branch.name,
is_protected_branch: true.to_s,
merged: merged.to_s,
default_branch_name: @project.repository.root_ref,
delete_path: project_branch_path(@project, branch.name) } }
- else
.js-delete-branch-button{ data: { is_protected_branch: true.to_s,
disabled: true.to_s } }
- else
.js-delete-branch-button{ data: { branch_name: branch.name,
merged: merged.to_s,
default_branch_name: @project.repository.root_ref,
delete_path: project_branch_path(@project, branch.name) } }
......@@ -54,4 +54,7 @@
.nothing-here-block
= s_('Branches|No branches to show')
= render 'projects/branches/delete_protected_modal'
- if Feature.enabled?(:delete_branch_confirmation_modals, @project, default_enabled: :yaml) && can?(current_user, :push_code, @project)
.js-delete-branch-modal
- elsif can?(current_user, :push_code, @project)
= render 'projects/branches/delete_protected_modal'
---
name: delete_branch_confirmation_modals
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56782
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329052
milestone: '13.12'
type: development
group: group::expansion
default_enabled: false
......@@ -5378,6 +5378,9 @@ msgstr ""
msgid "Branches|All"
msgstr ""
msgid "Branches|Cancel, keep branch"
msgstr ""
msgid "Branches|Cant find HEAD commit for this branch"
msgstr ""
......@@ -5390,6 +5393,9 @@ msgstr ""
msgid "Branches|Delete branch"
msgstr ""
msgid "Branches|Delete branch. Are you ABSOLUTELY SURE?"
msgstr ""
msgid "Branches|Delete merged branches"
msgstr ""
......@@ -5399,6 +5405,12 @@ msgstr ""
msgid "Branches|Delete protected branch '%{branch_name}'?"
msgstr ""
msgid "Branches|Delete protected branch. Are you ABSOLUTELY SURE?"
msgstr ""
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
msgstr ""
msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
msgstr ""
......@@ -5420,12 +5432,18 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
msgid "Branches|Once you confirm and press %{strongStart}%{buttonText},%{strongEnd} it cannot be undone or recovered."
msgstr ""
msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
msgstr ""
msgid "Branches|Please type the following to confirm:"
msgstr ""
msgid "Branches|Protected branches can be managed in %{project_settings_link}."
msgstr ""
......@@ -5459,6 +5477,9 @@ msgstr ""
msgid "Branches|The default branch cannot be deleted"
msgstr ""
msgid "Branches|This branch hasn’t been merged into %{defaultBranchName}. To avoid data loss, consider merging this branch before deleting it."
msgstr ""
msgid "Branches|This branch hasn’t been merged into %{default_branch}."
msgstr ""
......@@ -5471,6 +5492,18 @@ msgstr ""
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
msgid "Branches|Yes, delete branch"
msgstr ""
msgid "Branches|Yes, delete protected branch"
msgstr ""
msgid "Branches|You're about to permanently delete the branch %{strongStart}%{branchName}.%{strongEnd}"
msgstr ""
msgid "Branches|You're about to permanently delete the protected branch %{strongStart}%{branchName}.%{strongEnd}"
msgstr ""
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr ""
......
......@@ -5,13 +5,23 @@ module QA
module Project
module Branches
class Show < Page::Base
view 'app/assets/javascripts/branches/components/delete_branch_button.vue' do
element :delete_branch_button
end
view 'app/assets/javascripts/branches/components/delete_branch_modal.vue' do
element :delete_branch_confirmation_button
end
view 'app/views/projects/branches/_branch.html.haml' do
element :remove_btn
element :branch_name
end
view 'app/views/projects/branches/_panel.html.haml' do
element :all_branches
end
view 'app/views/projects/branches/index.html.haml' do
element :delete_merged_branches
end
......@@ -19,12 +29,12 @@ module QA
def delete_branch(branch_name)
within_element(:all_branches) do
within(".js-branch-#{branch_name}") do
accept_alert do
click_element(:remove_btn)
end
click_element(:delete_branch_button)
end
end
click_element(:delete_branch_confirmation_button)
finished_loading?
end
......
......@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
describe 'Create, list, and delete branches via web' do
describe 'Create, list, and delete branches via web', :requires_admin do
master_branch = nil
second_branch = 'second-branch'
third_branch = 'third-branch'
......@@ -24,6 +24,8 @@ module QA
proj.initialize_with_readme = true
end
Runtime::Feature.enable(:delete_branch_confirmation_modals, project: project)
master_branch = project.default_branch
Git::Repository.perform do |repository|
......
......@@ -12,7 +12,7 @@ RSpec.describe "User deletes branch", :js do
sign_in(user)
end
it "deletes branch" do
it "deletes branch", :js do
visit(project_branches_path(project))
branch_search = find('input[data-testid="branch-search"]')
......@@ -21,11 +21,38 @@ RSpec.describe "User deletes branch", :js do
branch_search.native.send_keys(:enter)
page.within(".js-branch-improve\\/awesome") do
accept_alert { click_link(title: 'Delete branch') }
find('.js-delete-branch-button').click
end
page.within '.modal-footer' do
click_button 'Yes, delete branch'
end
wait_for_requests
expect(page).to have_css(".js-branch-improve\\/awesome", visible: :hidden)
expect(page).to have_content('Branch was deleted')
end
context 'when the feature flag :delete_branch_confirmation_modals is disabled' do
before do
stub_feature_flags(delete_branch_confirmation_modals: false)
end
it "deletes branch" do
visit(project_branches_path(project))
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('improve/awesome')
branch_search.native.send_keys(:enter)
page.within(".js-branch-improve\\/awesome") do
accept_alert { click_link(title: 'Delete branch') }
end
wait_for_requests
expect(page).to have_css(".js-branch-improve\\/awesome", visible: :hidden)
end
end
end
......@@ -2,7 +2,7 @@
require "spec_helper"
RSpec.describe "User views branches" do
RSpec.describe "User views branches", :js do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.owner }
......@@ -10,9 +10,12 @@ RSpec.describe "User views branches" do
sign_in(user)
end
context "all branches" do
context "all branches", :js do
before do
visit(project_branches_path(project))
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('master')
branch_search.native.send_keys(:enter)
end
it "shows branches" do
......@@ -20,6 +23,10 @@ RSpec.describe "User views branches" do
expect(page.all(".graph-side")).to all( have_content(/\d+/) )
end
it "displays a disabled button with a tooltip for the default branch that cannot be deleted", :js do
expect(page).to have_button('The default branch cannot be deleted', disabled: true)
end
end
context "protected branches" do
......
......@@ -88,10 +88,7 @@ RSpec.describe 'Branches' do
it 'shows filtered branches', :js do
visit project_branches_path(project)
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('fix')
branch_search.native.send_keys(:enter)
search_for_branch('fix')
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
......@@ -103,11 +100,10 @@ RSpec.describe 'Branches' do
visit project_branches_filtered_path(project, state: 'all')
expect(all('.all-branches').last).to have_selector('li', count: 20)
accept_confirm do
within('.js-branch-item', match: :first) { click_link(title: 'Delete branch') }
end
expect(all('.all-branches').last).to have_selector('li', count: 19)
delete_branch_and_confirm
expect(page).to have_content('Branch was deleted')
end
end
......@@ -153,10 +149,7 @@ RSpec.describe 'Branches' do
it 'shows filtered branches', :js do
visit project_branches_filtered_path(project, state: 'all')
branch_search = find('input[data-testid="branch-search"]')
branch_search.set('fix')
branch_search.native.send_keys(:enter)
search_for_branch('fix')
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
......@@ -167,19 +160,39 @@ RSpec.describe 'Branches' do
it 'removes branch after confirmation', :js do
visit project_branches_filtered_path(project, state: 'all')
branch_search = find('input[data-testid="branch-search"]')
search_for_branch('fix')
branch_search.set('fix')
branch_search.native.send_keys(:enter)
expect(all('.all-branches').last).to have_selector('li', count: 1)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
accept_confirm do
within('.js-branch-fix') { click_link(title: 'Delete branch') }
end
delete_branch_and_confirm
expect(page).to have_content('Branch was deleted')
page.refresh
search_for_branch('fix')
expect(page).not_to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 0)
expect(all('.all-branches').last).to have_selector('li', count: 0)
end
context 'when the delete_branch_confirmation_modals feature flag is disabled' do
it 'removes branch after confirmation', :js do
stub_feature_flags(delete_branch_confirmation_modals: false)
visit project_branches_filtered_path(project, state: 'all')
search_for_branch('fix')
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
accept_confirm do
within('.js-branch-item', match: :first) { click_link(title: 'Delete branch') }
end
expect(page).not_to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 0)
end
end
end
......@@ -327,4 +340,18 @@ RSpec.describe 'Branches' do
def create_file(message: 'message', branch_name:)
repository.create_file(user, generate(:branch), 'content', message: message, branch_name: branch_name)
end
def search_for_branch(name)
branch_search = find('input[data-testid="branch-search"]')
branch_search.set(name)
branch_search.native.send_keys(:enter)
end
def delete_branch_and_confirm
find('.js-delete-branch-button', match: :first).click
within '.modal-footer' do
click_button 'Yes, delete branch'
end
end
end
......@@ -329,11 +329,11 @@ RSpec.describe 'Environment' do
expect(page).to have_button('Stop')
end
it 'user deletes the branch with running environment' do
it 'user deletes the branch with running environment', :js do
visit project_branches_filtered_path(project, state: 'all', search: 'feature')
remove_branch_with_hooks(project, user, 'feature') do
within('.js-branch-feature') { click_link(title: 'Delete branch') }
page.within('.js-branch-feature') { find('.js-delete-branch-button').click }
end
visit_environment(environment)
......@@ -341,6 +341,24 @@ RSpec.describe 'Environment' do
expect(page).not_to have_button('Stop')
end
context 'when the feature flag :delete_branch_confirmation_modals is disabled' do
before do
stub_feature_flags(delete_branch_confirmation_modals: false)
end
it 'user deletes the branch with running environment' do
visit project_branches_filtered_path(project, state: 'all', search: 'feature')
remove_branch_with_hooks(project, user, 'feature') do
within('.js-branch-feature') { click_link(title: 'Delete branch') }
end
visit_environment(environment)
expect(page).not_to have_button('Stop')
end
end
##
# This is a workaround for problem described in #24543
#
......
......@@ -27,7 +27,22 @@ RSpec.describe 'Protected Branches', :js do
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_selector('button[data-testid="remove-protected-branch"][disabled]')
expect(page).to have_button('Only a project maintainer or owner can delete a protected branch', disabled: true)
end
context 'when feature flag :delete_branch_confirmation_modals is disabled' do
before do
stub_feature_flags(delete_branch_confirmation_modals: false)
end
it 'does not allow developer to remove protected branch' do
visit project_branches_path(project)
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_selector('button[data-testid="remove-protected-branch"][disabled]')
end
end
end
end
......@@ -52,17 +67,44 @@ RSpec.describe 'Protected Branches', :js do
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
page.find('[data-target="#modal-delete-branch"]').click
expect(page).to have_css('.js-delete-branch[disabled]')
expect(page).to have_button('Delete protected branch', disabled: false)
page.find('.js-delete-branch-button').click
fill_in 'delete_branch_input', with: 'fix'
click_link 'Delete protected branch'
click_button 'Yes, delete protected branch'
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_content('No branches to show')
end
context 'when the feature flag :delete_branch_confirmation_modals is disabled' do
before do
stub_feature_flags(delete_branch_confirmation_modals: false)
end
it 'removes branch after modal confirmation' do
visit project_branches_path(project)
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
page.find('[data-target="#modal-delete-branch"]').click
expect(page).to have_css('.js-delete-branch[disabled]')
fill_in 'delete_branch_input', with: 'fix'
click_link 'Delete protected branch'
find('input[data-testid="branch-search"]').set('fix')
find('input[data-testid="branch-search"]').native.send_keys(:enter)
expect(page).to have_content('No branches to show')
end
end
end
end
......
// 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>"
`;
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 correct 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('title')).toBe('Delete protected branch');
});
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('disabled')).not.toBe('true');
});
// Used for unallowed users and for the default branch.
it('disables the button when mounted for a disabled modal', () => {
createComponent({ disabled: true });
expect(findDeleteButton().attributes('disabled')).toBe('true');
});
});
});
import { GlButton, GlModal, GlFormInput } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import DeleteBranchModal from '~/branches/components/delete_branch_modal.vue';
let wrapper;
const branchName = 'test_modal';
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,
},
});
};
const findDeleteButton = () => wrapper.find('[data-testid="delete_branch_confirmation_button"]');
const findFormInput = () => wrapper.findComponent(GlFormInput);
describe('Delete branch modal', () => {
afterEach(() => {
wrapper.destroy();
});
describe('Deleting a regular branch', () => {
beforeEach(() => {
createComponent();
});
it('renders the modal correctly', () => {
expect(wrapper.html()).toMatchSnapshot();
});
it('submits the form when clicked', () => {
const submitFormSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
return wrapper.vm.$nextTick().then(() => {
findDeleteButton().trigger('click');
expect(submitFormSpy).toHaveBeenCalled();
});
});
});
describe('Deleting a protected branch (for owner or maintainer)', () => {
beforeEach(() => {
createComponent({ isProtectedBranch: true });
});
it('renders the modal correctly', () => {
expect(wrapper.html()).toMatchSnapshot();
});
it('disables the delete button when branch name input is unconfirmed', () => {
expect(findDeleteButton().attributes('disabled')).toBe('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();
});
});
});
});
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