Commit 82ff8be6 authored by Markus Koller's avatar Markus Koller

Merge branch '36596-update-project-delete-modal' into 'master'

Update project delete modal

See merge request gitlab-org/gitlab!36962
parents 984c0b83 7c378891
......@@ -7,7 +7,7 @@ import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
import initFilePickers from '~/file_pickers';
import initProjectLoadingSpinner from '../shared/save_project_loader';
import initProjectPermissionsSettings from '../shared/permissions';
import initProjectRemoveModal from '~/projects/project_remove_modal';
import initProjectDeleteButton from '~/projects/project_delete_button';
import UserCallout from '~/user_callout';
import initServiceDesk from '~/projects/settings_service_desk';
......@@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
initFilePickers();
initConfirmDangerModal();
initSettingsPanels();
initProjectRemoveModal();
initProjectDeleteButton();
mountBadgeSettings(PROJECT_BADGE);
new UserCallout({ className: 'js-service-desk-callout' }); // eslint-disable-line no-new
......
<script>
import { GlAlert, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
import SharedDeleteButton from './shared/delete_button.vue';
export default {
components: {
GlSprintf,
GlAlert,
SharedDeleteButton,
},
props: {
confirmPhrase: {
type: String,
required: true,
},
formPath: {
type: String,
required: true,
},
},
strings: {
alertTitle: __('You are about to permanently delete this project'),
alertBody: __(
'Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its respositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc.',
),
modalBody: __(
"This action cannot be undone. You will lose the project's respository and all conent: issues, merge requests, etc.",
),
},
};
</script>
<template>
<shared-delete-button v-bind="{ confirmPhrase, formPath }">
<template #modal-body>
<gl-alert
class="gl-mb-5"
variant="danger"
:title="$options.strings.alertTitle"
:dismissible="false"
>
<gl-sprintf :message="$options.strings.alertBody">
<template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
</gl-sprintf>
</gl-alert>
<p>{{ $options.strings.modalBody }}</p>
</template>
</shared-delete-button>
</template>
<script>
import { GlModal, GlModalDirective, GlSprintf, GlFormInput, GlButton } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import { GlModal, GlModalDirective, GlFormInput, GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import { rstrip } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
export default {
components: {
GlModal,
GlSprintf,
GlFormInput,
GlButton,
},
......@@ -19,10 +18,6 @@ export default {
type: String,
required: true,
},
warningMessage: {
type: String,
required: true,
},
formPath: {
type: String,
required: true,
......@@ -31,15 +26,27 @@ export default {
data() {
return {
userInput: null,
modalId: uniqueId('delete-project-modal-'),
};
},
computed: {
buttonDisabled() {
return rstrip(this.userInput) !== this.confirmPhrase;
confirmDisabled() {
return this.userInput !== this.confirmPhrase;
},
csrfToken() {
return csrf.token;
},
modalActionProps() {
return {
primary: {
text: __('Yes, delete project'),
attributes: [{ variant: 'danger' }, { disabled: this.confirmDisabled }],
},
cancel: {
text: __('Cancel, keep project'),
},
};
},
},
methods: {
submitForm() {
......@@ -47,15 +54,10 @@ export default {
},
},
strings: {
removeProject: __('Remove project'),
title: __('Confirmation required'),
confirm: __('Confirm'),
dataLoss: __(
'This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention.',
),
confirmText: __('Please type %{phrase_code} to proceed or close this modal to cancel.'),
deleteProject: __('Remove project'),
title: __('Delete project. Are you ABSOLUTELY SURE?'),
confirmText: __('Please type the following to confirm:'),
},
modalId: 'remove-project-modal',
};
</script>
......@@ -63,38 +65,28 @@ export default {
<form ref="form" :action="formPath" method="post">
<input type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<gl-button v-gl-modal="$options.modalId" category="primary" variant="danger">{{
$options.strings.removeProject
<gl-button v-gl-modal="modalId" category="primary" variant="danger">{{
$options.strings.deleteProject
}}</gl-button>
<gl-modal
ref="removeModal"
:modal-id="$options.modalId"
:modal-id="modalId"
size="sm"
ok-variant="danger"
footer-class="bg-gray-light gl-p-5"
footer-class="gl-bg-gray-10 gl-p-5"
title-class="gl-text-red-500"
:action-primary="modalActionProps.primary"
:action-cancel="modalActionProps.cancel"
@ok="submitForm"
>
<template #modal-title>{{ $options.strings.title }}</template>
<template #modal-footer>
<div class="gl-w-full gl-display-flex gl-just-content-start gl-m-0">
<gl-button
:disabled="buttonDisabled"
category="primary"
variant="danger"
@click="submitForm"
>
{{ $options.strings.confirm }}
</gl-button>
</div>
</template>
<div>
<p class="gl-text-red-500 gl-font-weight-bold">{{ warningMessage }}</p>
<p class="gl-mb-0">{{ $options.strings.dataLoss }}</p>
<slot name="modal-body"></slot>
<p class="gl-mb-1">{{ $options.strings.confirmText }}</p>
<p>
<gl-sprintf :message="$options.strings.confirmText">
<template #phrase_code>
<code>{{ confirmPhrase }}</code>
</template>
</gl-sprintf>
</p>
<gl-form-input
id="confirm_name_input"
......@@ -102,6 +94,7 @@ export default {
name="confirm_name_input"
type="text"
/>
<slot name="modal-footer"></slot>
</div>
</gl-modal>
</form>
......
import Vue from 'vue';
import RemoveProjectModal from './components/remove_modal.vue';
import ProjectDeleteButton from './components/project_delete_button.vue';
export default (selector = '#js-confirm-project-remove') => {
export default (selector = '#js-project-delete-button') => {
const el = document.querySelector(selector);
if (!el) return;
const { formPath, confirmPhrase, warningMessage } = el.dataset;
const { confirmPhrase, formPath } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(RemoveProjectModal, {
return createElement(ProjectDeleteButton, {
props: {
confirmPhrase,
warningMessage,
formPath,
},
});
......
......@@ -6,4 +6,4 @@
%strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.')
%p
%strong= _('Removed projects cannot be restored!')
#js-confirm-project-remove{ data: { form_path: project_path(project), confirm_phrase: project.path, warning_message: remove_project_message(project) } }
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: project.path } }
---
title: Update project remove modal to add additional warnings
merge_request: 36962
author:
type: changed
......@@ -5,6 +5,7 @@ import mountApprovals from 'ee/approvals/mount_project_settings';
import UsersSelect from '~/users_select';
import UserCallout from '~/user_callout';
import groupsSelect from '~/groups_select';
import initProjectAdjournedDeleteButton from 'ee/projects/project_adjourned_delete_button';
document.addEventListener('DOMContentLoaded', () => {
new UsersSelect();
......@@ -13,4 +14,6 @@ document.addEventListener('DOMContentLoaded', () => {
new UserCallout({ className: 'js-mr-approval-callout' });
mountApprovals(document.getElementById('js-mr-approvals-settings'));
initProjectAdjournedDeleteButton();
});
<script>
import { GlLink, GlIcon, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
import SharedDeleteButton from '~/projects/components/shared/delete_button.vue';
export default {
components: {
GlSprintf,
GlIcon,
GlLink,
SharedDeleteButton,
},
props: {
confirmPhrase: {
type: String,
required: true,
},
formPath: {
type: String,
required: true,
},
adjournedRemovalDate: {
type: String,
required: true,
},
recoveryHelpPath: {
type: String,
required: true,
},
},
strings: {
modalBody: __(
"Once a project is permanently deleted it cannot be recovered. You will lose this project's repository and all content: issues, merge requests etc.",
),
helpLabel: __('Recovering projects'),
recoveryMessage: __('You can recover this project until %{date}'),
},
};
</script>
<template>
<shared-delete-button v-bind="{ confirmPhrase, formPath }">
<template #modal-body>
<p>{{ $options.strings.modalBody }}</p>
</template>
<template #modal-footer>
<p
class="gl-display-flex gl-display-flex gl-align-items-center gl-mt-3 gl-mb-0 gl-text-gray-500"
>
<gl-sprintf :message="$options.strings.recoveryMessage">
<template #date>
{{ adjournedRemovalDate }}
</template>
</gl-sprintf>
<gl-link
:aria-label="$options.strings.helpLabel"
class="gl-display-flex gl-ml-2 gl-text-gray-500"
:href="recoveryHelpPath"
>
<gl-icon name="question" />
</gl-link>
</p>
</template>
</shared-delete-button>
</template>
import Vue from 'vue';
import ProjectAdjournedDeleteButton from './components/project_adjourned_delete_button.vue';
export default (selector = '#js-project-adjourned-delete-button') => {
const el = document.querySelector(selector);
if (!el) return;
const { adjournedRemovalDate, confirmPhrase, formPath, recoveryHelpPath } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(ProjectAdjournedDeleteButton, {
props: {
adjournedRemovalDate,
confirmPhrase,
formPath,
recoveryHelpPath,
},
});
},
});
};
......@@ -94,6 +94,17 @@ module EE
{ date: date }
end
def permanent_delete_message(project)
message = _('This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all content: issues, merge requests, etc.')
html_escape(message) % remove_message_data(project)
end
def marked_for_removal_message(project)
date = permanent_deletion_date(Time.now.utc)
message = _('This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all content: issues, merge requests, etc.')
html_escape(message) % remove_message_data(project).merge(date: date)
end
def permanent_deletion_date(date)
(date + ::Gitlab::CurrentSettings.deletion_adjourned_period.days).strftime('%F')
end
......@@ -282,5 +293,15 @@ module EE
nav_tabs
end
def remove_message_data(project)
{
project: project.path,
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe,
codeOpen: '<code>'.html_safe,
codeClose: '</code>'.html_safe
}
end
end
end
- return unless can?(current_user, :remove_project, project)
- adjourned_deletion = project.adjourned_deletion?
- adjourned_date = adjourned_deletion ? permanent_deletion_date(Time.now.utc).to_s : nil;
- admin_help_path = help_page_path('user/admin_area/settings/visibility_and_access_controls', anchor: 'default-deletion-adjourned-period-premium-only')
- recovery_help_path = help_page_path('user/project/settings/index', anchor: 'remove-a-project')
- unless project.marked_for_deletion?
.sub-section
......@@ -8,14 +11,15 @@
%strong= s_('Delayed Project Deletion (%{adjourned_deletion})') % { adjourned_deletion: adjourned_deletion ? 'Enabled' : 'Disabled' }
- if adjourned_deletion
= render 'projects/settings/marked_for_removal'
#js-project-adjourned-delete-button{ data: { recovery_help_path: recovery_help_path, adjourned_removal_date: adjourned_date, form_path: project_path(project), confirm_phrase: project.path } }
- else
%p
= _("Removing a project deletes it immediately, there will be no delay before the project is permanently removed.")
%span.gl-text-gray-500= _('Projects will be permanently deleted immediately.')
= link_to(_('Customizable by an administrator.'), admin_help_path)
%p= permanent_delete_message(project)
%p
%strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.')
%p
%strong= _('Removed projects cannot be restored!')
#js-confirm-project-remove{ data: { form_path: project_path(project), confirm_phrase: project.path, warning_message: remove_project_message(project) } }
%strong= _('Are you ABSOLUTELY SURE you wish to delete this project?')
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: project.path } }
- else
= render 'projects/settings/restore', project: project
- return unless @project.feature_available?(:adjourned_deletion_for_projects_and_groups)
- date = permanent_deletion_date(Time.now.utc)
%p
= _("Removing a project places it into a read-only state until %{date}, at which point the project will be permanently removed.") %{ date: date }
%br
= _("Until that time, the project can be restored.")
%span.gl-text-gray-500= _('Projects will be permanently deleted after a 7-day waiting period.')
= link_to(_('Customizable by an administrator.'), help_page_path('user/admin_area/settings/visibility_and_access_controls', anchor: 'default-deletion-adjourned-period-premium-only'))
%p= marked_for_removal_message(@project)
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Project remove modal initialized matches the snapshot 1`] = `
<form
action="some/path"
method="post"
>
<input
name="_method"
type="hidden"
value="delete"
/>
<input
name="authenticity_token"
type="hidden"
/>
<gl-button-stub
category="primary"
icon=""
role="button"
size="medium"
tabindex="0"
variant="danger"
>
Remove project
</gl-button-stub>
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
footer-class="gl-bg-gray-10 gl-p-5"
modalclass=""
modalid="fakeUniqueId"
ok-variant="danger"
size="sm"
title-class="gl-text-red-500"
titletag="h4"
>
<div>
<p>
Once a project is permanently deleted it cannot be recovered. You will lose this project's repository and all content: issues, merge requests etc.
</p>
<p
class="gl-mb-1"
>
Please type the following to confirm:
</p>
<p>
<code>
foo
</code>
</p>
<gl-form-input-stub
id="confirm_name_input"
name="confirm_name_input"
type="text"
/>
<p
class="gl-display-flex gl-display-flex gl-align-items-center gl-mt-3 gl-mb-0 gl-text-gray-500"
>
<gl-sprintf-stub
message="You can recover this project until %{date}"
/>
<gl-link-stub
aria-label="Recovering projects"
class="gl-display-flex gl-ml-2 gl-text-gray-500"
href="recovery/help/path"
>
<gl-icon-stub
name="question"
size="16"
/>
</gl-link-stub>
</p>
</div>
</gl-modal-stub>
</form>
`;
import { shallowMount } from '@vue/test-utils';
import ProjectAdjournedDeleteButton from 'ee/projects/components/project_adjourned_delete_button.vue';
import SharedDeleteButton from '~/projects/components/shared/delete_button.vue';
jest.mock('lodash/uniqueId', () => () => 'fakeUniqueId');
describe('Project remove modal', () => {
let wrapper;
const findSharedDeleteButton = () => wrapper.find(SharedDeleteButton);
const defaultProps = {
adjournedRemovalDate: '2020-12-12',
confirmPhrase: 'foo',
formPath: 'some/path',
recoveryHelpPath: 'recovery/help/path',
};
const createComponent = (props = {}) => {
wrapper = shallowMount(ProjectAdjournedDeleteButton, {
propsData: {
...defaultProps,
...props,
},
stubs: {
SharedDeleteButton,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('initialized', () => {
beforeEach(() => {
createComponent();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('passes confirmPhrase and formPath props to the shared delete button', () => {
expect(findSharedDeleteButton().props()).toEqual({
confirmPhrase: defaultProps.confirmPhrase,
formPath: defaultProps.formPath,
});
});
});
});
......@@ -3083,6 +3083,9 @@ msgstr ""
msgid "Archiving the project will make it entirely read only. It is hidden from the dashboard and doesn't show up in searches. %{strong_start}The repository cannot be committed to, and no issues, comments, or other entities can be created.%{strong_end}"
msgstr ""
msgid "Are you ABSOLUTELY SURE you wish to delete this project?"
msgstr ""
msgid "Are you setting up GitLab for a company?"
msgstr ""
......@@ -4245,6 +4248,9 @@ msgstr ""
msgid "Cancel this job"
msgstr ""
msgid "Cancel, keep project"
msgstr ""
msgid "Canceled deployment to"
msgstr ""
......@@ -7283,6 +7289,9 @@ msgstr ""
msgid "Customer Portal"
msgstr ""
msgid "Customizable by an administrator."
msgstr ""
msgid "Customize colors"
msgstr ""
......@@ -7729,6 +7738,9 @@ msgstr ""
msgid "Delete project"
msgstr ""
msgid "Delete project. Are you ABSOLUTELY SURE?"
msgstr ""
msgid "Delete serverless domain?"
msgstr ""
......@@ -16547,6 +16559,12 @@ msgstr ""
msgid "OnDemandScans|Target URL"
msgstr ""
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its respositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
msgid "Once a project is permanently deleted it cannot be recovered. You will lose this project's repository and all content: issues, merge requests etc."
msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
......@@ -17773,6 +17791,9 @@ msgstr ""
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
msgstr ""
msgid "Please type the following to confirm:"
msgstr ""
msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
msgstr ""
......@@ -18991,6 +19012,12 @@ msgstr ""
msgid "Projects to index"
msgstr ""
msgid "Projects will be permanently deleted after a 7-day waiting period."
msgstr ""
msgid "Projects will be permanently deleted immediately."
msgstr ""
msgid "Projects with critical vulnerabilities"
msgstr ""
......@@ -19675,6 +19702,9 @@ msgstr ""
msgid "Recover hidden stage"
msgstr ""
msgid "Recovering projects"
msgstr ""
msgid "Recovery Codes"
msgstr ""
......@@ -20069,12 +20099,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
msgid "Removing a project deletes it immediately, there will be no delay before the project is permanently removed."
msgstr ""
msgid "Removing a project places it into a read-only state until %{date}, at which point the project will be permanently removed."
msgstr ""
msgid "Removing a project places it into a read-only state until %{date}, at which point the project will be permanently removed. Are you ABSOLUTELY sure?"
msgstr ""
......@@ -24392,6 +24416,15 @@ msgstr ""
msgid "This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention."
msgstr ""
msgid "This action cannot be undone. You will lose the project's respository and all conent: issues, merge requests, etc."
msgstr ""
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all content: issues, merge requests, etc."
msgstr ""
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all content: issues, merge requests, etc."
msgstr ""
msgid "This also resolves all related threads"
msgstr ""
......@@ -25851,9 +25884,6 @@ msgstr ""
msgid "Until"
msgstr ""
msgid "Until that time, the project can be restored."
msgstr ""
msgid "Unverified"
msgstr ""
......@@ -27315,6 +27345,9 @@ msgstr ""
msgid "Yes, close issue"
msgstr ""
msgid "Yes, delete project"
msgstr ""
msgid "Yes, let me map Google Code users to full names or GitLab users."
msgstr ""
......@@ -27330,6 +27363,9 @@ msgstr ""
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
msgid "You are about to permanently delete this project"
msgstr ""
msgid "You are about to transfer the control of your account to %{group_name} group. This action is NOT reversible, you won't be able to access any of your groups and projects outside of %{group_name} once this transfer is complete."
msgstr ""
......@@ -27480,6 +27516,9 @@ msgstr ""
msgid "You can only upload one design when dropping onto an existing design."
msgstr ""
msgid "You can recover this project until %{date}"
msgstr ""
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
......
......@@ -260,7 +260,7 @@ RSpec.describe 'Project' do
end
it 'removes a project', :sidekiq_might_not_need_inline do
expect { remove_with_confirm('Remove project', project.path) }.to change { Project.count }.by(-1)
expect { remove_with_confirm('Remove project', project.path, 'Yes, delete project') }.to change { Project.count }.by(-1)
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
expect(Project.all.count).to be_zero
expect(project.issues).to be_empty
......@@ -386,9 +386,9 @@ RSpec.describe 'Project' do
{ form: '.rspec-merge-request-settings', input: '#project_printing_merge_request_link_enabled' }]
end
def remove_with_confirm(button_text, confirm_with)
def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm')
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
click_button 'Confirm'
click_button confirm_button_text
end
end
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Project remove modal initialized matches the snapshot 1`] = `
<form
action="some/path"
method="post"
>
<input
name="_method"
type="hidden"
value="delete"
/>
<input
name="authenticity_token"
type="hidden"
/>
<gl-button-stub
category="primary"
icon=""
role="button"
size="medium"
tabindex="0"
variant="danger"
>
Remove project
</gl-button-stub>
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
footer-class="gl-bg-gray-10 gl-p-5"
modalclass=""
modalid="fakeUniqueId"
ok-variant="danger"
size="sm"
title-class="gl-text-red-500"
titletag="h4"
>
<div>
<gl-alert-stub
class="gl-mb-5"
dismisslabel="Dismiss"
primarybuttonlink=""
primarybuttontext=""
secondarybuttonlink=""
secondarybuttontext=""
title="You are about to permanently delete this project"
variant="danger"
>
<gl-sprintf-stub
message="Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its respositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
/>
</gl-alert-stub>
<p>
This action cannot be undone. You will lose the project's respository and all conent: issues, merge requests, etc.
</p>
<p
class="gl-mb-1"
>
Please type the following to confirm:
</p>
<p>
<code>
foo
</code>
</p>
<gl-form-input-stub
id="confirm_name_input"
name="confirm_name_input"
type="text"
/>
</div>
</gl-modal-stub>
</form>
`;
import { shallowMount } from '@vue/test-utils';
import ProjectDeleteButton from '~/projects/components/project_delete_button.vue';
import SharedDeleteButton from '~/projects/components/shared/delete_button.vue';
jest.mock('lodash/uniqueId', () => () => 'fakeUniqueId');
describe('Project remove modal', () => {
let wrapper;
const findSharedDeleteButton = () => wrapper.find(SharedDeleteButton);
const defaultProps = {
confirmPhrase: 'foo',
formPath: 'some/path',
};
const createComponent = (props = {}) => {
wrapper = shallowMount(ProjectDeleteButton, {
propsData: {
...defaultProps,
...props,
},
stubs: {
SharedDeleteButton,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('initialized', () => {
beforeEach(() => {
createComponent();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('passes confirmPhrase and formPath props to the shared delete button', () => {
expect(findSharedDeleteButton().props()).toEqual(defaultProps);
});
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Project remove modal initialized matches the snapshot 1`] = `
exports[`Project remove modal intialized matches the snapshot 1`] = `
<form
action="some/path"
method="post"
......@@ -14,37 +14,27 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
<input
name="authenticity_token"
type="hidden"
value="test-csrf-token"
/>
<b-button-stub
class="[object Object]"
event="click"
<gl-button-stub
category="primary"
icon=""
role="button"
routertag="a"
size="md"
size="medium"
tabindex="0"
tag="button"
type="button"
variant="danger"
>
<!---->
<!---->
<span
class="gl-button-text"
>
Remove project
</span>
</b-button-stub>
</gl-button-stub>
<b-modal-stub
canceltitle="Cancel"
cancelvariant="secondary"
footerclass="bg-gray-light gl-p-5"
footerclass="gl-bg-gray-10 gl-p-5"
headerclosecontent="&times;"
headercloselabel="Close"
id="remove-project-modal"
id="delete-project-modal-2"
ignoreenforcefocusselector=""
lazy="true"
modalclass="gl-modal,"
......@@ -52,26 +42,22 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
okvariant="danger"
size="sm"
title=""
titleclass="gl-text-red-500"
titletag="h4"
>
<div>
<p
class="gl-text-red-500 gl-font-weight-bold"
>
This can lead to data loss.
</p>
<p
class="gl-mb-0"
class="gl-mb-1"
>
This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention.
Please type the following to confirm:
</p>
<p>
<gl-sprintf-stub
message="Please type %{phrase_code} to proceed or close this modal to cancel."
/>
<code>
foo
</code>
</p>
<gl-form-input-stub
......@@ -79,12 +65,13 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
name="confirm_name_input"
type="text"
/>
</div>
<template />
<template>
Confirmation required
Delete project. Are you ABSOLUTELY SURE?
</template>
<template />
......@@ -94,32 +81,32 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
<template />
<template>
<div
class="gl-w-full gl-display-flex gl-just-content-start gl-m-0"
<gl-button-stub
category="tertiary"
class="js-modal-action-cancel"
icon=""
size="medium"
variant="default"
>
<b-button-stub
class="[object Object]"
disabled="true"
event="click"
routertag="a"
size="md"
tag="button"
type="button"
variant="danger"
>
<!---->
Cancel, keep project
</gl-button-stub>
<!---->
<span
class="gl-button-text"
<gl-button-stub
category="tertiary"
class="js-modal-action-primary"
disabled="true"
icon=""
size="medium"
variant="danger"
>
Confirm
Yes, delete project
</span>
</b-button-stub>
</div>
</gl-button-stub>
</template>
</b-modal-stub>
</form>
......
import { shallowMount } from '@vue/test-utils';
import { GlButton, GlModal } from '@gitlab/ui';
import ProjectRemoveModal from '~/projects/components/remove_modal.vue';
import { GlModal } from '@gitlab/ui';
import SharedDeleteButton from '~/projects/components/shared/delete_button.vue';
jest.mock('~/lib/utils/csrf', () => ({ token: 'test-csrf-token' }));
describe('Project remove modal', () => {
let wrapper;
const findFormElement = () => wrapper.find('form').element;
const findConfirmButton = () => wrapper.find(GlModal).find(GlButton);
const findFormElement = () => wrapper.find('form');
const findConfirmButton = () => wrapper.find('.js-modal-action-primary');
const findAuthenticityTokenInput = () => findFormElement().find('input[name=authenticity_token]');
const findModal = () => wrapper.find(GlModal);
const defaultProps = {
formPath: 'some/path',
confirmPhrase: 'foo',
warningMessage: 'This can lead to data loss.',
formPath: 'some/path',
};
const createComponent = (data = {}) => {
wrapper = shallowMount(ProjectRemoveModal, {
wrapper = shallowMount(SharedDeleteButton, {
propsData: defaultProps,
data: () => data,
stubs: {
GlButton,
GlModal,
},
});
......@@ -30,7 +32,7 @@ describe('Project remove modal', () => {
wrapper = null;
});
describe('initialized', () => {
describe('intialized', () => {
beforeEach(() => {
createComponent();
});
......@@ -38,25 +40,44 @@ describe('Project remove modal', () => {
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('sets a csrf token on the authenticity form input', () => {
expect(findAuthenticityTokenInput().element.value).toEqual('test-csrf-token');
});
it('sets the form action to the provided path', () => {
expect(findFormElement().attributes('action')).toEqual(defaultProps.formPath);
});
});
describe('when the user input does not match the confirmPhrase', () => {
beforeEach(() => {
createComponent({ userInput: 'bar' });
});
it('the confirm button is disabled', () => {
expect(findConfirmButton().attributes('disabled')).toBe('true');
});
});
describe('user input matches the confirmPhrase', () => {
describe('when the user input matches the confirmPhrase', () => {
beforeEach(() => {
createComponent({ userInput: defaultProps.confirmPhrase });
});
it('the confirm button is not dislabled', () => {
it('the confirm button is not disabled', () => {
expect(findConfirmButton().attributes('disabled')).toBe(undefined);
});
});
describe('and when the confirmation button is clicked', () => {
describe('when the modal is confirmed', () => {
beforeEach(() => {
findConfirmButton().vm.$emit('click');
createComponent();
findModal().vm.$emit('ok');
});
it('submits the form element', () => {
expect(findFormElement().submit).toHaveBeenCalled();
});
expect(findFormElement().element.submit).toHaveBeenCalled();
});
});
});
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