Merge pull request #535 from Human-Connection/463-delete-socialMedia

Delete SocialMedia
This commit is contained in:
Wolfgang Huß 2019-05-09 14:25:30 +02:00 committed by GitHub
commit a9860c49fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 203 additions and 29 deletions

View File

@ -75,6 +75,7 @@ const permissions = shield({
DeleteBadge: isAdmin,
AddUserBadges: isAdmin,
CreateSocialMedia: isAuthenticated,
DeleteSocialMedia: isAuthenticated,
// AddBadgeRewarded: isAdmin,
// RemoveBadgeRewarded: isAdmin,
reward: isAdmin,

View File

@ -3,6 +3,9 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
export default {
Mutation: {
CreateSocialMedia: async (object, params, context, resolveInfo) => {
/**
* TODO?: Creates double Nodes!
*/
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
const session = context.driver.session()
await session.run(
@ -15,6 +18,11 @@ export default {
)
session.close()
return socialMedia
},
DeleteSocialMedia: async (object, params, context, resolveInfo) => {
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
return socialMedia
}
}

View File

@ -7,9 +7,18 @@ const factory = Factory()
describe('CreateSocialMedia', () => {
let client
let headers
const mutation = `
const mutationC = `
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
`
const mutationD = `
mutation($id: ID!) {
DeleteSocialMedia(id: $id) {
id
url
}
}
@ -30,20 +39,63 @@ describe('CreateSocialMedia', () => {
await factory.cleanDatabase()
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
client = new GraphQLClient(host)
const variables = { url: 'http://nsosp.org' }
await expect(
client.request(mutationC, variables)
).rejects.toThrow('Not Authorised')
})
})
describe('authenticated', () => {
beforeEach(async () => {
headers = await login({ email: 'test@example.org', password: '1234' })
client = new GraphQLClient(host, { headers })
})
it('creates social media with correct URL', async () => {
const variables = { url: 'http://nsosp.org' }
await expect(
client.request(mutationC, variables)
).resolves.toEqual(expect.objectContaining({
CreateSocialMedia: {
id: expect.any(String),
url: 'http://nsosp.org'
}
}))
})
it('deletes social media', async () => {
const creationVariables = { url: 'http://nsosp.org' }
const { CreateSocialMedia } = await client.request(mutationC, creationVariables)
const { id } = CreateSocialMedia
const deletionVariables = { id }
const expected = {
DeleteSocialMedia: {
id: id,
url: 'http://nsosp.org'
}
}
await expect(
client.request(mutationD, deletionVariables)
).resolves.toEqual(expected)
})
it('rejects empty string', async () => {
const variables = { url: '' }
await expect(client.request(mutation, variables)).rejects.toThrow('Input is not a URL')
await expect(
client.request(mutationC, variables)
).rejects.toThrow('Input is not a URL')
})
it('validates URLs', async () => {
const variables = { url: 'not-a-url' }
await expect(client.request(mutation, variables)).rejects.toThrow('Input is not a URL')
await expect(
client.request(mutationC, variables)
).rejects.toThrow('Input is not a URL')
})
})
})

View File

@ -77,7 +77,7 @@ Then('I should be on the {string} page', page => {
.should('contain', 'Social media')
})
Then('I add a social media link', () => {
When('I add a social media link', () => {
cy.get("input[name='social-media']")
.type('https://freeradical.zone/peter-pan')
.get('button')
@ -87,7 +87,7 @@ Then('I add a social media link', () => {
Then('it gets saved successfully', () => {
cy.get('.iziToast-message')
.should('contain', 'Updated user')
.should('contain', 'Added social media')
})
Then('the new social media link shows up on the page', () => {
@ -110,3 +110,13 @@ Then('they should be able to see my social media links', () => {
.get('a[href="https://freeradical.zone/peter-pan"]')
.should('have.length', 1)
})
When('I delete a social media link', () => {
cy.get("a[name='delete']")
.click()
})
Then('it gets deleted successfully', () => {
cy.get('.iziToast-message')
.should('contain', 'Deleted social media')
})

View File

@ -19,3 +19,11 @@ Feature: List Social Media Accounts
Given I have added a social media link
When people visit my profile page
Then they should be able to see my social media links
Scenario: Deleting Social Media
Given I am on the "settings" page
And I click on the "Social media" link
Then I should be on the "/settings/my-social-media" page
Given I have added a social media link
When I delete a social media link
Then it gets deleted successfully

View File

@ -65,8 +65,10 @@
},
"social-media": {
"name": "Soziale Medien",
"placeholder": "Füge eine Social-Media URL hinzu",
"submit": "Link hinzufügen",
"success": "Profil aktualisiert"
"successAdd": "Social-Media hinzugefügt. Profil aktualisiert!",
"successDelete": "Social-Media gelöscht. Profil aktualisiert!"
}
},
"admin": {

View File

@ -65,8 +65,10 @@
},
"social-media": {
"name": "Social media",
"placeholder": "Add social media url",
"submit": "Add link",
"success": "Updated user profile"
"successAdd": "Added social media. Updated user profile!",
"successDelete": "Deleted social media. Updated user profile!"
}
},
"admin": {

View File

@ -71,6 +71,40 @@ describe('my-social-media.vue', () => {
const socialMediaLink = wrapper.find('a').attributes().href
expect(socialMediaLink).toBe(socialMediaUrl)
})
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockRejectedValue({ message: 'Ouch!' })
.mockResolvedValueOnce({
data: { DeleteSocialMeda: { id: 's1', url: socialMediaUrl } }
})
},
$toast: {
error: jest.fn(),
success: jest.fn()
}
}
getters = {
'auth/user': () => {
return {
socialMedia: [{ id: 's1', url: socialMediaUrl }]
}
}
}
})
it('displays a trash sympol after a social media and allows the user to delete it', () => {
wrapper = Wrapper()
const deleteSelector = wrapper.find({ name: 'delete' })
expect(deleteSelector).toEqual({ selector: 'Component' })
const icon = wrapper.find({ name: 'trash' })
icon.trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
})
})
describe('currentUser does not have a social media account linked', () => {

View File

@ -8,9 +8,12 @@
<ds-list>
<ds-list-item
v-for="link in socialMediaLinks"
:key="link.url"
:key="link.id"
>
<a :href="link.url">
<a
:href="link.url"
target="_blank"
>
<img
:src="link.favicon"
alt="Social Media link"
@ -19,26 +22,39 @@
>
{{ link.url }}
</a>
&nbsp;&nbsp; <span class="layout-leave-active">|</span> &nbsp;&nbsp;
<ds-icon
name="edit"
class="layout-leave-active"
/>
<a
name="delete"
@click="handleDeleteSocialMedia(link)"
>
<ds-icon name="trash" />
</a>
</ds-list-item>
</ds-list>
</ds-space>
<div>
<ds-input
v-model="value"
placeholder="Add social media url"
name="social-media"
:schema="{type: 'url'}"
/>
</div>
<ds-space margin-top="base">
<div>
<ds-button
primary
@click="handleAddSocialMedia"
>
{{ $t('settings.social-media.submit') }}
</ds-button>
<ds-input
v-model="value"
:placeholder="$t('settings.social-media.placeholder')"
name="social-media"
:schema="{type: 'url'}"
/>
</div>
<ds-space margin-top="base">
<div>
<ds-button
primary
@click="handleAddSocialMedia"
>
{{ $t('settings.social-media.submit') }}
</ds-button>
</div>
</ds-space>
</ds-space>
</ds-card>
</template>
@ -59,13 +75,13 @@ export default {
socialMediaLinks() {
const { socialMedia = [] } = this.currentUser
return socialMedia.map(socialMedia => {
const { url } = socialMedia
const { id, url } = socialMedia
const matches = url.match(
/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:\/\n?]+)/g
)
const [domain] = matches || []
const favicon = domain ? `${domain}/favicon.ico` : null
return { url, favicon }
return { id, url, favicon }
})
}
},
@ -79,6 +95,7 @@ export default {
mutation: gql`
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
@ -97,11 +114,51 @@ export default {
})
}
})
.then(
this.$toast.success(this.$t('settings.social-media.success')),
(this.value = '')
)
.then(() => {
this.$toast.success(this.$t('settings.social-media.successAdd')),
(this.value = '')
})
.catch(error => {
this.$toast.error(error.message)
})
},
handleDeleteSocialMedia(link) {
this.$apollo
.mutate({
mutation: gql`
mutation($id: ID!) {
DeleteSocialMedia(id: $id) {
id
url
}
}
`,
variables: {
id: link.id
},
update: (store, { data }) => {
const socialMedia = this.currentUser.socialMedia.filter(
element => element.id !== link.id
)
this.setCurrentUser({
...this.currentUser,
socialMedia
})
}
})
.then(() => {
this.$toast.success(this.$t('settings.social-media.successDelete'))
})
.catch(error => {
this.$toast.error(error.message)
})
}
}
}
</script>
<style lang="scss">
.layout-leave-active {
opacity: 0.4;
}
</style>