Commit 0aebb5d92ebefd9d0659d7341bf929a20df8b0b0

Authored by Victor Costa
2 parents 3ad422aa 022de891

Merge branch 'registration_errors'

src/app/account/register-component.html
@@ -3,6 +3,14 @@ @@ -3,6 +3,14 @@
3 <h1>{{"account.register.welcomeMessageTitle" | translate }}</h1> 3 <h1>{{"account.register.welcomeMessageTitle" | translate }}</h1>
4 <div class="environment-signup-intro" ng-bind-html="ctrl.environment.signup_intro"></div> 4 <div class="environment-signup-intro" ng-bind-html="ctrl.environment.signup_intro"></div>
5 </div> 5 </div>
  6 +
  7 + <div class="error-messages" ng-if="ctrl.errorMessages">
  8 + <p>{{ "account.register.errorMessagesTitle" | translate }}</p>
  9 + <ul>
  10 + <li ng-repeat="error in ctrl.errorMessages">{{ error.fieldName | translate }} {{ error.message | translate }}</li>
  11 + </ul>
  12 + </div>
  13 +
6 <form name="signupForm"> 14 <form name="signupForm">
7 <div class="row"> 15 <div class="row">
8 <div class="col-md-12 register-field"> 16 <div class="col-md-12 register-field">
@@ -17,10 +25,12 @@ @@ -17,10 +25,12 @@
17 <span class="input-group-addon" style="font-weight: bold;"><i class="fa fa-globe"></i>&nbsp;{{ ctrl.environment.host }}</span> 25 <span class="input-group-addon" style="font-weight: bold;"><i class="fa fa-globe"></i>&nbsp;{{ ctrl.environment.host }}</span>
18 <input type="text" id="username" name="username" class="form-control" placeholder="{{'account.register.usernameLabel' | translate }}" ng-model="ctrl.account.login" required> 26 <input type="text" id="username" name="username" class="form-control" placeholder="{{'account.register.usernameLabel' | translate }}" ng-model="ctrl.account.login" required>
19 </div> 27 </div>
20 - <div class="help-block" ng-show="signupForm.username.$touched" ng-messages="signupForm.username.$error">  
21 - <ul class="list-unstyled">  
22 - <li ng-messages-include="languages/messages.html"></li>  
23 - </ul> 28 + <div class="help-block" ng-messages="signupForm.username.$error">
  29 + <div ng-if="signupForm.username.$touched && !signupForm.username.$empty">
  30 + <ul class="list-unstyled">
  31 + <li ng-messages-include="app/shared/messages/form-errors.html"></li>
  32 + </ul>
  33 + </div>
24 </div> 34 </div>
25 </div> 35 </div>
26 36
@@ -29,22 +39,26 @@ @@ -29,22 +39,26 @@
29 <span class="input-group-addon"><i class="fa fa-envelope"></i></span> 39 <span class="input-group-addon"><i class="fa fa-envelope"></i></span>
30 <input type="email" class="form-control" id="email" name="email" placeholder="{{'account.register.emailLabel' | translate }}" ng-model="ctrl.account.email" required> 40 <input type="email" class="form-control" id="email" name="email" placeholder="{{'account.register.emailLabel' | translate }}" ng-model="ctrl.account.email" required>
31 </div> 41 </div>
32 - <div class="help-block" ng-show="signupForm.email.$touched" ng-messages="signupForm.email.$error">  
33 - <ul class="list-unstyled">  
34 - <li ng-messages-include="languages/messages.html"></li>  
35 - </ul> 42 + <div class="help-block" ng-messages="signupForm.email.$error">
  43 + <div ng-if="signupForm.email.$touched && !signupForm.email.$empty">
  44 + <ul class="list-unstyled">
  45 + <li ng-messages-include="app/shared/messages/form-errors.html"></li>
  46 + </ul>
  47 + </div>
