Commit f082feed3feb02387c2eea02f0c839653ad4270c

Authored by Victor Costa
1 parent c0ec34f9

Refactor language component

src/app/components/language-selector/language-selector.component.spec.ts
@@ -11,69 +11,28 @@ describe("Components", () => { @@ -11,69 +11,28 @@ describe("Components", () => {
11 11
12 beforeEach(angular.mock.module("templates")); 12 beforeEach(angular.mock.module("templates"));
13 13
  14 + let languageService: any;
  15 +
14 let buildComponent = (): Promise<ComponentFixture> => { 16 let buildComponent = (): Promise<ComponentFixture> => {
  17 + languageService = jasmine.createSpyObj("languageService", ["availableLanguages", "currentLanguage"])
15 return helpers.quickCreateComponent({ 18 return helpers.quickCreateComponent({
16 template: "<language-selector></language-selector>", 19 template: "<language-selector></language-selector>",
17 directives: [LanguageSelector], 20 directives: [LanguageSelector],
18 providers: [ 21 providers: [
19 - provide('$translate', {  
20 - useValue: helpers.mocks.$translate  
21 - }),  
22 - provide('tmhDynamicLocale', {  
23 - useValue: helpers.mocks.tmhDynamicLocale  
24 - }),  
25 - provide('amMoment', {  
26 - useValue: helpers.mocks.amMoment  
27 - }),  
28 - provide('angularLoad', {  
29 - useValue: helpers.mocks.angularLoad 22 + provide('LanguageService', {
  23 + useValue: languageService
30 }) 24 })
31 ].concat(helpers.provideFilters("translateFilter")) 25 ].concat(helpers.provideFilters("translateFilter"))
32 }); 26 });
33 } 27 }
34 28
35 - it("set available languages when change language", (done) => {  
36 - let component: LanguageSelector = new LanguageSelector(  
37 - <any>helpers.mocks.$translate,  
38 - <any>helpers.mocks.tmhDynamicLocale,  
39 - <any>helpers.mocks.amMoment,  
40 - <any>helpers.mocks.angularLoad  
41 - );  
42 - component.availableLanguages = null;  
43 - expect(component.availableLanguages).toBeNull();  
44 - component.changeLanguage('en');  
45 - expect(component.availableLanguages).toBeDefined();  
46 - done();  
47 - });  
48 -  
49 it("display language options", (done) => { 29 it("display language options", (done) => {
50 buildComponent().then(fixture => { 30 buildComponent().then(fixture => {
  31 + fixture.debugElement.getLocal("$rootScope").$apply();
51 expect(fixture.debugElement.queryAll('li.language').length).toEqual(2); 32 expect(fixture.debugElement.queryAll('li.language').length).toEqual(2);
52 done(); 33 done();
53 }); 34 });
54 }); 35 });
55 36
56 - it("change the language", (done) => {  
57 - buildComponent().then(fixture => {  
58 - let component: LanguageSelector = fixture.debugElement.componentViewChildren[0].componentInstance;  
59 - let $q = fixture.debugElement.getLocal("$q");  
60 - let loadScripPromise = $q.defer();  
61 - loadScripPromise.resolve();  
62 - component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue(loadScripPromise.promise);  
63 - component["tmhDynamicLocale"].set = jasmine.createSpy("set");  
64 - component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue("en");  
65 - component["$translate"].use = jasmine.createSpy("use");  
66 -  
67 - component.changeLanguage('pt');  
68 - fixture.debugElement.getLocal("$rootScope").$digest();  
69 -  
70 - expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/moment/locale/pt.js");  
71 - expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/messageformat/locale/pt.js");  
72 - expect(component["tmhDynamicLocale"].set).toHaveBeenCalledWith("pt");  
73 - expect(component["$translate"].use).toHaveBeenCalledWith("pt");  
74 - done();  
75 - });  
76 - });  
77 -  
78 }); 37 });
79 }); 38 });
src/app/components/language-selector/language-selector.component.ts
1 import {Component, Inject} from "ng-forward"; 1 import {Component, Inject} from "ng-forward";
  2 +import {LanguageService} from "./language.service";
