Merge pull request #2880 from gradido/2481-fix-admin-no-confirmation-when-changing-user-role

fix(admin): add confirmation modal for user role change and user (un)deletion
This commit is contained in:
mahula 2023-03-31 11:35:41 +02:00 committed by GitHub
commit 4a8420bfc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 312 additions and 150 deletions

View File

@ -28,6 +28,7 @@ const mocks = {
let propsData let propsData
let wrapper let wrapper
let spy
describe('ChangeUserRoleFormular', () => { describe('ChangeUserRoleFormular', () => {
const Wrapper = () => { const Wrapper = () => {
@ -70,12 +71,16 @@ describe('ChangeUserRoleFormular', () => {
expect(wrapper.text()).toContain('userRole.notChangeYourSelf') expect(wrapper.text()).toContain('userRole.notChangeYourSelf')
}) })
it('has role select disabled', () => { it('has no role select', () => {
expect(wrapper.find('select[disabled="disabled"]').exists()).toBe(true) expect(wrapper.find('select.role-select').exists()).toBe(false)
})
it('has no button', () => {
expect(wrapper.find('button.btn.btn-dange').exists()).toBe(false)
}) })
}) })
describe('change others role', () => { describe("change other user's role", () => {
let rolesToSelect let rolesToSelect
describe('general', () => { describe('general', () => {
@ -106,19 +111,12 @@ describe('ChangeUserRoleFormular', () => {
expect(wrapper.find('select.role-select[disabled="disabled"]').exists()).toBe(false) expect(wrapper.find('select.role-select[disabled="disabled"]').exists()).toBe(false)
}) })
describe('on API error', () => { it('has "change_user_role" button', () => {
beforeEach(() => { expect(wrapper.find('button.btn.btn-danger').text()).toBe('change_user_role')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
rolesToSelect.at(1).setSelected()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
}) })
}) })
describe('user is usual user', () => { describe('user has role "usual user"', () => {
beforeEach(() => { beforeEach(() => {
apolloMutateMock.mockResolvedValue({ apolloMutateMock.mockResolvedValue({
data: { data: {
@ -141,6 +139,10 @@ describe('ChangeUserRoleFormular', () => {
describe('change select to', () => { describe('change select to', () => {
describe('same role', () => { describe('same role', () => {
it('has "change_user_role" button disabled', () => {
expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(true)
})
it('does not call the API', () => { it('does not call the API', () => {
rolesToSelect.at(0).setSelected() rolesToSelect.at(0).setSelected()
expect(apolloMutateMock).not.toHaveBeenCalled() expect(apolloMutateMock).not.toHaveBeenCalled()
@ -152,39 +154,75 @@ describe('ChangeUserRoleFormular', () => {
rolesToSelect.at(1).setSelected() rolesToSelect.at(1).setSelected()
}) })
it('calls the API', () => { it('has "change_user_role" button enabled', () => {
expect(apolloMutateMock).toBeCalledWith( expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true)
expect.objectContaining({ expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(
mutation: setUserRole, false,
variables: {
userId: 1,
isAdmin: true,
},
}),
) )
}) })
it('emits "updateIsAdmin"', () => { describe('clicking the "change_user_role" button', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual( beforeEach(async () => {
expect.arrayContaining([ spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
expect.arrayContaining([ spy.mockImplementation(() => Promise.resolve(true))
{ await wrapper.find('button').trigger('click')
userId: 1, await wrapper.vm.$nextTick()
isAdmin: expect.any(Date), })
},
]),
]),
)
})
it('toasts success message', () => { it('calls the modal', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') expect(wrapper.emitted('showModal'))
expect(spy).toHaveBeenCalled()
})
describe('confirm role change with success', () => {
it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
mutation: setUserRole,
variables: {
userId: 1,
isAdmin: true,
},
}),
)
})
it('emits "updateIsAdmin"', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual(
expect.arrayContaining([
expect.arrayContaining([
{
userId: 1,
isAdmin: expect.any(Date),
},
]),
]),
)
})
it('toasts success message', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo')
})
})
describe('confirm role change with error', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
})
}) })
}) })
}) })
}) })
describe('user is admin', () => { describe('user has role "admin"', () => {
beforeEach(() => { beforeEach(() => {
apolloMutateMock.mockResolvedValue({ apolloMutateMock.mockResolvedValue({
data: { data: {
@ -207,6 +245,10 @@ describe('ChangeUserRoleFormular', () => {
describe('change select to', () => { describe('change select to', () => {
describe('same role', () => { describe('same role', () => {
it('has "change_user_role" button disabled', () => {
expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(true)
})
it('does not call the API', () => { it('does not call the API', () => {
rolesToSelect.at(1).setSelected() rolesToSelect.at(1).setSelected()
expect(apolloMutateMock).not.toHaveBeenCalled() expect(apolloMutateMock).not.toHaveBeenCalled()
@ -218,33 +260,69 @@ describe('ChangeUserRoleFormular', () => {
rolesToSelect.at(0).setSelected() rolesToSelect.at(0).setSelected()
}) })
it('calls the API', () => { it('has "change_user_role" button enabled', () => {
expect(apolloMutateMock).toBeCalledWith( expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true)
expect.objectContaining({ expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(
mutation: setUserRole, false,
variables: {
userId: 1,
isAdmin: false,
},
}),
) )
}) })
it('emits "updateIsAdmin"', () => { describe('clicking the "change_user_role" button', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual( beforeEach(async () => {
expect.arrayContaining([ spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
expect.arrayContaining([ spy.mockImplementation(() => Promise.resolve(true))
{ await wrapper.find('button').trigger('click')
userId: 1, await wrapper.vm.$nextTick()
isAdmin: null, })
},
]),
]),
)
})
it('toasts success message', () => { it('calls the modal', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') expect(wrapper.emitted('showModal'))
expect(spy).toHaveBeenCalled()
})
describe('confirm role change with success', () => {
it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
mutation: setUserRole,
variables: {
userId: 1,
isAdmin: false,
},
}),
)
})
it('emits "updateIsAdmin"', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual(
expect.arrayContaining([
expect.arrayContaining([
{
userId: 1,
isAdmin: null,
},
]),
]),
)
})
it('toasts success message', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo')
})
})
describe('confirm role change with error', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
})
}) })
}) })
}) })

View File

@ -4,19 +4,23 @@
<div v-if="item.userId === $store.state.moderator.id" class="m-3 mb-4"> <div v-if="item.userId === $store.state.moderator.id" class="m-3 mb-4">
{{ $t('userRole.notChangeYourSelf') }} {{ $t('userRole.notChangeYourSelf') }}
</div> </div>
<div class="m-3"> <div v-else class="m-3">
<label for="role" class="mr-3">{{ $t('userRole.selectLabel') }}</label> <label for="role" class="mr-3">{{ $t('userRole.selectLabel') }}</label>
<b-form-select <b-form-select class="role-select" v-model="roleSelected" :options="roles" />
class="role-select" <div class="mt-3 mb-5">
v-model="roleSelected" <b-button
:options="roles" variant="danger"
:disabled="item.userId === $store.state.moderator.id" v-b-modal.user-role-modal
/> :disabled="currentRole === roleSelected"
@click="showModal()"
>
{{ $t('change_user_role') }}
</b-button>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { setUserRole } from '../graphql/setUserRole' import { setUserRole } from '../graphql/setUserRole'
@ -35,6 +39,7 @@ export default {
}, },
data() { data() {
return { return {
currentRole: this.item.isAdmin ? rolesValues.admin : rolesValues.user,
roleSelected: this.item.isAdmin ? rolesValues.admin : rolesValues.user, roleSelected: this.item.isAdmin ? rolesValues.admin : rolesValues.user,
roles: [ roles: [
{ value: rolesValues.user, text: this.$t('userRole.selectRoles.user') }, { value: rolesValues.user, text: this.$t('userRole.selectRoles.user') },
@ -42,14 +47,35 @@ export default {
], ],
} }
}, },
watch: {
roleSelected(newRole, oldRole) {
if (newRole !== oldRole) {
this.setUserRole(newRole, oldRole)
}
},
},
methods: { methods: {
showModal() {
this.$bvModal
.msgBoxConfirm(
this.$t('overlay.changeUserRole.question', {
username: `${this.item.firstName} ${this.item.lastName}`,
newRole:
this.roleSelected === 'admin'
? this.$t('userRole.selectRoles.admin')
: this.$t('userRole.selectRoles.user'),
}),
{
cancelTitle: this.$t('overlay.cancel'),
centered: true,
hideHeaderClose: true,
title: this.$t('overlay.changeUserRole.title'),
okTitle: this.$t('overlay.changeUserRole.yes'),
okVariant: 'danger',
},
)
.then((okClicked) => {
if (okClicked) {
this.setUserRole(this.roleSelected, this.currentRole)
}
})
.catch((error) => {
this.toastError(error.message)
})
},
setUserRole(newRole, oldRole) { setUserRole(newRole, oldRole) {
this.$apollo this.$apollo
.mutate({ .mutate({

View File

@ -35,6 +35,7 @@ const propsData = {
describe('DeletedUserFormular', () => { describe('DeletedUserFormular', () => {
let wrapper let wrapper
let spy
const Wrapper = () => { const Wrapper = () => {
return mount(DeletedUserFormular, { localVue, mocks, propsData }) return mount(DeletedUserFormular, { localVue, mocks, propsData })
@ -62,6 +63,10 @@ describe('DeletedUserFormular', () => {
it('shows a text that you cannot delete yourself', () => { it('shows a text that you cannot delete yourself', () => {
expect(wrapper.text()).toBe('removeNotSelf') expect(wrapper.text()).toBe('removeNotSelf')
}) })
it('has no "delete_user" button', () => {
expect(wrapper.find('button').exists()).toBe(false)
})
}) })
describe('delete other user', () => { describe('delete other user', () => {
@ -71,35 +76,32 @@ describe('DeletedUserFormular', () => {
userId: 1, userId: 1,
deletedAt: null, deletedAt: null,
}, },
static: true,
}) })
}) })
it('has a checkbox', () => {
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
})
it('shows the text "delete_user"', () => { it('shows the text "delete_user"', () => {
expect(wrapper.text()).toBe('delete_user') expect(wrapper.text()).toBe('delete_user')
}) })
describe('click on checkbox', () => { it('has a "delete_user" button', () => {
expect(wrapper.find('button').text()).toBe('delete_user')
})
describe('click on "delete_user" button', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.find('input[type="checkbox"]').setChecked() spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve(true))
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
}) })
it('has a confirmation button', () => { it('calls the modal', () => {
expect(wrapper.find('button').exists()).toBe(true) expect(wrapper.emitted('showDeleteModal'))
}) expect(spy).toHaveBeenCalled()
it('has the button text "delete_user"', () => {
expect(wrapper.find('button').text()).toBe('delete_user')
}) })
describe('confirm delete with success', () => { describe('confirm delete with success', () => {
beforeEach(async () => {
await wrapper.find('button').trigger('click')
})
it('calls the API', () => { it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith( expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -123,32 +125,20 @@ describe('DeletedUserFormular', () => {
]), ]),
) )
}) })
it('unchecks the checkbox', () => {
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
})
}) })
describe('confirm delete with error', () => { describe('confirm delete with error', () => {
beforeEach(async () => { beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('button').trigger('click') await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
}) })
it('toasts an error message', () => { it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!') expect(toastErrorSpy).toBeCalledWith('Oh no!')
}) })
}) })
describe('click on checkbox again', () => {
beforeEach(async () => {
await wrapper.find('input[type="checkbox"]').setChecked(false)
})
it('has no confirmation button anymore', () => {
expect(wrapper.find('button').exists()).toBe(false)
})
})
}) })
}) })
@ -162,37 +152,33 @@ describe('DeletedUserFormular', () => {
}) })
}) })
it('has a checkbox', () => {
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
})
it('shows the text "undelete_user"', () => { it('shows the text "undelete_user"', () => {
expect(wrapper.text()).toBe('undelete_user') expect(wrapper.text()).toBe('undelete_user')
}) })
describe('click on checkbox', () => { it('has a "undelete_user" button', () => {
expect(wrapper.find('button').text()).toBe('undelete_user')
})
describe('click on "undelete_user" button', () => {
beforeEach(async () => { beforeEach(async () => {
apolloMutateMock.mockResolvedValue({ apolloMutateMock.mockResolvedValue({
data: { data: {
unDeleteUser: null, unDeleteUser: null,
}, },
}) })
await wrapper.find('input[type="checkbox"]').setChecked() spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve(true))
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
}) })
it('has a confirmation button', () => { it('calls the modal', () => {
expect(wrapper.find('button').exists()).toBe(true) expect(wrapper.emitted('showUndeleteModal'))
}) expect(spy).toHaveBeenCalled()
it('has the button text "undelete_user"', () => {
expect(wrapper.find('button').text()).toBe('undelete_user')
}) })
describe('confirm recover with success', () => { describe('confirm recover with success', () => {
beforeEach(async () => {
await wrapper.find('button').trigger('click')
})
it('calls the API', () => { it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith( expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -205,7 +191,7 @@ describe('DeletedUserFormular', () => {
}) })
it('emits update deleted At', () => { it('emits update deleted At', () => {
expect(wrapper.emitted('updateDeletedAt')).toEqual( expect(wrapper.emitted('updateDeletedAt')).toMatchObject(
expect.arrayContaining([ expect.arrayContaining([
expect.arrayContaining([ expect.arrayContaining([
{ {
@ -216,10 +202,6 @@ describe('DeletedUserFormular', () => {
]), ]),
) )
}) })
it('unchecks the checkbox', () => {
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
})
}) })
describe('confirm recover with error', () => { describe('confirm recover with error', () => {
@ -232,16 +214,6 @@ describe('DeletedUserFormular', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!') expect(toastErrorSpy).toBeCalledWith('Oh no!')
}) })
}) })
describe('click on checkbox again', () => {
beforeEach(async () => {
await wrapper.find('input[type="checkbox"]').setChecked(false)
})
it('has no confirmation button anymore', () => {
expect(wrapper.find('button').exists()).toBe(false)
})
})
}) })
}) })
}) })

