diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index cb9012133..66903aa63 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -6,10 +6,13 @@ import { createTestClient } from 'apollo-server-testing' const categoryIds = ['cat9'] let user +let anotherUser +let moderator +let admin +let authenticatedUser let query let mutate -let authenticatedUser let variables const driver = getDriver() @@ -35,19 +38,23 @@ beforeEach(async () => { describe('User', () => { describe('query by email address', () => { + let userQuery + beforeEach(async () => { + userQuery = gql` + query($email: String) { + User(email: $email) { + name + } + } + ` + variables = { + email: 'any-email-address@example.org', + } + await Factory.build('user', { name: 'Johnny' }, { email: 'any-email-address@example.org' }) }) - const userQuery = gql` - query($email: String) { - User(email: $email) { - name - } - } - ` - const variables = { email: 'any-email-address@example.org' } - it('is forbidden', async () => { await expect(query({ query: userQuery, variables })).resolves.toMatchObject({ errors: [{ message: 'Not Authorised!' }], @@ -95,38 +102,35 @@ describe('User', () => { }) describe('UpdateUser', () => { - let variables + let updateUserMutation beforeEach(async () => { + updateUserMutation = gql` + mutation( + $id: ID! + $name: String + $termsAndConditionsAgreedVersion: String + $locationName: String + ) { + UpdateUser( + id: $id + name: $name + termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion + locationName: $locationName + ) { + id + name + termsAndConditionsAgreedVersion + termsAndConditionsAgreedAt + locationName + } + } + ` variables = { id: 'u47', name: 'John Doughnut', } - }) - const updateUserMutation = gql` - mutation( - $id: ID! - $name: String - $termsAndConditionsAgreedVersion: String - $locationName: String - ) { - UpdateUser( - id: $id - name: $name - termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion - locationName: $locationName - ) { - id - name - termsAndConditionsAgreedVersion - termsAndConditionsAgreedAt - locationName - } - } - ` - - beforeEach(async () => { user = await Factory.build( 'user', { @@ -244,19 +248,29 @@ describe('UpdateUser', () => { }) }) -describe('DeleteUser', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { +describe('Delete a user', () => { + let deleteUserMutation + + beforeEach(async () => { + deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { id - content - contentExcerpt + name + about deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } comments { id content @@ -264,28 +278,20 @@ describe('DeleteUser', () => { deleted } } - comments { - id - content - contentExcerpt - deleted - } } - } - ` + ` + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + }) + describe('as another user', () => { beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - }) - - beforeEach(async () => { - const anotherUser = await Factory.build( + anotherUser = await Factory.build( 'user', { role: 'user', @@ -307,17 +313,7 @@ describe('DeleteUser', () => { describe('as moderator', () => { beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - }) - - beforeEach(async () => { - const moderator = await Factory.build( + moderator = await Factory.build( 'user', { role: 'moderator', @@ -338,19 +334,9 @@ describe('DeleteUser', () => { }) describe('as admin', () => { - beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - }) - describe('authenticated as Admin', () => { beforeEach(async () => { - const admin = await Factory.build( + admin = await Factory.build( 'user', { role: 'admin', @@ -365,7 +351,7 @@ describe('DeleteUser', () => { describe('deleting a user account', () => { beforeEach(() => { - variables = { ...variables, id: 'u343' } + variables = { id: 'u343' } }) describe('given posts and comments', () => { @@ -454,53 +440,51 @@ describe('DeleteUser', () => { variables = { ...variables, resource: ['Post'] } }) - describe("marks user's posts as deleted", () => { - it('on request', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, + it('on request', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) - it('deletes user avatar and post hero images', async () => { - await expect(neode.all('Image')).resolves.toHaveLength(22) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('Image')).resolves.toHaveLength(20) - }) + it('deletes user avatar and post hero images', async () => { + await expect(neode.all('Image')).resolves.toHaveLength(22) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('Image')).resolves.toHaveLength(20) }) }) @@ -553,7 +537,7 @@ describe('DeleteUser', () => { describe('deletion of all posts and comments requested', () => { beforeEach(() => { - variables = { ...variables, resource: ['Post', 'Comment'] } + variables = { ...variables, resource: ['Comment', 'Post'] } }) it('marks posts and comments as deleted', async () => { @@ -603,6 +587,7 @@ describe('DeleteUser', () => { it('will be removed completely', async () => { await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) }) }) @@ -622,299 +607,4 @@ describe('DeleteUser', () => { }) }) }) - - describe('user deletes his own account', () => { - beforeEach(async () => { - variables = { id: 'u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - await Factory.build( - 'user', - { - id: 'not-my-account', - }, - { - email: 'friends-account@example.org', - }, - ) - }) - - describe('authenticated', () => { - beforeEach(async () => { - authenticatedUser = await user.toJson() - }) - - describe("attempting to delete another user's account", () => { - beforeEach(() => { - variables = { ...variables, id: 'not-my-account' } - }) - - it('throws an authorization error', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') - }) - }) - - describe('attempting to delete my own account', () => { - beforeEach(() => { - variables = { ...variables, id: 'u343' } - }) - - describe('given posts and comments', () => { - beforeEach(async () => { - await Factory.build('category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }) - await Factory.build( - 'post', - { - id: 'p139', - content: 'Post by user u343', - }, - { - author: user, - categoryIds, - }, - ) - await Factory.build( - 'comment', - { - id: 'c155', - content: 'Comment by user u343', - }, - { - author: user, - }, - ) - await Factory.build( - 'comment', - { - id: 'c156', - content: "A comment by someone else on user u343's post", - }, - { - postId: 'p139', - }, - ) - }) - - it("deletes my account, but doesn't delete posts or comments by default", async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - - describe('deletion of all post requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post'] } - }) - - describe("marks user's posts as deleted", () => { - it('posts on request', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - - it('deletes user avatar and post hero images', async () => { - await expect(neode.all('Image')).resolves.toHaveLength(22) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('Image')).resolves.toHaveLength(20) - }) - }) - }) - - describe('deletion of all comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Comment'] } - }) - - it('marks comments as deleted', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - describe('deletion of all post and comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post', 'Comment'] } - }) - - it('marks posts and comments as deleted', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - }) - }) - }) - - describe('connected `EmailAddress` nodes', () => { - it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) - }) - }) - - describe('connected `SocialMedia` nodes', () => { - beforeEach(async () => { - const socialMedia = await Factory.build('socialMedia') - await socialMedia.relateTo(user, 'ownedBy') - }) - - it('will be removed completely', async () => { - await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) - }) - }) - }) }) diff --git a/webapp/components/DeleteData/DeleteData.spec.js b/webapp/components/DeleteData/DeleteData.spec.js index e9205fa5a..81a6c10c7 100644 --- a/webapp/components/DeleteData/DeleteData.spec.js +++ b/webapp/components/DeleteData/DeleteData.spec.js @@ -15,12 +15,10 @@ describe('DeleteData.vue', () => { let enableContributionDeletionCheckbox let enableCommentDeletionCheckbox const deleteAccountName = 'Delete MyAccount' - const deleteContributionsMessage = 'Delete my 2 posts' - const deleteCommentsMessage = 'Delete my 3 comments' beforeEach(() => { mocks = { - $t: jest.fn(), + $t: jest.fn((a) => a), $apollo: { mutate: jest .fn() @@ -45,7 +43,7 @@ describe('DeleteData.vue', () => { } getters = { 'auth/user': () => { - return { id: 'u343', name: deleteAccountName, contributionsCount: 2, commentedCount: 3 } + return { id: 'u343', name: deleteAccountName } }, } actions = { 'auth/logout': jest.fn() } @@ -68,15 +66,15 @@ describe('DeleteData.vue', () => { jest.clearAllMocks() }) - it('defaults to deleteContributions to false', () => { + it('checkbox deleteContributions defaults be false', () => { expect(wrapper.vm.deleteContributions).toEqual(false) }) - it('defaults to deleteComments to false', () => { + it('checkbox deleteComments defaults be false', () => { expect(wrapper.vm.deleteComments).toEqual(false) }) - it('defaults to deleteEnabled to false', () => { + it('deleteButton defaults be false', () => { expect(wrapper.vm.deleteEnabled).toEqual(false) }) @@ -93,7 +91,7 @@ describe('DeleteData.vue', () => { deleteAccountBtn = wrapper.find('[data-test="delete-button"]') }) - it('if deleteEnabled is true and only deletes user by default', () => { + it('if deleteEnabled is true and only deletes user ', () => { deleteAccountBtn.trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith( expect.objectContaining({ @@ -105,9 +103,28 @@ describe('DeleteData.vue', () => { ) }) + it("deletes user's posts and comments if requested by default ", () => { + enableContributionDeletionCheckbox = wrapper.find( + '[data-test="contributions-deletion-checkbox"]', + ) + enableContributionDeletionCheckbox.trigger('click') + enableCommentDeletionCheckbox = wrapper.find('[data-test="comments-deletion-checkbox"]') + enableCommentDeletionCheckbox.trigger('click') + deleteAccountBtn.trigger('click') + expect(mocks.$apollo.mutate).toHaveBeenCalledWith( + expect.objectContaining({ + variables: { + id: 'u343', + resource: ['Post', 'Comment'], + }, + }), + ) + }) + it("deletes a user's posts if requested", () => { - mocks.$t.mockImplementation(() => deleteContributionsMessage) - enableContributionDeletionCheckbox = wrapper.findAll('input[type="checkbox"]').at(0) + enableContributionDeletionCheckbox = wrapper.find( + '[data-test="contributions-deletion-checkbox"]', + ) enableContributionDeletionCheckbox.trigger('click') deleteAccountBtn.trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith( @@ -121,8 +138,7 @@ describe('DeleteData.vue', () => { }) it("deletes a user's comments if requested", () => { - mocks.$t.mockImplementation(() => deleteCommentsMessage) - enableCommentDeletionCheckbox = wrapper.findAll('input[type="checkbox"]').at(1) + enableCommentDeletionCheckbox = wrapper.find('[data-test="comments-deletion-checkbox"]') enableCommentDeletionCheckbox.trigger('click') deleteAccountBtn.trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith( @@ -135,24 +151,6 @@ describe('DeleteData.vue', () => { ) }) - it("deletes a user's posts and comments if requested", () => { - mocks.$t.mockImplementation(() => deleteContributionsMessage) - enableContributionDeletionCheckbox = wrapper.findAll('input[type="checkbox"]').at(0) - enableContributionDeletionCheckbox.trigger('click') - mocks.$t.mockImplementation(() => deleteCommentsMessage) - enableCommentDeletionCheckbox = wrapper.findAll('input[type="checkbox"]').at(1) - enableCommentDeletionCheckbox.trigger('click') - deleteAccountBtn.trigger('click') - expect(mocks.$apollo.mutate).toHaveBeenCalledWith( - expect.objectContaining({ - variables: { - id: 'u343', - resource: ['Post', 'Comment'], - }, - }), - ) - }) - it('shows a success toaster after successful mutation', async () => { await deleteAccountBtn.trigger('click') expect(mocks.$toast.success).toHaveBeenCalledTimes(1) diff --git a/webapp/components/DeleteData/DeleteData.vue b/webapp/components/DeleteData/DeleteData.vue index 714e4c410..36d166207 100644 --- a/webapp/components/DeleteData/DeleteData.vue +++ b/webapp/components/DeleteData/DeleteData.vue @@ -9,23 +9,35 @@

{{ $t('settings.deleteUserAccount.accountDescription') }}

-