diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js
index 66903aa63..cce45ae6e 100644
--- a/backend/src/schema/resolvers/users.spec.js
+++ b/backend/src/schema/resolvers/users.spec.js
@@ -6,8 +6,6 @@ import { createTestClient } from 'apollo-server-testing'
const categoryIds = ['cat9']
let user
-let anotherUser
-let moderator
let admin
let authenticatedUser
@@ -18,6 +16,35 @@ let variables
const driver = getDriver()
const neode = getNeode()
+const deleteUserMutation = gql`
+ mutation($id: ID!, $resource: [Deletable]) {
+ DeleteUser(id: $id, resource: $resource) {
+ id
+ name
+ about
+ deleted
+ contributions {
+ id
+ content
+ contentExcerpt
+ deleted
+ comments {
+ id
+ content
+ contentExcerpt
+ deleted
+ }
+ }
+ comments {
+ id
+ content
+ contentExcerpt
+ deleted
+ }
+ }
+ }
+`
+
beforeAll(() => {
const { server } = createServer({
context: () => {
@@ -51,7 +78,6 @@ describe('User', () => {
variables = {
email: 'any-email-address@example.org',
}
-
await Factory.build('user', { name: 'Johnny' }, { email: 'any-email-address@example.org' })
})
@@ -248,38 +274,8 @@ describe('UpdateUser', () => {
})
})
-describe('Delete a user', () => {
- let deleteUserMutation
-
+describe('Delete a User as admin', () => {
beforeEach(async () => {
- deleteUserMutation = gql`
- mutation($id: ID!, $resource: [Deletable]) {
- DeleteUser(id: $id, resource: $resource) {
- id
- name
- about
- deleted
- contributions {
- id
- content
- contentExcerpt
- deleted
- comments {
- id
- content
- contentExcerpt
- deleted
- }
- }
- comments {
- id
- content
- contentExcerpt
- deleted
- }
- }
- }
- `
variables = { id: ' u343', resource: [] }
user = await Factory.build('user', {
@@ -289,112 +285,113 @@ describe('Delete a user', () => {
})
})
- describe('as another user', () => {
+ describe('authenticated as Admin', () => {
beforeEach(async () => {
- anotherUser = await Factory.build(
+ admin = await Factory.build(
'user',
{
- role: 'user',
+ role: 'admin',
},
{
- email: 'user@example.org',
+ email: 'admin@example.org',
password: '1234',
},
)
-
- authenticatedUser = await anotherUser.toJson()
+ authenticatedUser = await admin.toJson()
})
- it("an ordinary user has no authorization to delete another user's account", async () => {
- const { errors } = await mutate({ mutation: deleteUserMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('as moderator', () => {
- beforeEach(async () => {
- moderator = await Factory.build(
- 'user',
- {
- role: 'moderator',
- },
- {
- email: 'moderator@example.org',
- password: '1234',
- },
- )
-
- authenticatedUser = await moderator.toJson()
- })
-
- it('moderator is not allowed to delete other user accounts', async () => {
- const { errors } = await mutate({ mutation: deleteUserMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('as admin', () => {
- describe('authenticated as Admin', () => {
- beforeEach(async () => {
- admin = await Factory.build(
- 'user',
- {
- role: 'admin',
- },
- {
- email: 'admin@example.org',
- password: '1234',
- },
- )
- authenticatedUser = await admin.toJson()
+ describe('deleting a user account', () => {
+ beforeEach(() => {
+ variables = { ...variables, id: 'u343' }
})
- describe('deleting a user account', () => {
- beforeEach(() => {
- 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',
+ },
+ )
})
- 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',
+ it("deletes 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,
+ },
+ ],
},
- {
- 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',
- },
- )
+ },
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject(
+ expectedResponse,
+ )
+ })
+
+ describe('deletion of all posts and comments requested', () => {
+ beforeEach(() => {
+ variables = { ...variables, resource: ['Comment', 'Post'] }
})
- it("deletes account, but doesn't delete posts or comments by default", async () => {
+ it('marks posts and comments as deleted', async () => {
const expectedResponse = {
data: {
DeleteUser: {
@@ -405,15 +402,15 @@ describe('Delete a user', () => {
contributions: [
{
id: 'p139',
- content: 'Post by user u343',
- contentExcerpt: 'Post by user u343',
- deleted: false,
+ content: 'UNAVAILABLE',
+ contentExcerpt: 'UNAVAILABLE',
+ deleted: true,
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,
+ content: 'UNAVAILABLE',
+ contentExcerpt: 'UNAVAILABLE',
+ deleted: true,
},
],
},
@@ -421,9 +418,9 @@ describe('Delete a user', () => {
comments: [
{
id: 'c155',
- content: 'Comment by user u343',
- contentExcerpt: 'Comment by user u343',
- deleted: false,
+ content: 'UNAVAILABLE',
+ contentExcerpt: 'UNAVAILABLE',
+ deleted: true,
},
],
},
@@ -434,175 +431,28 @@ describe('Delete a user', () => {
mutate({ mutation: deleteUserMutation, variables }),
).resolves.toMatchObject(expectedResponse)
})
+ })
+ })
- describe('deletion of all post requested', () => {
- beforeEach(() => {
- variables = { ...variables, resource: ['Post'] }
- })
+ describe('connected `EmailAddress` nodes', () => {
+ it('will be removed completely', async () => {
+ await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
+ await mutate({ mutation: deleteUserMutation, variables })
- 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)
- })
+ await expect(neode.all('EmailAddress')).resolves.toHaveLength(1)
+ })
+ })
- 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 posts and comments requested', () => {
- beforeEach(() => {
- variables = { ...variables, resource: ['Comment', 'Post'] }
- })
-
- 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 `SocialMedia` nodes', () => {
+ beforeEach(async () => {
+ const socialMedia = await Factory.build('socialMedia')
+ await socialMedia.relateTo(user, 'ownedBy')
})
- 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)
- })
+ 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/backend/src/schema/resolvers/users/location.spec.js b/backend/src/schema/resolvers/users/location.spec.js
index 59442a9ca..3044e4b6f 100644
--- a/backend/src/schema/resolvers/users/location.spec.js
+++ b/backend/src/schema/resolvers/users/location.spec.js
@@ -51,8 +51,8 @@ const newlyCreatedNodesWithLocales = [
country: {
id: expect.stringContaining('country'),
type: 'country',
- name: 'United States of America',
- nameEN: 'United States of America',
+ name: 'United States',
+ nameEN: 'United States',
nameDE: 'Vereinigte Staaten',
namePT: 'Estados Unidos',
nameES: 'Estados Unidos',
diff --git a/backend/src/schema/types/schema.gql b/backend/src/schema/types/schema.gql
index 23c2ded4d..b15179443 100644
--- a/backend/src/schema/types/schema.gql
+++ b/backend/src/schema/types/schema.gql
@@ -12,11 +12,6 @@ type Mutation {
unfollowUser(id: ID!): User
}
-enum Deletable {
- Post
- Comment
-}
-
enum ShoutTypeEnum {
Post
}
diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql
index af525396b..e6e7191c5 100644
--- a/backend/src/schema/types/type/User.gql
+++ b/backend/src/schema/types/type/User.gql
@@ -185,6 +185,11 @@ type Query {
)
}
+enum Deletable {
+ Post
+ Comment
+}
+
type Mutation {
UpdateUser (
id: ID!
diff --git a/webapp/components/ContentMenu/ContentMenu.spec.js b/webapp/components/ContentMenu/ContentMenu.spec.js
index b49a1158a..37bef14f6 100644
--- a/webapp/components/ContentMenu/ContentMenu.spec.js
+++ b/webapp/components/ContentMenu/ContentMenu.spec.js
@@ -134,6 +134,47 @@ describe('ContentMenu.vue', () => {
],
])
})
+
+ it('can delete another user', () => {
+ getters['auth/user'] = () => {
+ return { id: 'some-user', slug: 'some-user' }
+ }
+ const wrapper = openContentMenu({
+ resourceType: 'user',
+ resource: {
+ id: 'another-user',
+ slug: 'another-user',
+ },
+ })
+ wrapper
+ .findAll('.ds-menu-item')
+ .filter((item) => item.text() === 'settings.deleteUserAccount.name')
+ .at(0)
+ .trigger('click')
+ expect(wrapper.emitted('delete')).toEqual([
+ [
+ {
+ id: 'another-user',
+ slug: 'another-user',
+ },
+ ],
+ ])
+ })
+
+ it('can not delete the own account', () => {
+ const wrapper = openContentMenu({
+ resourceType: 'user',
+ resource: {
+ id: 'some-user',
+ slug: 'some-user',
+ },
+ })
+ expect(
+ wrapper
+ .findAll('.ds-menu-item')
+ .filter((item) => item.text() === 'settings.deleteUserAccount.name'),
+ ).toEqual({})
+ })
})
describe('owner of comment can', () => {
diff --git a/webapp/components/ContentMenu/ContentMenu.vue b/webapp/components/ContentMenu/ContentMenu.vue
index ef2df822b..88d83d059 100644
--- a/webapp/components/ContentMenu/ContentMenu.vue
+++ b/webapp/components/ContentMenu/ContentMenu.vue
@@ -191,6 +191,15 @@ export default {
icon: 'user-times',
})
}
+ if (this.isAdmin === true) {
+ routes.push({
+ label: this.$t(`settings.deleteUserAccount.name`),
+ callback: () => {
+ this.$emit('delete', this.resource)
+ },
+ icon: 'trash',
+ })
+ }
}
}
diff --git a/webapp/components/DeleteData/DeleteData.spec.js b/webapp/components/DeleteData/DeleteData.spec.js
index 81a6c10c7..70f98424a 100644
--- a/webapp/components/DeleteData/DeleteData.spec.js
+++ b/webapp/components/DeleteData/DeleteData.spec.js
@@ -50,12 +50,20 @@ describe('DeleteData.vue', () => {
})
describe('mount', () => {
+ const data = () => {
+ return {
+ currentUserCounts: {
+ contributionsCount: 4,
+ commentedCount: 2,
+ },
+ }
+ }
const Wrapper = () => {
const store = new Vuex.Store({
getters,
actions,
})
- return mount(DeleteData, { mocks, localVue, store })
+ return mount(DeleteData, { mocks, localVue, store, data })
}
beforeEach(() => {
diff --git a/webapp/components/DeleteData/DeleteData.vue b/webapp/components/DeleteData/DeleteData.vue
index 36d166207..21b842fe6 100644
--- a/webapp/components/DeleteData/DeleteData.vue
+++ b/webapp/components/DeleteData/DeleteData.vue
@@ -8,7 +8,9 @@
{{ $t('settings.deleteUserAccount.pleaseConfirm', { confirm: currentUser.name }) }}
{{ $t('settings.deleteUserAccount.accountDescription') }}
++ {{ $t('settings.deleteUserAccount.accountDescription') }} +