View File

@ -4,15 +4,16 @@
{{ $t('removeNotSelf') }} {{ $t('removeNotSelf') }}
</div> </div>
<div v-else class="mt-5"> <div v-else class="mt-5">
<b-form-checkbox switch size="lg" v-model="checked">
<div>{{ item.deletedAt ? $t('undelete_user') : $t('delete_user') }}</div>
</b-form-checkbox>
<div class="mt-3 mb-5"> <div class="mt-3 mb-5">
<b-button v-if="checked && item.deletedAt === null" variant="danger" @click="deleteUser"> <b-button
v-if="!item.deletedAt"
variant="danger"
v-b-modal.delete-user-modal
@click="showDeleteModal()"
>
{{ $t('delete_user') }} {{ $t('delete_user') }}
</b-button> </b-button>
<b-button v-if="checked && item.deletedAt !== null" variant="success" @click="unDeleteUser"> <b-button v-else variant="success" v-b-modal.delete-user-modal @click="showUndeleteModal()">
{{ $t('undelete_user') }} {{ $t('undelete_user') }}
</b-button> </b-button>
</div> </div>
@ -31,12 +32,56 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
checked: false,
}
},
methods: { methods: {
showDeleteModal() {
this.$bvModal
.msgBoxConfirm(
this.$t('overlay.deleteUser.question', {
username: `${this.item.firstName} ${this.item.lastName}`,
}),
{
cancelTitle: this.$t('overlay.cancel'),
centered: true,
hideHeaderClose: true,
title: this.$t('overlay.deleteUser.title'),
okTitle: this.$t('overlay.deleteUser.yes'),
okVariant: 'danger',
static: true,
},
)
.then((okClicked) => {
if (okClicked) {
this.deleteUser()
}
})
.catch((error) => {
this.toastError(error.message)
})
},
showUndeleteModal() {
this.$bvModal
.msgBoxConfirm(
this.$t('overlay.undeleteUser.question', {
username: `${this.item.firstName} ${this.item.lastName}`,
}),
{
cancelTitle: this.$t('overlay.cancel'),
centered: true,
hideHeaderClose: true,
title: this.$t('overlay.undeleteUser.title'),
okTitle: this.$t('overlay.undeleteUser.yes'),
okVariant: 'success',
},
)
.then((okClicked) => {
if (okClicked) {
this.unDeleteUser()
}
})
.catch((error) => {
this.toastError(error.message)
})
},
deleteUser() { deleteUser() {
this.$apollo this.$apollo
.mutate({ .mutate({
@ -50,7 +95,6 @@ export default {
userId: this.item.userId, userId: this.item.userId,
deletedAt: result.data.deleteUser, deletedAt: result.data.deleteUser,
}) })
this.checked = false
}) })
.catch((error) => { .catch((error) => {
this.toastError(error.message) this.toastError(error.message)
@ -69,7 +113,6 @@ export default {
userId: this.item.userId, userId: this.item.userId,
deletedAt: result.data.unDeleteUser, deletedAt: result.data.unDeleteUser,
}) })
this.checked = false
}) })
.catch((error) => { .catch((error) => {
this.toastError(error.message) this.toastError(error.message)

View File

@ -1,6 +1,7 @@
{ {
"all_emails": "Alle Nutzer", "all_emails": "Alle Nutzer",
"back": "zurück", "back": "zurück",
"change_user_role": "Nutzerrolle ändern",
"chat": "Chat", "chat": "Chat",
"contributionLink": { "contributionLink": {
"amount": "Betrag", "amount": "Betrag",
@ -114,6 +115,11 @@
"open_creations": "Offene Schöpfungen", "open_creations": "Offene Schöpfungen",
"overlay": { "overlay": {
"cancel": "Abbrechen", "cancel": "Abbrechen",
"changeUserRole": {
"question": "Willst du die Rolle von {username} wirklich zu {newRole} ändern?",
"title": "Nutzerrolle ändern",
"yes": "Ja, Nutzerrolle ändern"
},
"confirm": { "confirm": {
"question": "Willst du diesen Gemeinwohl-Beitrag wirklich bestätigen und gutschreiben?", "question": "Willst du diesen Gemeinwohl-Beitrag wirklich bestätigen und gutschreiben?",
"text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar. Bitte überprüfe genau, dass alles stimmt.", "text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar. Bitte überprüfe genau, dass alles stimmt.",
@ -126,11 +132,21 @@
"title": "Gemeinwohl-Beitrag löschen!", "title": "Gemeinwohl-Beitrag löschen!",
"yes": "Ja, Beitrag löschen!" "yes": "Ja, Beitrag löschen!"
}, },
"deleteUser": {
"question": "Willst du {username} wirklich löschen?",
"title": "Nutzer löschen",
"yes": "Ja, Nutzer löschen"
},
"deny": { "deny": {
"question": "Willst du diesen Gemeinwohl-Beitrag wirklich ablehnen?", "question": "Willst du diesen Gemeinwohl-Beitrag wirklich ablehnen?",
"text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar und kann auch nicht mehr gelöscht werden. Bitte überprüfe genau, dass alles stimmt.", "text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar und kann auch nicht mehr gelöscht werden. Bitte überprüfe genau, dass alles stimmt.",
"title": "Gemeinwohl-Beitrag ablehnen!", "title": "Gemeinwohl-Beitrag ablehnen!",
"yes": "Ja, Beitrag ablehnen und speichern!" "yes": "Ja, Beitrag ablehnen und speichern!"
},
"undeleteUser": {
"question": "Willst du wirklich {username} wiederherstellen?",
"title": "Nutzer wiederherstellen",
"yes": "Ja, Nutzer wiederherstellen"
} }
}, },
"redeemed": "eingelöst", "redeemed": "eingelöst",

View File

@ -1,6 +1,7 @@
{ {
"all_emails": "All users", "all_emails": "All users",
"back": "back", "back": "back",
"change_user_role": "Change user role",
"chat": "Chat", "chat": "Chat",
"contributionLink": { "contributionLink": {
"amount": "Amount", "amount": "Amount",
@ -114,6 +115,11 @@
"open_creations": "Open creations", "open_creations": "Open creations",
"overlay": { "overlay": {
"cancel": "Cancel", "cancel": "Cancel",
"changeUserRole": {
"question": "Do you really want to change {username}'s role to {newRole}?",
"title": "Change user role",
"yes": "Yes, change user role"
},
"confirm": { "confirm": {
"question": "Do you really want to carry out and finally save this pre-stored creation?", "question": "Do you really want to carry out and finally save this pre-stored creation?",
"text": "After saving, the record can no longer be changed. Please check carefully that everything is correct.", "text": "After saving, the record can no longer be changed. Please check carefully that everything is correct.",
@ -126,11 +132,21 @@
"title": "Delete creation!", "title": "Delete creation!",
"yes": "Yes, delete and save creation!" "yes": "Yes, delete and save creation!"
}, },
"deleteUser": {
"question": "Do you really want to delete {username}?",
"title": "Delete user",
"yes": "Yes, delete user"
},
"deny": { "deny": {
"question": "Do you really want to carry out and finally save this pre-stored creation?", "question": "Do you really want to carry out and finally save this pre-stored creation?",
"text": "After saving, the record can no longer be changed or deleted. Please check carefully that everything is correct.", "text": "After saving, the record can no longer be changed or deleted. Please check carefully that everything is correct.",
"title": "Reject creation!", "title": "Reject creation!",
"yes": "Yes, reject and save creation!" "yes": "Yes, reject and save creation!"
},
"undeleteUser": {
"question": "Do you really want to undelete {username}",
"title": "Undelete user",
"yes": "Yes,undelete user"
} }
}, },
"redeemed": "redeemed", "redeemed": "redeemed",

View File

@ -25,7 +25,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
email: 'benjamin@bluemchen.de', email: 'benjamin@bluemchen.de',
creation: [1000, 1000, 1000], creation: [1000, 1000, 1000],
emailChecked: true, emailChecked: true,
deletedAt: null, deletedAt: new Date(),
}, },
{ {
userId: 3, userId: 3,
@ -243,6 +243,17 @@ describe('UserSearch', () => {
}) })
}) })
describe('recover user', () => {
const userId = 2
beforeEach(() => {
wrapper.findComponent({ name: 'SearchUserTable' }).vm.$emit('updateDeletedAt', userId, null)
})
it('toasts a success message', () => {
expect(toastSuccessSpy).toBeCalledWith('user_recovered')
})
})
describe('apollo returns error', () => { describe('apollo returns error', () => {
beforeEach(() => { beforeEach(() => {
apolloQueryMock.mockRejectedValue({ apolloQueryMock.mockRejectedValue({