2 3
3 @Component({ 4 @Component({
4 selector: "language-selector", 5 selector: "language-selector",
5 templateUrl: "app/components/language-selector/language-selector.html" 6 templateUrl: "app/components/language-selector/language-selector.html"
6 }) 7 })
7 -@Inject("$translate", "tmhDynamicLocale", "amMoment", "angularLoad") 8 +@Inject(LanguageService)
8 export class LanguageSelector { 9 export class LanguageSelector {
9 10
10 - availableLanguages: any;  
11 -  
12 - constructor(private $translate: angular.translate.ITranslateService,  
13 - private tmhDynamicLocale: any,  
14 - private amMoment: any,  
15 - private angularLoad: any) {  
16 -  
17 - this.configAvailableLanguages();  
18 - this.changeLanguage(tmhDynamicLocale.get() || $translate.use());  
19 - } 11 + constructor(private languageService: LanguageService) { }
20 12
21 currentLanguage() { 13 currentLanguage() {
22 - return this.$translate.use(); 14 + return this.languageService.currentLanguage();
23 } 15 }
24 16
25 changeLanguage(language: string) { 17 changeLanguage(language: string) {
26 - this.changeMomentLocale(language);  
27 - this.tmhDynamicLocale.set(language);  
28 - this.angularLoad.loadScript(`/bower_components/messageformat/locale/${language}.js`).then(() => {  
29 - return this.$translate.use(language);  
30 - }).then(() => {  
31 - this.configAvailableLanguages();  
32 - });  
33 - }  
34 -  
35 - private configAvailableLanguages() {  
36 - this.availableLanguages = {  
37 - "en": this.$translate.instant("language.en"),  
38 - "pt": this.$translate.instant("language.pt")  
39 - }; 18 + this.languageService.changeLanguage(language);
40 } 19 }
41 20
42 - private changeMomentLocale(language: string) {  
43 - let localePromise = Promise.resolve();  
44 - if (language != "en") {  
45 - localePromise = this.angularLoad.loadScript(`/bower_components/moment/locale/${language}.js`);  
46 - }  
47 - localePromise.then(() => {  
48 - this.amMoment.changeLocale(language);  
49 - }); 21 + availableLanguages() {
  22 + return this.languageService.availableLanguages;
50 } 23 }
51 } 24 }
src/app/components/language-selector/language-selector.html
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 <span>{{"language.selector" | translate}}</span> <b class="caret"></b> 3 <span>{{"language.selector" | translate}}</span> <b class="caret"></b>
4 </a> 4 </a>
5 <ul class="dropdown-menu" dropdown-menu> 5 <ul class="dropdown-menu" dropdown-menu>
6 - <li ng-repeat="(language, description) in ctrl.availableLanguages" 6 + <li ng-repeat="(language, description) in ctrl.availableLanguages()"
7 class="language language-{{language}}" ng-class="{'active': language==ctrl.currentLanguage()}"> 7 class="language language-{{language}}" ng-class="{'active': language==ctrl.currentLanguage()}">
8 <a href="#" ng-click="ctrl.changeLanguage(language)"> 8 <a href="#" ng-click="ctrl.changeLanguage(language)">
9 {{description}} 9 {{description}}
src/app/components/language-selector/language.service.spec.ts 0 → 100644
@@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
  1 +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {provide} from 'ng-forward';
  3 +
  4 +import {LanguageService} from './language.service';
  5 +
  6 +import * as helpers from "../../../spec/helpers";
  7 +
  8 +describe("Services", () => {
  9 +
  10 + describe("Language Service", () => {
  11 +
  12 + let $rootScope: ng.IScope;
  13 + let $q: ng.IQService;
  14 +
  15 + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => {
  16 + $rootScope = _$rootScope_;
  17 + $q = _$q_;
  18 + }));
  19 +
  20 + it("set available languages when change language", (done) => {
  21 + let component: LanguageService = new LanguageService(
  22 + <any>helpers.mocks.$translate,
  23 + <any>helpers.mocks.tmhDynamicLocale,
  24 + <any>helpers.mocks.amMoment,
  25 + <any>helpers.mocks.angularLoad,
  26 + helpers.mocks.scopeWithEvents
  27 + );
  28 + component.availableLanguages = null;
  29 + expect(component.availableLanguages).toBeNull();
  30 + component.changeLanguage('en');
  31 + expect(component.availableLanguages).toBeDefined();
  32 + done();
  33 + });
  34 +
  35 + it("change the language", (done) => {
  36 + let component: LanguageService = new LanguageService(
  37 + <any>helpers.mocks.$translate,
  38 + <any>helpers.mocks.tmhDynamicLocale,
  39 + <any>helpers.mocks.amMoment,
  40 + <any>helpers.mocks.angularLoad,
  41 + helpers.mocks.scopeWithEvents
  42 + );
  43 + let loadScripPromise = $q.defer();
  44 + loadScripPromise.resolve();
  45 + component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue(loadScripPromise.promise);
  46 + component["tmhDynamicLocale"].set = jasmine.createSpy("set");
  47 + component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue("en");
  48 + component["$translate"].use = jasmine.createSpy("use");
  49 +
  50 + component.changeLanguage('pt');
  51 + $rootScope.$digest();
  52 +
  53 + expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/moment/locale/pt.js");
  54 + expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/messageformat/locale/pt.js");
  55 + expect(component["tmhDynamicLocale"].set).toHaveBeenCalledWith("pt");
  56 + expect(component["$translate"].use).toHaveBeenCalledWith("pt");
  57 + done();
  58 + });
  59 +
  60 + });
  61 +});
