From f6af85c93c146a2499c1ab3c1124cb1993301354 Mon Sep 17 00:00:00 2001 From: senderfm Date: Fri, 7 Jun 2019 20:33:16 +0200 Subject: [PATCH 01/89] Language Packs Spanish and French current status --- webapp/locales/de.json | 14 ++-- webapp/locales/en.json | 10 +-- webapp/locales/es.json | 185 ++++++++++++++++++++++++++++++++++++++--- webapp/locales/fr.json | 158 +++++++++++++++++++++++++++++------ 4 files changed, 318 insertions(+), 49 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index a790e6461..8a5fe42c9 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -19,9 +19,9 @@ "profile": { "name": "Mein Profil", "memberSince": "Mitglied seit", - "follow": "Folgen", - "followers": "Folgen", - "following": "Folgt", + "follow": "abonnieren", + "followers": "Abonnenten", + "following": "abonniert", "shouted": "Empfohlen", "commented": "Kommentiert", "userAnonym": "Anonymus", @@ -91,9 +91,9 @@ } }, "admin": { - "name": "Systemverwaltung", + "name": "Admin", "dashboard": { - "name": "Startzentrale", + "name": "Dashboard", "users": "Benutzer", "posts": "Beiträge", "comments": "Kommentare", @@ -102,7 +102,7 @@ "projects": "Projekte", "invites": "Einladungen", "follows": "Folgen", - "shouts": "Shouts" + "shouts": "Empfehlungen" }, "organizations": { "name": "Organisationen" @@ -261,7 +261,7 @@ }, "followButton": { "follow": "Folgen", - "following": "Folge Ich" + "following": "Folge ich" }, "shoutButton": { "shouted": "empfohlen" diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 289928f92..fe17f7b48 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -14,7 +14,7 @@ "hello": "Hello" }, "editor": { - "placeholder": "Leave your inspirational thoughts..." + "placeholder": "Write something inspiring..." }, "profile": { "name": "My Profile", @@ -22,14 +22,14 @@ "follow": "Follow", "followers": "Followers", "following": "Following", - "shouted": "Shouted", + "shouted": "Recommended", "commented": "Commented", "userAnonym": "Anonymous", "socialMedia": "Where else can I find" }, "notifications": { "menu": { - "mentioned": "has mentioned you in a post" + "mentioned": "mentioned you in a post" } }, "search": { @@ -43,7 +43,7 @@ "name": "Your data", "labelName": "Your Name", "namePlaceholder": "Femanon Funny", - "labelCity": "Your City or Region", + "labelCity": "Su ciudad o región", "labelBio": "About You", "success": "Your data was successfully updated!" }, @@ -102,7 +102,7 @@ "projects": "Projects", "invites": "Invites", "follows": "Follows", - "shouts": "Shouts" + "shouts": "Recommended" }, "organizations": { "name": "Organizations" diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 5beab2eef..bdff69850 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -1,4 +1,7 @@ { + "filter-menu": { + "title": "Su burbuja de filtro" + }, "login": { "copy": "Si ya tiene una cuenta de Human Connection, inicie sesión aquí.", "login": "Iniciar sesión", @@ -6,39 +9,85 @@ "email": "Tu correo electrónico", "password": "Tu contraseña", "moreInfo": "¿Qué es Human Connection?", + "moreInfoURL": "https://human-connection.org/es/", + "moreInfoHint": "a la página de presentación", "hello": "Hola" }, + "editor": { + "placeholder": "Write something inspiring..." + }, "profile": { - "name": "Mi perfil", + "name": "Mi Perfil", "memberSince": "Miembro desde", "follow": "Seguir", "followers": "Seguidores", "following": "Siguiendo", - "shouted": "Gritar", - "commented": "Comentado" + "shouted": "Recomendado", + "commented": "Comentado", + "userAnonym": "Anónimo", + "socialMedia": "¿Dónde más puedo encontrar" + }, + "notifications": { + "menu": { + "mentioned": "te ha mencionado en un post" + } + }, + "search": { + "placeholder": "Buscar", + "hint": "¿Qué estás buscando?", + "failed": "no encontró nada" }, "settings": { - "name": "Configuración", + "name": "Ajustes", "data": { - "name": "Sus datos" + "name": "Sus datos", + "labelName": "Su nombre", + "namePlaceholder": "Femanon Funny", + "labelCity": "Your City or Region", + "labelBio": "Acerca de usted", + "success": "Sus datos han sido actualizados con éxito!" }, "security": { - "name": "Seguridad" + "name": "Seguridad", + "change-password": { + "button": "Cambiar contraseña", + "success": "Contraseña cambiada con éxito!", + "label-old-password": "Su contraseña antigua", + "label-new-password": "Su nueva contraseña", + "label-new-password-confirm": "Confirm new password", + "message-old-password-required": "Ingrese su contraseña anterior", + "message-new-password-required": "Introduzca una nueva contraseña", + "message-new-password-confirm-required": "Confirme su nueva contraseña", + "message-new-password-missmatch": "Vuelva a escribir la misma contraseña", + "passwordSecurity": "Seguridad de la contraseña", + "passwordStrength0": "Contraseña muy insegura", + "passwordStrength1": "Contraseña insegura", + "passwordStrength2": "Contraseña mediocre", + "passwordStrength3": "Contraseña segura", + "passwordStrength4": "Contraseña muy sólida" + } }, "invites": { - "name": "Invita" + "name": "invitaciones" }, "download": { "name": "Descargar datos" }, "delete": { - "name": "Borrar cuenta" + "name": "Eliminar cuenta" }, "organizations": { "name": "Mis organizaciones" }, "languages": { - "name": "Idiomas" + "name": "idiomas" + }, + "social-media": { + "name": "Medios de comunicación social", + "placeholder": "Agregar una URL de Social-Media", + "submit": "Añadir enlace", + "successAdd": "Social-Media agregó. Perfil actualizado!", + "successDelete": "Social-Media borrado. Perfil actualizado!" } }, "admin": { @@ -53,7 +102,7 @@ "projects": "Proyectos", "invites": "Invita", "follows": "Sigue", - "shouts": "Gritos" + "shouts": "Recomendado" }, "organizations": { "name": "Organizaciones" @@ -105,6 +154,11 @@ } }, "common": { + "your": { + "post": "Your Post ::: Your Posts", + "comment": "Your Comment ::: Your Comments", + "shout": "Your Shout ::: Your Shouts" + }, "post": "Mensaje ::: Mensajes", "comment": "Comentario ::: Comentarios", "letsTalk": "Hablemos", @@ -119,6 +173,113 @@ "tag": "Etiqueta ::: Etiquetas", "name": "Nombre", "loadMore": "cargar más", - "loading": "cargando" + "loading": "cargando", + "reportContent": "Report" + }, + "actions": { + "loading": "cargamento", + "loadMore": "cargar más", + "create": "Crear", + "save": "Guardar", + "edit": "Edite", + "delete": "Delete", + "cancel": "Cancelar" + }, + "moderation": { + "name": "Moderación", + "reports": { + "empty": "Felicitaciones, nada que moderar.", + "name": "Informes", + "submitter": "comunicado por", + "disabledBy": "desactivado por" + } + }, + "disable": { + "submit": "Desactivar", + "cancel": "Cancelar", + "success": "Discapacitado con éxito", + "user": { + "title": "Desactivar usuario", + "type": "Usuario", + "message": "¿Realmente quieres deshabilitar el usuario \"{name}\"?" + }, + "contribution": { + "title": "Deshabilitar contribución", + "type": "Contribución", + "message": "¿Realmente quieres deshabilitar la contribución \"{name}\"?" + }, + "comment": { + "title": "Desactivar comentario", + "type": "Comentario", + "message": "¿Realmente quieres deshabilitar el comentario de \"{name}\"?" + } + }, + "delete": { + "submit": "Borrar", + "cancel": "Cancelar", + "contribution": { + "title": "Borrar contribución", + "type": "Contribución", + "message": "¿Realmente desea eliminar la Contribución \"{name}\" ?", + "success": "Contribución eliminada con éxito!" + }, + "comment": { + "title": "Eliminar comentario", + "type": "Comentario", + "message": "¿Realmente quieres borrar el comentario de \"{name}\" ?", + "success": "Comentario eliminado con éxito!" + } + }, + "report": { + "submit": "Informe", + "cancel": "Cancelar", + "success": "Gracias por informarnos!", + "user": { + "title": "Usuario de informe", + "type": "Usuario", + "message": "¿Realmente quieres reportar al usuario \"{name}\"?" + }, + "contribution": { + "title": "Informe Contribución", + "type": "Contribución", + "message": "¿Realmente quieres informar al usuario de la contribución \"{name}\"?" + }, + "comment": { + "title": "Informe Comentario", + "type": "Comentario", + "message": "¿Realmente quieres reportar el comentario de \"{name}\"?" + } + }, + "followButton": { + "follow": "Folgen", + "following": "Folge Ich" + }, + "shoutButton": { + "shouted": "empfohlen" + }, + "release": { + "submit": "Liberación", + "cancel": "Cancelar", + "success": "Liberar con éxito!", + "user": { + "title": "Usuario de la versión ", + "type": "Usuario", + "message": "¿Realmente quieres liberar al usuario \"{name}\"?" + }, + "contribution": { + "title": "Contribución de la versión ", + "type": "Contribución", + "message": "¿Realmente quieres liberar la contribución \"{name}\"?" + }, + "comment": { + "title": "Comentario de la versión", + "type": "Comentario", + "message": "¿Realmente quieres liberar el comentario de \"{name}\"?" + } + }, + "user": { + "avatar": { + "submitted": "Carga con éxito" + } } -} \ No newline at end of file +} diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index bb0caaaa3..26d479538 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -1,4 +1,7 @@ { + "filter-menu": { + "title": "Votre bulle de filtre" + }, "login": { "copy": "Si vous avez déjà un compte human-connection, connectez-vous ici.", "login": "Connexion", @@ -6,25 +9,66 @@ "email": "Votre courriel", "password": "Votre mot de passe", "moreInfo": "Qu'est-ce que Human Connection?", + "moreInfoURL": "https://human-connection.org/fr/", + "moreInfoHint": "à la page de présentation", "hello": "Bonjour" }, + "editor": { + "placeholder": "Écrivez quelque chose d'inspirant..." + }, "profile": { "name": "Mon profil", "memberSince": "Membre depuis", "follow": "Suivre", "followers": "Suiveurs", - "following": "Suivant" + "following": "Suivant", + "shouted": "Recommandé", + "commented": "Comentado", + "userAnonym": "Anónimo", + "socialMedia": "Où d'autre puis-je trouver" + }, + "notifications": { + "menu": { + "mentioned": "a parlé de vous dans un article" + } + }, + "search": { + "placeholder": "Rechercher", + "hint": "Qu'est-ce que vous cherchez ?", + "failed": "Rien trouvé" }, "settings": { - "name": "Paramètres", + "name": "Configurations", "data": { - "name": "Vos données" + "name": "Vos données", + "labelName": "Votre nom", + "namePlaceholder": "Fémanon Funny", + "labelCity": "Votre ville ou région", + "labelBio": "À propos de vous", + "success": "Vos données ont été mises à jour avec succès !" }, "security": { - "name": "Sécurité" + "name": "Sécurité", + "change-password": { + "button": "Modifier le mot de passe", + "success": "Mot de passe modifié avec succès !", + "label-old-password": "Votre ancien mot de passe", + "label-new-password": "Votre nouveau mot de passe", + "label-new-password-confirm": "Confirmez votre nouveau mot de passe", + "message-old-password-required": "Entrez votre ancien mot de passe", + "message-new-password-required": "Entrez un nouveau mot de passe", + "message-new-password-confirm-required": "Confirmez votre nouveau mot de passe", + "message-new-password-missmatch": "Tapez à nouveau le même mot de passe", + "passwordSecurity": "Sécurité par mot de passe", + "passwordStrength0": "Mot de passe très peu sûr", + "passwordStrength1": "Mot de passe non sécurisé", + "passwordStrength2": "Mot de passe médiocre", + "passwordStrength3": "Mot de passe fort", + "passwordStrength4": "Mot de passe très fort" + } }, "invites": { - "name": "Invite" + "name": "invitations" }, "download": { "name": "Télécharger les données" @@ -36,7 +80,14 @@ "name": "Mes organisations" }, "languages": { - "name": "Langues" + "name": "langues" + }, + "social-media": { + "name": "Médias sociaux", + "placeholder": "Ajouter une URL pour les médias sociaux", + "submit": "Ajouter un lien", + "successAdd": "Les médias sociaux ont été ajoutés. Profil mis à jour !", + "successDelete": "Médias sociaux supprimé. Profil mis à jour !" } }, "admin": { @@ -51,7 +102,7 @@ "projects": "Projets", "invites": "Invite", "follows": "Suit", - "shouts": "Cris" + "shouts": "Recommandé" }, "organizations": { "name": "Organisations" @@ -95,13 +146,18 @@ } }, "common": { + "your": { + "post": "Votre message ::: Votre messages", + "comment": "Votre Commentaire ::: Votre Commentaires ", + "shout": "Votre Recommandation ::: Votre Recommandations" + }, "post": "Message ::: Messages", "comment": "Commentaire ::: Commentaires", "letsTalk": "Parlons-en", "versus": "Versus", "moreInfo": "Plus d'infos", "takeAction": "Passer à l'action", - "shout": "Shout ::: Shouts", + "shout": "Recommandation ::: Recommandations", "user": "Utilisateur ::: Utilisateurs", "category": "Catégorie ::: Catégories", "organization": "Organisation ::: Organisations", @@ -112,33 +168,64 @@ "loading": "chargement", "reportContent": "Signaler" }, + "actions": { + "loading": "chargement", + "loadMore": "charger plus", + "create": "Créer", + "save": "sauvegarde", + "edit": "Modifier", + "delete": "Supprimer", + "cancel": "Annuler" + }, "moderation": { + "name": "Modération", "reports": { "empty": "Félicitations, rien à modérer.", - "name": "Signalisations", - "reporter": "signalé par" + "name": "Rapports", + "submitter": "signalé par", + "disabledBy": "ddésactivé par" } }, "disable": { + "submit": "Désactiver", + "cancel": "Annuler", + "success": "Désactivé avec succès", "user": { "title": "Désactiver l'utilisateur", "type": "Utilisateur", - "message": "Souhaitez-vous vraiment désactiver l'utilisateur \" {name} \"?" + "message": "Voulez-vous vraiment désactiver l'utilisateur \"{name}\"?" }, "contribution": { - "title": "Désactiver l'apport", - "type": "apport", - "message": "Souhaitez-vous vraiment signaler l'entrée\" {name} \"?" + "title": "Cotisation d'invalidité", + "type": "Contribution", + "message": "Voulez-vous vraiment désactiver la contribution \" {name} \"?" }, "comment": { - "title": "Désactiver le commentaire", + "title": "Désactiver commentaire", "type": "Commentaire", - "message": "Souhaitez-vous vraiment désactiver le commentaire de \"{name}\" ?" + "message": "Voulez-vous vraiment désactiver le commentaire de \"{name}\" ?" + } + }, + "delete": { + "submit": "Supprimer", + "cancel": "Annuler", + "contribution": { + "title": "Supprimer la contribution", + "type": "Contribution", + "message": "Voulez-vous vraiment supprimer la contribution \"{name}\" löschen möchtest?", + "success": "Contribution supprimée avec succès !" + }, + "comment": { + "title": "Supprimer un commentaire", + "type": "Commentaire", + "message": "Voulez-vous vraiment supprimer le commentaire de \"{name}\" löschen möchtest?", + "success": "Commentaire supprimé avec succès !" } }, "report": { - "submit": "Envoyer le rapport", + "submit": "Rapport", "cancel": "Annuler", + "success": "Merci de nous avoir fait part de vos commentaires!", "user": { "title": "Signaler l'utilisateur", "type": "Utilisateur", @@ -155,15 +242,36 @@ "message": "Souhaitez-vous vraiment signaler l'utilisateur \" {name} \"?" } }, - "actions": { - "cancel": "Annuler" + "followButton": { + "follow": "découler", + "following": "Je suis les" }, - "contribution": { - "edit": "Rédiger l'apport", - "delete": "Supprimer l'entrée" + "shoutButton": { + "shouted": "recommandé" }, - "comment": { - "edit": "Rédiger un commentaire", - "delete": "Supprimer le commentaire" + "release": { + "submit": "Relâchez", + "cancel": "Annuler", + "success": "Relâchez avec succès!", + "user": { + "title": "Validation par l'utilisateur", + "type": "Utilisateur", + "message": "Voulez-vous vraiment libérer l'utilisateur \"{name}\"?" + }, + "contribution": { + "title": "Versement de la contribution", + "type": "Contribution", + "message": "Voulez-vous vraiment débloquer la contribution \"{name}\"?" + }, + "comment": { + "title": "Publication des commentaires", + "type": "Commentaire", + "message": "Voulez-vous vraiment publier le commentaire de \"{name}\"?" + } + }, + "user": { + "avatar": { + "submitted": "Téléchargement réussi" + } } } From dba719ea1c8f0ef93fbca9a3c8dd570953febdd2 Mon Sep 17 00:00:00 2001 From: senderfm Date: Wed, 12 Jun 2019 12:54:33 +0200 Subject: [PATCH 02/89] english translation changes undone. --- webapp/locales/en.json | 2 +- webapp/locales/es.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/locales/en.json b/webapp/locales/en.json index fe17f7b48..54b9e598e 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -14,7 +14,7 @@ "hello": "Hello" }, "editor": { - "placeholder": "Write something inspiring..." + "placeholder": "Leave your inspirational thoughts..." }, "profile": { "name": "My Profile", diff --git a/webapp/locales/es.json b/webapp/locales/es.json index bdff69850..af9c21c3f 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -38,7 +38,7 @@ "failed": "no encontró nada" }, "settings": { - "name": "Ajustes", + "name": "Configuración", "data": { "name": "Sus datos", "labelName": "Su nombre", From d024d7df91dbac80237fa74e62bba1a8469ed463 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 14 Jun 2019 14:03:21 +0200 Subject: [PATCH 03/89] fixed importing of urls - remove url prefix --- .../maintenance-worker/migration/neo4j/badges.cql | 2 +- .../maintenance-worker/migration/neo4j/contributions.cql | 6 +++--- .../maintenance-worker/migration/neo4j/users.cql | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql index 62cd4a2cc..2d1548d4f 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql @@ -45,7 +45,7 @@ MERGE(b:Badge {id: badge._id["$oid"]}) ON CREATE SET b.key = badge.key, b.type = badge.type, -b.icon = badge.image.path, +b.icon = substring(badge.image.path, 38), b.status = badge.status, b.createdAt = badge.createdAt.`$date`, b.updatedAt = badge.updatedAt.`$date` diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql index 98d8f24e9..70f09e035 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql @@ -28,7 +28,7 @@ [?] unique: true, // Unique value is not enforced in Nitro? [-] index: true }, -[ ] type: { +[ ] type: { // db.getCollection('contributions').distinct('type') -> 'DELETED', 'cando', 'post' [ ] type: String, [ ] required: true, [-] index: true @@ -50,7 +50,7 @@ [?] required: true // Not required in Nitro }, [ ] hasMore: { type: Boolean }, -[?] teaserImg: { type: String }, // Path is incorrect in Nitro +[X] teaserImg: { type: String }, [ ] language: { [ ] type: String, [ ] required: true, @@ -131,7 +131,7 @@ MERGE (p:Post {id: post._id["$oid"]}) ON CREATE SET p.title = post.title, p.slug = post.slug, -p.image = post.teaserImg, +p.image = substring(post.teaserImg, 38), p.content = post.content, p.contentExcerpt = post.contentExcerpt, p.visibility = toLower(post.visibility), diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql index aec5499fc..96251a9ce 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql @@ -49,8 +49,8 @@ } }, [ ] timezone: { type: String }, -[?] avatar: { type: String }, // Path is incorrect in Nitro -[?] coverImg: { type: String }, // Path is incorrect in Nitro, was not modeled in latest Nitro - do we want this? +[X] avatar: { type: String }, +[X] coverImg: { type: String }, [ ] doiToken: { type: String }, [ ] confirmedAt: { type: Date }, [?] badgeIds: [], // Verify this is working properly @@ -102,8 +102,8 @@ u.name = user.name, u.slug = user.slug, u.email = user.email, u.password = user.password, -u.avatar = user.avatar, -u.coverImg = user.coverImg, +u.avatar = substring(user.avatar, 38), +u.coverImg = substring(user.coverImg, 38), u.wasInvited = user.wasInvited, u.wasSeeded = user.wasSeeded, u.role = toLower(user.role), From 89d630b1eb82914996e140e6c481b292611f800a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 14 Jun 2019 14:03:37 +0200 Subject: [PATCH 04/89] removed fixImageUrlsMiddleware --- .../src/middleware/fixImageUrlsMiddleware.js | 48 ------------------- .../middleware/fixImageUrlsMiddleware.spec.js | 36 -------------- 2 files changed, 84 deletions(-) delete mode 100644 backend/src/middleware/fixImageUrlsMiddleware.js delete mode 100644 backend/src/middleware/fixImageUrlsMiddleware.spec.js diff --git a/backend/src/middleware/fixImageUrlsMiddleware.js b/backend/src/middleware/fixImageUrlsMiddleware.js deleted file mode 100644 index c930915bf..000000000 --- a/backend/src/middleware/fixImageUrlsMiddleware.js +++ /dev/null @@ -1,48 +0,0 @@ -const legacyUrls = [ - 'https://api-alpha.human-connection.org', - 'https://staging-api.human-connection.org', - 'http://localhost:3000', -] - -export const fixUrl = url => { - legacyUrls.forEach(legacyUrl => { - url = url.replace(legacyUrl, '/api') - }) - return url -} - -const checkUrl = thing => { - return ( - thing && - typeof thing === 'string' && - legacyUrls.find(legacyUrl => { - return thing.indexOf(legacyUrl) === 0 - }) - ) -} - -export const fixImageURLs = (result, recursive) => { - if (checkUrl(result)) { - result = fixUrl(result) - } else if (result && Array.isArray(result)) { - result.forEach((res, index) => { - result[index] = fixImageURLs(result[index], true) - }) - } else if (result && typeof result === 'object') { - Object.keys(result).forEach(key => { - result[key] = fixImageURLs(result[key], true) - }) - } - return result -} - -export default { - Mutation: async (resolve, root, args, context, info) => { - const result = await resolve(root, args, context, info) - return fixImageURLs(result) - }, - Query: async (resolve, root, args, context, info) => { - let result = await resolve(root, args, context, info) - return fixImageURLs(result) - }, -} diff --git a/backend/src/middleware/fixImageUrlsMiddleware.spec.js b/backend/src/middleware/fixImageUrlsMiddleware.spec.js deleted file mode 100644 index b2d808dd9..000000000 --- a/backend/src/middleware/fixImageUrlsMiddleware.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -import { fixImageURLs } from './fixImageUrlsMiddleware' - -describe('fixImageURLs', () => { - describe('image url of legacy alpha', () => { - it('removes domain', () => { - const url = - 'https://api-alpha.human-connection.org/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png' - expect(fixImageURLs(url)).toEqual( - '/api/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png', - ) - }) - }) - - describe('image url of legacy staging', () => { - it('removes domain', () => { - const url = - 'https://staging-api.human-connection.org/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg' - expect(fixImageURLs(url)).toEqual( - '/api/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg', - ) - }) - }) - - describe('object', () => { - it('returns untouched', () => { - const object = { some: 'thing' } - expect(fixImageURLs(object)).toEqual(object) - }) - }) - - describe('some string', () => { - it('returns untouched', () => {}) - const string = "Yeah I'm a String" - expect(fixImageURLs(string)).toEqual(string) - }) -}) From 794bb08f141eeed293c9a59821dc506d87eba841 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 14 Jun 2019 14:04:15 +0200 Subject: [PATCH 05/89] removed reference for fixImageUrls middleware --- backend/src/middleware/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js index 75314abc0..aae2dcef3 100644 --- a/backend/src/middleware/index.js +++ b/backend/src/middleware/index.js @@ -3,7 +3,6 @@ import activityPub from './activityPubMiddleware' import password from './passwordMiddleware' import softDelete from './softDeleteMiddleware' import sluggify from './sluggifyMiddleware' -import fixImageUrls from './fixImageUrlsMiddleware' import excerpt from './excerptMiddleware' import dateTime from './dateTimeMiddleware' import xss from './xssMiddleware' @@ -25,7 +24,6 @@ export default schema => { excerpt: excerpt, notifications: notifications, xss: xss, - fixImageUrls: fixImageUrls, softDelete: softDelete, user: user, includedFields: includedFields, From d558a4de370a36acbefba5e8c565a958f1780768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2019 11:09:09 +0200 Subject: [PATCH 06/89] Add mailserver for development --- docker-compose.override.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index a71418229..d7ee86bd6 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,6 +1,10 @@ version: "3.4" services: + mailserver: + image: djfarrelly/maildev + ports: + - 1080:80 webapp: build: context: webapp From 64c5245f5a4f70bf9b63fe21f26a534d39e8db30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2019 11:25:01 +0200 Subject: [PATCH 07/89] Update date-fns manually to get passed this bug: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` ERROR Failed to compile with 16 errors This dependency was not found: * date-fns/addSeconds in ./plugins/vue-filters.js To install it, you can run: npm install --save date-fns/addSeconds These relative modules were not found: * ../../../../_lib/isSameUTCWeek/index.js in ./node_modules/date-fns/esm/locale/be/_lib/formatRelative/index.js, ./node_modules/date-fns/esm/locale/lv/_lib/formatRelative/index.js and 4 others * ../_lib/format/formatters/index.js in ./node_modules/date-fns/format/index.js * ../_lib/format/longFormatters/index.js in ./node_modules/date-fns/format/index.js * ../_lib/getTimezoneOffsetInMilliseconds/index.js in ./node_modules/date-fns/format/index.js, ./node_modules/date-fns/formatRelative/index.js * ../_lib/protectedTokens/index.js in ./node_modules/date-fns/format/index.js * ../_lib/toInteger/index.js in ./node_modules/date-fns/format/index.js, ./node_modules/date-fns/subMilliseconds/index.js * ../addMilliseconds/index.js in ./node_modules/date-fns/subMilliseconds/index.js * ../differenceInCalendarDays/index.js in ./node_modules/date-fns/formatRelative/index.js ℹ Waiting for file changes ``` --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 1abaf479b..5e42d0332 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -59,7 +59,7 @@ "apollo-client": "~2.6.2", "cookie-universal-nuxt": "~2.0.16", "cross-env": "~5.2.0", - "date-fns": "2.0.0-alpha.33", + "date-fns": "2.0.0-alpha.34", "express": "~4.17.1", "graphql": "~14.3.1", "jsonwebtoken": "~8.5.1", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index e5c1daf8a..de0d07924 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -3754,10 +3754,10 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@2.0.0-alpha.33: - version "2.0.0-alpha.33" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.33.tgz#c2f73c3cc50ac301c9217eb93603c9bc40e891bf" - integrity sha512-tqUVEk3oxnJuNIvwAMKHAMo4uFRG0zXvjxZQll+BonoPt+m4NMcUgO14NDxbHuy7uYcrVErd2GdSsw02EDZQ7w== +date-fns@2.0.0-alpha.34: + version "2.0.0-alpha.34" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.34.tgz#5d3ae7ca0d08915ccfc87a20545250af4e9c3cae" + integrity sha512-yjSYUHASHvzOZl++cEms+Tw7oQOFA+7Z6/lL7L3lRO9j6pMfT48N6oEyvCGo/MVlH08XWmydgf8X9Y1eedf9sQ== date-now@^0.1.4: version "0.1.4" From f9d25828d520fd838e4cefa367de948908641f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2019 11:47:24 +0200 Subject: [PATCH 08/89] Add reset password page --- webapp/locales/de.json | 4 ++++ webapp/locales/en.json | 4 ++++ webapp/nuxt.config.js | 11 ++++++++++- webapp/pages/login.vue | 5 +++++ webapp/pages/password-reset.vue | 19 +++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 webapp/pages/password-reset.vue diff --git a/webapp/locales/de.json b/webapp/locales/de.json index efe05a472..d783f9e37 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -8,11 +8,15 @@ "logout": "Ausloggen", "email": "Deine E-Mail", "password": "Dein Passwort", + "forgotPassword": "Passwort vergessen?", "moreInfo": "Was ist Human Connection?", "moreInfoURL": "https://human-connection.org", "moreInfoHint": "zur Präsentationsseite", "hello": "Hallo" }, + "password-reset": { + "title": "Passwort zurücksetzen" + }, "editor": { "placeholder": "Schreib etwas Inspirierendes..." }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 4fdcadedb..3a5405eec 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -8,11 +8,15 @@ "logout": "Logout", "email": "Your Email", "password": "Your Password", + "forgotPassword": "Forgot Password?", "moreInfo": "What is Human Connection?", "moreInfoURL": "https://human-connection.org/en/", "moreInfoHint": "to the presentation page", "hello": "Hello" }, + "password-reset": { + "title": "Reset your password" + }, "editor": { "placeholder": "Leave your inspirational thoughts..." }, diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 49f2f5d0a..8af3dbb16 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -25,7 +25,16 @@ module.exports = { env: { // pages which do NOT require a login - publicPages: ['login', 'logout', 'register', 'signup', 'reset', 'reset-token', 'pages-slug'], + publicPages: [ + 'login', + 'logout', + 'password-reset', + 'register', + 'signup', + 'reset', + 'reset-token', + 'pages-slug', + ], // pages to keep alive keepAlivePages: ['index'], // active locales diff --git a/webapp/pages/login.vue b/webapp/pages/login.vue index a96fbdbf1..94c974b29 100644 --- a/webapp/pages/login.vue +++ b/webapp/pages/login.vue @@ -45,6 +45,11 @@ name="password" type="password" /> + + + {{ $t('login.forgotPassword') }} + + + + + + + + {{ $t('password-reset.title') }} + + + + + + + + From e44ed7d281d5077786a56590dae11306681a9810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2019 15:42:17 +0200 Subject: [PATCH 09/89] Start writing a resolver for requestPasswordReset --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/resolvers/passwordReset.js | 10 ++++ .../schema/resolvers/passwordReset.spec.js | 46 +++++++++++++++++++ .../src/schema/resolvers/user_management.js | 2 +- backend/src/schema/types/schema.gql | 2 + 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 backend/src/schema/resolvers/passwordReset.js create mode 100644 backend/src/schema/resolvers/passwordReset.spec.js diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 10b777748..ad2787579 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -147,6 +147,7 @@ const permissions = shield( CreateComment: isAuthenticated, DeleteComment: isAuthor, DeleteUser: isDeletingOwnAccount, + requestPasswordReset: allow, }, User: { email: isMyOwn, diff --git a/backend/src/schema/resolvers/passwordReset.js b/backend/src/schema/resolvers/passwordReset.js new file mode 100644 index 000000000..83a1080d0 --- /dev/null +++ b/backend/src/schema/resolvers/passwordReset.js @@ -0,0 +1,10 @@ +export default { + Mutation: { + requestPasswordReset: async (_, { email }, { driver }) => { + throw Error('Not Implemented') + }, + resetPassword: async (_, { email, token, newPassword }, { driver }) => { + throw Error('Not Implemented') + } + } +} diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js new file mode 100644 index 000000000..d07ca4b09 --- /dev/null +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -0,0 +1,46 @@ +import { GraphQLClient } from 'graphql-request' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' +import { getDriver } from '../../bootstrap/neo4j' + +const factory = Factory() +let client +const driver = getDriver() + +const getAllPasswordResets = async () => { + const session = driver.session() + let transactionRes = await session.run('MATCH (r:PasswordReset) RETURN r') + const resets = transactionRes.records.map(record => record.get('r')) + session.close() + return resets +} + +describe('passwordReset', () => { + beforeEach(async () => { + client = new GraphQLClient(host) + await factory.create('User', { + email: 'user@example.org', + role: 'user', + password: '1234', + }) + }) + + afterEach(async () => { + await factory.cleanDatabase() + }) + + describe('requestPasswordReset', () => { + const variables = { email: 'user@example.org' } + const mutation = `mutation($email: String!) { requestPasswordReset(email: $email) }` + + it('resolves', async () => { + await expect(client.request(mutation, variables)).resolves.toEqual(true) + }) + + it('creates node with label `PasswordReset`', async () => { + await client.request(mutation, variables) + const resets = await getAllPasswordResets() + expect(resets).toHaveLength(1) + }) + }) +}) diff --git a/backend/src/schema/resolvers/user_management.js b/backend/src/schema/resolvers/user_management.js index eb07a07b3..e33314f7e 100644 --- a/backend/src/schema/resolvers/user_management.js +++ b/backend/src/schema/resolvers/user_management.js @@ -59,7 +59,7 @@ export default { changePassword: async (_, { oldPassword, newPassword }, { driver, user }) => { const session = driver.session() let result = await session.run( - `MATCH (user:User {email: $userEmail}) + `MATCH (user:User {email: $userEmail}) RETURN user {.id, .email, .password}`, { userEmail: user.email, diff --git a/backend/src/schema/types/schema.gql b/backend/src/schema/types/schema.gql index 2a8be9e09..358797631 100644 --- a/backend/src/schema/types/schema.gql +++ b/backend/src/schema/types/schema.gql @@ -25,6 +25,8 @@ type Mutation { login(email: String!, password: String!): String! signup(email: String!, password: String!): Boolean! changePassword(oldPassword: String!, newPassword: String!): String! + requestPasswordReset(email: String!): Boolean! + resetPassword(email: String!, resetToken: String!, newPassword: String!): String! report(id: ID!, description: String): Report disable(id: ID!): ID enable(id: ID!): ID From c7ee0c8121c09a0ee2f25786a325266b968c6e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2019 16:22:28 +0200 Subject: [PATCH 10/89] Implement tests for requestPasswordReset --- backend/src/schema/resolvers/passwordReset.js | 13 ++++++++++++- .../schema/resolvers/passwordReset.spec.js | 19 ++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/backend/src/schema/resolvers/passwordReset.js b/backend/src/schema/resolvers/passwordReset.js index 83a1080d0..f3e1d32d2 100644 --- a/backend/src/schema/resolvers/passwordReset.js +++ b/backend/src/schema/resolvers/passwordReset.js @@ -1,7 +1,18 @@ export default { Mutation: { requestPasswordReset: async (_, { email }, { driver }) => { - throw Error('Not Implemented') + const session = driver.session() + let validUntil = new Date() + validUntil += 3*60*1000 + const cypher = ` + MATCH(u:User) WHERE u.email = $email + CREATE(pr:PasswordReset {id: apoc.create.uuid(), validUntil: $validUntil, redeemedAt: NULL}) + MERGE (u)-[:REQUESTED]->(pr) + RETURN u,pr + ` + await session.run(cypher, { email, validUntil }) + session.close() + return true }, resetPassword: async (_, { email, token, newPassword }, { driver }) => { throw Error('Not Implemented') diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index d07ca4b09..3b0d39864 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -34,7 +34,7 @@ describe('passwordReset', () => { const mutation = `mutation($email: String!) { requestPasswordReset(email: $email) }` it('resolves', async () => { - await expect(client.request(mutation, variables)).resolves.toEqual(true) + await expect(client.request(mutation, variables)).resolves.toEqual({"requestPasswordReset": true}) }) it('creates node with label `PasswordReset`', async () => { @@ -42,5 +42,22 @@ describe('passwordReset', () => { const resets = await getAllPasswordResets() expect(resets).toHaveLength(1) }) + + it('creates an id used as a reset token', async () => { + await client.request(mutation, variables) + const [reset] = await getAllPasswordResets() + const { id: token } = reset.properties + expect(token).toMatch(/^........-....-....-....-............$/) + }) + + it('created PasswordReset is valid for less than 4 minutes', async () => { + await client.request(mutation, variables) + const [reset] = await getAllPasswordResets() + let { validUntil } = reset.properties + validUntil = Date.parse(validUntil) + const now = (new Date()).getTime() + expect(validUntil).toBeGreaterThan(now - 60*1000) + expect(validUntil).toBeLessThan(now + 4*60*1000) + }) }) }) From e583486143f208690abb3084e44cce76c8630b27 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Sat, 15 Jun 2019 15:23:51 -0300 Subject: [PATCH 11/89] Set up editor placeholder to use Vuex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - to make reactive - safer than using global event buses ($root.$emit) Co-authored-by: Wolfgang Huß Co-authored-by: Mike Aono --- webapp/components/Editor/index.vue | 26 +++++++++++++------------- webapp/components/Editor/spec.js | 20 ++++++++++++++++++++ webapp/components/LocaleSwitch.vue | 4 +++- webapp/store/editor.js | 17 +++++++++++++++++ webapp/store/editor.spec.js | 20 ++++++++++++++++++++ 5 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 webapp/store/editor.js create mode 100644 webapp/store/editor.spec.js diff --git a/webapp/components/Editor/index.vue b/webapp/components/Editor/index.vue index 0690b15bd..84649f436 100644 --- a/webapp/components/Editor/index.vue +++ b/webapp/components/Editor/index.vue @@ -12,9 +12,7 @@ @{{ user.slug }} -
- No users found -
+
No users found
@@ -175,6 +173,7 @@ import { History, } from 'tiptap-extensions' import Mention from './nodes/Mention.js' +import { mapGetters } from 'vuex' let throttleInputEvent @@ -212,7 +211,7 @@ export default { new ListItem(), new Placeholder({ emptyNodeClass: 'is-empty', - emptyNodeText: this.$t('editor.placeholder'), + emptyNodeText: this.placeholder || this.$t('editor.placeholder'), }), new History(), new Mention({ @@ -297,6 +296,7 @@ export default { } }, computed: { + ...mapGetters({ placeholder: 'editor/placeholder' }), hasResults() { return this.filteredUsers.length }, @@ -316,20 +316,20 @@ export default { this.editor.setContent(content) }, }, - }, - mounted() { - this.$root.$on('changeLanguage', () => { - this.changePlaceHolderText() - }) + placeholder: { + immediate: true, + handler: function(val) { + if (!val) { + return + } + this.editor.extensions.options.placeholder.emptyNodeText = val + }, + }, }, beforeDestroy() { - this.$root.$off('changeLanguage') this.editor.destroy() }, methods: { - changePlaceHolderText() { - this.editor.extensions.options.placeholder.emptyNodeText = this.$t('editor.placeholder') - }, // navigate to the previous item // if it's the first item, navigate to the last one upHandler() { diff --git a/webapp/components/Editor/spec.js b/webapp/components/Editor/spec.js index 249192b57..b982d941d 100644 --- a/webapp/components/Editor/spec.js +++ b/webapp/components/Editor/spec.js @@ -1,31 +1,43 @@ import { mount, createLocalVue } from '@vue/test-utils' import Editor from './' +import Vuex from 'vuex' import Styleguide from '@human-connection/styleguide' const localVue = createLocalVue() +localVue.use(Vuex) localVue.use(Styleguide) describe('Editor.vue', () => { let wrapper let propsData let mocks + let getters beforeEach(() => { propsData = {} mocks = { $t: () => {}, } + getters = { + 'editor/placeholder': () => { + return 'some cool placeholder' + }, + } }) describe('mount', () => { let Wrapper = () => { + const store = new Vuex.Store({ + getters, + }) return (wrapper = mount(Editor, { mocks, propsData, localVue, sync: false, stubs: { transition: false }, + store, })) } @@ -43,5 +55,13 @@ describe('Editor.vue', () => { expect(wrapper.find('.ProseMirror').text()).toContain('I am a piece of text') }) }) + + describe('uses the placeholder', () => { + it('from the store', () => { + expect(wrapper.vm.editor.extensions.options.placeholder.emptyNodeText).toEqual( + 'some cool placeholder', + ) + }) + }) }) }) diff --git a/webapp/components/LocaleSwitch.vue b/webapp/components/LocaleSwitch.vue index f6f1a9727..aeee580b5 100644 --- a/webapp/components/LocaleSwitch.vue +++ b/webapp/components/LocaleSwitch.vue @@ -36,6 +36,7 @@ import Dropdown from '~/components/Dropdown' import find from 'lodash/find' import orderBy from 'lodash/orderBy' +import { mapMutations } from 'vuex' export default { components: { @@ -65,10 +66,11 @@ export default { }, }, methods: { + ...mapMutations({ setPlaceholderText: 'editor/SET_PLACEHOLDER_TEXT' }), changeLanguage(locale, toggleMenu) { this.$i18n.set(locale) toggleMenu() - this.$root.$emit('changeLanguage') + this.setPlaceholderText(this.$t('editor.placeholder')) }, matcher(locale) { return locale === this.$i18n.locale() diff --git a/webapp/store/editor.js b/webapp/store/editor.js new file mode 100644 index 000000000..9c5f665a0 --- /dev/null +++ b/webapp/store/editor.js @@ -0,0 +1,17 @@ +export const state = () => { + return { + placeholder: null, + } +} + +export const getters = { + placeholder(state) { + return state.placeholder + }, +} + +export const mutations = { + SET_PLACEHOLDER_TEXT(state, text) { + state.placeholder = text + }, +} diff --git a/webapp/store/editor.spec.js b/webapp/store/editor.spec.js new file mode 100644 index 000000000..90477ea20 --- /dev/null +++ b/webapp/store/editor.spec.js @@ -0,0 +1,20 @@ +import { getters, mutations } from './editor.js' + +let state + +describe('getters', () => { + describe('placeholder', () => { + it('return the value in state', () => { + state = { placeholder: null } + expect(getters.placeholder(state)).toBe(null) + }) + }) +}) + +describe('mutations', () => { + it('SET_PLACEHOLDER_TEXT', () => { + state = { placeholder: null } + mutations.SET_PLACEHOLDER_TEXT(state, 'new placeholder') + expect(getters.placeholder(state)).toBe('new placeholder') + }) +}) From 4adc450f439c06c645ea7bc46f16b8cccfffe953 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Sat, 15 Jun 2019 15:53:30 -0300 Subject: [PATCH 12/89] Fix failing test - add Vuex, with editor/placeholder getter --- .../ContributionForm/ContributionForm.spec.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/webapp/components/ContributionForm/ContributionForm.spec.js b/webapp/components/ContributionForm/ContributionForm.spec.js index f7f306fc3..8b741443f 100644 --- a/webapp/components/ContributionForm/ContributionForm.spec.js +++ b/webapp/components/ContributionForm/ContributionForm.spec.js @@ -1,9 +1,11 @@ import { config, mount, createLocalVue } from '@vue/test-utils' import ContributionForm from './index.vue' import Styleguide from '@human-connection/styleguide' +import Vuex from 'vuex' const localVue = createLocalVue() +localVue.use(Vuex) localVue.use(Styleguide) config.stubs['no-ssr'] = '' @@ -53,8 +55,16 @@ describe('ContributionForm.vue', () => { }) describe('mount', () => { + const getters = { + 'editor/placeholder': () => { + return 'some cool placeholder' + }, + } + const store = new Vuex.Store({ + getters, + }) const Wrapper = () => { - return mount(ContributionForm, { mocks, localVue, computed }) + return mount(ContributionForm, { mocks, localVue, computed, store }) } beforeEach(() => { From 01eb25eca67311526e9377a83cf3f4198a787d7b Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Sat, 15 Jun 2019 16:15:49 -0300 Subject: [PATCH 13/89] Update CommentForm with vuex --- webapp/components/comments/CommentForm/spec.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/webapp/components/comments/CommentForm/spec.js b/webapp/components/comments/CommentForm/spec.js index ded57348c..16a4d454d 100644 --- a/webapp/components/comments/CommentForm/spec.js +++ b/webapp/components/comments/CommentForm/spec.js @@ -1,9 +1,10 @@ import { mount, createLocalVue, createWrapper } from '@vue/test-utils' import CommentForm from './index.vue' import Styleguide from '@human-connection/styleguide' +import Vuex from 'vuex' const localVue = createLocalVue() - +localVue.use(Vuex) localVue.use(Styleguide) describe('CommentForm.vue', () => { @@ -35,8 +36,16 @@ describe('CommentForm.vue', () => { }) describe('mount', () => { + const getters = { + 'editor/placeholder': () => { + return 'some cool placeholder' + }, + } + const store = new Vuex.Store({ + getters, + }) const Wrapper = () => { - return mount(CommentForm, { mocks, localVue, propsData }) + return mount(CommentForm, { mocks, localVue, propsData, store }) } beforeEach(() => { From 145a8d8bf65efa55fd53e5a70d711ed5241aa11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2019 23:01:22 +0200 Subject: [PATCH 14/89] Check invalid email Sending a mail with further instructions even if the email is invalid seems to be a good practice: A potential attacker will not now if a user has an account under that email address. If a user does not remember the email address, but has control over the other mail account, she will get feedback that this mail account is incorrect. --- .../schema/resolvers/passwordReset.spec.js | 61 ++++++++++++------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index 3b0d39864..4bd29c9c6 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -30,34 +30,51 @@ describe('passwordReset', () => { }) describe('requestPasswordReset', () => { - const variables = { email: 'user@example.org' } const mutation = `mutation($email: String!) { requestPasswordReset(email: $email) }` - it('resolves', async () => { - await expect(client.request(mutation, variables)).resolves.toEqual({"requestPasswordReset": true}) + describe('with invalid email', () => { + const variables = { email: 'non-existent@example.org' } + + it('resolves anyways', async () => { + await expect(client.request(mutation, variables)).resolves.toEqual({"requestPasswordReset": true}) + }) + + it('creates no node', async () => { + await client.request(mutation, variables) + const resets = await getAllPasswordResets() + expect(resets).toHaveLength(0) + }) }) - it('creates node with label `PasswordReset`', async () => { - await client.request(mutation, variables) - const resets = await getAllPasswordResets() - expect(resets).toHaveLength(1) - }) + describe('with a valid email', () => { + const variables = { email: 'user@example.org' } - it('creates an id used as a reset token', async () => { - await client.request(mutation, variables) - const [reset] = await getAllPasswordResets() - const { id: token } = reset.properties - expect(token).toMatch(/^........-....-....-....-............$/) - }) + it('resolves', async () => { + await expect(client.request(mutation, variables)).resolves.toEqual({"requestPasswordReset": true}) + }) - it('created PasswordReset is valid for less than 4 minutes', async () => { - await client.request(mutation, variables) - const [reset] = await getAllPasswordResets() - let { validUntil } = reset.properties - validUntil = Date.parse(validUntil) - const now = (new Date()).getTime() - expect(validUntil).toBeGreaterThan(now - 60*1000) - expect(validUntil).toBeLessThan(now + 4*60*1000) + it('creates node with label `PasswordReset`', async () => { + await client.request(mutation, variables) + const resets = await getAllPasswordResets() + expect(resets).toHaveLength(1) + }) + + it('creates an id used as a reset token', async () => { + await client.request(mutation, variables) + const [reset] = await getAllPasswordResets() + const { id: token } = reset.properties + expect(token).toMatch(/^........-....-....-....-............$/) + }) + + it('created PasswordReset is valid for less than 4 minutes', async () => { + await client.request(mutation, variables) + const [reset] = await getAllPasswordResets() + let { validUntil } = reset.properties + validUntil = Date.parse(validUntil) + const now = (new Date()).getTime() + expect(validUntil).toBeGreaterThan(now - 60*1000) + expect(validUntil).toBeLessThan(now + 4*60*1000) + }) }) }) }) From c9ea956f858c631e0735cc93ab28f571ed55bedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sun, 16 Jun 2019 00:08:36 +0200 Subject: [PATCH 15/89] Test+Implement resetPassword --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/resolvers/passwordReset.js | 53 +++++-- .../schema/resolvers/passwordReset.spec.js | 143 ++++++++++++++++-- backend/src/schema/types/schema.gql | 2 +- 4 files changed, 174 insertions(+), 25 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index ad2787579..dbcde849c 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -148,6 +148,7 @@ const permissions = shield( DeleteComment: isAuthor, DeleteUser: isDeletingOwnAccount, requestPasswordReset: allow, + resetPassword: allow, }, User: { email: isMyOwn, diff --git a/backend/src/schema/resolvers/passwordReset.js b/backend/src/schema/resolvers/passwordReset.js index f3e1d32d2..d6d2a6e6c 100644 --- a/backend/src/schema/resolvers/passwordReset.js +++ b/backend/src/schema/resolvers/passwordReset.js @@ -1,21 +1,46 @@ -export default { - Mutation: { - requestPasswordReset: async (_, { email }, { driver }) => { - const session = driver.session() - let validUntil = new Date() - validUntil += 3*60*1000 - const cypher = ` - MATCH(u:User) WHERE u.email = $email - CREATE(pr:PasswordReset {id: apoc.create.uuid(), validUntil: $validUntil, redeemedAt: NULL}) +import uuid from 'uuid/v4' +import bcrypt from 'bcryptjs' + +export async function createPasswordReset({ driver, token, email, validUntil }) { + const session = driver.session() + const cypher = ` + MATCH (u:User) WHERE u.email = $email + CREATE(pr:PasswordReset {token: $token, validUntil: $validUntil, redeemedAt: NULL}) MERGE (u)-[:REQUESTED]->(pr) RETURN u,pr ` - await session.run(cypher, { email, validUntil }) - session.close() + const transactionRes = await session.run(cypher, { token, email, validUntil }) + const resets = transactionRes.records.map(record => record.get('pr')) + session.close() + return resets +} + +export default { + Mutation: { + requestPasswordReset: async (_, { email }, { driver }) => { + let validUntil = new Date() + validUntil += 3 * 60 * 1000 + const token = uuid() + await createPasswordReset({ driver, token, email, validUntil }) return true }, resetPassword: async (_, { email, token, newPassword }, { driver }) => { - throw Error('Not Implemented') - } - } + const session = driver.session() + const now = new Date().getTime() + const newHashedPassword = await bcrypt.hashSync(newPassword, 10) + const cypher = ` + MATCH (r:PasswordReset {token: $token}) + MATCH (u:User {email: $email})-[:REQUESTED]->(r) + WHERE r.validUntil > $now AND r.redeemedAt IS NULL + SET r.redeemedAt = $now + SET u.password = $newHashedPassword + RETURN r + ` + let transactionRes = await session.run(cypher, { now, email, token, newHashedPassword }) + const [reset] = transactionRes.records.map(record => record.get('r')) + const result = !!(reset && reset.properties.redeemedAt) + session.close() + return result + }, + }, } diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index 4bd29c9c6..89b724fca 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -1,7 +1,8 @@ import { GraphQLClient } from 'graphql-request' import Factory from '../../seed/factories' -import { host, login } from '../../jest/helpers' +import { host } from '../../jest/helpers' import { getDriver } from '../../bootstrap/neo4j' +import { createPasswordReset } from './passwordReset' const factory = Factory() let client @@ -36,7 +37,9 @@ describe('passwordReset', () => { const variables = { email: 'non-existent@example.org' } it('resolves anyways', async () => { - await expect(client.request(mutation, variables)).resolves.toEqual({"requestPasswordReset": true}) + await expect(client.request(mutation, variables)).resolves.toEqual({ + requestPasswordReset: true, + }) }) it('creates no node', async () => { @@ -50,7 +53,9 @@ describe('passwordReset', () => { const variables = { email: 'user@example.org' } it('resolves', async () => { - await expect(client.request(mutation, variables)).resolves.toEqual({"requestPasswordReset": true}) + await expect(client.request(mutation, variables)).resolves.toEqual({ + requestPasswordReset: true, + }) }) it('creates node with label `PasswordReset`', async () => { @@ -59,21 +64,139 @@ describe('passwordReset', () => { expect(resets).toHaveLength(1) }) - it('creates an id used as a reset token', async () => { + it('creates a reset token', async () => { await client.request(mutation, variables) - const [reset] = await getAllPasswordResets() - const { id: token } = reset.properties + const resets = await getAllPasswordResets() + const [reset] = resets + const { token } = reset.properties expect(token).toMatch(/^........-....-....-....-............$/) }) it('created PasswordReset is valid for less than 4 minutes', async () => { await client.request(mutation, variables) - const [reset] = await getAllPasswordResets() + const resets = await getAllPasswordResets() + const [reset] = resets let { validUntil } = reset.properties validUntil = Date.parse(validUntil) - const now = (new Date()).getTime() - expect(validUntil).toBeGreaterThan(now - 60*1000) - expect(validUntil).toBeLessThan(now + 4*60*1000) + const now = new Date().getTime() + expect(validUntil).toBeGreaterThan(now - 60 * 1000) + expect(validUntil).toBeLessThan(now + 4 * 60 * 1000) + }) + }) + }) + + describe('resetPassword', () => { + const setup = async (options = {}) => { + const { + email = 'user@example.org', + validUntil = new Date().getTime() + 3 * 60 * 1000, + token = 'abcdefgh-ijkl-mnop-qrst-uvwxyz123456', + } = options + + const session = driver.session() + await createPasswordReset({ driver, email, validUntil, token }) + session.close() + } + + const mutation = `mutation($token: String!, $email: String!, $newPassword: String!) { resetPassword(token: $token, email: $email, newPassword: $newPassword) }` + let email = 'user@example.org' + let token = 'abcdefgh-ijkl-mnop-qrst-uvwxyz123456' + let newPassword = 'supersecret' + let variables + + describe('invalid email', () => { + it('resolves to false', async () => { + await setup() + variables = { newPassword, email: 'non-existent@example.org', token } + await expect(client.request(mutation, variables)).resolves.toEqual({ resetPassword: false }) + }) + }) + + describe('valid email', () => { + describe('but invalid token', () => { + it('resolves to false', async () => { + await setup() + variables = { newPassword, email, token: 'slkdjfldsjflsdjfsjdfl' } + await expect(client.request(mutation, variables)).resolves.toEqual({ + resetPassword: false, + }) + }) + }) + + describe('but invalid token', () => { + it('resolves to false', async () => { + variables = { newPassword, email: 'user@example.org', token: 'lksjdflksjdflksjdlkfjsf' } + await expect(client.request(mutation, variables)).resolves.toEqual({ + resetPassword: false, + }) + }) + }) + + describe('and valid token', () => { + beforeEach(() => { + variables = { + newPassword, + email: 'user@example.org', + token: 'abcdefgh-ijkl-mnop-qrst-uvwxyz123456', + } + }) + + describe('and token not expired', () => { + beforeEach(async () => { + await setup() + }) + + it('resolves to true', async () => { + await expect(client.request(mutation, variables)).resolves.toEqual({ + resetPassword: true, + }) + }) + + it('updates PasswordReset `redeemedAt` property', async () => { + await client.request(mutation, variables) + const requests = await getAllPasswordResets() + const [request] = requests + const { redeemedAt } = request.properties + expect(redeemedAt).not.toBeNull() + }) + + it('updates password of the user', async () => { + await client.request(mutation, variables) + const checkLoginMutation = ` + mutation($email: String!, $password: String!) { + login(email: $email, password: $password) + } + ` + const expected = expect.objectContaining({ login: expect.any(String) }) + await expect( + client.request(checkLoginMutation, { + email: 'user@example.org', + password: 'supersecret', + }), + ).resolves.toEqual(expected) + }) + }) + + describe('but expired token', () => { + beforeEach(async () => { + const validUntil = new Date().getTime() - 1000 + await setup({ validUntil }) + }) + + it('resolves to false', async () => { + await expect(client.request(mutation, variables)).resolves.toEqual({ + resetPassword: false, + }) + }) + + it('does not update PasswordReset `redeemedAt` property', async () => { + await client.request(mutation, variables) + const requests = await getAllPasswordResets() + const [request] = requests + const { redeemedAt } = request.properties + expect(redeemedAt).toBeUndefined() + }) + }) }) }) }) diff --git a/backend/src/schema/types/schema.gql b/backend/src/schema/types/schema.gql index 358797631..ae77ef8e8 100644 --- a/backend/src/schema/types/schema.gql +++ b/backend/src/schema/types/schema.gql @@ -26,7 +26,7 @@ type Mutation { signup(email: String!, password: String!): Boolean! changePassword(oldPassword: String!, newPassword: String!): String! requestPasswordReset(email: String!): Boolean! - resetPassword(email: String!, resetToken: String!, newPassword: String!): String! + resetPassword(email: String!, token: String!, newPassword: String!): Boolean! report(id: ID!, description: String): Report disable(id: ID!): ID enable(id: ID!): ID From 31dde68ff23c3d36417b0161dff88c9495f5c73a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 17 Jun 2019 04:56:26 +0000 Subject: [PATCH 16/89] Bump date-fns from 2.0.0-alpha.33 to 2.0.0-alpha.34 in /webapp Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.0.0-alpha.33 to 2.0.0-alpha.34. - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v2.0.0-alpha.33...v2.0.0-alpha.34) Signed-off-by: dependabot-preview[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 1abaf479b..5e42d0332 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -59,7 +59,7 @@ "apollo-client": "~2.6.2", "cookie-universal-nuxt": "~2.0.16", "cross-env": "~5.2.0", - "date-fns": "2.0.0-alpha.33", + "date-fns": "2.0.0-alpha.34", "express": "~4.17.1", "graphql": "~14.3.1", "jsonwebtoken": "~8.5.1", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index e5c1daf8a..de0d07924 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -3754,10 +3754,10 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@2.0.0-alpha.33: - version "2.0.0-alpha.33" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.33.tgz#c2f73c3cc50ac301c9217eb93603c9bc40e891bf" - integrity sha512-tqUVEk3oxnJuNIvwAMKHAMo4uFRG0zXvjxZQll+BonoPt+m4NMcUgO14NDxbHuy7uYcrVErd2GdSsw02EDZQ7w== +date-fns@2.0.0-alpha.34: + version "2.0.0-alpha.34" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.34.tgz#5d3ae7ca0d08915ccfc87a20545250af4e9c3cae" + integrity sha512-yjSYUHASHvzOZl++cEms+Tw7oQOFA+7Z6/lL7L3lRO9j6pMfT48N6oEyvCGo/MVlH08XWmydgf8X9Y1eedf9sQ== date-now@^0.1.4: version "0.1.4" From 1cdaef179eeaea8a24fca216bb64a64feedc7630 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 17 Jun 2019 04:56:53 +0000 Subject: [PATCH 17/89] Bump eslint-config-prettier from 4.3.0 to 5.0.0 in /webapp Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 4.3.0 to 5.0.0. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v4.3.0...v5.0.0) Signed-off-by: dependabot-preview[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 1abaf479b..5f926320b 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -90,7 +90,7 @@ "babel-eslint": "~10.0.1", "babel-jest": "~24.8.0", "eslint": "~5.16.0", - "eslint-config-prettier": "~4.3.0", + "eslint-config-prettier": "~5.0.0", "eslint-config-standard": "~12.0.0", "eslint-loader": "~2.1.2", "eslint-plugin-import": "~2.17.3", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index e5c1daf8a..dcf099ab7 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4236,10 +4236,10 @@ eslint-config-prettier@^3.3.0: dependencies: get-stdin "^6.0.0" -eslint-config-prettier@~4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-4.3.0.tgz#c55c1fcac8ce4518aeb77906984e134d9eb5a4f0" - integrity sha512-sZwhSTHVVz78+kYD3t5pCWSYEdVSBR0PXnwjDRsUs8ytIrK8PLXw+6FKp8r3Z7rx4ZszdetWlXYKOHoUrrwPlA== +eslint-config-prettier@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-5.0.0.tgz#f7a94b2b8ae7cbf25842c36fa96c6d32cd0a697c" + integrity sha512-c17Aqiz5e8LEqoc/QPmYnaxQFAHTx2KlCZBPxXXjEMmNchOLnV/7j0HoPZuC+rL/tDC9bazUYOKJW9bOhftI/w== dependencies: get-stdin "^6.0.0" From dc420d4116b37607ceeba737c0000f9f5bdf1c76 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 17 Jun 2019 04:57:46 +0000 Subject: [PATCH 18/89] Bump date-fns from 2.0.0-alpha.33 to 2.0.0-alpha.34 in /backend Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.0.0-alpha.33 to 2.0.0-alpha.34. - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v2.0.0-alpha.33...v2.0.0-alpha.34) Signed-off-by: dependabot-preview[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index c8537ae0b..d0ecf618c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -52,7 +52,7 @@ "cheerio": "~1.0.0-rc.3", "cors": "~2.8.5", "cross-env": "~5.2.0", - "date-fns": "2.0.0-alpha.33", + "date-fns": "2.0.0-alpha.34", "debug": "~4.1.1", "dotenv": "~8.0.0", "express": "~4.17.1", diff --git a/backend/yarn.lock b/backend/yarn.lock index d2c5da176..c8a5b4254 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2586,10 +2586,10 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@2.0.0-alpha.33: - version "2.0.0-alpha.33" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.33.tgz#c2f73c3cc50ac301c9217eb93603c9bc40e891bf" - integrity sha512-tqUVEk3oxnJuNIvwAMKHAMo4uFRG0zXvjxZQll+BonoPt+m4NMcUgO14NDxbHuy7uYcrVErd2GdSsw02EDZQ7w== +date-fns@2.0.0-alpha.34: + version "2.0.0-alpha.34" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.34.tgz#5d3ae7ca0d08915ccfc87a20545250af4e9c3cae" + integrity sha512-yjSYUHASHvzOZl++cEms+Tw7oQOFA+7Z6/lL7L3lRO9j6pMfT48N6oEyvCGo/MVlH08XWmydgf8X9Y1eedf9sQ== debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" From 8ff98cbc25f8fbe315c1021d61d87207541edc80 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 17 Jun 2019 04:58:20 +0000 Subject: [PATCH 19/89] Bump apollo-link-context from 1.0.17 to 1.0.18 in /backend Bumps [apollo-link-context](https://github.com/apollographql/apollo-link) from 1.0.17 to 1.0.18. - [Release notes](https://github.com/apollographql/apollo-link/releases) - [Changelog](https://github.com/apollographql/apollo-link/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollographql/apollo-link/compare/apollo-link-context@1.0.17...apollo-link-context@1.0.18) Signed-off-by: dependabot-preview[bot] --- backend/package.json | 2 +- backend/yarn.lock | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/package.json b/backend/package.json index c8537ae0b..47d080de3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -45,7 +45,7 @@ "activitystrea.ms": "~2.1.3", "apollo-cache-inmemory": "~1.6.2", "apollo-client": "~2.6.2", - "apollo-link-context": "~1.0.14", + "apollo-link-context": "~1.0.18", "apollo-link-http": "~1.5.14", "apollo-server": "~2.6.3", "bcryptjs": "~2.4.3", diff --git a/backend/yarn.lock b/backend/yarn.lock index d2c5da176..200370ea2 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1388,12 +1388,12 @@ apollo-graphql@^0.3.0: apollo-env "0.5.1" lodash.sortby "^4.7.0" -apollo-link-context@~1.0.14: - version "1.0.17" - resolved "https://registry.yarnpkg.com/apollo-link-context/-/apollo-link-context-1.0.17.tgz#439272cfb43ec1891506dd175ed907845b7de36c" - integrity sha512-W5UUfHcrrlP5uqJs5X1zbf84AMXhPZGAqX/7AQDgR6wY/7//sMGfJvm36KDkpIeSOElztGtM9z6zdPN1NbT41Q== +apollo-link-context@~1.0.18: + version "1.0.18" + resolved "https://registry.yarnpkg.com/apollo-link-context/-/apollo-link-context-1.0.18.tgz#9e700e3314da8ded50057fee0a18af2bfcedbfc3" + integrity sha512-aG5cbUp1zqOHHQjAJXG7n/izeMQ6LApd/whEF5z6qZp5ATvcyfSNkCfy3KRJMMZZ3iNfVTs6jF+IUA8Zvf+zeg== dependencies: - apollo-link "^1.2.11" + apollo-link "^1.2.12" tslib "^1.9.3" apollo-link-http-common@^0.2.13: @@ -1414,15 +1414,15 @@ apollo-link-http@~1.5.14: apollo-link-http-common "^0.2.13" tslib "^1.9.3" -apollo-link@^1.0.0, apollo-link@^1.2.11, apollo-link@^1.2.3: - version "1.2.11" - resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.11.tgz#493293b747ad3237114ccd22e9f559e5e24a194d" - integrity sha512-PQvRCg13VduLy3X/0L79M6uOpTh5iHdxnxYuo8yL7sJlWybKRJwsv4IcRBJpMFbChOOaHY7Og9wgPo6DLKDKDA== +apollo-link@^1.0.0, apollo-link@^1.2.11, apollo-link@^1.2.12, apollo-link@^1.2.3: + version "1.2.12" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.12.tgz#014b514fba95f1945c38ad4c216f31bcfee68429" + integrity sha512-fsgIAXPKThyMVEMWQsUN22AoQI+J/pVXcjRGAShtk97h7D8O+SPskFinCGEkxPeQpE83uKaqafB2IyWdjN+J3Q== dependencies: - apollo-utilities "^1.2.1" - ts-invariant "^0.3.2" + apollo-utilities "^1.3.0" + ts-invariant "^0.4.0" tslib "^1.9.3" - zen-observable-ts "^0.8.18" + zen-observable-ts "^0.8.19" apollo-server-caching@0.4.0: version "0.4.0" @@ -1566,7 +1566,7 @@ apollo-upload-server@^7.0.0: http-errors "^1.7.0" object-path "^0.11.4" -apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.2: +apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.3.0, apollo-utilities@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9" integrity sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg== @@ -8112,10 +8112,10 @@ yup@^0.27.0: synchronous-promise "^2.0.6" toposort "^2.0.2" -zen-observable-ts@^0.8.18: - version "0.8.18" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.18.tgz#ade44b1060cc4a800627856ec10b9c67f5f639c8" - integrity sha512-q7d05s75Rn1j39U5Oapg3HI2wzriVwERVo4N7uFGpIYuHB9ff02P/E92P9B8T7QVC93jCMHpbXH7X0eVR5LA7A== +zen-observable-ts@^0.8.19: + version "0.8.19" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz#c094cd20e83ddb02a11144a6e2a89706946b5694" + integrity sha512-u1a2rpE13G+jSzrg3aiCqXU5tN2kw41b+cBZGmnc+30YimdkKiDj9bTowcB41eL77/17RF/h+393AuVgShyheQ== dependencies: tslib "^1.9.3" zen-observable "^0.8.0" From 4566980eaafa63d19f54311d445246d6c52cbae3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 17 Jun 2019 04:58:59 +0000 Subject: [PATCH 20/89] Bump eslint-config-prettier from 4.3.0 to 5.0.0 in /backend Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 4.3.0 to 5.0.0. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v4.3.0...v5.0.0) Signed-off-by: dependabot-preview[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index c8537ae0b..f4fa102ab 100644 --- a/backend/package.json +++ b/backend/package.json @@ -94,7 +94,7 @@ "chai": "~4.2.0", "cucumber": "~5.1.0", "eslint": "~5.16.0", - "eslint-config-prettier": "~4.3.0", + "eslint-config-prettier": "~5.0.0", "eslint-config-standard": "~12.0.0", "eslint-plugin-import": "~2.17.3", "eslint-plugin-jest": "~22.6.4", diff --git a/backend/yarn.lock b/backend/yarn.lock index d2c5da176..89ea212e4 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2995,10 +2995,10 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@~4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-4.3.0.tgz#c55c1fcac8ce4518aeb77906984e134d9eb5a4f0" - integrity sha512-sZwhSTHVVz78+kYD3t5pCWSYEdVSBR0PXnwjDRsUs8ytIrK8PLXw+6FKp8r3Z7rx4ZszdetWlXYKOHoUrrwPlA== +eslint-config-prettier@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-5.0.0.tgz#f7a94b2b8ae7cbf25842c36fa96c6d32cd0a697c" + integrity sha512-c17Aqiz5e8LEqoc/QPmYnaxQFAHTx2KlCZBPxXXjEMmNchOLnV/7j0HoPZuC+rL/tDC9bazUYOKJW9bOhftI/w== dependencies: get-stdin "^6.0.0" From 7458fd62e222f43845a5f89de84c59358d1db789 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Mon, 17 Jun 2019 04:59:16 +0000 Subject: [PATCH 21/89] Bump apollo-link-http from 1.5.14 to 1.5.15 in /backend Bumps [apollo-link-http](https://github.com/apollographql/apollo-link) from 1.5.14 to 1.5.15. - [Release notes](https://github.com/apollographql/apollo-link/releases) - [Changelog](https://github.com/apollographql/apollo-link/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollographql/apollo-link/compare/apollo-link-http@1.5.14...apollo-link-http@1.5.15) Signed-off-by: dependabot-preview[bot] --- backend/package.json | 2 +- backend/yarn.lock | 55 +++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/backend/package.json b/backend/package.json index c8537ae0b..a20dbe698 100644 --- a/backend/package.json +++ b/backend/package.json @@ -46,7 +46,7 @@ "apollo-cache-inmemory": "~1.6.2", "apollo-client": "~2.6.2", "apollo-link-context": "~1.0.14", - "apollo-link-http": "~1.5.14", + "apollo-link-http": "~1.5.15", "apollo-server": "~2.6.3", "bcryptjs": "~2.4.3", "cheerio": "~1.0.0-rc.3", diff --git a/backend/yarn.lock b/backend/yarn.lock index d2c5da176..d53e5af20 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1396,33 +1396,33 @@ apollo-link-context@~1.0.14: apollo-link "^1.2.11" tslib "^1.9.3" -apollo-link-http-common@^0.2.13: - version "0.2.13" - resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.13.tgz#c688f6baaffdc7b269b2db7ae89dae7c58b5b350" - integrity sha512-Uyg1ECQpTTA691Fwx5e6Rc/6CPSu4TB4pQRTGIpwZ4l5JDOQ+812Wvi/e3IInmzOZpwx5YrrOfXrtN8BrsDXoA== +apollo-link-http-common@^0.2.14: + version "0.2.14" + resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.14.tgz#d3a195c12e00f4e311c417f121181dcc31f7d0c8" + integrity sha512-v6mRU1oN6XuX8beVIRB6OpF4q1ULhSnmy7ScnHnuo1qV6GaFmDcbdvXqxIkAV1Q8SQCo2lsv4HeqJOWhFfApOg== dependencies: - apollo-link "^1.2.11" - ts-invariant "^0.3.2" + apollo-link "^1.2.12" + ts-invariant "^0.4.0" tslib "^1.9.3" -apollo-link-http@~1.5.14: - version "1.5.14" - resolved "https://registry.yarnpkg.com/apollo-link-http/-/apollo-link-http-1.5.14.tgz#ed6292248d1819ccd16523e346d35203a1b31109" - integrity sha512-XEoPXmGpxFG3wioovgAlPXIarWaW4oWzt8YzjTYZ87R4R7d1A3wKR/KcvkdMV1m5G7YSAHcNkDLe/8hF2nH6cg== +apollo-link-http@~1.5.15: + version "1.5.15" + resolved "https://registry.yarnpkg.com/apollo-link-http/-/apollo-link-http-1.5.15.tgz#106ab23bb8997bd55965d05855736d33119652cf" + integrity sha512-epZFhCKDjD7+oNTVK3P39pqWGn4LEhShAoA1Q9e2tDrBjItNfviiE33RmcLcCURDYyW5JA6SMgdODNI4Is8tvQ== dependencies: - apollo-link "^1.2.11" - apollo-link-http-common "^0.2.13" + apollo-link "^1.2.12" + apollo-link-http-common "^0.2.14" tslib "^1.9.3" -apollo-link@^1.0.0, apollo-link@^1.2.11, apollo-link@^1.2.3: - version "1.2.11" - resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.11.tgz#493293b747ad3237114ccd22e9f559e5e24a194d" - integrity sha512-PQvRCg13VduLy3X/0L79M6uOpTh5iHdxnxYuo8yL7sJlWybKRJwsv4IcRBJpMFbChOOaHY7Og9wgPo6DLKDKDA== +apollo-link@^1.0.0, apollo-link@^1.2.11, apollo-link@^1.2.12, apollo-link@^1.2.3: + version "1.2.12" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.12.tgz#014b514fba95f1945c38ad4c216f31bcfee68429" + integrity sha512-fsgIAXPKThyMVEMWQsUN22AoQI+J/pVXcjRGAShtk97h7D8O+SPskFinCGEkxPeQpE83uKaqafB2IyWdjN+J3Q== dependencies: - apollo-utilities "^1.2.1" - ts-invariant "^0.3.2" + apollo-utilities "^1.3.0" + ts-invariant "^0.4.0" tslib "^1.9.3" - zen-observable-ts "^0.8.18" + zen-observable-ts "^0.8.19" apollo-server-caching@0.4.0: version "0.4.0" @@ -1566,7 +1566,7 @@ apollo-upload-server@^7.0.0: http-errors "^1.7.0" object-path "^0.11.4" -apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.2: +apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.3.0, apollo-utilities@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9" integrity sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg== @@ -7543,13 +7543,6 @@ trunc-text@1.0.1: resolved "https://registry.yarnpkg.com/trunc-text/-/trunc-text-1.0.1.tgz#58f876d8ac59b224b79834bb478b8656e69622b5" integrity sha1-WPh22KxZsiS3mDS7R4uGVuaWIrU= -ts-invariant@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.3.2.tgz#89a2ffeb70879b777258df1df1c59383c35209b0" - integrity sha512-QsY8BCaRnHiB5T6iE4DPlJMAKEG3gzMiUco9FEt1jUXQf0XP6zi0idT0i0rMTu8A326JqNSDsmlkA9dRSh1TRg== - dependencies: - tslib "^1.9.3" - ts-invariant@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.2.tgz#8685131b8083e67c66d602540e78763408be9113" @@ -8112,10 +8105,10 @@ yup@^0.27.0: synchronous-promise "^2.0.6" toposort "^2.0.2" -zen-observable-ts@^0.8.18: - version "0.8.18" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.18.tgz#ade44b1060cc4a800627856ec10b9c67f5f639c8" - integrity sha512-q7d05s75Rn1j39U5Oapg3HI2wzriVwERVo4N7uFGpIYuHB9ff02P/E92P9B8T7QVC93jCMHpbXH7X0eVR5LA7A== +zen-observable-ts@^0.8.19: + version "0.8.19" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz#c094cd20e83ddb02a11144a6e2a89706946b5694" + integrity sha512-u1a2rpE13G+jSzrg3aiCqXU5tN2kw41b+cBZGmnc+30YimdkKiDj9bTowcB41eL77/17RF/h+393AuVgShyheQ== dependencies: tslib "^1.9.3" zen-observable "^0.8.0" From 65df4c5a20b9d05cd08cfd4a33cebab1ccc29461 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 17 Jun 2019 12:16:15 +0200 Subject: [PATCH 22/89] use replace instead of substring --- .../maintenance-worker/migration/neo4j/badges.cql | 2 +- .../maintenance-worker/migration/neo4j/contributions.cql | 2 +- .../maintenance-worker/migration/neo4j/users.cql | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql index 2d1548d4f..027cea019 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql @@ -45,7 +45,7 @@ MERGE(b:Badge {id: badge._id["$oid"]}) ON CREATE SET b.key = badge.key, b.type = badge.type, -b.icon = substring(badge.image.path, 38), +b.icon = replace(badge.image.path, 'https://api-alpha.human-connection.org', ''), b.status = badge.status, b.createdAt = badge.createdAt.`$date`, b.updatedAt = badge.updatedAt.`$date` diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql index 70f09e035..472354763 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql @@ -131,7 +131,7 @@ MERGE (p:Post {id: post._id["$oid"]}) ON CREATE SET p.title = post.title, p.slug = post.slug, -p.image = substring(post.teaserImg, 38), +p.image = replace(post.teaserImg, 'https://api-alpha.human-connection.org', ''), p.content = post.content, p.contentExcerpt = post.contentExcerpt, p.visibility = toLower(post.visibility), diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql index 96251a9ce..4d7c9aa9f 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql @@ -102,8 +102,8 @@ u.name = user.name, u.slug = user.slug, u.email = user.email, u.password = user.password, -u.avatar = substring(user.avatar, 38), -u.coverImg = substring(user.coverImg, 38), +u.avatar = replace(user.avatar, 'https://api-alpha.human-connection.org', ''), +u.coverImg = replace(user.coverImg, 'https://api-alpha.human-connection.org', ''), u.wasInvited = user.wasInvited, u.wasSeeded = user.wasSeeded, u.role = toLower(user.role), From 5a806ca99e3fba3e178954d462c0f5b32610f078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 17 Jun 2019 12:24:14 +0200 Subject: [PATCH 23/89] Remove duplicate test case --- backend/src/schema/resolvers/passwordReset.spec.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index 89b724fca..1fbd96b7a 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -123,15 +123,6 @@ describe('passwordReset', () => { }) }) - describe('but invalid token', () => { - it('resolves to false', async () => { - variables = { newPassword, email: 'user@example.org', token: 'lksjdflksjdflksjdlkfjsf' } - await expect(client.request(mutation, variables)).resolves.toEqual({ - resetPassword: false, - }) - }) - }) - describe('and valid token', () => { beforeEach(() => { variables = { From 7228d68149e10517be360f1f6db3a08285ffd633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 17 Jun 2019 12:30:39 +0200 Subject: [PATCH 24/89] Write a nice form to reset your password --- webapp/locales/de.json | 6 +++++- webapp/locales/en.json | 6 +++++- webapp/pages/password-reset.vue | 23 ++++++++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index d783f9e37..14ab9d906 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -15,7 +15,11 @@ "hello": "Hallo" }, "password-reset": { - "title": "Passwort zurücksetzen" + "title": "Passwort zurücksetzen", + "form": { + "description": "Eine Mail zum Zurücksetzen des Passworts wird an die angegebene E-Mail Adresse geschickt.", + "submit": "Email anfordern" + } }, "editor": { "placeholder": "Schreib etwas Inspirierendes..." diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 3a5405eec..3c2b7c8d8 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -15,7 +15,11 @@ "hello": "Hello" }, "password-reset": { - "title": "Reset your password" + "title": "Reset your password", + "form": { + "description": "A password reset email will be sent to the given email address.", + "submit": "Request email" + } }, "editor": { "placeholder": "Leave your inspirational thoughts..." diff --git a/webapp/pages/password-reset.vue b/webapp/pages/password-reset.vue index 0205ea4f4..bd7da49be 100644 --- a/webapp/pages/password-reset.vue +++ b/webapp/pages/password-reset.vue @@ -3,9 +3,26 @@ - - {{ $t('password-reset.title') }} - + + +
+ + + + {{ $t('password-reset.form.description') }} + + + + {{ $t('password-reset.form.submit') }} + + +
+
From c4eb2178f241a7119028888c028768176c71dbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 17 Jun 2019 12:39:36 +0200 Subject: [PATCH 25/89] Scaffold page component test for password reset --- webapp/pages/password-reset.spec.js | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 webapp/pages/password-reset.spec.js diff --git a/webapp/pages/password-reset.spec.js b/webapp/pages/password-reset.spec.js new file mode 100644 index 000000000..3c1f5b286 --- /dev/null +++ b/webapp/pages/password-reset.spec.js @@ -0,0 +1,41 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils' +import PasswordResetPage from './password-reset.vue' +import Styleguide from '@human-connection/styleguide' + +const localVue = createLocalVue() + +localVue.use(Styleguide) + +describe('ProfileSlug', () => { + let wrapper + let Wrapper + let mocks + + beforeEach(() => { + mocks = { + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + $t: jest.fn(), + $apollo: { + loading: false, + mutate: jest.fn().mockResolvedValue(), + }, + } + }) + + describe('shallowMount', () => { + Wrapper = () => { + return shallowMount(PasswordResetPage, { + mocks, + localVue, + }) + } + + it('renders a password reset form', () => { + wrapper = Wrapper() + expect(wrapper.find('.password-reset-card').exists()).toBe(true) + }) + }) +}) From 4dde53f67d4dca075240c90466fd765c67e24d19 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 17 Jun 2019 13:30:18 +0200 Subject: [PATCH 26/89] added coverage report text in oder to fix coverage --- webapp/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/package.json b/webapp/package.json index 1abaf479b..4f203ef5c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -29,6 +29,7 @@ "!**/?(*.)+(spec|test).js?(x)" ], "coverageReporters": [ + "text", "lcov" ], "transform": { @@ -110,4 +111,4 @@ "vue-jest": "~3.0.4", "vue-svg-loader": "~0.12.0" } -} +} \ No newline at end of file From a501e1145daf8ad8fe4200b19750963a2018a879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 17 Jun 2019 14:02:25 +0200 Subject: [PATCH 27/89] Component test to call a mutation passes --- webapp/locales/de.json | 8 +++- webapp/locales/en.json | 8 +++- webapp/pages/password-reset.spec.js | 31 +++++++++++++-- webapp/pages/password-reset.vue | 59 +++++++++++++++++++++++++++-- 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 14ab9d906..7497648e1 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -18,7 +18,8 @@ "title": "Passwort zurücksetzen", "form": { "description": "Eine Mail zum Zurücksetzen des Passworts wird an die angegebene E-Mail Adresse geschickt.", - "submit": "Email anfordern" + "submit": "Email anfordern", + "submitted": "Eine E-Mail zum Zurücksetzen wurde angefordert" } }, "editor": { @@ -201,7 +202,10 @@ "name": "Name", "loadMore": "mehr laden", "loading": "wird geladen", - "reportContent": "Melden" + "reportContent": "Melden", + "validations": { + "email": "muss eine gültige E-Mail Adresse sein" + } }, "actions": { "loading": "lade", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 3c2b7c8d8..fd7968428 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -18,7 +18,8 @@ "title": "Reset your password", "form": { "description": "A password reset email will be sent to the given email address.", - "submit": "Request email" + "submit": "Request email", + "submitted": "Reset email was requested" } }, "editor": { @@ -201,7 +202,10 @@ "name": "Name", "loadMore": "load more", "loading": "loading", - "reportContent": "Report" + "reportContent": "Report", + "validations": { + "email": "must be a valid email address" + } }, "actions": { "loading": "loading", diff --git a/webapp/pages/password-reset.spec.js b/webapp/pages/password-reset.spec.js index 3c1f5b286..bba001614 100644 --- a/webapp/pages/password-reset.spec.js +++ b/webapp/pages/password-reset.spec.js @@ -1,4 +1,4 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils' +import { mount, createLocalVue } from '@vue/test-utils' import PasswordResetPage from './password-reset.vue' import Styleguide from '@human-connection/styleguide' @@ -25,9 +25,9 @@ describe('ProfileSlug', () => { } }) - describe('shallowMount', () => { + describe('mount', () => { Wrapper = () => { - return shallowMount(PasswordResetPage, { + return mount(PasswordResetPage, { mocks, localVue, }) @@ -37,5 +37,30 @@ describe('ProfileSlug', () => { wrapper = Wrapper() expect(wrapper.find('.password-reset-card').exists()).toBe(true) }) + + describe('submit', () => { + beforeEach(async () => { + wrapper = Wrapper() + wrapper.find('input#email').setValue('mail@example.org') + await wrapper.find('form').trigger('submit') + }) + + it('calls requestPasswordReset graphql mutation', () => { + expect(mocks.$apollo.mutate).toHaveBeenCalled() + }) + + it.todo('delivers email to backend') + it.todo('disables form to avoid re-submission') + it.todo('displays a message that a password email was requested') + }) + + describe('given password reset token as URL param', () => { + it.todo('displays a form to update your password') + describe('submitting new password', () => { + it.todo('calls resetPassword graphql mutation') + it.todo('delivers new password to backend') + it.todo('displays success message') + }) + }) }) }) diff --git a/webapp/pages/password-reset.vue b/webapp/pages/password-reset.vue index bd7da49be..0ad9b7997 100644 --- a/webapp/pages/password-reset.vue +++ b/webapp/pages/password-reset.vue @@ -5,10 +5,17 @@ -
+ @@ -17,10 +24,12 @@ {{ $t('password-reset.form.description') }} - + {{ $t('password-reset.form.submit') }} - +
@@ -30,7 +39,51 @@ From de9ed55738f31442f81f19cbe9aa90c20c2947d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 17 Jun 2019 14:29:24 +0200 Subject: [PATCH 28/89] Display nice success message on password reset --- webapp/locales/de.json | 2 +- webapp/locales/en.json | 2 +- webapp/pages/password-reset.spec.js | 18 ++++++++--- webapp/pages/password-reset.vue | 49 +++++++++++++++++++++-------- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 7497648e1..88afa07e0 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -19,7 +19,7 @@ "form": { "description": "Eine Mail zum Zurücksetzen des Passworts wird an die angegebene E-Mail Adresse geschickt.", "submit": "Email anfordern", - "submitted": "Eine E-Mail zum Zurücksetzen wurde angefordert" + "submitted": "E-Mail verschickt an {email}" } }, "editor": { diff --git a/webapp/locales/en.json b/webapp/locales/en.json index fd7968428..53ad9fb40 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -19,7 +19,7 @@ "form": { "description": "A password reset email will be sent to the given email address.", "submit": "Request email", - "submitted": "Reset email was requested" + "submitted": "Email sent to {email}" } }, "editor": { diff --git a/webapp/pages/password-reset.spec.js b/webapp/pages/password-reset.spec.js index bba001614..e78ace4ba 100644 --- a/webapp/pages/password-reset.spec.js +++ b/webapp/pages/password-reset.spec.js @@ -20,7 +20,7 @@ describe('ProfileSlug', () => { $t: jest.fn(), $apollo: { loading: false, - mutate: jest.fn().mockResolvedValue(), + mutate: jest.fn().mockResolvedValue({ data: { reqestPasswordReset: true } }), }, } }) @@ -49,9 +49,19 @@ describe('ProfileSlug', () => { expect(mocks.$apollo.mutate).toHaveBeenCalled() }) - it.todo('delivers email to backend') - it.todo('disables form to avoid re-submission') - it.todo('displays a message that a password email was requested') + it('delivers email to backend', () => { + const expected = expect.objectContaining({ variables: { email: 'mail@example.org' } }) + expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) + }) + + it('hides form to avoid re-submission', () => { + expect(wrapper.find('form').exists()).not.toBeTruthy() + }) + + it('displays a message that a password email was requested', () => { + const expected = ['password-reset.form.submitted', { email: 'mail@example.org' }] + expect(mocks.$t).toHaveBeenCalledWith(...expected) + }) }) describe('given password reset token as URL param', () => { diff --git a/webapp/pages/password-reset.vue b/webapp/pages/password-reset.vue index 0ad9b7997..9e7d2d195 100644 --- a/webapp/pages/password-reset.vue +++ b/webapp/pages/password-reset.vue @@ -6,11 +6,13 @@ + @submit="handleSubmit" + > + :loading="$apollo.loading" + primary + fullwidth + name="submit" + type="submit" + icon="envelope" + > {{ $t('password-reset.form.submit') }} +
+ + + + + + +
@@ -40,33 +56,43 @@ From 35428fbaaad0b061bb5466b9b3e781d59107d332 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Mon, 17 Jun 2019 10:07:53 -0300 Subject: [PATCH 29/89] Add test for LocaleSwitch --- .../LocaleSwitch/LocaleSwitch.spec.js | 68 +++++++++++++++++++ .../{ => LocaleSwitch}/LocaleSwitch.vue | 0 webapp/layouts/default.vue | 2 +- webapp/pages/login.vue | 2 +- 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 webapp/components/LocaleSwitch/LocaleSwitch.spec.js rename webapp/components/{ => LocaleSwitch}/LocaleSwitch.vue (100%) diff --git a/webapp/components/LocaleSwitch/LocaleSwitch.spec.js b/webapp/components/LocaleSwitch/LocaleSwitch.spec.js new file mode 100644 index 000000000..ae81881d6 --- /dev/null +++ b/webapp/components/LocaleSwitch/LocaleSwitch.spec.js @@ -0,0 +1,68 @@ +import { mount, createLocalVue } from '@vue/test-utils' +import Styleguide from '@human-connection/styleguide' +import Vuex from 'vuex' +import VTooltip from 'v-tooltip' +import LocaleSwitch from './LocaleSwitch.vue' +import { mutations } from '~/store/editor' + +const localVue = createLocalVue() + +localVue.use(Vuex) +localVue.use(Styleguide) +localVue.use(VTooltip) + +describe('LocaleSwitch.vue', () => { + let wrapper + let mocks + let computed + let deutschLanguageItem + + beforeEach(() => { + mocks = { + $i18n: { + locale: () => 'de', + set: jest.fn(), + }, + $t: jest.fn(), + setPlaceholderText: jest.fn(), + } + computed = { + current: () => { + return { code: 'en' } + }, + routes: () => { + return [ + { + name: 'English', + path: 'en', + }, + { + name: 'Deutsch', + path: 'de', + }, + ] + }, + } + }) + + describe('mount', () => { + const store = new Vuex.Store({ + mutations: { + 'editor/SET_PLACEHOLDER_TEXT': mutations.SET_PLACEHOLDER_TEXT, + }, + }) + const Wrapper = () => { + return mount(LocaleSwitch, { mocks, localVue, store, computed }) + } + beforeEach(() => { + wrapper = Wrapper() + wrapper.find('.locale-menu').trigger('click') + deutschLanguageItem = wrapper.findAll('li').at(1) + deutschLanguageItem.trigger('click') + }) + + it("changes a user's locale", () => { + expect(mocks.$i18n.set).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/webapp/components/LocaleSwitch.vue b/webapp/components/LocaleSwitch/LocaleSwitch.vue similarity index 100% rename from webapp/components/LocaleSwitch.vue rename to webapp/components/LocaleSwitch/LocaleSwitch.vue diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index 7708d6d2e..162bae0fb 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -92,7 +92,7 @@ diff --git a/webapp/pages/password-reset.vue b/webapp/pages/password-reset.vue index 9e7d2d195..98642cfe8 100644 --- a/webapp/pages/password-reset.vue +++ b/webapp/pages/password-reset.vue @@ -3,51 +3,7 @@ - - - - - - - {{ $t('password-reset.form.description') }} - - - - {{ $t('password-reset.form.submit') }} - - -
- - - - - - -
-
-
+
@@ -55,58 +11,12 @@ From aa6855434de199fa68e09125d0ea5642db759249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 17 Jun 2019 15:10:57 +0200 Subject: [PATCH 31/89] Emit `submitted` from PasswordReset component --- .../PasswordReset/PasswordReset.spec.js | 19 +++---- .../PasswordReset/PasswordReset.vue | 6 ++- .../PasswordReset/VerifyCode.spec.js | 50 +++++++++++++++++++ .../components/PasswordReset/VerifyCode.vue | 9 ++++ webapp/locales/de.json | 5 ++ webapp/locales/en.json | 5 ++ 6 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 webapp/components/PasswordReset/VerifyCode.spec.js create mode 100644 webapp/components/PasswordReset/VerifyCode.vue diff --git a/webapp/components/PasswordReset/PasswordReset.spec.js b/webapp/components/PasswordReset/PasswordReset.spec.js index e55b9273a..6284fed36 100644 --- a/webapp/components/PasswordReset/PasswordReset.spec.js +++ b/webapp/components/PasswordReset/PasswordReset.spec.js @@ -6,7 +6,7 @@ const localVue = createLocalVue() localVue.use(Styleguide) -describe('ProfileSlug', () => { +describe('PasswordReset', () => { let wrapper let Wrapper let mocks @@ -26,6 +26,8 @@ describe('ProfileSlug', () => { }) describe('mount', () => { + beforeEach(jest.useFakeTimers) + Wrapper = () => { return mount(PasswordReset, { mocks, @@ -35,7 +37,7 @@ describe('ProfileSlug', () => { it('renders a password reset form', () => { wrapper = Wrapper() - expect(wrapper.find('.password-reset-card').exists()).toBe(true) + expect(wrapper.find('.password-reset').exists()).toBe(true) }) describe('submit', () => { @@ -62,14 +64,13 @@ describe('ProfileSlug', () => { const expected = ['password-reset.form.submitted', { email: 'mail@example.org' }] expect(mocks.$t).toHaveBeenCalledWith(...expected) }) - }) - describe('given password reset token as URL param', () => { - it.todo('displays a form to update your password') - describe('submitting new password', () => { - it.todo('calls resetPassword graphql mutation') - it.todo('delivers new password to backend') - it.todo('displays success message') + describe('after animation', () => { + beforeEach(jest.runAllTimers) + + it('emits `submitted`', () => { + expect(wrapper.emitted('submitted')).toBeTruthy() + }) }) }) }) diff --git a/webapp/components/PasswordReset/PasswordReset.vue b/webapp/components/PasswordReset/PasswordReset.vue index 7a74bbe31..175207949 100644 --- a/webapp/components/PasswordReset/PasswordReset.vue +++ b/webapp/components/PasswordReset/PasswordReset.vue @@ -1,5 +1,5 @@