36 </div> 48 </div>
37 </div> 49 </div>
38 50
39 <div class="form-group col-md-6 register-field" ng-class="ctrl.isInvalid(signupForm.password)"> 51 <div class="form-group col-md-6 register-field" ng-class="ctrl.isInvalid(signupForm.password)">
40 <div class="input-group"> 52 <div class="input-group">
41 <span class="input-group-addon"><i class="fa fa-lock"></i></span> 53 <span class="input-group-addon"><i class="fa fa-lock"></i></span>
42 - <input type="password" class="form-control" id="password" name="password" placeholder="{{'account.register.passwordLabel' | translate }}" ng-model="ctrl.account.password" required> 54 + <input type="password" class="form-control" id="password" name="password" placeholder="{{'account.register.passwordLabel' | translate }}" ng-model="ctrl.account.password" required minlength="4">
43 </div> 55 </div>
44 - <div class="help-block" ng-show="signupForm.password.$touched" ng-messages="signupForm.password.$error">  
45 - <ul class="list-unstyled">  
46 - <li ng-messages-include="languages/messages.html"></li>  
47 - </ul> 56 + <div class="help-block" ng-messages="signupForm.password.$error">
  57 + <div ng-if="signupForm.password.$touched && !signupForm.password.$empty">
  58 + <ul class="list-unstyled">
  59 + <li ng-messages-include="app/shared/messages/form-errors.html"></li>
  60 + </ul>
  61 + </div>