src/app/components/language-selector/language.service.ts 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +import {Injectable, Inject} from "ng-forward";
  2 +
  3 +@Injectable()
  4 +@Inject("$translate", "tmhDynamicLocale", "amMoment", "angularLoad", "$rootScope")
  5 +export class LanguageService {
  6 +
  7 + availableLanguages: any;
  8 +
  9 + constructor(private $translate: angular.translate.ITranslateService,
  10 + private tmhDynamicLocale: angular.dynamicLocale.tmhDynamicLocaleService,
  11 + private amMoment: any,
  12 + private angularLoad: any,
  13 + private $rootScope: any) {
  14 +
  15 + this.configAvailableLanguages();
  16 + this.$rootScope.$on("$localeChangeSuccess", () => {
  17 + this.changeLanguage(tmhDynamicLocale.get() || $translate.use());
  18 + });
  19 + }
  20 +
  21 + currentLanguage() {
  22 + return this.$translate.use();
  23 + }
  24 +
  25 + changeLanguage(language: string) {
  26 + if (!language) {
  27 + console.log("WARN: language undefined");
  28 + return;
  29 + }
  30 + this.changeMomentLocale(language);
  31 + this.tmhDynamicLocale.set(language);
  32 + this.angularLoad.loadScript(`/bower_components/messageformat/locale/${language}.js`).then(() => {
  33 + return this.$translate.use(language);
  34 + }).then(() => {
  35 + this.configAvailableLanguages();
  36 + });
  37 + }
  38 +
  39 + translate(text: string) {
  40 + return this.$translate.instant(text);
  41 + }
  42 +
  43 + private configAvailableLanguages() {
  44 + this.availableLanguages = {
  45 + "en": this.$translate.instant("language.en"),
  46 + "pt": this.$translate.instant("language.pt")
  47 + };
  48 + }
  49 +
  50 + private changeMomentLocale(language: string) {
  51 + let localePromise = Promise.resolve();
  52 + if (language != "en") {
  53 + localePromise = this.angularLoad.loadScript(`/bower_components/moment/locale/${language}.js`);
  54 + }
  55 + localePromise.then(() => {
  56 + this.amMoment.changeLocale(language);
  57 + });
  58 + }
  59 +}
