From aca3562e229f4618cd8733fa76c782d2d4e23fe8 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Fri, 22 Mar 2019 14:40:23 -0300 Subject: [PATCH 01/32] Migrate PRs from archived repos to monorepo Co-authored-by: Joseph Ngugi --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/resolvers/user_management.js | 19 +++++ backend/src/schema.graphql | 2 + cypress/integration/SocialMedia.feature | 14 ++++ cypress/integration/common/settings.js | 27 +++++++ webapp/locales/en.json | 5 ++ webapp/pages/settings.vue | 4 + webapp/pages/settings/my-social-media.vue | 75 +++++++++++++++++++ webapp/store/auth.js | 1 + 9 files changed, 148 insertions(+) create mode 100644 cypress/integration/SocialMedia.feature create mode 100644 webapp/pages/settings/my-social-media.vue diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 736ce20a9..0f45304fd 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -57,6 +57,7 @@ const permissions = shield({ UpdateBadge: isAdmin, DeleteBadge: isAdmin, AddUserBadges: isAdmin, + addSocialMedia: isAuthenticated, // AddBadgeRewarded: isAdmin, // RemoveBadgeRewarded: isAdmin, reward: isAdmin, diff --git a/backend/src/resolvers/user_management.js b/backend/src/resolvers/user_management.js index 26dfb81db..d77b67c82 100644 --- a/backend/src/resolvers/user_management.js +++ b/backend/src/resolvers/user_management.js @@ -100,6 +100,25 @@ export default { return encode(currentUser) } + }, + addSocialMedia: async (_, { url }, { driver, user }) => { + const session = driver.session() + + const { email } = user + const result = await session.run( + `MATCH (user:User {email: $userEmail}) + SET user.socialMedia = [$url] + RETURN user {.socialMedia}`, + { + userEmail: email, + url + } + ) + session.close() + const [currentUser] = result.records.map(record => { + return record.get('user') + }) + return !!currentUser.socialMedia } } } diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql index 3b1d95a95..6d757cb41 100644 --- a/backend/src/schema.graphql +++ b/backend/src/schema.graphql @@ -26,6 +26,7 @@ type Mutation { disable(id: ID!): ID enable(id: ID!): ID reward(fromBadgeId: ID!, toUserId: ID!): ID + addSocialMedia(url: String!): Boolean! unreward(fromBadgeId: ID!, toUserId: ID!): ID "Shout the given Type and ID" shout(id: ID!, type: ShoutTypeEnum): Boolean! @cypher(statement: """ @@ -120,6 +121,7 @@ type User { location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l") locationName: String about: String + socialMedia: [String] createdAt: String updatedAt: String diff --git a/cypress/integration/SocialMedia.feature b/cypress/integration/SocialMedia.feature new file mode 100644 index 000000000..05ecfc882 --- /dev/null +++ b/cypress/integration/SocialMedia.feature @@ -0,0 +1,14 @@ +Feature: List Social Media Accounts + As a User + I'd like to enter my social media + So I can show them to other users to get in contact + + Background: + Given I have a user account + And I am logged in + And I am on the "settings" page + + Scenario: Adding Social Media + Given I click on the "My social media" link + Then I should be on the "/settings/my-social-media" page + And I should be able to add a social media link diff --git a/cypress/integration/common/settings.js b/cypress/integration/common/settings.js index 3aa6022a8..c87d15aec 100644 --- a/cypress/integration/common/settings.js +++ b/cypress/integration/common/settings.js @@ -61,3 +61,30 @@ Then( 'I can see my new name {string} when I click on my profile picture in the top right', name => matchNameInUserMenu(name) ) + +When('I click on the {string} link', link => { + cy.get('a') + .contains(link) + .click() +}) + +Then('I should be on the {string} page', page => { + cy.location() + .should(loc => { + expect(loc.pathname).to.eq(page) + }) + .get('h3') + .should('contain', 'My social media') +}) + +Then('I should be able to add a social media link', () => { + cy.get("input[name='social-media']") + .type('https://freeradical.zone/@mattwr18') + .get('button') + .contains('Add social media') + .click() + .get('.iziToast-message') + .should('contain', 'Updated user') + .get('a') + .contains("src='https://freeradical.zone/@mattwr18'") +}) diff --git a/webapp/locales/en.json b/webapp/locales/en.json index fe92f901a..b3e2909ca 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -47,6 +47,11 @@ }, "languages": { "name": "Languages" + }, + "social-media": { + "name": "My social media", + "submit": "Add social media", + "success": "Updated user" } }, "admin": { diff --git a/webapp/pages/settings.vue b/webapp/pages/settings.vue index 9e2d63056..0352975d0 100644 --- a/webapp/pages/settings.vue +++ b/webapp/pages/settings.vue @@ -54,6 +54,10 @@ export default { { name: this.$t('settings.languages.name'), path: `/settings/languages` + }, + { + name: this.$t('settings.social-media.name'), + path: `/settings/my-social-media` } ] } diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue new file mode 100644 index 000000000..07d981a76 --- /dev/null +++ b/webapp/pages/settings/my-social-media.vue @@ -0,0 +1,75 @@ + + diff --git a/webapp/store/auth.js b/webapp/store/auth.js index 4785ff0c0..2f99b7fd5 100644 --- a/webapp/store/auth.js +++ b/webapp/store/auth.js @@ -83,6 +83,7 @@ export const actions = { role about locationName + socialMedia } }`) }) From 599f3814bac1a8a905d6d077a9d04857e955ef15 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Mon, 25 Mar 2019 20:10:49 -0300 Subject: [PATCH 02/32] Create users with default socialMedia, append url - It was such that every time a user updates their social media it would replace the existing url with the one to be added. creating a user with a default empty array means that we can concatenate the url to the array and not have a complicated cypher query to account for an intial NULL value. Co-authored-by: Joseph Ngugi --- backend/src/resolvers/user_management.js | 9 +++++---- backend/src/schema.graphql | 2 +- backend/src/seed/factories/users.js | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/src/resolvers/user_management.js b/backend/src/resolvers/user_management.js index d77b67c82..814aee3fb 100644 --- a/backend/src/resolvers/user_management.js +++ b/backend/src/resolvers/user_management.js @@ -32,7 +32,7 @@ export default { const session = driver.session() const result = await session.run( 'MATCH (user:User {email: $userEmail}) ' + - 'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role, .disabled} as user LIMIT 1', + 'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role, .disabled} as user LIMIT 1', { userEmail: email } @@ -107,8 +107,9 @@ export default { const { email } = user const result = await session.run( `MATCH (user:User {email: $userEmail}) - SET user.socialMedia = [$url] - RETURN user {.socialMedia}`, + SET user.socialMedia = user.socialMedia + $url + RETURN user {.socialMedia} + `, { userEmail: email, url @@ -118,7 +119,7 @@ export default { const [currentUser] = result.records.map(record => { return record.get('user') }) - return !!currentUser.socialMedia + return currentUser.socialMedia } } } diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql index 6d757cb41..e4530c435 100644 --- a/backend/src/schema.graphql +++ b/backend/src/schema.graphql @@ -26,7 +26,7 @@ type Mutation { disable(id: ID!): ID enable(id: ID!): ID reward(fromBadgeId: ID!, toUserId: ID!): ID - addSocialMedia(url: String!): Boolean! + addSocialMedia(url: String!): [String]! unreward(fromBadgeId: ID!, toUserId: ID!): ID "Shout the given Type and ID" shout(id: ID!, type: ShoutTypeEnum): Boolean! @cypher(statement: """ diff --git a/backend/src/seed/factories/users.js b/backend/src/seed/factories/users.js index 491b3f9e1..b6b2384b7 100644 --- a/backend/src/seed/factories/users.js +++ b/backend/src/seed/factories/users.js @@ -25,7 +25,8 @@ export default function create (params) { about: "${about}", role: ${role}, disabled: ${disabled}, - deleted: ${deleted} + deleted: ${deleted}, + socialMedia: [] ) { id name @@ -34,6 +35,7 @@ export default function create (params) { role deleted disabled + socialMedia } } ` From 7da9baf74866a278a8d1b67f7414695b00d06141 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Mon, 25 Mar 2019 20:18:49 -0300 Subject: [PATCH 03/32] Update socialMediaIconUrl, display linked icons - After successfully adding a socialMedia, update the sociaMeidaIconUrl, and iterate over them to display their icons, which link to the user's profile. Co-authored-by: Joseph Ngugi --- webapp/pages/settings/my-social-media.vue | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index 07d981a76..334d21770 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -23,11 +23,17 @@ margin-top="base" margin="x-small" > -
- + @@ -38,7 +44,7 @@ import { mapGetters } from 'vuex' export default { props: { - socialMedia: { + socialMediaIconUrl: { type: Array, default: () => [] } @@ -66,9 +72,12 @@ export default { url: this.value } }) - .then(() => - this.$toast.success(this.$t('settings.social-media.success')) + .then( + response => (this.socialMediaIconUrl = response.data.addSocialMedia) ) + .finally(() => { + this.$toast.success(this.$t('settings.social-media.success')) + }) } } } From 597d1a0cb02aaf559e216ab103003e730cb4ae9e Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Tue, 26 Mar 2019 17:43:23 -0300 Subject: [PATCH 04/32] Pull in icons from currentUser Co-authored-by: Joseph Ngugi --- webapp/pages/settings/my-social-media.vue | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index 334d21770..6bccd9949 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -24,13 +24,13 @@ margin="x-small" >
@@ -43,12 +43,6 @@ import gql from 'graphql-tag' import { mapGetters } from 'vuex' export default { - props: { - socialMediaIconUrl: { - type: Array, - default: () => [] - } - }, data() { return { value: '' @@ -72,12 +66,7 @@ export default { url: this.value } }) - .then( - response => (this.socialMediaIconUrl = response.data.addSocialMedia) - ) - .finally(() => { - this.$toast.success(this.$t('settings.social-media.success')) - }) + .then(this.$toast.success(this.$t('settings.social-media.success'))) } } } From 08c2e5d708754a4b22e784d903ac140f26c51191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 27 Mar 2019 13:56:28 +0100 Subject: [PATCH 05/32] I was able to save an empty string as social media --- backend/src/resolvers/user_management.spec.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/backend/src/resolvers/user_management.spec.js b/backend/src/resolvers/user_management.spec.js index 1c21adac1..a6ef48ee5 100644 --- a/backend/src/resolvers/user_management.spec.js +++ b/backend/src/resolvers/user_management.spec.js @@ -309,3 +309,30 @@ describe('change password', () => { }) }) }) + +describe('addSocialMedia', () => { + let client + let headers + const mutation = ` + mutation($url: String!) { + addSocialMedia(url: $url) + } + ` + + describe('authenticated', () => { + beforeEach(async () => { + headers = await login({ email: 'test@example.org', password: '1234' }) + client = new GraphQLClient(host, { headers }) + }) + + it('rejects empty string', async () => { + const variables = { url: '' } + await expect(client.request(mutation, 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') + }) + }) +}) From 8fb9ebd2b5ba5dcd6e23755ce0d808610564e28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 27 Mar 2019 14:07:24 +0100 Subject: [PATCH 06/32] Expose bug: Server-side render error --- webapp/pages/settings/my-social-media.spec.js | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 webapp/pages/settings/my-social-media.spec.js diff --git a/webapp/pages/settings/my-social-media.spec.js b/webapp/pages/settings/my-social-media.spec.js new file mode 100644 index 000000000..b3e198a6f --- /dev/null +++ b/webapp/pages/settings/my-social-media.spec.js @@ -0,0 +1,60 @@ +import { shallowMount, mount, createLocalVue } from '@vue/test-utils' +import MySocialMedia from './my-social-media.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('my-social-media.vue', () => { + let wrapper + let Wrapper + let store + let mocks + let getters + + beforeEach(() => { + mocks = { + $t: jest.fn() + } + getters = { + 'auth/user': () => { + return {} + } + } + }) + + describe('shallowMount', () => { + const Wrapper = () => { + store = new Vuex.Store({ + getters + }) + return shallowMount(MySocialMedia, { store, mocks, localVue }) + } + + it('renders', () => { + wrapper = Wrapper() + expect(wrapper.contains('div')).toBe(true) + }) + + describe('given currentUser has social media accounts', () => { + beforeEach(() => { + getters = { + 'auth/user': () => { + return { + socialMedia: [''] + } + } + } + }) + + it('renders', () => { + wrapper = Wrapper() + expect(wrapper.contains('div')).toBe(true) + }) + }) + }) +}) From 9de652c631e5630ee819452e317e75c819241a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 27 Mar 2019 14:16:00 +0100 Subject: [PATCH 07/32] Fix render error bug :raised_hands: @mattwr18 --- webapp/pages/settings/my-social-media.vue | 25 ++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index 6bccd9949..1552c5512 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -18,19 +18,19 @@
-
@@ -51,7 +51,18 @@ export default { computed: { ...mapGetters({ currentUser: 'auth/user' - }) + }), + socialMediaLinks() { + const { socialMedia = [] } = this.currentUser + return socialMedia.map(url => { + const matches = url.match( + /^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:\/\n?]+)/g + ) + const [domain] = matches || [] + const favicon = domain ? `${domain}/favicon.ico` : null + return { url, favicon } + }) + } }, methods: { handleAddSocialMedia() { From 209b63715fa65c8f08655874e1da514928a74421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 27 Mar 2019 14:28:20 +0100 Subject: [PATCH 08/32] Show social media links as list --- webapp/pages/settings/my-social-media.vue | 38 ++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index 1552c5512..201c0759f 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -1,5 +1,25 @@