48 </div> 62 </div>
49 </div> 63 </div>
50 64
@@ -55,7 +69,7 @@ @@ -55,7 +69,7 @@
55 </div> 69 </div>
56 <div class="help-block" ng-show="signupForm.passwordConfirm.$touched" ng-messages="signupForm.passwordConfirm.$error"> 70 <div class="help-block" ng-show="signupForm.passwordConfirm.$touched" ng-messages="signupForm.passwordConfirm.$error">
57 <ul class="list-unstyled"> 71 <ul class="list-unstyled">
58 - <li ng-messages-include="languages/messages.html"></li> 72 + <li ng-messages-include="app/shared/messages/form-errors.html"></li>
59 <li ng-message="passwordMatch" translate="messages.invalid.passwordMatch"></li> 73 <li ng-message="passwordMatch" translate="messages.invalid.passwordMatch"></li>
60 </ul> 74 </ul>
61 </div> 75 </div>
@@ -72,6 +86,5 @@ @@ -72,6 +86,5 @@
72 </div> 86 </div>
73 </form> 87 </form>
74 88
75 - <p class="already-registered-message">{{"account.register.haveAccountMessage" | translate}}</p>  
76 - 89 + <a class="already-registered-message" ng-href="#" ng-click="ctrl.openLogin()">{{"account.register.haveAccountMessage" | translate}}</a>
77 </div> 90 </div>
src/app/account/register-terms.html
1 <div class="modal-header"> 1 <div class="modal-header">
2 - <h3 class="modal-title">Register terms</h3> 2 + <h3 class="modal-title">{{ "account.register.termsOfUseTitle" | translate }} - {{ ctrl.environment.name }}</h3>
3 </div> 3 </div>
4 <div class="modal-body modal-body-overflow" ng-bind-html="ctrl.environment.terms_of_use"></div> 4 <div class="modal-body modal-body-overflow" ng-bind-html="ctrl.environment.terms_of_use"></div>
5 <div class="modal-footer"> 5 <div class="modal-footer">
src/app/account/register.component.spec.ts
1 import { ComponentTestHelper, createClass } from "../../spec/component-test-helper"; 1 import { ComponentTestHelper, createClass } from "../../spec/component-test-helper";
2 import * as helpers from "../../spec/helpers"; 2 import * as helpers from "../../spec/helpers";
3 import { RegisterComponent } from "./register.component"; 3 import { RegisterComponent } from "./register.component";
4 -// import {RegisterService} from "../../lib/ng-noosfero-api/http/register.service"  
5 -  
6 4
7 describe("Register Component", () => { 5 describe("Register Component", () => {
8 const htmlTemplate: string = '<noosfero-register></noosfero-register>'; 6 const htmlTemplate: string = '<noosfero-register></noosfero-register>';
9 7
10 let helper: ComponentTestHelper<RegisterComponent>; 8 let helper: ComponentTestHelper<RegisterComponent>;
11 let registerService = helpers.mocks.registerService; 9 let registerService = helpers.mocks.registerService;
12 - let stateService = jasmine.createSpyObj("$state", ["transitionTo"]); 10 + let stateService: angular.ui.IStateService;
13 let notificationService = helpers.mocks.notificationService; 11 let notificationService = helpers.mocks.notificationService;
14 notificationService.success = jasmine.createSpy('success'); 12 notificationService.success = jasmine.createSpy('success');
15 notificationService.error = jasmine.createSpy('error'); 13 notificationService.error = jasmine.createSpy('error');
16 -  
17 -  
18 - let account: any = {  
19 - id: 1,  
20 - login: 'test',  
21 - email: 'test@email.com',  
22 - password: 'xxx',  
23 - passwordConfirmation: 'xxx'  
24 - }; 14 + let user_data: any;
  15 + let response: any;
  16 + let deferred: any;
  17 + let $rootScope: ng.IRootScopeService;
  18 + let $q: ng.IQService;
25 19
26 beforeEach(() => { 20 beforeEach(() => {
  21 + stateService = jasmine.createSpyObj("$state", ["transitionTo"]);
  22 +
27 angular.mock.module('templates'); 23 angular.mock.module('templates');
28 angular.mock.module('ngSanitize'); 24 angular.mock.module('ngSanitize');
29 angular.mock.module('ngMessages'); 25 angular.mock.module('ngMessages');
@@ -45,8 +41,53 @@ describe(&quot;Register Component&quot;, () =&gt; { @@ -45,8 +41,53 @@ describe(&quot;Register Component&quot;, () =&gt; {
45 helper = new ComponentTestHelper<RegisterComponent>(cls, done); 41 helper = new ComponentTestHelper<RegisterComponent>(cls, done);
46 }); 42 });
47 43
  44 + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => {
  45 + $rootScope = _$rootScope_;
  46 + $q = _$q_;
  47 + }));
  48 +
48 it('register page was rendered', () => { 49 it('register page was rendered', () => {
49 expect(helper.debugElement.query('div.register-page').length).toEqual(1); 50 expect(helper.debugElement.query('div.register-page').length).toEqual(1);
50 }); 51 });
51 52
  53 + it("registers a new user", done => {
  54 + user_data = { username: "username", password: "password", password_confirmation: "password", email: "user@company.com" };
  55 + response = {};
  56 +
  57 + helper.component.account = user_data;
  58 +
  59 + deferred = $q.defer();
  60 + deferred.resolve({ data: response });
  61 + registerService.createAccount = jasmine.createSpy("createAccount").and.returnValue(deferred.promise);
  62 +
  63 + helper.component.signup();
  64 + helper.detectChanges();
  65 +
  66 + expect(registerService.createAccount).toHaveBeenCalledWith(user_data);
  67 + expect(stateService.transitionTo).toHaveBeenCalledWith("main.environment");
  68 + expect(notificationService.success).toHaveBeenCalled();
  69 +
  70 + done();
  71 + });
  72 +
  73 + it("gives error when registration fails", done => {
  74 + user_data = { password: "pas" };
  75 + response = { data: { message: '{ "password": ["is too short"] }' } };
  76 +
  77 + helper.component.account = user_data;
  78 +
  79 + deferred = $q.defer();
  80 + deferred.reject(response);
  81 + registerService.createAccount = jasmine.createSpy("createAccount").and.returnValue(deferred.promise);
  82 +
  83 + helper.component.signup();
  84 + helper.detectChanges();
  85 +
  86 + expect(registerService.createAccount).toHaveBeenCalledWith(user_data);
  87 +
  88 + expect(stateService.transitionTo).not.toHaveBeenCalledWith("main.environment");
  89 + expect(notificationService.error).toHaveBeenCalled();
  90 +
  91 + done();
  92 + });
52 }); 93 });
src/app/account/register.component.ts
@@ -3,6 +3,7 @@ import { RegisterService } from &quot;./../../lib/ng-noosfero-api/http/register.servi @@ -3,6 +3,7 @@ import { RegisterService } from &quot;./../../lib/ng-noosfero-api/http/register.servi
3 import { NotificationService } from "./../shared/services/notification.service"; 3 import { NotificationService } from "./../shared/services/notification.service";
4 import { EnvironmentService } from "../../lib/ng-noosfero-api/http/environment.service"; 4 import { EnvironmentService } from "../../lib/ng-noosfero-api/http/environment.service";
5 import { RegisterController } from "./register.controller"; 5 import { RegisterController } from "./register.controller";
  6 +import { AuthController } from "./../login";
6 import { IModalComponent } from "../shared/components/interfaces"; 7 import { IModalComponent } from "../shared/components/interfaces";
7 8
8 @Component({ 9 @Component({
@@ -20,6 +21,8 @@ export class RegisterComponent { @@ -20,6 +21,8 @@ export class RegisterComponent {
20 21
21 modalInstance: ng.ui.bootstrap.IModalServiceInstance; 22 modalInstance: ng.ui.bootstrap.IModalServiceInstance;
22 23
  24 + errorMessages: any[];
  25 +
23 constructor( 26 constructor(
24 private $state: ng.ui.IStateService, 27 private $state: ng.ui.IStateService,
25 private $uibModal: ng.ui.bootstrap.IModalService, 28 private $uibModal: ng.ui.bootstrap.IModalService,
@@ -33,19 +36,27 @@ export class RegisterComponent { @@ -33,19 +36,27 @@ export class RegisterComponent {
33 } 36 }
34 37
35 signup() { 38 signup() {
36 - if (this.account.password === this.account.passwordConfirmation) {  
37 - this.registerService.createAccount(this.account).then((response) => {  
38 -  
39 - if (response.status === 201) {  
40 - this.$state.transitionTo('main.environment');  
41 - this.notificationService.success({ title: "account.register.success.title", message: "account.register.success.message" });  
42 - } else {  
43 - throw new Error('Invalid attributes'); 39 + let error = '';
  40 + let errors: any;
  41 + let field = '';
  42 + this.errorMessages = [];
  43 + this.registerService.createAccount(this.account).then((response) => {
  44 + this.$state.transitionTo('main.environment');
  45 + this.notificationService.success({ title: "account.register.success.title", message: "account.register.success.message" });
  46 + }).catch((response) => {
  47 + if (response.data.error) {
  48 + errors = response.data['error'].split(', ');
  49 + for (error in errors) {
  50 + this.errorMessages.push({ message: errors[error] });
  51 + }
  52 + } else if (response.data.message) {
  53 + errors = JSON.parse(response.data.message);
  54 + for (field in errors) {
  55 + this.errorMessages.push({ fieldName: field, message: errors[field][0] });
44 } 56 }
45 - });  
46 - } else {  
47 - this.notificationService.error({ message: "account.register.passwordConfirmation.failed" });  
48 - } 57 + }
  58 + this.notificationService.error({ title: "account.register.save.failed" });
  59 + });
49 } 60 }
50 61
51 isInvalid(field: any): any { 62 isInvalid(field: any): any {
@@ -63,4 +74,14 @@ export class RegisterComponent { @@ -63,4 +74,14 @@ export class RegisterComponent {
63 scope: this.$scope 74 scope: this.$scope
64 }); 75 });
65 } 76 }
  77 +
  78 + openLogin() {
  79 + this.modalInstance = this.$uibModal.open({
  80 + templateUrl: 'app/login/login.html',
  81 + controller: AuthController,
  82 + controllerAs: 'vm',
  83 + bindToController: true
  84 + });
  85 + }
  86 +
66 } 87 }
src/app/account/scss/register.scss
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 .register-page .already-registered-message { 29 .register-page .already-registered-message {
30 margin-top: 60px; 30 margin-top: 60px;
31 text-align: center; 31 text-align: center;
  32 + display: block;
32 } 33 }
33 34
34 .register-page input { 35 .register-page input {
src/app/shared/messages/form-errors.html 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +<li ng-message="required" translate="messages.invalid.required"></li>
  2 +<li ng-message="minlength" translate="messages.invalid.minlength"></li>
  3 +<li ng-message="maxlength" translate="messages.invalid.maxlength"></li>
  4 +<li ng-message="email" translate="messages.invalid.email"></li>
src/languages/en.json
@@ -120,6 +120,7 @@ @@ -120,6 +120,7 @@
120 "account.register.signupMessage": "Register", 120 "account.register.signupMessage": "Register",
121 "account.register.haveAccountMessage": "Already have an account?", 121 "account.register.haveAccountMessage": "Already have an account?",
122 "account.register.termsOfUseMessage": "terms of use", 122 "account.register.termsOfUseMessage": "terms of use",
  123 + "account.register.termsOfUseTitle": "Terms of use",
123 "account.register.success.title": "Good job!", 124 "account.register.success.title": "Good job!",
124 "account.register.success.message": "Account created!", 125 "account.register.success.message": "Account created!",
125 "account.register.passwordConfirmation.failed": "Wrong password confirmation", 126 "account.register.passwordConfirmation.failed": "Wrong password confirmation",
@@ -148,5 +149,20 @@ @@ -148,5 +149,20 @@
148 "tasks.add_friend.message": "wants to be friend of", 149 "tasks.add_friend.message": "wants to be friend of",
149 "tasks.add_member.message": "wants to join", 150 "tasks.add_member.message": "wants to join",
150 "tasks.create_community.message": "wants to create a new community:", 151 "tasks.create_community.message": "wants to create a new community:",
151 - "tasks.suggest_article.message": "suggested a new article:" 152 + "tasks.suggest_article.message": "suggested a new article:",
  153 + "account.register.save.failed": "This account could not be created",
  154 + "account.register.mandatoryField": "This field is mandatory",
  155 + "account.register.invalidEmail": "This field must be a valid email address.",
  156 + "account.register.shortPassword": "The password must have at least 4 characters)",
  157 + "account.register.errorMessagesTitle": "There were problems with the following fields:",
  158 + "email is missing": "email can't be blank",
  159 + "login is missing": "username can't be blank",
  160 + "password is missing": "password can't be blank",
  161 + "login": "username",
  162 + "has already been taken": "has already been taken",
  163 + "email": "email",
  164 + "identifier": "identifier",
  165 + "is not available.": "is not available",
  166 + "user": "user",
  167 + "is invalid": "is invalid"
152 } 168 }
src/languages/messages.html
@@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
1 -<li ng-message="required" translate="messages.invalid.required"></li>  
2 -<li ng-message="minlength" translate="messages.invalid.minlength"></li>  
3 -<li ng-message="maxlength" translate="messages.invalid.maxlength"></li>  
4 -<li ng-message="email" translate="messages.invalid.email"></li>  
src/languages/pt.json
@@ -121,6 +121,7 @@ @@ -121,6 +121,7 @@
121 "account.register.passwordConfirmationLabel": "Confirmar senha", 121 "account.register.passwordConfirmationLabel": "Confirmar senha",
122 "account.register.accountCreatingMessage": "Ao criar uma conta, você está concordando com os ", 122 "account.register.accountCreatingMessage": "Ao criar uma conta, você está concordando com os ",
123 "account.register.termsOfUseMessage": "termos de uso", 123 "account.register.termsOfUseMessage": "termos de uso",
  124 + "account.register.termsOfUseTitle": "Termos de uso",
124 "account.register.signupMessage": "Criar Conta", 125 "account.register.signupMessage": "Criar Conta",
125 "account.register.haveAccountMessage": "Já possui uma conta?", 126 "account.register.haveAccountMessage": "Já possui uma conta?",
126 "account.register.success.title": "Bom trabalho!", 127 "account.register.success.title": "Bom trabalho!",
@@ -151,5 +152,20 @@ @@ -151,5 +152,20 @@
151 "tasks.add_friend.message": "quer ser amigo de", 152 "tasks.add_friend.message": "quer ser amigo de",
152 "tasks.add_member.message": "quer entrar em", 153 "tasks.add_member.message": "quer entrar em",
153 "tasks.create_community.message": "quer criar uma nova comunidade:", 154 "tasks.create_community.message": "quer criar uma nova comunidade:",
154 - "tasks.suggest_article.message": "sugeriu um novo artigo:" 155 + "tasks.suggest_article.message": "sugeriu um novo artigo:",
  156 + "account.register.save.failed": "A conta não pode ser criada",
  157 + "account.register.mandatoryField": "Este campo é obrigatório",
  158 + "account.register.invalidEmail": "Este campo deve ser um endereço de e-mail válido.",
  159 + "account.register.shortPassword": "A senha deve ter no mínimo 4 caracteres",
  160 + "account.register.errorMessagesTitle": "Houve erros com os seguintes campos:",
  161 + "email is missing": "email não pode ficar em branco",
  162 + "login is missing": "nome de usuário não pode ficar em branco",
  163 + "password is missing": "senha não pode ficar em branco",
  164 + "login": "nome de usuário",
  165 + "has already been taken": "já está sendo usado",
  166 + "email": "email",
  167 + "identifier": "identificador",
  168 + "is not available.": "não está disponível",
  169 + "user": "usuário",
  170 + "is invalid": "é inválido"
155 } 171 }