diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1e363b52..b3c097993 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -422,7 +422,7 @@ jobs: report_name: Coverage Admin Interface type: lcov result_path: ./coverage/lcov.info - min_coverage: 94 + min_coverage: 95 token: ${{ github.token }} ############################################################################## diff --git a/admin/public/img/elopage_favicon.png b/admin/public/img/elopage_favicon.png new file mode 100644 index 000000000..2da7efbc0 Binary files /dev/null and b/admin/public/img/elopage_favicon.png differ diff --git a/admin/src/components/DeletedUserFormular.spec.js b/admin/src/components/DeletedUserFormular.spec.js new file mode 100644 index 000000000..bad97c1d7 --- /dev/null +++ b/admin/src/components/DeletedUserFormular.spec.js @@ -0,0 +1,262 @@ +import { mount } from '@vue/test-utils' +import DeletedUserFormular from './DeletedUserFormular.vue' +import { deleteUser } from '../graphql/deleteUser' +import { unDeleteUser } from '../graphql/unDeleteUser' + +const localVue = global.localVue + +const date = new Date() + +const apolloMutateMock = jest.fn().mockResolvedValue({ + data: { + deleteUser: date, + }, +}) + +const toastedErrorMock = jest.fn() +const toastedSuccessMock = jest.fn() + +const mocks = { + $t: jest.fn((t) => t), + $apollo: { + mutate: apolloMutateMock, + }, + $store: { + state: { + moderator: { + id: 0, + name: 'test moderator', + }, + }, + }, + $toasted: { + error: toastedErrorMock, + success: toastedSuccessMock, + }, +} + +const propsData = { + item: {}, +} + +describe('DeletedUserFormular', () => { + let wrapper + + const Wrapper = () => { + return mount(DeletedUserFormular, { localVue, mocks, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + jest.clearAllMocks() + wrapper = Wrapper() + }) + + it('has a DIV element with the class.delete-user-formular', () => { + expect(wrapper.find('.deleted-user-formular').exists()).toBeTruthy() + }) + }) + + describe('delete self', () => { + beforeEach(() => { + wrapper.setProps({ + item: { + userId: 0, + }, + }) + }) + + it('shows a text that you cannot delete yourself', () => { + expect(wrapper.text()).toBe('removeNotSelf') + }) + }) + + describe('delete other user', () => { + beforeEach(() => { + wrapper.setProps({ + item: { + userId: 1, + deletedAt: null, + }, + }) + }) + + it('has a checkbox', () => { + expect(wrapper.find('input[type="checkbox"]').exists()).toBeTruthy() + }) + + it('shows the text "delete_user"', () => { + expect(wrapper.text()).toBe('delete_user') + }) + + describe('click on checkbox', () => { + beforeEach(async () => { + await wrapper.find('input[type="checkbox"]').setChecked() + }) + + it('has a confirmation button', () => { + expect(wrapper.find('button').exists()).toBeTruthy() + }) + + it('has the button text "delete_user"', () => { + expect(wrapper.find('button').text()).toBe('delete_user') + }) + + describe('confirm delete with success', () => { + beforeEach(async () => { + await wrapper.find('button').trigger('click') + }) + + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: deleteUser, + variables: { + userId: 1, + }, + }), + ) + }) + + it('toasts a success message', () => { + expect(toastedSuccessMock).toBeCalledWith('user_deleted') + }) + + it('emits update deleted At', () => { + expect(wrapper.emitted('updateDeletedAt')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + userId: 1, + deletedAt: date, + }, + ]), + ]), + ) + }) + + it('unchecks the checkbox', () => { + expect(wrapper.find('input').attributes('checked')).toBe(undefined) + }) + }) + + describe('confirm delete with error', () => { + beforeEach(async () => { + apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) + await wrapper.find('button').trigger('click') + }) + + it('toasts an error message', () => { + expect(toastedErrorMock).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()).toBeFalsy() + }) + }) + }) + }) + + describe('recover user', () => { + beforeEach(() => { + wrapper.setProps({ + item: { + userId: 1, + deletedAt: date, + }, + }) + }) + + it('has a checkbox', () => { + expect(wrapper.find('input[type="checkbox"]').exists()).toBeTruthy() + }) + + it('shows the text "undelete_user"', () => { + expect(wrapper.text()).toBe('undelete_user') + }) + + describe('click on checkbox', () => { + beforeEach(async () => { + apolloMutateMock.mockResolvedValue({ + data: { + unDeleteUser: null, + }, + }) + await wrapper.find('input[type="checkbox"]').setChecked() + }) + + it('has a confirmation button', () => { + expect(wrapper.find('button').exists()).toBeTruthy() + }) + + it('has the button text "undelete_user"', () => { + expect(wrapper.find('button').text()).toBe('undelete_user') + }) + + describe('confirm recover with success', () => { + beforeEach(async () => { + await wrapper.find('button').trigger('click') + }) + + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: unDeleteUser, + variables: { + userId: 1, + }, + }), + ) + }) + + it('toasts a success message', () => { + expect(toastedSuccessMock).toBeCalledWith('user_recovered') + }) + + it('emits update deleted At', () => { + expect(wrapper.emitted('updateDeletedAt')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + userId: 1, + deletedAt: null, + }, + ]), + ]), + ) + }) + + it('unchecks the checkbox', () => { + expect(wrapper.find('input').attributes('checked')).toBe(undefined) + }) + }) + + describe('confirm recover with error', () => { + beforeEach(async () => { + apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) + await wrapper.find('button').trigger('click') + }) + + it('toasts an error message', () => { + expect(toastedErrorMock).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()).toBeFalsy() + }) + }) + }) + }) +}) diff --git a/admin/src/components/DeletedUserFormular.vue b/admin/src/components/DeletedUserFormular.vue new file mode 100644 index 000000000..b840fdb23 --- /dev/null +++ b/admin/src/components/DeletedUserFormular.vue @@ -0,0 +1,86 @@ + + + diff --git a/admin/src/components/RowDetails.vue b/admin/src/components/RowDetails.vue index 319936a70..84dfebbb5 100644 --- a/admin/src/components/RowDetails.vue +++ b/admin/src/components/RowDetails.vue @@ -1,10 +1,7 @@ @@ -27,7 +27,7 @@ type="show-creation" slotName="show-creation" :index="0" - @row-toogle-details="rowToogleDetails" + @row-toggle-details="rowToggleDetails" >