src/app/components/notification/notification.component.spec.ts
@@ -13,12 +13,12 @@ describe(&quot;Components&quot;, () =&gt; { @@ -13,12 +13,12 @@ describe(&quot;Components&quot;, () =&gt; {
13 13
14 beforeEach(angular.mock.module("templates")); 14 beforeEach(angular.mock.module("templates"));
15 15
16 - it("use the default message when call notification component without a specific message", done => { 16 + it("use the default message when call notification component without a message", done => {
17 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); 17 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]);
18 sweetAlert.swal = jasmine.createSpy("swal"); 18 sweetAlert.swal = jasmine.createSpy("swal");
19 19
20 - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.$translate);  
21 - component.httpError(500, {}); 20 + let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.languageService);
  21 + component.error();
22 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ 22 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
23 text: Notification.DEFAULT_ERROR_MESSAGE, 23 text: Notification.DEFAULT_ERROR_MESSAGE,
24 type: "error" 24 type: "error"
@@ -26,27 +26,26 @@ describe(&quot;Components&quot;, () =&gt; { @@ -26,27 +26,26 @@ describe(&quot;Components&quot;, () =&gt; {
26 done(); 26 done();
27 }); 27 });
28 28
29 - it("use the default message when call notification component without error data", done => { 29 + it("display a success message when call notification success", done => {
30 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); 30 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]);
31 sweetAlert.swal = jasmine.createSpy("swal"); 31 sweetAlert.swal = jasmine.createSpy("swal");
32 32
33 - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.$translate);  
34 - component.httpError(500, null); 33 + let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.languageService);
  34 + component.success("title", "message", 1000);
35 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ 35 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
36 - text: Notification.DEFAULT_ERROR_MESSAGE,  
37 - type: "error" 36 + type: "success"
38 })); 37 }));
39 done(); 38 done();
40 }); 39 });
41 40
42 - it("display a success message when call notification success", done => { 41 + it("display a message relative to the http error code", done => {
43 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); 42 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]);
44 sweetAlert.swal = jasmine.createSpy("swal"); 43 sweetAlert.swal = jasmine.createSpy("swal");
45 44
46 - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.$translate);  
47 - component.success("title", "message", 1000); 45 + let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.languageService);
  46 + component.httpError(500, {});
48 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ 47 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
49 - type: "success" 48 + text: "notification.http_error.500.message"
50 })); 49 }));
51 done(); 50 done();
52 }); 51 });
@@ -55,7 +54,7 @@ describe(&quot;Components&quot;, () =&gt; { @@ -55,7 +54,7 @@ describe(&quot;Components&quot;, () =&gt; {
55 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); 54 let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]);
56 sweetAlert.swal = jasmine.createSpy("swal"); 55 sweetAlert.swal = jasmine.createSpy("swal");
57 56
58 - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.$translate); 57 + let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.languageService);
59 component.success("title", "message"); 58 component.success("title", "message");
60 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ 59 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
61 type: "success", 60 type: "success",
src/app/components/notification/notification.component.ts
1 import {Injectable, Inject} from "ng-forward"; 1 import {Injectable, Inject} from "ng-forward";
  2 +import {LanguageService} from "../language-selector/language.service";
