mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge branch 'master' of github.com:Human-Connection/Human-Connection into 296-image_component
This commit is contained in:
commit
cc61ce5461
@ -15,7 +15,7 @@
|
|||||||
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
|
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
|
||||||
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
||||||
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
||||||
"test:jest": "run-p --race test:before:* 'test:jest:cmd {@}' --",
|
"test:jest": "run-p --race test:before:* \"test:jest:cmd {@}\" --",
|
||||||
"test:cucumber": " cross-env CLIENT_URI=http://localhost:4123 run-p --race test:before:* 'test:cucumber:cmd {@}' --",
|
"test:cucumber": " cross-env CLIENT_URI=http://localhost:4123 run-p --race test:before:* 'test:cucumber:cmd {@}' --",
|
||||||
"test:jest:debug": "run-p --race test:before:* 'test:jest:cmd:debug {@}' --",
|
"test:jest:debug": "run-p --race test:before:* 'test:jest:cmd:debug {@}' --",
|
||||||
"db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js",
|
"db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js",
|
||||||
|
|||||||
@ -75,6 +75,7 @@ const permissions = shield({
|
|||||||
DeleteBadge: isAdmin,
|
DeleteBadge: isAdmin,
|
||||||
AddUserBadges: isAdmin,
|
AddUserBadges: isAdmin,
|
||||||
CreateSocialMedia: isAuthenticated,
|
CreateSocialMedia: isAuthenticated,
|
||||||
|
DeleteSocialMedia: isAuthenticated,
|
||||||
// AddBadgeRewarded: isAdmin,
|
// AddBadgeRewarded: isAdmin,
|
||||||
// RemoveBadgeRewarded: isAdmin,
|
// RemoveBadgeRewarded: isAdmin,
|
||||||
reward: isAdmin,
|
reward: isAdmin,
|
||||||
|
|||||||
@ -40,10 +40,13 @@ describe('authorization', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('does not expose the owner\'s email address', async () => {
|
it('does not expose the owner\'s email address', async () => {
|
||||||
|
let response = {}
|
||||||
try {
|
try {
|
||||||
await action()
|
await action()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error.response.data).toEqual({ User: [ { email: null } ] })
|
response = error.response.data
|
||||||
|
} finally {
|
||||||
|
expect(response).toEqual({ User: [ null ] })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -74,11 +77,13 @@ describe('authorization', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('does not expose the owner\'s email address', async () => {
|
it('does not expose the owner\'s email address', async () => {
|
||||||
|
let response
|
||||||
try {
|
try {
|
||||||
await action()
|
await action()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error.response.data).toEqual({ User: [ { email: null } ] })
|
response = error.response.data
|
||||||
}
|
}
|
||||||
|
expect(response).toEqual({ User: [ null ] })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import slugify from 'slug'
|
import slugify from 'slug'
|
||||||
export default async function uniqueSlug (string, isUnique) {
|
export default async function uniqueSlug (string, isUnique) {
|
||||||
let slug = slugify(string, {
|
let slug = slugify(string || 'anonymous', {
|
||||||
lower: true
|
lower: true
|
||||||
})
|
})
|
||||||
if (await isUnique(slug)) return slug
|
if (await isUnique(slug)) return slug
|
||||||
|
|||||||
@ -15,4 +15,11 @@ describe('uniqueSlug', () => {
|
|||||||
.mockResolvedValueOnce(true)
|
.mockResolvedValueOnce(true)
|
||||||
expect(uniqueSlug(string, isUnique)).resolves.toEqual('hello-world-1')
|
expect(uniqueSlug(string, isUnique)).resolves.toEqual('hello-world-1')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('slugify null string', () => {
|
||||||
|
const string = null
|
||||||
|
const isUnique = jest.fn()
|
||||||
|
.mockResolvedValue(true)
|
||||||
|
expect(uniqueSlug(string, isUnique)).resolves.toEqual('anonymous')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -77,7 +77,7 @@ describe('slugify', () => {
|
|||||||
describe('CreateUser', () => {
|
describe('CreateUser', () => {
|
||||||
const action = async (mutation, params) => {
|
const action = async (mutation, params) => {
|
||||||
return authenticatedClient.request(`mutation {
|
return authenticatedClient.request(`mutation {
|
||||||
${mutation}(password: "yo", ${params}) { slug }
|
${mutation}(password: "yo", email: "123@123.de", ${params}) { slug }
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
it('generates a slug based on name', async () => {
|
it('generates a slug based on name', async () => {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import createOrUpdateLocations from './nodes/locations'
|
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
|
import { UserInputError } from 'apollo-server'
|
||||||
|
|
||||||
|
import createOrUpdateLocations from './nodes/locations'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
@ -11,6 +13,10 @@ export default {
|
|||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
UpdateUser: async (resolve, root, args, context, info) => {
|
UpdateUser: async (resolve, root, args, context, info) => {
|
||||||
|
const USERNAME_MIN_LENGTH = 3 // TODO move to the correct place
|
||||||
|
if (!args.name || args.name.length < USERNAME_MIN_LENGTH) {
|
||||||
|
throw new UserInputError(`Username must be at least ${USERNAME_MIN_LENGTH} characters long!`)
|
||||||
|
}
|
||||||
const result = await resolve(root, args, context, info)
|
const result = await resolve(root, args, context, info)
|
||||||
await createOrUpdateLocations(args.id, args.locationName, context.driver)
|
await createOrUpdateLocations(args.id, args.locationName, context.driver)
|
||||||
return result
|
return result
|
||||||
|
|||||||
81
backend/src/middleware/userMiddleware.spec.js
Normal file
81
backend/src/middleware/userMiddleware.spec.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { GraphQLClient } from 'graphql-request'
|
||||||
|
import { host } from '../jest/helpers'
|
||||||
|
import Factory from '../seed/factories'
|
||||||
|
|
||||||
|
const factory = Factory()
|
||||||
|
let client
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await factory.cleanDatabase()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('userMiddleware', () => {
|
||||||
|
describe('create User', () => {
|
||||||
|
const mutation = `
|
||||||
|
mutation($id: ID, $password: String!, $email: String!) {
|
||||||
|
CreateUser(id: $id, password: $password, email: $email) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
client = new GraphQLClient(host)
|
||||||
|
|
||||||
|
it('with password and email', async () => {
|
||||||
|
const variables = {
|
||||||
|
password: '123',
|
||||||
|
email: '123@123.de'
|
||||||
|
}
|
||||||
|
const expected = {
|
||||||
|
CreateUser: {
|
||||||
|
id: expect.any(String)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await expect(client.request(mutation, variables))
|
||||||
|
.resolves.toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('update User', () => {
|
||||||
|
const mutation = `
|
||||||
|
mutation($id: ID!, $name: String) {
|
||||||
|
UpdateUser(id: $id, name: $name) {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
client = new GraphQLClient(host)
|
||||||
|
|
||||||
|
// TODO why is this failing - it returns { UpdateUser: null } - that should not be
|
||||||
|
/* it('name within specifications', async () => {
|
||||||
|
const variables = {
|
||||||
|
id: 'u1',
|
||||||
|
name: 'Peter Lustig'
|
||||||
|
}
|
||||||
|
const expected = {
|
||||||
|
UpdateUser: {
|
||||||
|
name: 'Peter Lustig'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await expect(client.request(mutation, variables))
|
||||||
|
.resolves.toEqual(expected)
|
||||||
|
}) */
|
||||||
|
|
||||||
|
it('with no name', async () => {
|
||||||
|
const variables = {
|
||||||
|
id: 'u1'
|
||||||
|
}
|
||||||
|
const expected = 'Username must be at least 3 characters long!'
|
||||||
|
await expect(client.request(mutation, variables))
|
||||||
|
.rejects.toThrow(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('with too short name', async () => {
|
||||||
|
const variables = {
|
||||||
|
id: 'u1'
|
||||||
|
}
|
||||||
|
const expected = 'Username must be at least 3 characters long!'
|
||||||
|
await expect(client.request(mutation, variables))
|
||||||
|
.rejects.toThrow(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -3,6 +3,9 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
|
|||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreateSocialMedia: async (object, params, context, resolveInfo) => {
|
CreateSocialMedia: async (object, params, context, resolveInfo) => {
|
||||||
|
/**
|
||||||
|
* TODO?: Creates double Nodes!
|
||||||
|
*/
|
||||||
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
await session.run(
|
await session.run(
|
||||||
@ -15,6 +18,11 @@ export default {
|
|||||||
)
|
)
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
return socialMedia
|
||||||
|
},
|
||||||
|
DeleteSocialMedia: async (object, params, context, resolveInfo) => {
|
||||||
|
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
|
|
||||||
return socialMedia
|
return socialMedia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,18 @@ const factory = Factory()
|
|||||||
describe('CreateSocialMedia', () => {
|
describe('CreateSocialMedia', () => {
|
||||||
let client
|
let client
|
||||||
let headers
|
let headers
|
||||||
const mutation = `
|
const mutationC = `
|
||||||
mutation($url: String!) {
|
mutation($url: String!) {
|
||||||
CreateSocialMedia(url: $url) {
|
CreateSocialMedia(url: $url) {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const mutationD = `
|
||||||
|
mutation($id: ID!) {
|
||||||
|
DeleteSocialMedia(id: $id) {
|
||||||
|
id
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,20 +39,63 @@ describe('CreateSocialMedia', () => {
|
|||||||
await factory.cleanDatabase()
|
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', () => {
|
describe('authenticated', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||||
client = new GraphQLClient(host, { headers })
|
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 () => {
|
it('rejects empty string', async () => {
|
||||||
const variables = { url: '' }
|
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 () => {
|
it('validates URLs', async () => {
|
||||||
const variables = { url: 'not-a-url' }
|
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')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -339,7 +339,7 @@ describe('do not expose private RSA key', () => {
|
|||||||
email: 'apfel-strudel@test.org'
|
email: 'apfel-strudel@test.org'
|
||||||
}
|
}
|
||||||
await client.request(gql`
|
await client.request(gql`
|
||||||
mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String) {
|
mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String!) {
|
||||||
CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) {
|
CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,7 +95,7 @@ type User {
|
|||||||
id: ID!
|
id: ID!
|
||||||
actorId: String
|
actorId: String
|
||||||
name: String
|
name: String
|
||||||
email: String
|
email: String!
|
||||||
slug: String
|
slug: String
|
||||||
password: String!
|
password: String!
|
||||||
avatar: String
|
avatar: String
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export default function create (params) {
|
|||||||
$name: String
|
$name: String
|
||||||
$slug: String
|
$slug: String
|
||||||
$password: String!
|
$password: String!
|
||||||
$email: String
|
$email: String!
|
||||||
$avatar: String
|
$avatar: String
|
||||||
$about: String
|
$about: String
|
||||||
$role: UserGroupEnum
|
$role: UserGroupEnum
|
||||||
|
|||||||
@ -77,7 +77,7 @@ Then('I should be on the {string} page', page => {
|
|||||||
.should('contain', 'Social media')
|
.should('contain', 'Social media')
|
||||||
})
|
})
|
||||||
|
|
||||||
Then('I add a social media link', () => {
|
When('I add a social media link', () => {
|
||||||
cy.get("input[name='social-media']")
|
cy.get("input[name='social-media']")
|
||||||
.type('https://freeradical.zone/peter-pan')
|
.type('https://freeradical.zone/peter-pan')
|
||||||
.get('button')
|
.get('button')
|
||||||
@ -87,7 +87,7 @@ Then('I add a social media link', () => {
|
|||||||
|
|
||||||
Then('it gets saved successfully', () => {
|
Then('it gets saved successfully', () => {
|
||||||
cy.get('.iziToast-message')
|
cy.get('.iziToast-message')
|
||||||
.should('contain', 'Updated user')
|
.should('contain', 'Added social media')
|
||||||
})
|
})
|
||||||
|
|
||||||
Then('the new social media link shows up on the page', () => {
|
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"]')
|
.get('a[href="https://freeradical.zone/peter-pan"]')
|
||||||
.should('have.length', 1)
|
.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')
|
||||||
|
})
|
||||||
|
|||||||
@ -19,3 +19,11 @@ Feature: List Social Media Accounts
|
|||||||
Given I have added a social media link
|
Given I have added a social media link
|
||||||
When people visit my profile page
|
When people visit my profile page
|
||||||
Then they should be able to see my social media links
|
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
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
"test:jest": "cd webapp && yarn test && cd ../backend && yarn test:jest && codecov"
|
"test:jest": "cd webapp && yarn test && cd ../backend && yarn test:jest && codecov"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"codecov": "^3.3.0",
|
"codecov": "^3.4.0",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"cypress": "^3.2.0",
|
"cypress": "^3.2.0",
|
||||||
"cypress-cucumber-preprocessor": "^1.11.0",
|
"cypress-cucumber-preprocessor": "^1.11.0",
|
||||||
|
|||||||
@ -51,8 +51,8 @@ describe('User', () => {
|
|||||||
|
|
||||||
it('renders anonymous user', () => {
|
it('renders anonymous user', () => {
|
||||||
const wrapper = Wrapper()
|
const wrapper = Wrapper()
|
||||||
expect(wrapper.text()).not.toMatch('Tilda Swinton')
|
expect(wrapper.text()).toBe('')
|
||||||
expect(wrapper.text()).toMatch('Anonymus')
|
expect(mocks.$t).toHaveBeenCalledWith('profile.userAnonym')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('given an user', () => {
|
describe('given an user', () => {
|
||||||
@ -65,7 +65,7 @@ describe('User', () => {
|
|||||||
|
|
||||||
it('renders user name', () => {
|
it('renders user name', () => {
|
||||||
const wrapper = Wrapper()
|
const wrapper = Wrapper()
|
||||||
expect(wrapper.text()).not.toMatch('Anonymous')
|
expect(mocks.$t).not.toHaveBeenCalledWith('profile.userAnonym')
|
||||||
expect(wrapper.text()).toMatch('Tilda Swinton')
|
expect(wrapper.text()).toMatch('Tilda Swinton')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ describe('User', () => {
|
|||||||
it('renders anonymous user', () => {
|
it('renders anonymous user', () => {
|
||||||
const wrapper = Wrapper()
|
const wrapper = Wrapper()
|
||||||
expect(wrapper.text()).not.toMatch('Tilda Swinton')
|
expect(wrapper.text()).not.toMatch('Tilda Swinton')
|
||||||
expect(wrapper.text()).toMatch('Anonymus')
|
expect(mocks.$t).toHaveBeenCalledWith('profile.userAnonym')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('current user is a moderator', () => {
|
describe('current user is a moderator', () => {
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<b
|
<b
|
||||||
class="username"
|
class="username"
|
||||||
style="vertical-align: middle;"
|
style="vertical-align: middle;"
|
||||||
>Anonymus</b>
|
>{{ $t('profile.userAnonym') }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dropdown
|
<dropdown
|
||||||
@ -38,7 +38,7 @@
|
|||||||
>
|
>
|
||||||
<ds-avatar
|
<ds-avatar
|
||||||
:image="user.avatar"
|
:image="user.avatar"
|
||||||
:name="user.name"
|
:name="userName"
|
||||||
style="display: inline-block; vertical-align: middle;"
|
style="display: inline-block; vertical-align: middle;"
|
||||||
size="32px"
|
size="32px"
|
||||||
/>
|
/>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<b
|
<b
|
||||||
class="username"
|
class="username"
|
||||||
style="vertical-align: middle;"
|
style="vertical-align: middle;"
|
||||||
>{{ user.name | truncate(trunc, 18) }}</b>
|
>{{ userName | truncate(18) }}</b>
|
||||||
</div>
|
</div>
|
||||||
<!-- Time -->
|
<!-- Time -->
|
||||||
<div
|
<div
|
||||||
@ -141,8 +141,8 @@
|
|||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
import HcRelativeDateTime from '~/components/RelativeDateTime'
|
import HcRelativeDateTime from '~/components/RelativeDateTime'
|
||||||
import HcFollowButton from '~/components/FollowButton.vue'
|
import HcFollowButton from '~/components/FollowButton'
|
||||||
import HcBadges from '~/components/Badges.vue'
|
import HcBadges from '~/components/Badges'
|
||||||
import Dropdown from '~/components/Dropdown'
|
import Dropdown from '~/components/Dropdown'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -173,6 +173,10 @@ export default {
|
|||||||
const { id, slug } = this.user
|
const { id, slug } = this.user
|
||||||
if (!(id && slug)) return ''
|
if (!(id && slug)) return ''
|
||||||
return { name: 'profile-id-slug', params: { slug, id } }
|
return { name: 'profile-id-slug', params: { slug, id } }
|
||||||
|
},
|
||||||
|
userName() {
|
||||||
|
const { name } = this.user || {}
|
||||||
|
return name || this.$t('profile.userAnonym')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,7 +61,8 @@
|
|||||||
slot-scope="{closeMenu}"
|
slot-scope="{closeMenu}"
|
||||||
>
|
>
|
||||||
<div class="avatar-menu-popover">
|
<div class="avatar-menu-popover">
|
||||||
{{ $t('login.hello') }} <b>{{ user.name }}</b>
|
{{ $t('login.hello') }}
|
||||||
|
<b>{{ userName }}</b>
|
||||||
<template v-if="user.role !== 'user'">
|
<template v-if="user.role !== 'user'">
|
||||||
<ds-text
|
<ds-text
|
||||||
color="softer"
|
color="softer"
|
||||||
@ -83,7 +84,8 @@
|
|||||||
:parents="item.parents"
|
:parents="item.parents"
|
||||||
@click.native="closeMenu(false)"
|
@click.native="closeMenu(false)"
|
||||||
>
|
>
|
||||||
<ds-icon :name="item.route.icon" /> {{ item.route.name }}
|
<ds-icon :name="item.route.icon" />
|
||||||
|
{{ item.route.name }}
|
||||||
</ds-menu-item>
|
</ds-menu-item>
|
||||||
</ds-menu>
|
</ds-menu>
|
||||||
<hr>
|
<hr>
|
||||||
@ -91,7 +93,8 @@
|
|||||||
class="logout-link"
|
class="logout-link"
|
||||||
:to="{ name: 'logout'}"
|
:to="{ name: 'logout'}"
|
||||||
>
|
>
|
||||||
<ds-icon name="sign-out" /> {{ $t('login.logout') }}
|
<ds-icon name="sign-out" />
|
||||||
|
{{ $t('login.logout') }}
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -146,6 +149,10 @@ export default {
|
|||||||
quickSearchResults: 'search/quickResults',
|
quickSearchResults: 'search/quickResults',
|
||||||
quickSearchPending: 'search/quickPending'
|
quickSearchPending: 'search/quickPending'
|
||||||
}),
|
}),
|
||||||
|
userName() {
|
||||||
|
const { name } = this.user || {}
|
||||||
|
return name || this.$t('profile.userAnonym')
|
||||||
|
},
|
||||||
routes() {
|
routes() {
|
||||||
if (!this.user.slug) {
|
if (!this.user.slug) {
|
||||||
return []
|
return []
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"following": "Folgt",
|
"following": "Folgt",
|
||||||
"shouted": "Empfohlen",
|
"shouted": "Empfohlen",
|
||||||
"commented": "Kommentiert",
|
"commented": "Kommentiert",
|
||||||
|
"userAnonym": "Anonymus",
|
||||||
"socialMedia": "Wo sonst finde ich"
|
"socialMedia": "Wo sonst finde ich"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
@ -38,8 +39,10 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Deine Daten",
|
"name": "Deine Daten",
|
||||||
"labelName": "Dein Name",
|
"labelName": "Dein Name",
|
||||||
|
"namePlaceholder": "Petra Lustig",
|
||||||
"labelCity": "Deine Stadt oder Region",
|
"labelCity": "Deine Stadt oder Region",
|
||||||
"labelBio": "Über dich"
|
"labelBio": "Über dich",
|
||||||
|
"success": "Deine Daten wurden erfolgreich aktualisiert!"
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"name": "Sicherheit",
|
"name": "Sicherheit",
|
||||||
@ -65,8 +68,10 @@
|
|||||||
},
|
},
|
||||||
"social-media": {
|
"social-media": {
|
||||||
"name": "Soziale Medien",
|
"name": "Soziale Medien",
|
||||||
|
"placeholder": "Füge eine Social-Media URL hinzu",
|
||||||
"submit": "Link hinzufügen",
|
"submit": "Link hinzufügen",
|
||||||
"success": "Profil aktualisiert"
|
"successAdd": "Social-Media hinzugefügt. Profil aktualisiert!",
|
||||||
|
"successDelete": "Social-Media gelöscht. Profil aktualisiert!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"following": "Following",
|
"following": "Following",
|
||||||
"shouted": "Shouted",
|
"shouted": "Shouted",
|
||||||
"commented": "Commented",
|
"commented": "Commented",
|
||||||
|
"userAnonym": "Anonymous",
|
||||||
"socialMedia": "Where else can I find"
|
"socialMedia": "Where else can I find"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
@ -38,8 +39,10 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Your data",
|
"name": "Your data",
|
||||||
"labelName": "Your Name",
|
"labelName": "Your Name",
|
||||||
|
"namePlaceholder": "Femanon Funny",
|
||||||
"labelCity": "Your City or Region",
|
"labelCity": "Your City or Region",
|
||||||
"labelBio": "About You"
|
"labelBio": "About You",
|
||||||
|
"success": "Your data was successfully updated!"
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"name": "Security",
|
"name": "Security",
|
||||||
@ -65,8 +68,10 @@
|
|||||||
},
|
},
|
||||||
"social-media": {
|
"social-media": {
|
||||||
"name": "Social media",
|
"name": "Social media",
|
||||||
|
"placeholder": "Add social media url",
|
||||||
"submit": "Add link",
|
"submit": "Add link",
|
||||||
"success": "Updated user profile"
|
"successAdd": "Added social media. Updated user profile!",
|
||||||
|
"successDelete": "Deleted social media. Updated user profile!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
|
|||||||
@ -15,13 +15,15 @@
|
|||||||
"followers": "Seguenti",
|
"followers": "Seguenti",
|
||||||
"following": "Seguendo",
|
"following": "Seguendo",
|
||||||
"shouted": "Gridato",
|
"shouted": "Gridato",
|
||||||
"commented": "Commentato"
|
"commented": "Commentato",
|
||||||
|
"userAnonym": "Anonymous"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "Impostazioni",
|
"name": "Impostazioni",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "I tuoi dati",
|
"name": "I tuoi dati",
|
||||||
"labelName": "Nome",
|
"labelName": "Nome",
|
||||||
|
"namePlaceholder": "Anonymous",
|
||||||
"labelCity": "La tua città o regione",
|
"labelCity": "La tua città o regione",
|
||||||
"labelBio": "Su di te"
|
"labelBio": "Su di te"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -15,13 +15,15 @@
|
|||||||
"followers": "Obserwujący",
|
"followers": "Obserwujący",
|
||||||
"following": "Obserwowani",
|
"following": "Obserwowani",
|
||||||
"shouted": "Krzyknij",
|
"shouted": "Krzyknij",
|
||||||
"commented": "Skomentuj"
|
"commented": "Skomentuj",
|
||||||
|
"userAnonym": "Anonymous"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "Ustawienia",
|
"name": "Ustawienia",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Twoje dane",
|
"name": "Twoje dane",
|
||||||
"labelName": "Twoje dane",
|
"labelName": "Twoje dane",
|
||||||
|
"namePlaceholder": "Anonymous",
|
||||||
"labelCity": "Twoje miasto lub region",
|
"labelCity": "Twoje miasto lub region",
|
||||||
"labelBio": "O Tobie"
|
"labelBio": "O Tobie"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -15,13 +15,15 @@
|
|||||||
"followers": "Seguidores",
|
"followers": "Seguidores",
|
||||||
"following": "Seguindo",
|
"following": "Seguindo",
|
||||||
"shouted": "Aclamou",
|
"shouted": "Aclamou",
|
||||||
"commented": "Comentou"
|
"commented": "Comentou",
|
||||||
|
"userAnonym": "Anonymous"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "Configurações",
|
"name": "Configurações",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Seus dados",
|
"name": "Seus dados",
|
||||||
"labelName": "Seu nome",
|
"labelName": "Seu nome",
|
||||||
|
"namePlaceholder": "Anonymous",
|
||||||
"labelCity": "Sua cidade ou estado",
|
"labelCity": "Sua cidade ou estado",
|
||||||
"labelBio": "Sobre você"
|
"labelBio": "Sobre você"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@human-connection/styleguide": "0.5.15",
|
"@human-connection/styleguide": "0.5.17",
|
||||||
"@nuxtjs/apollo": "4.0.0-rc4",
|
"@nuxtjs/apollo": "4.0.0-rc4",
|
||||||
"@nuxtjs/axios": "~5.4.1",
|
"@nuxtjs/axios": "~5.4.1",
|
||||||
"@nuxtjs/dotenv": "~1.3.0",
|
"@nuxtjs/dotenv": "~1.3.0",
|
||||||
@ -65,8 +65,8 @@
|
|||||||
"nuxt-env": "~0.1.0",
|
"nuxt-env": "~0.1.0",
|
||||||
"stack-utils": "^1.0.2",
|
"stack-utils": "^1.0.2",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
"tiptap": "^1.18.0",
|
"tiptap": "1.17.0",
|
||||||
"tiptap-extensions": "^1.18.1",
|
"tiptap-extensions": "1.17.0",
|
||||||
"v-tooltip": "~2.0.2",
|
"v-tooltip": "~2.0.2",
|
||||||
"vue-count-to": "~1.0.13",
|
"vue-count-to": "~1.0.13",
|
||||||
"vue-izitoast": "1.1.2",
|
"vue-izitoast": "1.1.2",
|
||||||
@ -95,7 +95,7 @@
|
|||||||
"nodemon": "~1.19.0",
|
"nodemon": "~1.19.0",
|
||||||
"prettier": "~1.14.3",
|
"prettier": "~1.14.3",
|
||||||
"sass-loader": "~7.1.0",
|
"sass-loader": "~7.1.0",
|
||||||
"tippy.js": "^4.3.0",
|
"tippy.js": "^4.3.1",
|
||||||
"vue-jest": "~3.0.4",
|
"vue-jest": "~3.0.4",
|
||||||
"vue-svg-loader": "~0.12.0"
|
"vue-svg-loader": "~0.12.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<ds-avatar
|
<ds-avatar
|
||||||
:image="user.avatar"
|
:image="user.avatar"
|
||||||
:name="user.name || 'Anonymus'"
|
:name="userName"
|
||||||
class="profile-avatar"
|
class="profile-avatar"
|
||||||
size="120px"
|
size="120px"
|
||||||
/>
|
/>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
align="center"
|
align="center"
|
||||||
no-margin
|
no-margin
|
||||||
>
|
>
|
||||||
{{ user.name }}
|
{{ userName }}
|
||||||
</ds-heading>
|
</ds-heading>
|
||||||
<ds-text
|
<ds-text
|
||||||
v-if="user.location"
|
v-if="user.location"
|
||||||
@ -123,7 +123,7 @@
|
|||||||
tag="h5"
|
tag="h5"
|
||||||
color="soft"
|
color="soft"
|
||||||
>
|
>
|
||||||
Wem folgt {{ user.name | truncate(15) }}?
|
Wem folgt {{ userName | truncate(15) }}?
|
||||||
</ds-text>
|
</ds-text>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<template v-if="user.following && user.following.length">
|
<template v-if="user.following && user.following.length">
|
||||||
@ -154,7 +154,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<p style="text-align: center; opacity: .5;">
|
<p style="text-align: center; opacity: .5;">
|
||||||
{{ user.name }} folgt niemandem
|
{{ userName }} folgt niemandem
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</ds-card>
|
</ds-card>
|
||||||
@ -168,7 +168,7 @@
|
|||||||
tag="h5"
|
tag="h5"
|
||||||
color="soft"
|
color="soft"
|
||||||
>
|
>
|
||||||
Wer folgt {{ user.name | truncate(15) }}?
|
Wer folgt {{ userName | truncate(15) }}?
|
||||||
</ds-text>
|
</ds-text>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<template v-if="user.followedBy && user.followedBy.length">
|
<template v-if="user.followedBy && user.followedBy.length">
|
||||||
@ -199,7 +199,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<p style="text-align: center; opacity: .5;">
|
<p style="text-align: center; opacity: .5;">
|
||||||
niemand folgt {{ user.name }}
|
niemand folgt {{ userName }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</ds-card>
|
</ds-card>
|
||||||
@ -208,9 +208,7 @@
|
|||||||
margin="large"
|
margin="large"
|
||||||
>
|
>
|
||||||
<ds-card style="position: relative; height: auto;">
|
<ds-card style="position: relative; height: auto;">
|
||||||
<ds-space
|
<ds-space margin="x-small">
|
||||||
margin="x-small"
|
|
||||||
>
|
|
||||||
<ds-text
|
<ds-text
|
||||||
tag="h5"
|
tag="h5"
|
||||||
color="soft"
|
color="soft"
|
||||||
@ -224,9 +222,7 @@
|
|||||||
margin="x-small"
|
margin="x-small"
|
||||||
>
|
>
|
||||||
<a :href="link.url">
|
<a :href="link.url">
|
||||||
<ds-avatar
|
<ds-avatar :image="link.favicon" />
|
||||||
:image="link.favicon"
|
|
||||||
/>
|
|
||||||
{{ link.username }}
|
{{ link.username }}
|
||||||
</a>
|
</a>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
@ -393,6 +389,10 @@ export default {
|
|||||||
const username = url.split('/').pop()
|
const username = url.split('/').pop()
|
||||||
return { url, username, favicon }
|
return { url, username, favicon }
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
userName() {
|
||||||
|
const { name } = this.user || {}
|
||||||
|
return name || this.$t('profile.userAnonym')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
73
webapp/pages/settings/index.spec.js
Normal file
73
webapp/pages/settings/index.spec.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { mount, createLocalVue } from '@vue/test-utils'
|
||||||
|
import index from './index.vue'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import Styleguide from '@human-connection/styleguide'
|
||||||
|
|
||||||
|
const localVue = createLocalVue()
|
||||||
|
|
||||||
|
localVue.use(Vuex)
|
||||||
|
localVue.use(Styleguide)
|
||||||
|
|
||||||
|
describe('index.vue', () => {
|
||||||
|
let Wrapper
|
||||||
|
let store
|
||||||
|
let mocks
|
||||||
|
let getters
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks = {
|
||||||
|
$t: jest.fn(),
|
||||||
|
$apollo: {
|
||||||
|
mutate: jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue({ message: 'Ouch!' })
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
data: {
|
||||||
|
UpdateUser: {
|
||||||
|
id: 'u1',
|
||||||
|
name: 'Peter',
|
||||||
|
locationName: 'Berlin',
|
||||||
|
about: 'Smth'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
$toast: {
|
||||||
|
error: jest.fn(),
|
||||||
|
success: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getters = {
|
||||||
|
'auth/user': () => {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
const Wrapper = () => {
|
||||||
|
store = new Vuex.Store({
|
||||||
|
getters
|
||||||
|
})
|
||||||
|
return mount(index, { store, mocks, localVue })
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders', () => {
|
||||||
|
expect(Wrapper().contains('div')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given a new username and hitting submit', () => {
|
||||||
|
it('calls updateUser mutation', () => {
|
||||||
|
const wrapper = Wrapper()
|
||||||
|
const input = wrapper.find('#name')
|
||||||
|
const submitForm = wrapper.find('.ds-form')
|
||||||
|
|
||||||
|
input.setValue('Peter')
|
||||||
|
submitForm.trigger('submit')
|
||||||
|
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -9,7 +9,7 @@
|
|||||||
model="name"
|
model="name"
|
||||||
icon="user"
|
icon="user"
|
||||||
:label="$t('settings.data.labelName')"
|
:label="$t('settings.data.labelName')"
|
||||||
:placeholder="$t('settings.data.labelName')"
|
:placeholder="$t('settings.data.namePlaceholder')"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-disable vue/use-v-on-exact -->
|
<!-- eslint-disable vue/use-v-on-exact -->
|
||||||
<ds-select
|
<ds-select
|
||||||
@ -19,7 +19,7 @@
|
|||||||
:options="cities"
|
:options="cities"
|
||||||
:label="$t('settings.data.labelCity')"
|
:label="$t('settings.data.labelCity')"
|
||||||
:placeholder="$t('settings.data.labelCity')"
|
:placeholder="$t('settings.data.labelCity')"
|
||||||
:loading="loading"
|
:loading="loadingGeo"
|
||||||
@input.native="handleCityInput"
|
@input.native="handleCityInput"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-enable vue/use-v-on-exact -->
|
<!-- eslint-enable vue/use-v-on-exact -->
|
||||||
@ -36,7 +36,7 @@
|
|||||||
style="float: right;"
|
style="float: right;"
|
||||||
icon="check"
|
icon="check"
|
||||||
type="submit"
|
type="submit"
|
||||||
:loading="sending"
|
:loading="loadingData"
|
||||||
primary
|
primary
|
||||||
>
|
>
|
||||||
{{ $t('actions.save') }}
|
{{ $t('actions.save') }}
|
||||||
@ -88,8 +88,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
axiosSource: null,
|
axiosSource: null,
|
||||||
cities: [],
|
cities: [],
|
||||||
sending: false,
|
loadingData: false,
|
||||||
loading: false,
|
loadingGeo: false,
|
||||||
formData: {}
|
formData: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -111,13 +111,13 @@ export default {
|
|||||||
...mapMutations({
|
...mapMutations({
|
||||||
setCurrentUser: 'auth/SET_USER'
|
setCurrentUser: 'auth/SET_USER'
|
||||||
}),
|
}),
|
||||||
submit() {
|
async submit() {
|
||||||
this.sending = true
|
this.loadingData = true
|
||||||
const { name, about } = this.formData
|
const { name, about } = this.formData
|
||||||
let { locationName } = this.formData
|
let { locationName } = this.formData
|
||||||
locationName = locationName && (locationName['label'] || locationName)
|
locationName = locationName && (locationName['label'] || locationName)
|
||||||
this.$apollo
|
try {
|
||||||
.mutate({
|
const { data } = await this.$apollo.mutate({
|
||||||
mutation,
|
mutation,
|
||||||
variables: {
|
variables: {
|
||||||
id: this.currentUser.id,
|
id: this.currentUser.id,
|
||||||
@ -135,15 +135,12 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(data => {
|
this.$toast.success(this.$t('settings.data.success'))
|
||||||
this.$toast.success('Updated user')
|
} catch (err) {
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$toast.error(err.message)
|
this.$toast.error(err.message)
|
||||||
})
|
} finally {
|
||||||
.finally(() => {
|
this.loadingData = false
|
||||||
this.sending = false
|
}
|
||||||
})
|
|
||||||
},
|
},
|
||||||
handleCityInput(value) {
|
handleCityInput(value) {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
@ -181,7 +178,7 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
this.loadingGeo = true
|
||||||
this.axiosSource = CancelToken.source()
|
this.axiosSource = CancelToken.source()
|
||||||
|
|
||||||
const place = encodeURIComponent(value)
|
const place = encodeURIComponent(value)
|
||||||
@ -198,7 +195,7 @@ export default {
|
|||||||
this.cities = this.processCityResults(res)
|
this.cities = this.processCityResults(res)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false
|
this.loadingGeo = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,6 +71,40 @@ describe('my-social-media.vue', () => {
|
|||||||
const socialMediaLink = wrapper.find('a').attributes().href
|
const socialMediaLink = wrapper.find('a').attributes().href
|
||||||
expect(socialMediaLink).toBe(socialMediaUrl)
|
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', () => {
|
describe('currentUser does not have a social media account linked', () => {
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
<ds-list>
|
<ds-list>
|
||||||
<ds-list-item
|
<ds-list-item
|
||||||
v-for="link in socialMediaLinks"
|
v-for="link in socialMediaLinks"
|
||||||
:key="link.url"
|
:key="link.id"
|
||||||
>
|
>
|
||||||
<a :href="link.url">
|
<a :href="link.url">
|
||||||
<hc-image
|
<hc-image
|
||||||
@ -19,13 +19,25 @@
|
|||||||
/>
|
/>
|
||||||
{{ link.url }}
|
{{ link.url }}
|
||||||
</a>
|
</a>
|
||||||
|
<span class="layout-leave-active">|</span>
|
||||||
|
<ds-icon
|
||||||
|
name="edit"
|
||||||
|
class="layout-leave-active"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
name="delete"
|
||||||
|
@click="handleDeleteSocialMedia(link)"
|
||||||
|
>
|
||||||
|
<ds-icon name="trash" />
|
||||||
|
</a>
|
||||||
</ds-list-item>
|
</ds-list-item>
|
||||||
</ds-list>
|
</ds-list>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
|
<ds-space margin-top="base">
|
||||||
<div>
|
<div>
|
||||||
<ds-input
|
<ds-input
|
||||||
v-model="value"
|
v-model="value"
|
||||||
placeholder="Add social media url"
|
:placeholder="$t('settings.social-media.placeholder')"
|
||||||
name="social-media"
|
name="social-media"
|
||||||
:schema="{type: 'url'}"
|
:schema="{type: 'url'}"
|
||||||
/>
|
/>
|
||||||
@ -40,6 +52,7 @@
|
|||||||
</ds-button>
|
</ds-button>
|
||||||
</div>
|
</div>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
|
</ds-space>
|
||||||
</ds-card>
|
</ds-card>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -63,13 +76,13 @@ export default {
|
|||||||
socialMediaLinks() {
|
socialMediaLinks() {
|
||||||
const { socialMedia = [] } = this.currentUser
|
const { socialMedia = [] } = this.currentUser
|
||||||
return socialMedia.map(socialMedia => {
|
return socialMedia.map(socialMedia => {
|
||||||
const { url } = socialMedia
|
const { id, url } = socialMedia
|
||||||
const matches = url.match(
|
const matches = url.match(
|
||||||
/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:\/\n?]+)/g
|
/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:\/\n?]+)/g
|
||||||
)
|
)
|
||||||
const [domain] = matches || []
|
const [domain] = matches || []
|
||||||
const favicon = domain ? `${domain}/favicon.ico` : null
|
const favicon = domain ? `${domain}/favicon.ico` : null
|
||||||
return { url, favicon }
|
return { id, url, favicon }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -83,6 +96,7 @@ export default {
|
|||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation($url: String!) {
|
mutation($url: String!) {
|
||||||
CreateSocialMedia(url: $url) {
|
CreateSocialMedia(url: $url) {
|
||||||
|
id
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,11 +115,51 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(
|
.then(() => {
|
||||||
this.$toast.success(this.$t('settings.social-media.success')),
|
this.$toast.success(this.$t('settings.social-media.successAdd')),
|
||||||
(this.value = '')
|
(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>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.layout-leave-active {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
1936
webapp/yarn.lock
1936
webapp/yarn.lock
File diff suppressed because it is too large
Load Diff
24
yarn.lock
24
yarn.lock
@ -1503,15 +1503,15 @@ code-point-at@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||||
|
|
||||||
codecov@^3.3.0:
|
codecov@^3.4.0:
|
||||||
version "3.3.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.3.0.tgz#7bf337b3f7b0474606b5c31c56dd9e44e395e15d"
|
resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.4.0.tgz#7d16d9d82b0ce20efe5dbf66245a9740779ff61b"
|
||||||
integrity sha512-S70c3Eg9SixumOvxaKE/yKUxb9ihu/uebD9iPO2IR73IdP4i6ZzjXEULj3d0HeyWPr0DqBfDkjNBWxURjVO5hw==
|
integrity sha512-+vtyL1B11MWiRIBaPnsIALKKpLFck9m6QdyI20ZnG8WqLG2cxwCTW9x/LbG4Ht8b81equZWw5xLcr+0BIvmdJQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
argv "^0.0.2"
|
argv "^0.0.2"
|
||||||
ignore-walk "^3.0.1"
|
ignore-walk "^3.0.1"
|
||||||
js-yaml "^3.12.0"
|
js-yaml "^3.13.0"
|
||||||
teeny-request "^3.7.0"
|
teeny-request "^3.11.3"
|
||||||
urlgrey "^0.4.4"
|
urlgrey "^0.4.4"
|
||||||
|
|
||||||
coffee-react-transform@^3.1.0:
|
coffee-react-transform@^3.1.0:
|
||||||
@ -3055,7 +3055,7 @@ js-levenshtein@^1.1.3:
|
|||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
js-yaml@^3.12.0:
|
js-yaml@^3.13.0, js-yaml@^3.9.0:
|
||||||
version "3.13.1"
|
version "3.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
|
||||||
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
|
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
|
||||||
@ -3063,14 +3063,6 @@ js-yaml@^3.12.0:
|
|||||||
argparse "^1.0.7"
|
argparse "^1.0.7"
|
||||||
esprima "^4.0.0"
|
esprima "^4.0.0"
|
||||||
|
|
||||||
js-yaml@^3.9.0:
|
|
||||||
version "3.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e"
|
|
||||||
integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==
|
|
||||||
dependencies:
|
|
||||||
argparse "^1.0.7"
|
|
||||||
esprima "^4.0.0"
|
|
||||||
|
|
||||||
jsbn@~0.1.0:
|
jsbn@~0.1.0:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||||
@ -4682,7 +4674,7 @@ tar@^4:
|
|||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
yallist "^3.0.2"
|
yallist "^3.0.2"
|
||||||
|
|
||||||
teeny-request@^3.7.0:
|
teeny-request@^3.11.3:
|
||||||
version "3.11.3"
|
version "3.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-3.11.3.tgz#335c629f7645e5d6599362df2f3230c4cbc23a55"
|
resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-3.11.3.tgz#335c629f7645e5d6599362df2f3230c4cbc23a55"
|
||||||
integrity sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==
|
integrity sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user