2 3
3 @Injectable() 4 @Injectable()
4 -@Inject("$log", "SweetAlert", "$translate") 5 +@Inject("$log", "SweetAlert", LanguageService)
5 export class Notification { 6 export class Notification {
6 7
7 constructor( 8 constructor(
8 private $log: ng.ILogService, 9 private $log: ng.ILogService,
9 private SweetAlert: any, 10 private SweetAlert: any,
10 - private $translate: angular.translate.ITranslateService 11 + private languageService: LanguageService
11 ) { } 12 ) { }
12 13
13 public static DEFAULT_ERROR_TITLE = "notification.error.default.title"; 14 public static DEFAULT_ERROR_TITLE = "notification.error.default.title";
@@ -15,16 +16,15 @@ export class Notification { @@ -15,16 +16,15 @@ export class Notification {
15 public static DEFAULT_SUCCESS_TIMER = 1000; 16 public static DEFAULT_SUCCESS_TIMER = 1000;
16 17
17 error(message: string = Notification.DEFAULT_ERROR_MESSAGE, title: string = Notification.DEFAULT_ERROR_TITLE) { 18 error(message: string = Notification.DEFAULT_ERROR_MESSAGE, title: string = Notification.DEFAULT_ERROR_TITLE) {
18 - this.$log.debug("Notification error:", title, message); 19 + this.$log.debug("Notification error:", title, message, this.languageService.currentLanguage());
19 this.SweetAlert.swal({ 20 this.SweetAlert.swal({
20 - title: this.$translate.instant(title),  
21 - text: this.$translate.instant(message), 21 + title: this.languageService.translate(title),
  22 + text: this.languageService.translate(message),
22 type: "error" 23 type: "error"
23 }); 24 });
24 } 25 }
25 26
26 httpError(status: number, data: any): boolean { 27 httpError(status: number, data: any): boolean {
27 - let message = (data || {}).message || Notification.DEFAULT_ERROR_MESSAGE;  
28 this.error(`notification.http_error.${status}.message`); 28 this.error(`notification.http_error.${status}.message`);
29 return true; // return true to indicate that the error was already handled 29 return true; // return true to indicate that the error was already handled
30 } 30 }
src/spec/mocks.ts
@@ -6,13 +6,11 @@ class ScopeWithEvents { @@ -6,13 +6,11 @@ class ScopeWithEvents {
6 } 6 }
7 7
8 public $on(eventName: string, func: Function) { 8 public $on(eventName: string, func: Function) {
9 - console.log(this.listeners);  
10 if ((<any>this.listeners)[eventName]) { 9 if ((<any>this.listeners)[eventName]) {
11 (<any>this.listeners)[eventName].push(func); 10 (<any>this.listeners)[eventName].push(func);
12 } else { 11 } else {
13 (<any>this.listeners)[eventName] = [func]; 12 (<any>this.listeners)[eventName] = [func];
14 } 13 }
15 - console.log(this.listeners);  
16 } 14 }
17 15
18 public $emit(message: string, arg?: any) { 16 public $emit(message: string, arg?: any) {
@@ -97,5 +95,10 @@ export var mocks = { @@ -97,5 +95,10 @@ export var mocks = {
97 }, 95 },
98 $log: { 96 $log: {
99 debug: () => { } 97 debug: () => { }
  98 + },
  99 + languageService: {
  100 + currentLanguage: () => { },
  101 + changeLanguage: (lang: string) => { },
  102 + translate: (text: string) => { return text }
100 } 103 }
101 }; 104 };
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 "devDependencies": {}, 4 "devDependencies": {},
5 "ambientDependencies": { 5 "ambientDependencies": {
6 "angular": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular.d.ts#1c4a34873c9e70cce86edd0e61c559e43dfa5f75", 6 "angular": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular.d.ts#1c4a34873c9e70cce86edd0e61c559e43dfa5f75",
  7 + "angular-dynamic-locale": "github:DefinitelyTyped/DefinitelyTyped/angular-dynamic-locale/angular-dynamic-locale.d.ts#42ab06c354ccbadeafa60df1efc76b81a22809b1",
7 "angular-mocks": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular-mocks.d.ts", 8 "angular-mocks": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular-mocks.d.ts",
8 "angular-translate": "github:DefinitelyTyped/DefinitelyTyped/angular-translate/angular-translate.d.ts#19850bf86c876e0c2544842114878ece4664941a", 9 "angular-translate": "github:DefinitelyTyped/DefinitelyTyped/angular-translate/angular-translate.d.ts#19850bf86c876e0c2544842114878ece4664941a",
9 "angular-ui-router": "github:DefinitelyTyped/DefinitelyTyped/angular-ui-router/angular-ui-router.d.ts#655f8c1bf3c71b0e1ba415b36309604f79326ac8", 10 "angular-ui-router": "github:DefinitelyTyped/DefinitelyTyped/angular-ui-router/angular-ui-router.d.ts#655f8c1bf3c71b0e1ba415b36309604f79326ac8",