Commit 7044b1ae115fc6d86408eeb89aa97a8e2b356ebc
1 parent
5ef3ec06
Exists in
master
and in
31 other branches
refactory of the folder structure and file name convention
Showing
232 changed files
with
3197 additions
and
3132 deletions
Show diff stats
.vscode/settings.json
... | ... | @@ -0,0 +1 @@ |
1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
... | ... | @@ -0,0 +1,108 @@ |
1 | + | |
2 | +import {Input, provide, Component} from 'ng-forward'; | |
3 | +import {ArticleViewComponent, ArticleDefaultViewComponent} from './article-default-view.component'; | |
4 | + | |
5 | +import {createComponentFromClass, quickCreateComponent} from "../../spec/helpers"; | |
6 | + | |
7 | +// this htmlTemplate will be re-used between the container components in this spec file | |
8 | +const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-article>'; | |
9 | + | |
10 | + | |
11 | +describe("Components", () => { | |
12 | + | |
13 | + describe("ArticleView Component", () => { | |
14 | + | |
15 | + // the karma preprocessor html2js transform the templates html into js files which put | |
16 | + // the templates to the templateCache into the module templates | |
17 | + // we need to load the module templates here as the template for the | |
18 | + // component Noosfero ArtileView will be load on our tests | |
19 | + beforeEach(angular.mock.module("templates")); | |
20 | + | |
21 | + it("renders the default component when no specific component is found", (done: Function) => { | |
22 | + // Creating a container component (ArticleContainerComponent) to include | |
23 | + // the component under test (ArticleView) | |
24 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent] }) | |
25 | + class ArticleContainerComponent { | |
26 | + article = { type: 'anyArticleType' }; | |
27 | + profile = { name: 'profile-name' }; | |
28 | + constructor() { | |
29 | + } | |
30 | + } | |
31 | + | |
32 | + createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
33 | + // and here we can inspect and run the test assertions | |
34 | + | |
35 | + // gets the children component of ArticleContainerComponent | |
36 | + let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
37 | + | |
38 | + // and checks if the article View rendered was the Default Article View | |
39 | + expect(articleView.constructor.prototype).toEqual(ArticleDefaultViewComponent.prototype); | |
40 | + | |
41 | + // done needs to be called (it isn't really needed, as we can read in | |
42 | + // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
43 | + // because createAsync in ng-forward is not really async, but as the intention | |
44 | + // here is write tests in angular 2 ways, this is recommended | |
45 | + done(); | |
46 | + }); | |
47 | + | |
48 | + }); | |
49 | + | |
50 | + it("receives the article and profile as inputs", (done: Function) => { | |
51 | + | |
52 | + // Creating a container component (ArticleContainerComponent) to include | |
53 | + // the component under test (ArticleView) | |
54 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent] }) | |
55 | + class ArticleContainerComponent { | |
56 | + article = { type: 'anyArticleType' }; | |
57 | + profile = { name: 'profile-name' }; | |
58 | + constructor() { | |
59 | + } | |
60 | + } | |
61 | + | |
62 | + // uses the TestComponentBuilder instance to initialize the component | |
63 | + createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
64 | + // and here we can inspect and run the test assertions | |
65 | + let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
66 | + | |
67 | + // assure the article object inside the ArticleView matches | |
68 | + // the provided through the parent component | |
69 | + expect(articleView.article.type).toEqual("anyArticleType"); | |
70 | + expect(articleView.profile.name).toEqual("profile-name"); | |
71 | + | |
72 | + // done needs to be called (it isn't really needed, as we can read in | |
73 | + // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
74 | + // because createAsync in ng-forward is not really async, but as the intention | |
75 | + // here is write tests in angular 2 ways, this is recommended | |
76 | + done(); | |
77 | + }); | |
78 | + }); | |
79 | + | |
80 | + | |
81 | + it("renders a article view which matches to the article type", done => { | |
82 | + // NoosferoTinyMceArticle component created to check if it will be used | |
83 | + // when a article with type 'TinyMceArticle' is provided to the noosfero-article (ArticleView) | |
84 | + // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
85 | + @Component({ selector: 'noosfero-tiny-mce-article', template: "<h1>TinyMceArticle</h1>" }) | |
86 | + class TinyMceArticleView { | |
87 | + @Input() article: any; | |
88 | + @Input() profile: any; | |
89 | + } | |
90 | + | |
91 | + // Creating a container component (ArticleContainerComponent) to include our NoosferoTinyMceArticle | |
92 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent, TinyMceArticleView] }) | |
93 | + class CustomArticleType { | |
94 | + article = { type: 'TinyMceArticle' }; | |
95 | + profile = { name: 'profile-name' }; | |
96 | + constructor() { | |
97 | + } | |
98 | + } | |
99 | + createComponentFromClass(CustomArticleType).then(fixture => { | |
100 | + let myComponent: CustomArticleType = fixture.componentInstance; | |
101 | + expect(myComponent.article.type).toEqual("TinyMceArticle"); | |
102 | + expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("TinyMceArticle"); | |
103 | + done(); | |
104 | + }); | |
105 | + }); | |
106 | + | |
107 | + }); | |
108 | +}); | |
0 | 109 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,57 @@ |
1 | +import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; | |
2 | +import {ArticleBlogComponent} from "./types/blog/blog.component"; | |
3 | + | |
4 | +/** | |
5 | + * @ngdoc controller | |
6 | + * @name ArticleDefaultView | |
7 | + * @description | |
8 | + * A default view for Noosfero Articles. If the specific article view is | |
9 | + * not implemented, then this view is used. | |
10 | + */ | |
11 | +@Component({ | |
12 | + selector: 'noosfero-default-article', | |
13 | + templateUrl: 'app/article/article.html' | |
14 | +}) | |
15 | +export class ArticleDefaultViewComponent { | |
16 | + | |
17 | + @Input() article: noosfero.Article; | |
18 | + @Input() profile: noosfero.Profile; | |
19 | + | |
20 | +} | |
21 | + | |
22 | +/** | |
23 | + * @ngdoc controller | |
24 | + * @name ArticleView | |
25 | + * @description | |
26 | + * A dynamic view for articles. It uses the article type to replace | |
27 | + * the default template with the custom article directive. | |
28 | + */ | |
29 | +@Component({ | |
30 | + selector: 'noosfero-article', | |
31 | + template: 'not-used', | |
32 | + directives: [ArticleDefaultViewComponent, ArticleBlogComponent] | |
33 | +}) | |
34 | +@Inject("$element", "$scope", "$injector", "$compile") | |
35 | +export class ArticleViewComponent { | |
36 | + | |
37 | + @Input() article: noosfero.Article; | |
38 | + @Input() profile: noosfero.Profile; | |
39 | + directiveName: string; | |
40 | + | |
41 | + ngOnInit() { | |
42 | + let specificDirective = 'noosfero' + this.article.type; | |
43 | + this.directiveName = "noosfero-default-article"; | |
44 | + if (this.$injector.has(specificDirective + 'Directive')) { | |
45 | + this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
46 | + } | |
47 | + this.$element.replaceWith(this.$compile('<' + this.directiveName + ' [article]="ctrl.article" [profile]="ctrl.profile"></' + this.directiveName + '>')(this.$scope)); | |
48 | + } | |
49 | + | |
50 | + constructor( | |
51 | + private $element: any, | |
52 | + private $scope: ng.IScope, | |
53 | + private $injector: ng.auto.IInjectorService, | |
54 | + private $compile: ng.ICompileService) { | |
55 | + | |
56 | + } | |
57 | +} | ... | ... |
... | ... | @@ -0,0 +1,23 @@ |
1 | +<div class="article"> | |
2 | + <div class="page-header"> | |
3 | + <h3 ng-bind="ctrl.article.title"></h3> | |
4 | + </div> | |
5 | + | |
6 | + <div class="sub-header clearfix"> | |
7 | + <div class="page-info pull-right small text-muted"> | |
8 | + <span class="time"> | |
9 | + <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> | |
10 | + </span> | |
11 | + <span class="author" ng-if="ctrl.article.author"> | |
12 | + <i class="fa fa-user"></i> | |
13 | + <a ui-sref="main.profile.home({profile: ctrl.article.author.identifier})"> | |
14 | + <span class="author-name" ng-bind="ctrl.article.author.name"></span> | |
15 | + </a> | |
16 | + </span> | |
17 | + </div> | |
18 | + </div> | |
19 | + | |
20 | + <div class="page-body"> | |
21 | + <div ng-bind-html="ctrl.article.body"></div> | |
22 | + </div> | |
23 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,55 @@ |
1 | +import {quickCreateComponent} from "../../spec/helpers"; | |
2 | +import {BasicEditorComponent} from "./basic-editor.component"; | |
3 | + | |
4 | + | |
5 | +describe("Article BasicEditor", () => { | |
6 | + | |
7 | + let $rootScope: ng.IRootScopeService; | |
8 | + let $q: ng.IQService; | |
9 | + let articleServiceMock: any; | |
10 | + let profileServiceMock: any; | |
11 | + let $state: any; | |
12 | + let profile = { id: 1 }; | |
13 | + let notification: any; | |
14 | + | |
15 | + | |
16 | + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
17 | + $rootScope = _$rootScope_; | |
18 | + $q = _$q_; | |
19 | + })); | |
20 | + | |
21 | + beforeEach(() => { | |
22 | + $state = jasmine.createSpyObj("$state", ["transitionTo"]); | |
23 | + notification = jasmine.createSpyObj("notification", ["success"]); | |
24 | + profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); | |
25 | + articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInProfile"]); | |
26 | + | |
27 | + let getCurrentProfileResponse = $q.defer(); | |
28 | + getCurrentProfileResponse.resolve(profile); | |
29 | + | |
30 | + let articleCreate = $q.defer(); | |
31 | + articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } }); | |
32 | + | |
33 | + profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
34 | + articleServiceMock.createInProfile = jasmine.createSpy("createInProfile").and.returnValue(articleCreate.promise); | |
35 | + }); | |
36 | + | |
37 | + it("create an article in the current profile when save", done => { | |
38 | + let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | |
39 | + component.save(); | |
40 | + $rootScope.$apply(); | |
41 | + expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
42 | + expect(articleServiceMock.createInProfile).toHaveBeenCalledWith(profile, component.article); | |
43 | + done(); | |
44 | + }); | |
45 | + | |
46 | + it("got to the new article page and display an alert when saving sucessfully", done => { | |
47 | + let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | |
48 | + component.save(); | |
49 | + $rootScope.$apply(); | |
50 | + expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | |
51 | + expect(notification.success).toHaveBeenCalled(); | |
52 | + done(); | |
53 | + }); | |
54 | + | |
55 | +}); | ... | ... |
... | ... | @@ -0,0 +1,35 @@ |
1 | +import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
2 | +import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
3 | +import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
4 | +import {NotificationService} from "../shared/services/notification.service.ts"; | |
5 | + | |
6 | +@Component({ | |
7 | + selector: 'article-basic-editor', | |
8 | + templateUrl: "app/article/basic-editor.html", | |
9 | + providers: [ | |
10 | + provide('articleService', { useClass: ArticleService }), | |
11 | + provide('profileService', { useClass: ProfileService }), | |
12 | + provide('notification', { useClass: NotificationService }) | |
13 | + ] | |
14 | +}) | |
15 | +@Inject(ArticleService, ProfileService, "$state", NotificationService) | |
16 | +export class BasicEditorComponent { | |
17 | + | |
18 | + article: noosfero.Article = <noosfero.Article>{}; | |
19 | + | |
20 | + constructor(private articleService: ArticleService, | |
21 | + private profileService: ProfileService, | |
22 | + private $state: ng.ui.IStateService, | |
23 | + private notification: NotificationService) { } | |
24 | + | |
25 | + save() { | |
26 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
27 | + return this.articleService.createInProfile(profile, this.article); | |
28 | + }).then((response: noosfero.RestResult<noosfero.Article>) => { | |
29 | + let article = (<noosfero.Article>response.data); | |
30 | + this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); | |
31 | + this.notification.success("Good job!", "Article saved!"); | |
32 | + }); | |
33 | + } | |
34 | + | |
35 | +} | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +<form> | |
2 | + <div class="form-group"> | |
3 | + <label for="titleInput">Title</label> | |
4 | + <input type="text" class="form-control" id="titleInput" placeholder="title" ng-model="vm.article.name"> | |
5 | + </div> | |
6 | + <div class="form-group"> | |
7 | + <label for="bodyInput">Text</label> | |
8 | + <textarea class="form-control" id="bodyInput" rows="10" ng-model="vm.article.body"></textarea> | |
9 | + </div> | |
10 | + <button type="submit" class="btn btn-default" ng-click="vm.save()">Save</button> | |
11 | +</form> | ... | ... |
src/app/article/content-viewer/content-viewer-actions.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,67 @@ |
1 | +import {providers} from 'ng-forward/cjs/testing/providers'; | |
2 | + | |
3 | +import {Input, Component, provide} from 'ng-forward'; | |
4 | + | |
5 | +import * as helpers from "../../../spec/helpers"; | |
6 | + | |
7 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
8 | +import {ContentViewerActions} from './content-viewer-actions.component'; | |
9 | + | |
10 | +// this htmlTemplate will be re-used between the container components in this spec file | |
11 | +const htmlTemplate: string = '<content-viewer-actions [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer-actions>'; | |
12 | + | |
13 | +describe('Content Viewer Actions Component', () => { | |
14 | + | |
15 | + beforeEach(() => { | |
16 | + | |
17 | + angular.mock.module("templates"); | |
18 | + | |
19 | + providers((provide: any) => { | |
20 | + return <any>[ | |
21 | + provide('ProfileService', { | |
22 | + useValue: helpers.mocks.profileService | |
23 | + }) | |
24 | + ]; | |
25 | + }); | |
26 | + }); | |
27 | + | |
28 | + let buildComponent = (): Promise<ComponentFixture> => { | |
29 | + return helpers.quickCreateComponent({ | |
30 | + providers: [ | |
31 | + helpers.provideEmptyObjects('Restangular'), | |
32 | + helpers.provideFilters('translateFilter') | |
33 | + ], | |
34 | + directives: [ContentViewerActions], | |
35 | + template: htmlTemplate | |
36 | + }); | |
37 | + }; | |
38 | + | |
39 | + it('renders content viewer actions directive', (done: Function) => { | |
40 | + buildComponent().then((fixture: ComponentFixture) => { | |
41 | + expect(fixture.debugElement.query('content-viewer-actions').length).toEqual(1); | |
42 | + | |
43 | + done(); | |
44 | + }); | |
45 | + }); | |
46 | + | |
47 | + it('check if profile was loaded', (done: Function) => { | |
48 | + let profile: any = { | |
49 | + id: 1, | |
50 | + identifier: 'the-profile-test', | |
51 | + type: 'Person' | |
52 | + }; | |
53 | + | |
54 | + helpers.mocks.profileService.getCurrentProfile = () => { | |
55 | + return helpers.mocks.promiseResultTemplate(profile); | |
56 | + }; | |
57 | + | |
58 | + buildComponent().then((fixture: ComponentFixture) => { | |
59 | + let contentViewerComp: ContentViewerActions = fixture.debugElement.componentViewChildren[0].componentInstance; | |
60 | + | |
61 | + expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile)); | |
62 | + | |
63 | + done(); | |
64 | + }); | |
65 | + }); | |
66 | + | |
67 | +}); | ... | ... |
src/app/article/content-viewer/content-viewer-actions.component.ts
0 → 100644
... | ... | @@ -0,0 +1,20 @@ |
1 | +import {Component, Inject, provide} from "ng-forward"; | |
2 | +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: "content-viewer-actions", | |
6 | + templateUrl: "app/article/content-viewer/navbar-actions.html", | |
7 | + providers: [provide('profileService', { useClass: ProfileService })] | |
8 | +}) | |
9 | +@Inject(ProfileService) | |
10 | +export class ContentViewerActions { | |
11 | + | |
12 | + article: noosfero.Article; | |
13 | + profile: noosfero.Profile; | |
14 | + | |
15 | + constructor(profileService: ProfileService) { | |
16 | + profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
17 | + this.profile = profile; | |
18 | + }); | |
19 | + } | |
20 | +} | ... | ... |
src/app/article/content-viewer/content-viewer.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,88 @@ |
1 | +import {providers} from 'ng-forward/cjs/testing/providers'; | |
2 | + | |
3 | +import {Input, Component, provide} from 'ng-forward'; | |
4 | + | |
5 | +import * as helpers from "../../../spec/helpers"; | |
6 | + | |
7 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
8 | +import {ContentViewerComponent} from './content-viewer.component'; | |
9 | + | |
10 | +// this htmlTemplate will be re-used between the container components in this spec file | |
11 | +const htmlTemplate: string = '<content-viewer [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer>'; | |
12 | + | |
13 | +describe('Content Viewer Component', () => { | |
14 | + | |
15 | + let stateParamsService: any; | |
16 | + | |
17 | + // loading the templates | |
18 | + beforeEach(() => { | |
19 | + angular.mock.module("templates"); | |
20 | + | |
21 | + stateParamsService = { page: 1 }; | |
22 | + | |
23 | + providers((provide: any) => { | |
24 | + return <any>[ | |
25 | + provide('ArticleService', { | |
26 | + useValue: helpers.mocks.articleService | |
27 | + }), | |
28 | + provide('ProfileService', { | |
29 | + useValue: helpers.mocks.profileService | |
30 | + }), | |
31 | + // TODO: Como criar um mock do atributo "page" de stateParams | |
32 | + provide('$stateParams', { | |
33 | + useValue: stateParamsService | |
34 | + }) | |
35 | + ]; | |
36 | + }); | |
37 | + }); | |
38 | + | |
39 | + let buildComponent = (): Promise<ComponentFixture> => { | |
40 | + return helpers.quickCreateComponent({ | |
41 | + providers: [ | |
42 | + helpers.provideEmptyObjects('Restangular') | |
43 | + ], | |
44 | + directives: [ContentViewerComponent], | |
45 | + template: htmlTemplate | |
46 | + }); | |
47 | + }; | |
48 | + | |
49 | + it('renders content viewer directive', (done: Function) => { | |
50 | + buildComponent().then((fixture: ComponentFixture) => { | |
51 | + expect(fixture.debugElement.query('content-viewer').length).toEqual(1); | |
52 | + | |
53 | + done(); | |
54 | + }); | |
55 | + }); | |
56 | + | |
57 | + it('check if article was loaded', (done: Function) => { | |
58 | + let article: any = { | |
59 | + id: 1, | |
60 | + title: 'The article test' | |
61 | + }; | |
62 | + let profile: any = { | |
63 | + id: 1, | |
64 | + identifier: 'the-profile-test', | |
65 | + type: 'Person' | |
66 | + }; | |
67 | + | |
68 | + helpers.mocks.profileService.getCurrentProfile = () => { | |
69 | + return helpers.mocks.promiseResultTemplate(profile); | |
70 | + }; | |
71 | + | |
72 | + helpers.mocks.articleService.getArticleByProfileAndPath = (profile: noosfero.Profile, path: string) => { | |
73 | + return helpers.mocks.promiseResultTemplate({ | |
74 | + data: article | |
75 | + }); | |
76 | + }; | |
77 | + | |
78 | + | |
79 | + buildComponent().then((fixture: ComponentFixture) => { | |
80 | + let contentViewerComp: ContentViewerComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
81 | + | |
82 | + expect(contentViewerComp.profile).toEqual(profile); | |
83 | + expect(contentViewerComp.article).toEqual(article); | |
84 | + | |
85 | + done(); | |
86 | + }); | |
87 | + }); | |
88 | +}); | ... | ... |
src/app/article/content-viewer/content-viewer.component.ts
0 → 100644
... | ... | @@ -0,0 +1,38 @@ |
1 | +import {ArticleViewComponent} from "./../article-default-view.component"; | |
2 | +import {Input, Component, StateConfig, Inject, provide} from "ng-forward"; | |
3 | + | |
4 | +import {ArticleBlogComponent} from "./../types/blog/blog.component"; | |
5 | +import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service"; | |
6 | +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
7 | + | |
8 | +@Component({ | |
9 | + selector: "content-viewer", | |
10 | + templateUrl: "app/article/content-viewer/page.html", | |
11 | + directives: [ArticleBlogComponent, ArticleViewComponent], | |
12 | + providers: [ | |
13 | + provide('articleService', { useClass: ArticleService }), | |
14 | + provide('profileService', { useClass: ProfileService }) | |
15 | + ] | |
16 | +}) | |
17 | +@Inject(ArticleService, ProfileService, "$log", "$stateParams") | |
18 | +export class ContentViewerComponent { | |
19 | + | |
20 | + @Input() | |
21 | + article: noosfero.Article = null; | |
22 | + | |
23 | + @Input() | |
24 | + profile: noosfero.Profile = null; | |
25 | + | |
26 | + constructor(private articleService: ArticleService, private profileService: ProfileService, private $log: ng.ILogService, private $stateParams: angular.ui.IStateParamsService) { | |
27 | + this.activate(); | |
28 | + } | |
29 | + | |
30 | + activate() { | |
31 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
32 | + this.profile = profile; | |
33 | + return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]); | |
34 | + }).then((result: noosfero.RestResult<any>) => { | |
35 | + this.article = <noosfero.Article>result.data; | |
36 | + }); | |
37 | + } | |
38 | +} | ... | ... |
... | ... | @@ -0,0 +1,7 @@ |
1 | +<ul class="nav navbar-nav"> | |
2 | + <li ng-show="vm.profile"> | |
3 | + <a href="#" role="button" ui-sref="main.profile.cms({profile: vm.profile.identifier})"> | |
4 | + <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | |
5 | + </a> | |
6 | + </li> | |
7 | +</ul> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<noosfero-article ng-if="vm.article" [article]="vm.article" [profile]="vm.profile"></noosfero-article> | ... | ... |
... | ... | @@ -0,0 +1,129 @@ |
1 | +import { | |
2 | +providers | |
3 | +} from 'ng-forward/cjs/testing/providers'; | |
4 | + | |
5 | +import { | |
6 | +Input, | |
7 | +Component | |
8 | +} from 'ng-forward'; | |
9 | +import { | |
10 | +ArticleBlogComponent | |
11 | +} from './blog.component'; | |
12 | + | |
13 | +import { | |
14 | +createComponentFromClass, | |
15 | +quickCreateComponent, | |
16 | +provideEmptyObjects, | |
17 | +createProviderToValue, | |
18 | +provideFilters | |
19 | +} from "../../../../spec/helpers.ts"; | |
20 | + | |
21 | +// this htmlTemplate will be re-used between the container components in this spec file | |
22 | +const htmlTemplate: string = '<noosfero-blog [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-blog>'; | |
23 | + | |
24 | +describe("Blog Component", () => { | |
25 | + | |
26 | + function promiseResultTemplate(response?: {}) { | |
27 | + let thenFuncEmpty = (func: Function) => { | |
28 | + // does nothing | |
29 | + }; | |
30 | + if (response) { | |
31 | + return { | |
32 | + then: (func: (response: any) => void) => { | |
33 | + func(response); | |
34 | + } | |
35 | + }; | |
36 | + } else { | |
37 | + return { | |
38 | + then: (func: (response: any) => void) => { | |
39 | + // does nothing | |
40 | + } | |
41 | + }; | |
42 | + } | |
43 | + } | |
44 | + | |
45 | + let articleService = { | |
46 | + getChildren: (article_id: number, filters: {}) => { | |
47 | + return promiseResultTemplate(null); | |
48 | + } | |
49 | + }; | |
50 | + | |
51 | + @Component({ | |
52 | + selector: 'test-container-component', | |
53 | + template: htmlTemplate, | |
54 | + directives: [ArticleBlogComponent], | |
55 | + providers: [ | |
56 | + provideEmptyObjects('Restangular'), | |
57 | + createProviderToValue('ArticleService', articleService), | |
58 | + provideFilters('truncateFilter') | |
59 | + ] | |
60 | + }) | |
61 | + class BlogContainerComponent { | |
62 | + article = { | |
63 | + type: 'anyArticleType' | |
64 | + }; | |
65 | + profile = { | |
66 | + name: 'profile-name' | |
67 | + }; | |
68 | + } | |
69 | + | |
70 | + beforeEach(() => { | |
71 | + | |
72 | + // the karma preprocessor html2js transform the templates html into js files which put | |
73 | + // the templates to the templateCache into the module templates | |
74 | + // we need to load the module templates here as the template for the | |
75 | + // component Noosfero ArtileView will be load on our tests | |
76 | + angular.mock.module("templates"); | |
77 | + | |
78 | + providers((provide: any) => { | |
79 | + return <any>[ | |
80 | + provide('ArticleService', { | |
81 | + useValue: articleService | |
82 | + }) | |
83 | + ]; | |
84 | + }); | |
85 | + }); | |
86 | + | |
87 | + it("renders the blog content", (done: Function) => { | |
88 | + | |
89 | + createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
90 | + | |
91 | + expect(fixture.debugElement.query('div.blog').length).toEqual(1); | |
92 | + | |
93 | + done(); | |
94 | + }); | |
95 | + }); | |
96 | + | |
97 | + it("verify the blog data", (done: Function) => { | |
98 | + | |
99 | + let articles = [{ | |
100 | + id: 1, | |
101 | + title: 'The article test' | |
102 | + }]; | |
103 | + | |
104 | + let result = { data: articles, headers: (name: string) => { return 1; } }; | |
105 | + | |
106 | + // defining a mock result to articleService.getChildren method | |
107 | + articleService.getChildren = (article_id: number, filters: {}) => { | |
108 | + return promiseResultTemplate(result); | |
109 | + }; | |
110 | + | |
111 | + createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
112 | + | |
113 | + // gets the children component of BlogContainerComponent | |
114 | + let articleBlog: BlogContainerComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
115 | + | |
116 | + // check if the component property are the provided by the mocked articleService | |
117 | + let post = { | |
118 | + id: 1, | |
119 | + title: 'The article test' | |
120 | + }; | |
121 | + expect((<any>articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post)); | |
122 | + expect((<any>articleBlog)["totalPosts"]).toEqual(1); | |
123 | + | |
124 | + done(); | |
125 | + }); | |
126 | + | |
127 | + }); | |
128 | + | |
129 | +}); | |
0 | 130 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,47 @@ |
1 | +import {Component, Input, Inject} from "ng-forward"; | |
2 | + | |
3 | +import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
4 | + | |
5 | +/** | |
6 | + * @ngdoc controller | |
7 | + * @name ArticleBlog | |
8 | + * @description | |
9 | + * An specific {@link ArticleView} for Blog articles. | |
10 | + */ | |
11 | +@Component({ | |
12 | + selector: "noosfero-blog", | |
13 | + templateUrl: "app/article/types/blog/blog.html" | |
14 | +}) | |
15 | +@Inject(ArticleService) | |
16 | +export class ArticleBlogComponent { | |
17 | + | |
18 | + @Input() article: noosfero.Article; | |
19 | + @Input() profile: noosfero.Profile; | |
20 | + | |
21 | + private posts: noosfero.Article[]; | |
22 | + private perPage: number = 3; | |
23 | + private currentPage: number; | |
24 | + private totalPosts: number = 0; | |
25 | + | |
26 | + constructor(private articleService: ArticleService) { } | |
27 | + | |
28 | + ngOnInit() { | |
29 | + this.loadPage(); | |
30 | + } | |
31 | + | |
32 | + loadPage() { | |
33 | + let filters = { | |
34 | + content_type: "TinyMceArticle", | |
35 | + per_page: this.perPage, | |
36 | + page: this.currentPage | |
37 | + }; | |
38 | + | |
39 | + this.articleService | |
40 | + .getChildren(this.article, filters) | |
41 | + .then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
42 | + this.totalPosts = <number>result.headers("total"); | |
43 | + this.posts = result.data; | |
44 | + }); | |
45 | + } | |
46 | + | |
47 | +} | ... | ... |
... | ... | @@ -0,0 +1,24 @@ |
1 | +<div class="blog"> | |
2 | + <div class="blog-cover" ng-show="ctrl.article.image"> | |
3 | + <img ng-src="{{ctrl.article.image.url}}" class="img-responsive"> | |
4 | + <h3 ng-bind="ctrl.article.title"></h3> | |
5 | + </div> | |
6 | + | |
7 | + <div class="page-header" ng-show="!ctrl.article.image"> | |
8 | + <h3 ng-bind="ctrl.article.title"></h3> | |
9 | + </div> | |
10 | + | |
11 | + <div> | |
12 | + <div ng-repeat="child in ctrl.posts | orderBy: 'created_at':true"> | |
13 | + <div class="page-header"> | |
14 | + <a class="title" ui-sref="main.profile.page({profile: ctrl.profile.identifier, page: child.path})"><h4 ng-bind="child.title"></h4></a> | |
15 | + <div class="post-lead" ng-bind-html="child.body | truncate: 500: '...': true"></div> | |
16 | + </div> | |
17 | + </div> | |
18 | + </div> | |
19 | + | |
20 | + <pagination ng-model="ctrl.currentPage" total-items="ctrl.totalPosts" class="pagination-sm center-block" | |
21 | + boundary-links="true" items-per-page="ctrl.perPage" ng-change="ctrl.loadPage()" | |
22 | + first-text="«" last-text="»" previous-text="‹" next-text="›"> | |
23 | + </pagination> | |
24 | +</div> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
src/app/cms/cms.component.spec.ts
... | ... | @@ -1,56 +0,0 @@ |
1 | -import {quickCreateComponent} from "../../spec/helpers"; | |
2 | -import {Cms} from "./cms.component"; | |
3 | - | |
4 | -describe("Components", () => { | |
5 | - describe("Cms Component", () => { | |
6 | - | |
7 | - let $rootScope: ng.IRootScopeService; | |
8 | - let $q: ng.IQService; | |
9 | - let articleServiceMock: any; | |
10 | - let profileServiceMock: any; | |
11 | - let $state: any; | |
12 | - let profile = { id: 1 }; | |
13 | - let notification: any; | |
14 | - | |
15 | - | |
16 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
17 | - $rootScope = _$rootScope_; | |
18 | - $q = _$q_; | |
19 | - })); | |
20 | - | |
21 | - beforeEach(() => { | |
22 | - $state = jasmine.createSpyObj("$state", ["transitionTo"]); | |
23 | - notification = jasmine.createSpyObj("notification", ["success"]); | |
24 | - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); | |
25 | - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInProfile"]); | |
26 | - | |
27 | - let getCurrentProfileResponse = $q.defer(); | |
28 | - getCurrentProfileResponse.resolve(profile); | |
29 | - | |
30 | - let articleCreate = $q.defer(); | |
31 | - articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } }}); | |
32 | - | |
33 | - profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
34 | - articleServiceMock.createInProfile = jasmine.createSpy("createInProfile").and.returnValue(articleCreate.promise); | |
35 | - }); | |
36 | - | |
37 | - it("create an article in the current profile when save", done => { | |
38 | - let component: Cms = new Cms(articleServiceMock, profileServiceMock, $state, notification); | |
39 | - component.save(); | |
40 | - $rootScope.$apply(); | |
41 | - expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
42 | - expect(articleServiceMock.createInProfile).toHaveBeenCalledWith(profile, component.article); | |
43 | - done(); | |
44 | - }); | |
45 | - | |
46 | - it("got to the new article page and display an alert when saving sucessfully", done => { | |
47 | - let component: Cms = new Cms(articleServiceMock, profileServiceMock, $state, notification); | |
48 | - component.save(); | |
49 | - $rootScope.$apply(); | |
50 | - expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | |
51 | - expect(notification.success).toHaveBeenCalled(); | |
52 | - done(); | |
53 | - }); | |
54 | - | |
55 | - }); | |
56 | -}); |
src/app/cms/cms.component.ts
... | ... | @@ -1,35 +0,0 @@ |
1 | -import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
2 | -import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
3 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
4 | -import {Notification} from "../components/notification/notification.component"; | |
5 | - | |
6 | -@Component({ | |
7 | - selector: 'cms', | |
8 | - templateUrl: "app/cms/cms.html", | |
9 | - providers: [ | |
10 | - provide('articleService', { useClass: ArticleService }), | |
11 | - provide('profileService', { useClass: ProfileService }), | |
12 | - provide('notification', { useClass: Notification }) | |
13 | - ] | |
14 | -}) | |
15 | -@Inject(ArticleService, ProfileService, "$state", Notification) | |
16 | -export class Cms { | |
17 | - | |
18 | - article: noosfero.Article = <noosfero.Article>{}; | |
19 | - | |
20 | - constructor(private articleService: ArticleService, | |
21 | - private profileService: ProfileService, | |
22 | - private $state: ng.ui.IStateService, | |
23 | - private notification: Notification) { } | |
24 | - | |
25 | - save() { | |
26 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
27 | - return this.articleService.createInProfile(profile, this.article); | |
28 | - }).then((response: noosfero.RestResult<noosfero.Article>) => { | |
29 | - let article = (<noosfero.Article>response.data); | |
30 | - this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); | |
31 | - this.notification.success("Good job!", "Article saved!"); | |
32 | - }); | |
33 | - } | |
34 | - | |
35 | -} |
src/app/cms/cms.html
... | ... | @@ -1,11 +0,0 @@ |
1 | -<form> | |
2 | - <div class="form-group"> | |
3 | - <label for="titleInput">Title</label> | |
4 | - <input type="text" class="form-control" id="titleInput" placeholder="title" ng-model="vm.article.name"> | |
5 | - </div> | |
6 | - <div class="form-group"> | |
7 | - <label for="bodyInput">Text</label> | |
8 | - <textarea class="form-control" id="bodyInput" rows="10" ng-model="vm.article.body"></textarea> | |
9 | - </div> | |
10 | - <button type="submit" class="btn btn-default" ng-click="vm.save()">Save</button> | |
11 | -</form> |
src/app/cms/index.ts
src/app/components/auth/auth_controller.spec.ts
... | ... | @@ -1,33 +0,0 @@ |
1 | -import {AuthController} from "./auth_controller"; | |
2 | -import {AuthService} from "./auth_service"; | |
3 | - | |
4 | -describe("Controllers", () => { | |
5 | - | |
6 | - | |
7 | - describe("AuthController", () => { | |
8 | - | |
9 | - it("calls authenticate on AuthService when login called", () => { | |
10 | - | |
11 | - // creating a Mock AuthService | |
12 | - let AuthServiceMock: AuthService = jasmine.createSpyObj("AuthService", ["login"]); | |
13 | - | |
14 | - // pass AuthServiceMock into the constructor | |
15 | - let authController = new AuthController(null, null, AuthServiceMock); | |
16 | - | |
17 | - // setup of authController -> set the credentials instance property | |
18 | - let credentials = { username: "username", password: "password" }; | |
19 | - | |
20 | - authController.credentials = credentials; | |
21 | - | |
22 | - // calls the authController login method | |
23 | - authController.login(); | |
24 | - | |
25 | - // checks if the method login of the injected AuthService has been called | |
26 | - expect(AuthServiceMock.login).toHaveBeenCalledWith(credentials); | |
27 | - | |
28 | - }); | |
29 | - | |
30 | - | |
31 | - | |
32 | - }); | |
33 | -}); |
src/app/components/auth/auth_controller.ts
... | ... | @@ -1,20 +0,0 @@ |
1 | -import {AuthService} from "./auth_service"; | |
2 | - | |
3 | -export class AuthController { | |
4 | - | |
5 | - static $inject = ["$log", "$stateParams", "AuthService"]; | |
6 | - | |
7 | - constructor( | |
8 | - private $log: ng.ILogService, | |
9 | - private $stateParams: any, | |
10 | - private AuthService: AuthService | |
11 | - ) { | |
12 | - | |
13 | - } | |
14 | - | |
15 | - credentials: noosfero.Credentials; | |
16 | - | |
17 | - login() { | |
18 | - this.AuthService.login(this.credentials); | |
19 | - } | |
20 | -} |
src/app/components/auth/auth_events.ts
... | ... | @@ -1,11 +0,0 @@ |
1 | -export interface IAuthEvents { | |
2 | - loginSuccess: string; | |
3 | - loginFailed: string; | |
4 | - logoutSuccess: string; | |
5 | -} | |
6 | - | |
7 | -export const AUTH_EVENTS: IAuthEvents = { | |
8 | - loginSuccess: "auth-login-success", | |
9 | - loginFailed: "auth-login-failed", | |
10 | - logoutSuccess: "auth-logout-success" | |
11 | -}; | |
12 | 0 | \ No newline at end of file |
src/app/components/auth/auth_service.spec.ts
... | ... | @@ -1,81 +0,0 @@ |
1 | - | |
2 | - | |
3 | -import {AuthService, AUTH_EVENTS} from "./"; | |
4 | - | |
5 | -describe("Services", () => { | |
6 | - | |
7 | - | |
8 | - describe("Auth Service", () => { | |
9 | - | |
10 | - let $httpBackend: ng.IHttpBackendService; | |
11 | - let authService: AuthService; | |
12 | - let credentials: noosfero.Credentials; | |
13 | - let $rootScope: ng.IRootScopeService; | |
14 | - let user: noosfero.User; | |
15 | - | |
16 | - beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => { | |
17 | - $translateProvider.translations('en', {}); | |
18 | - })); | |
19 | - | |
20 | - beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService, _AuthService_: AuthService) => { | |
21 | - $httpBackend = _$httpBackend_; | |
22 | - authService = _AuthService_; | |
23 | - $rootScope = _$rootScope_; | |
24 | - | |
25 | - user = <noosfero.User>{ | |
26 | - id: 1, | |
27 | - login: "user" | |
28 | - }; | |
29 | - })); | |
30 | - | |
31 | - | |
32 | - describe("Succesffull login", () => { | |
33 | - | |
34 | - beforeEach(() => { | |
35 | - credentials = { username: "user", password: "password" }; | |
36 | - | |
37 | - $httpBackend.expectPOST("/api/v1/login", "login=user&password=password").respond(200, { user: user }); | |
38 | - }); | |
39 | - | |
40 | - it("should return loggedUser", (done) => { | |
41 | - authService.login(credentials).then((loggedUser) => { | |
42 | - expect(loggedUser).toBeDefined(); | |
43 | - done(); | |
44 | - }); | |
45 | - $httpBackend.flush(); | |
46 | - expect($httpBackend.verifyNoOutstandingRequest()); | |
47 | - }); | |
48 | - | |
49 | - | |
50 | - it("should emit event loggin successful with user logged data", () => { | |
51 | - | |
52 | - authService.login(credentials); | |
53 | - | |
54 | - let eventEmmited: boolean = false; | |
55 | - $rootScope.$on(AUTH_EVENTS.loginSuccess, (event: ng.IAngularEvent, userThroughEvent: noosfero.User) => { | |
56 | - eventEmmited = true; | |
57 | - expect(userThroughEvent).toEqual(user); | |
58 | - }); | |
59 | - | |
60 | - $httpBackend.flush(); | |
61 | - | |
62 | - expect(eventEmmited).toBeTruthy(AUTH_EVENTS.loginSuccess + " was not emmited!"); | |
63 | - }); | |
64 | - | |
65 | - it("should return the current logged in user", () => { | |
66 | - authService.login(credentials); | |
67 | - $httpBackend.flush(); | |
68 | - let actual: noosfero.User = authService.currentUser(); | |
69 | - expect(actual).toEqual(user, "The returned user must be present"); | |
70 | - }); | |
71 | - | |
72 | - it("should not return the current user after logout", () => { | |
73 | - authService.logout(); | |
74 | - let actual: any = authService.currentUser(); | |
75 | - expect(actual).toEqual(undefined, "The returned user must not be defined"); | |
76 | - }); | |
77 | - }); | |
78 | - | |
79 | - | |
80 | - }); | |
81 | -}); |
src/app/components/auth/auth_service.ts
... | ... | @@ -1,69 +0,0 @@ |
1 | -import {Injectable, Inject} from "ng-forward"; | |
2 | - | |
3 | -import {NoosferoRootScope, UserResponse} from "./../../models/interfaces"; | |
4 | -import {Session} from "./session"; | |
5 | - | |
6 | -import {AUTH_EVENTS, IAuthEvents} from "./auth_events"; | |
7 | - | |
8 | -@Injectable() | |
9 | -@Inject("$q", "$http", "$rootScope", "Session", "$log", "AUTH_EVENTS") | |
10 | -export class AuthService { | |
11 | - | |
12 | - constructor(private $q: ng.IQService, | |
13 | - private $http: ng.IHttpService, | |
14 | - private $rootScope: NoosferoRootScope, | |
15 | - private session: Session, | |
16 | - private $log: ng.ILogService, | |
17 | - private auth_events: IAuthEvents) { | |
18 | - | |
19 | - } | |
20 | - | |
21 | - loginFromCookie() { | |
22 | - let url: string = '/api/v1/login_from_cookie'; | |
23 | - return this.$http.post(url, null).then(this.loginSuccessCallback.bind(this), this.loginFailedCallback.bind(this)); | |
24 | - } | |
25 | - | |
26 | - | |
27 | - private loginSuccessCallback(response: ng.IHttpPromiseCallbackArg<UserResponse>) { | |
28 | - this.$log.debug('AuthService.login [SUCCESS] response', response); | |
29 | - let currentUser: noosfero.User = this.session.create(response.data); | |
30 | - this.$rootScope.currentUser = currentUser; | |
31 | - this.$rootScope.$broadcast(this.auth_events.loginSuccess, currentUser); | |
32 | - return currentUser; | |
33 | - } | |
34 | - | |
35 | - login(credentials: noosfero.Credentials): ng.IPromise<noosfero.User> { | |
36 | - let url = '/api/v1/login'; | |
37 | - let encodedData = 'login=' + credentials.username + '&password=' + credentials.password; | |
38 | - return this.$http.post(url, encodedData).then(this.loginSuccessCallback.bind(this), this.loginFailedCallback.bind(this)); | |
39 | - } | |
40 | - | |
41 | - private loginFailedCallback(response: ng.IHttpPromiseCallbackArg<any>): any { | |
42 | - this.$log.debug('AuthService.login [FAIL] response', response); | |
43 | - this.$rootScope.$broadcast(this.auth_events.loginFailed); | |
44 | - // return $q.reject(response); | |
45 | - return null; | |
46 | - } | |
47 | - | |
48 | - public logout() { | |
49 | - this.session.destroy(); | |
50 | - this.$rootScope.currentUser = undefined; | |
51 | - this.$rootScope.$broadcast(this.auth_events.logoutSuccess); | |
52 | - this.$http.jsonp('/account/logout'); // FIXME logout from noosfero to sync login state | |
53 | - } | |
54 | - | |
55 | - public isAuthenticated() { | |
56 | - return !!this.session.currentUser(); | |
57 | - } | |
58 | - | |
59 | - public currentUser(): noosfero.User { | |
60 | - return this.session.currentUser(); | |
61 | - } | |
62 | - | |
63 | - public isAuthorized(authorizedRoles: string | string[]) { | |
64 | - if (!angular.isArray(authorizedRoles)) { | |
65 | - authorizedRoles = [<string>authorizedRoles]; | |
66 | - } | |
67 | - return (this.isAuthenticated() && authorizedRoles.indexOf(this.session.currentUser().userRole) !== -1); | |
68 | - } | |
69 | -} | |
70 | 0 | \ No newline at end of file |
src/app/components/auth/index.ts
src/app/components/auth/login.html
... | ... | @@ -1,16 +0,0 @@ |
1 | -<div class="modal-header"> | |
2 | - <h3 class="modal-title">{{"auth.title" | translate}}</h3> | |
3 | -</div> | |
4 | -<div class="modal-body"> | |
5 | - <form> | |
6 | - <div class="form-group"> | |
7 | - <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label> | |
8 | - <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="vm.credentials.username"> | |
9 | - </div> | |
10 | - <div class="form-group"> | |
11 | - <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label> | |
12 | - <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="vm.credentials.password"> | |
13 | - </div> | |
14 | - <button type="submit" class="btn btn-default" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> | |
15 | - </form> | |
16 | -</div> |
src/app/components/auth/session.ts
... | ... | @@ -1,27 +0,0 @@ |
1 | -import {Injectable, Inject} from "ng-forward"; | |
2 | -import {UserResponse, INoosferoLocalStorage} from "./../../models/interfaces"; | |
3 | - | |
4 | -@Injectable() | |
5 | -@Inject("$localStorage", "$log") | |
6 | -export class Session { | |
7 | - | |
8 | - constructor(private $localStorage: INoosferoLocalStorage, private $log: ng.ILogService) { | |
9 | - | |
10 | - } | |
11 | - | |
12 | - create(data: UserResponse): noosfero.User { | |
13 | - this.$localStorage.currentUser = data.user; | |
14 | - this.$log.debug('User session created.', this.$localStorage.currentUser); | |
15 | - return this.$localStorage.currentUser; | |
16 | - }; | |
17 | - | |
18 | - destroy() { | |
19 | - delete this.$localStorage.currentUser; | |
20 | - this.$log.debug('User session destroyed.'); | |
21 | - }; | |
22 | - | |
23 | - currentUser(): noosfero.User { | |
24 | - return this.$localStorage.currentUser; | |
25 | - }; | |
26 | - | |
27 | -} | |
28 | 0 | \ No newline at end of file |
src/app/components/auth/session_spec.ts
... | ... | @@ -1,49 +0,0 @@ |
1 | -import {Component} from "ng-forward"; | |
2 | -import {Session} from "./"; | |
3 | -import {fixtures, createComponentFromClass, createProviderToValue} from "./../../../spec/helpers"; | |
4 | -import {UserResponse, INoosferoLocalStorage} from "./../../models/interfaces"; | |
5 | - | |
6 | - | |
7 | -describe("Services", () => { | |
8 | - | |
9 | - | |
10 | - describe("Session Service", () => { | |
11 | - | |
12 | - let $localStorage: INoosferoLocalStorage = null; | |
13 | - let $log: any; | |
14 | - | |
15 | - beforeEach(() => { | |
16 | - $localStorage = <INoosferoLocalStorage>{ currentUser: null }; | |
17 | - $log = jasmine.createSpyObj('$log', ['debug']); | |
18 | - }); | |
19 | - | |
20 | - it("method 'create()' saves the current user on $localstorage service", () => { | |
21 | - let session = new Session($localStorage, $log); | |
22 | - let userResponse = <UserResponse>{ | |
23 | - user: fixtures.user | |
24 | - }; | |
25 | - session.create(userResponse); | |
26 | - expect($localStorage.currentUser).toEqual(userResponse.user); | |
27 | - }); | |
28 | - | |
29 | - it("method 'destroy()' clean the currentUser on $localstorage", () => { | |
30 | - let session = new Session($localStorage, $log); | |
31 | - let userResponse = <UserResponse>{ | |
32 | - user: fixtures.user | |
33 | - }; | |
34 | - $localStorage.currentUser = fixtures.user; | |
35 | - session.destroy(); | |
36 | - expect($localStorage.currentUser).toBeUndefined(); | |
37 | - }); | |
38 | - | |
39 | - it("method 'currentUser()' returns the user recorded on $localstorage service", () => { | |
40 | - let session = new Session($localStorage, $log); | |
41 | - let userResponse = <UserResponse>{ | |
42 | - user: fixtures.user | |
43 | - }; | |
44 | - $localStorage.currentUser = fixtures.user; | |
45 | - expect(session.currentUser()).toEqual($localStorage.currentUser); | |
46 | - }); | |
47 | - }); | |
48 | - | |
49 | -}); | |
50 | 0 | \ No newline at end of file |
src/app/components/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/language-selector/language-selector.component.ts
... | ... | @@ -1,24 +0,0 @@ |
1 | -import {Component, Inject} from "ng-forward"; | |
2 | -import {TranslatorService} from "../translator/translator.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "language-selector", | |
6 | - templateUrl: "app/components/language-selector/language-selector.html" | |
7 | -}) | |
8 | -@Inject(TranslatorService) | |
9 | -export class LanguageSelector { | |
10 | - | |
11 | - constructor(private translatorService: TranslatorService) { } | |
12 | - | |
13 | - currentLanguage() { | |
14 | - return this.translatorService.currentLanguage(); | |
15 | - } | |
16 | - | |
17 | - changeLanguage(language: string) { | |
18 | - this.translatorService.changeLanguage(language); | |
19 | - } | |
20 | - | |
21 | - availableLanguages() { | |
22 | - return this.translatorService.availableLanguages; | |
23 | - } | |
24 | -} |
src/app/components/language-selector/language-selector.html
... | ... | @@ -1,13 +0,0 @@ |
1 | -<li class="dropdown profile-menu" dropdown> | |
2 | - <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
3 | - <span>{{"language.selector" | translate}}</span> <b class="caret"></b> | |
4 | - </a> | |
5 | - <ul class="dropdown-menu" dropdown-menu> | |
6 | - <li ng-repeat="(language, description) in ctrl.availableLanguages()" | |
7 | - class="language language-{{language}}" ng-class="{'active': language==ctrl.currentLanguage()}"> | |
8 | - <a href="#" ng-click="ctrl.changeLanguage(language)"> | |
9 | - {{description}} | |
10 | - </a> | |
11 | - </li> | |
12 | - </ul> | |
13 | -</li> |
src/app/components/navbar/index.ts
src/app/components/navbar/navbar.directive.spec.js
... | ... | @@ -1,45 +0,0 @@ |
1 | -(function() { | |
2 | - 'use strict'; | |
3 | - | |
4 | - describe('directive navbar', function() { | |
5 | - var vm; | |
6 | - var el; | |
7 | - var AUTH_EVENTS; | |
8 | - var $state; | |
9 | - | |
10 | - beforeEach(module('angular')); | |
11 | - beforeEach(inject(function($compile, $rootScope, $httpBackend, _AUTH_EVENTS_, _$state_) { | |
12 | - $state = _$state_; | |
13 | - AUTH_EVENTS = _AUTH_EVENTS_; | |
14 | - $httpBackend.when('POST','/api/v1/login_from_cookie').respond({}); | |
15 | - | |
16 | - el = angular.element('<acme-navbar></acme-navbar>'); | |
17 | - | |
18 | - $compile(el)($rootScope.$new()); | |
19 | - $rootScope.$digest(); | |
20 | - vm = el.isolateScope().vm; | |
21 | - })); | |
22 | - | |
23 | - it('should be compiled', function() { | |
24 | - expect(el.html()).not.toEqual(null); | |
25 | - }); | |
26 | - | |
27 | - it('should have isolate scope object with instanciate members', function() { | |
28 | - expect(vm).toEqual(jasmine.any(Object)); | |
29 | - expect(vm.currentUser).toEqual(undefined); | |
30 | - }); | |
31 | - | |
32 | - it('should reload current state after login', function() { | |
33 | - spyOn($state, 'go'); | |
34 | - el.isolateScope().$broadcast(AUTH_EVENTS.loginSuccess, {}); | |
35 | - expect($state.go).toHaveBeenCalled(); | |
36 | - }); | |
37 | - | |
38 | - it('should open login when not logged in', function() { | |
39 | - spyOn(vm, 'openLogin'); | |
40 | - vm.activate(); | |
41 | - expect(vm.openLogin).toHaveBeenCalled(); | |
42 | - }); | |
43 | - | |
44 | - }); | |
45 | -})(); |
src/app/components/navbar/navbar.html
... | ... | @@ -1,49 +0,0 @@ |
1 | -<nav class="navbar navbar-static-top navbar-inverse"> | |
2 | - <div class="container-fluid"> | |
3 | - <div class="navbar-header"> | |
4 | - <button type="button" class="navbar-toggle collapsed" ng-click="isCollapsed = !isCollapsed"> | |
5 | - <span class="sr-only">{{"navbar.toggle_menu" | translate}}</span> | |
6 | - <span class="icon-bar"></span> | |
7 | - <span class="icon-bar"></span> | |
8 | - <span class="icon-bar"></span> | |
9 | - </button> | |
10 | - <a class="navbar-brand" ui-sref="main"> | |
11 | - <span class="noosfero-logo"><img src="assets/images/logo-noosfero.png"></span> {{"noosfero.name" | translate}} | |
12 | - </a> | |
13 | - </div> | |
14 | - | |
15 | - <div class="collapse navbar-collapse" id="navbar-collapse" collapse="isCollapsed"> | |
16 | - <ul class="nav navbar-nav"> | |
17 | - </ul> | |
18 | - | |
19 | - <ul class="nav navbar-nav navbar-right"> | |
20 | - <li ng-show="!ctrl.currentUser"> | |
21 | - <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a> | |
22 | - </li> | |
23 | - | |
24 | - <li class="dropdown profile-menu" ng-show="ctrl.currentUser" dropdown> | |
25 | - <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
26 | - <noosfero-profile-image [profile]="ctrl.currentUser.person" class="profile-image"></noosfero-profile-image> | |
27 | - <span ng-bind="ctrl.currentUser.person.name"></span> <b class="caret"></b> | |
28 | - </a> | |
29 | - <ul class="dropdown-menu" dropdown-menu> | |
30 | - <li> | |
31 | - <a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-user"></i> {{"navbar.profile" | translate}}</a> | |
32 | - </li> | |
33 | - <li> | |
34 | - <a target="_self" ui-sref="main.profile.settings({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-gear"></i> {{"navbar.settings" | translate}}</a> | |
35 | - </li> | |
36 | - <li class="divider"></li> | |
37 | - <li> | |
38 | - <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a> | |
39 | - </li> | |
40 | - </ul> | |
41 | - </li> | |
42 | - </ul> | |
43 | - <ul class="nav navbar-nav navbar-right"> | |
44 | - <language-selector class="nav navbar-nav navbar-right"></language-selector> | |
45 | - </ul> | |
46 | - <div ui-view="actions"></div> | |
47 | - </div> | |
48 | - </div> | |
49 | -</nav> |
src/app/components/navbar/navbar.scss
... | ... | @@ -1,31 +0,0 @@ |
1 | -.navbar { | |
2 | - | |
3 | - .container-fluid { | |
4 | - padding-right: 12%; | |
5 | - padding-left: 12%; | |
6 | - @media (max-width: 978px) { | |
7 | - padding-right: 2%; | |
8 | - padding-left: 2%; | |
9 | - } | |
10 | - | |
11 | - .navbar-brand { | |
12 | - .noosfero-logo img { | |
13 | - height: 35px; | |
14 | - } | |
15 | - } | |
16 | - | |
17 | - .navbar-nav { | |
18 | - .profile-menu .profile-image { | |
19 | - img { | |
20 | - height: 30px; | |
21 | - width: 30px; | |
22 | - display: inline-block; | |
23 | - @extend .img-circle; | |
24 | - } | |
25 | - i { | |
26 | - font-size: 1.7em; | |
27 | - } | |
28 | - } | |
29 | - } | |
30 | - } | |
31 | -} |
src/app/components/navbar/navbar.spec.ts
... | ... | @@ -1,187 +0,0 @@ |
1 | -import * as helpers from "./../../../spec/helpers"; | |
2 | -import {Navbar} from "./navbar"; | |
3 | -import {AUTH_EVENTS} from "./../auth"; | |
4 | -import {Injectable, Provider, provide} from "ng-forward"; | |
5 | - | |
6 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
7 | - | |
8 | -import {Session, AuthService, AuthController, IAuthEvents} from "./../auth"; | |
9 | - | |
10 | - | |
11 | -describe("Components", () => { | |
12 | - | |
13 | - describe("Navbar Component", () => { | |
14 | - | |
15 | - let user: noosfero.User = null; | |
16 | - let scope: any; | |
17 | - let $rootScope: ng.IRootScopeService; | |
18 | - | |
19 | - let modalInstance: any; | |
20 | - let $modal: any; | |
21 | - let authService: any; | |
22 | - let stateService: any; | |
23 | - let sessionService: Session; | |
24 | - | |
25 | - let provideFunc = provide; | |
26 | - | |
27 | - // before Each -> loading mocks on locals variables | |
28 | - beforeEach(() => { | |
29 | - user = <noosfero.User>{ | |
30 | - id: 1, | |
31 | - login: "user" | |
32 | - }; | |
33 | - scope = helpers.mocks.scopeWithEvents; | |
34 | - modalInstance = helpers.mocks.modalInstance; | |
35 | - $modal = helpers.mocks.$modal; | |
36 | - authService = helpers.mocks.authService; | |
37 | - stateService = jasmine.createSpyObj("$state", ["go"]); | |
38 | - sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); | |
39 | - }); | |
40 | - | |
41 | - | |
42 | - // loading the templates | |
43 | - beforeEach(angular.mock.module("templates")); | |
44 | - | |
45 | - | |
46 | - // this function allow build the fixture of the container component | |
47 | - // and is reused in each test | |
48 | - // The main idea behing not prebuild it on a general beforeEach block is | |
49 | - // to allow tests configure the mock services accordilly their own needs | |
50 | - let buildComponent = (): Promise<ComponentFixture> => { | |
51 | - return helpers.quickCreateComponent({ | |
52 | - providers: [ | |
53 | - provide('$modal', { | |
54 | - useValue: $modal | |
55 | - }), | |
56 | - provide('AuthService', { | |
57 | - useValue: authService | |
58 | - }), | |
59 | - helpers.provideEmptyObjects('moment'), | |
60 | - provide('$state', { | |
61 | - useValue: stateService | |
62 | - }), | |
63 | - provide("$scope", { | |
64 | - useValue: scope | |
65 | - }), | |
66 | - provide('Session', { | |
67 | - useValue: sessionService | |
68 | - }), | |
69 | - provide('AUTH_EVENTS', { | |
70 | - useValue: { | |
71 | - AUTH_EVENTS | |
72 | - } | |
73 | - }), | |
74 | - provide('TranslatorService', { | |
75 | - useValue: helpers.mocks.translatorService | |
76 | - }) | |
77 | - ].concat(helpers.provideFilters("translateFilter")), | |
78 | - directives: [Navbar], | |
79 | - template: '<acme-navbar></acme-navbar>' | |
80 | - }); | |
81 | - }; | |
82 | - | |
83 | - | |
84 | - it('should get the loggedIn user', (done: Function) => { | |
85 | - buildComponent().then((fixture: ComponentFixture) => { | |
86 | - let navbarInstance: Navbar = fixture.debugElement.componentViewChildren[0].componentInstance; | |
87 | - expect(navbarInstance).toBeDefined(); | |
88 | - expect(navbarInstance["currentUser"]).toEqual(user); | |
89 | - done(); | |
90 | - }); | |
91 | - }); | |
92 | - | |
93 | - it('should open on click', (done: Function) => { | |
94 | - spyOn($modal, "open"); | |
95 | - buildComponent().then((fixture: ComponentFixture) => { | |
96 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
97 | - navbarComp.openLogin(); | |
98 | - expect($modal.open).toHaveBeenCalled(); | |
99 | - expect($modal.open).toHaveBeenCalledWith({ | |
100 | - templateUrl: 'app/components/auth/login.html', | |
101 | - controller: AuthController, | |
102 | - controllerAs: 'vm', | |
103 | - bindToController: true | |
104 | - }); | |
105 | - done(); | |
106 | - }); | |
107 | - }); | |
108 | - | |
109 | - it('should logout', (done: Function) => { | |
110 | - buildComponent().then((fixture: ComponentFixture) => { | |
111 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
112 | - spyOn(authService, "logout"); | |
113 | - try { | |
114 | - navbarComp.logout(); | |
115 | - expect(authService.logout).toHaveBeenCalled(); | |
116 | - done(); | |
117 | - } catch (e) { | |
118 | - console.error(e); | |
119 | - fail(e.message); | |
120 | - done(); | |
121 | - } | |
122 | - }); | |
123 | - }); | |
124 | - | |
125 | - | |
126 | - it('should not activate user when logged in', (done: Function) => { | |
127 | - buildComponent().then((fixture: ComponentFixture) => { | |
128 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
129 | - spyOn(navbarComp, "openLogin"); | |
130 | - navbarComp.activate(); | |
131 | - expect((<any>navbarComp.openLogin).calls.count()).toBe(0); | |
132 | - done(); | |
133 | - }); | |
134 | - }); | |
135 | - | |
136 | - it('should activate when user not logged in', (done: Function) => { | |
137 | - spyOn(sessionService, 'currentUser').and.returnValue(null); | |
138 | - buildComponent().then((fixture: ComponentFixture) => { | |
139 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
140 | - spyOn(navbarComp, "openLogin"); | |
141 | - navbarComp.activate(); | |
142 | - expect(navbarComp.openLogin).toHaveBeenCalled(); | |
143 | - done(); | |
144 | - }); | |
145 | - }); | |
146 | - | |
147 | - | |
148 | - it('closes the modal after login', (done: Function) => { | |
149 | - modalInstance = jasmine.createSpyObj("modalInstance", ["close"]); | |
150 | - modalInstance.close = jasmine.createSpy("close"); | |
151 | - | |
152 | - $modal.open = () => { | |
153 | - return modalInstance; | |
154 | - }; | |
155 | - | |
156 | - buildComponent().then((fixture: ComponentFixture) => { | |
157 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
158 | - let localScope: ng.IScope = navbarComp["$scope"]; | |
159 | - | |
160 | - navbarComp.openLogin(); | |
161 | - localScope.$emit(AUTH_EVENTS.loginSuccess); | |
162 | - expect(modalInstance.close).toHaveBeenCalled(); | |
163 | - done(); | |
164 | - }); | |
165 | - }); | |
166 | - | |
167 | - it('updates current user on logout', (done: Function) => { | |
168 | - buildComponent().then((fixture: ComponentFixture) => { | |
169 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
170 | - let localScope: ng.IScope = navbarComp["$scope"]; | |
171 | - | |
172 | - // init navbar currentUser with some user | |
173 | - navbarComp["currentUser"] = user; | |
174 | - | |
175 | - // changes the current User to return null, | |
176 | - // and emmit the 'logoutSuccess' event | |
177 | - // just what happens when user logsout | |
178 | - sessionService.currentUser = () => { return null; }; | |
179 | - localScope.$emit(AUTH_EVENTS.logoutSuccess); | |
180 | - expect(navbarComp["currentUser"]).toBeNull(); | |
181 | - done(); | |
182 | - }); | |
183 | - }); | |
184 | - | |
185 | - | |
186 | - }); | |
187 | -}); |
src/app/components/navbar/navbar.ts
... | ... | @@ -1,66 +0,0 @@ |
1 | -import {Component, Inject} from "ng-forward"; | |
2 | -import {LanguageSelector} from "../language-selector/language-selector.component"; | |
3 | - | |
4 | - | |
5 | -import {Session, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../auth"; | |
6 | - | |
7 | -@Component({ | |
8 | - selector: "acme-navbar", | |
9 | - templateUrl: "app/components/navbar/navbar.html", | |
10 | - directives: [LanguageSelector], | |
11 | - providers: [AuthService, Session] | |
12 | -}) | |
13 | -@Inject("$modal", AuthService, "Session", "$scope", "$state") | |
14 | -export class Navbar { | |
15 | - | |
16 | - private currentUser: noosfero.User; | |
17 | - private modalInstance: any = null; | |
18 | - /** | |
19 | - * | |
20 | - */ | |
21 | - constructor( | |
22 | - private $modal: any, | |
23 | - private authService: AuthService, | |
24 | - private session: Session, | |
25 | - private $scope: ng.IScope, | |
26 | - private $state: ng.ui.IStateService | |
27 | - ) { | |
28 | - this.currentUser = this.session.currentUser(); | |
29 | - | |
30 | - this.$scope.$on(AUTH_EVENTS.loginSuccess, () => { | |
31 | - if (this.modalInstance) { | |
32 | - this.modalInstance.close(); | |
33 | - this.modalInstance = null; | |
34 | - } | |
35 | - | |
36 | - this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
37 | - }); | |
38 | - | |
39 | - this.$scope.$on(AUTH_EVENTS.logoutSuccess, () => { | |
40 | - this.currentUser = this.session.currentUser(); | |
41 | - }); | |
42 | - } | |
43 | - | |
44 | - openLogin() { | |
45 | - this.modalInstance = this.$modal.open({ | |
46 | - templateUrl: 'app/components/auth/login.html', | |
47 | - controller: AuthController, | |
48 | - controllerAs: 'vm', | |
49 | - bindToController: true | |
50 | - }); | |
51 | - }; | |
52 | - | |
53 | - logout() { | |
54 | - this.authService.logout(); | |
55 | - this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
56 | - }; | |
57 | - | |
58 | - | |
59 | - | |
60 | - activate() { | |
61 | - if (!this.currentUser) { | |
62 | - this.openLogin(); | |
63 | - } | |
64 | - } | |
65 | - | |
66 | -} |
src/app/components/noosfero-activities/activities.component.spec.ts
... | ... | @@ -1,36 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | -import {provideFilters} from '../../../spec/helpers'; | |
4 | - | |
5 | -import {NoosferoActivities} from './activities.component'; | |
6 | - | |
7 | -const tcb = new TestComponentBuilder(); | |
8 | - | |
9 | -const htmlTemplate: string = '<noosfero-activities [activities]="ctrl.activities"></noosfero-activities>'; | |
10 | - | |
11 | - | |
12 | -describe("Components", () => { | |
13 | - | |
14 | - describe("Noosfero Activities", () => { | |
15 | - | |
16 | - beforeEach(angular.mock.module("templates")); | |
17 | - | |
18 | - @Component({ | |
19 | - selector: 'test-container-component', | |
20 | - template: htmlTemplate, | |
21 | - directives: [NoosferoActivities], | |
22 | - providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
23 | - }) | |
24 | - class BlockContainerComponent { | |
25 | - activities = [{ name: "activity1", verb: "create_article" }, { name: "activity2", verb: "create_article" }]; | |
26 | - } | |
27 | - | |
28 | - it("render a noosfero activity tag for each activity", done => { | |
29 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
30 | - expect(fixture.debugElement.queryAll("noosfero-activity").length).toEqual(2); | |
31 | - done(); | |
32 | - }); | |
33 | - }); | |
34 | - }); | |
35 | - | |
36 | -}); |
src/app/components/noosfero-activities/activities.component.ts
... | ... | @@ -1,27 +0,0 @@ |
1 | -import {Component, Input} from "ng-forward"; | |
2 | -import {NoosferoActivity} from "./activity/activity.component"; | |
3 | - | |
4 | -/** | |
5 | - * @ngdoc controller | |
6 | - * @name NoosferoActivities | |
7 | - * @description | |
8 | - * The controller responsible to retreive profile activities. | |
9 | - */ | |
10 | - | |
11 | -@Component({ | |
12 | - selector: "noosfero-activities", | |
13 | - templateUrl: 'app/components/noosfero-activities/activities.html', | |
14 | - directives: [NoosferoActivity] | |
15 | -}) | |
16 | -export class NoosferoActivities { | |
17 | - | |
18 | - /** | |
19 | - * @ngdoc property | |
20 | - * @propertyOf NoosferoActivities | |
21 | - * @name activities | |
22 | - * @returns {Activity[]} An array of {@link Activity}. | |
23 | - */ | |
24 | - @Input() activities: noosfero.Activity[]; | |
25 | - | |
26 | - | |
27 | -} |
src/app/components/noosfero-activities/activities.html
src/app/components/noosfero-activities/activities.scss
src/app/components/noosfero-activities/activity/activity.component.spec.ts
... | ... | @@ -1,38 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | -import {provideFilters} from '../../../../spec/helpers'; | |
4 | - | |
5 | -import {NoosferoActivity} from './activity.component'; | |
6 | - | |
7 | -const tcb = new TestComponentBuilder(); | |
8 | - | |
9 | -const htmlTemplate: string = '<noosfero-activity [activity]="ctrl.activity"></noosfero-activity>'; | |
10 | - | |
11 | - | |
12 | -describe("Components", () => { | |
13 | - | |
14 | - describe("Noosfero Activity", () => { | |
15 | - | |
16 | - beforeEach(angular.mock.module("templates")); | |
17 | - | |
18 | - @Component({ | |
19 | - selector: 'test-container-component', | |
20 | - template: htmlTemplate, | |
21 | - directives: [NoosferoActivity], | |
22 | - providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
23 | - }) | |
24 | - class BlockContainerComponent { | |
25 | - activity = { name: "activity1", verb: "create_article" }; | |
26 | - } | |
27 | - | |
28 | - it("render the specific template for an activity verb", done => { | |
29 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
30 | - let component: NoosferoActivity = fixture.debugElement.componentViewChildren[0].componentInstance; | |
31 | - expect(component.getActivityTemplate()).toEqual('app/components/noosfero-activities/activity/create_article.html'); | |
32 | - expect(fixture.debugElement.queryAll(".activity.create_article").length).toEqual(1); | |
33 | - done(); | |
34 | - }); | |
35 | - }); | |
36 | - }); | |
37 | - | |
38 | -}); |
src/app/components/noosfero-activities/activity/activity.component.ts
... | ... | @@ -1,15 +0,0 @@ |
1 | -import {Component, Input} from "ng-forward"; | |
2 | - | |
3 | -@Component({ | |
4 | - selector: "noosfero-activity", | |
5 | - templateUrl: 'app/components/noosfero-activities/activity/activity.html' | |
6 | -}) | |
7 | -export class NoosferoActivity { | |
8 | - | |
9 | - @Input() activity: noosfero.Activity; | |
10 | - | |
11 | - getActivityTemplate() { | |
12 | - return 'app/components/noosfero-activities/activity/' + this.activity.verb + '.html'; | |
13 | - } | |
14 | - | |
15 | -} |
src/app/components/noosfero-activities/activity/activity.html
src/app/components/noosfero-activities/activity/add_member_in_community.html
... | ... | @@ -1,13 +0,0 @@ |
1 | -<timeline-badge class="info"> | |
2 | - <i class="fa fa-user-plus"></i> | |
3 | -</timeline-badge> | |
4 | -<timeline-panel> | |
5 | - <timeline-heading> | |
6 | - <h4 class="timeline-title"> | |
7 | - <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
8 | - <span> {{"activities.add_member_in_community.description" | translate}}</span> | |
9 | - </h4> | |
10 | - <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
11 | - </timeline-heading> | |
12 | - <div class="timeline-body"></div> | |
13 | -</timeline-panel> |
src/app/components/noosfero-activities/activity/create_article.html
... | ... | @@ -1,26 +0,0 @@ |
1 | -<timeline-badge class="success"> | |
2 | - <i class="fa fa-file-text"></i> | |
3 | -</timeline-badge> | |
4 | -<timeline-panel> | |
5 | - <timeline-heading> | |
6 | - <h4 class="timeline-title"> | |
7 | - <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
8 | - <span> {{"activities.create_article.description" | translate}} </span> | |
9 | - <a ui-sref="main.profile.info({profile: ctrl.activity.target.article.profile.identifier})"> | |
10 | - <strong ng-bind="ctrl.activity.target.article.profile.name"></strong></span> | |
11 | - </a> | |
12 | - </h4> | |
13 | - <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
14 | - </timeline-heading> | |
15 | - <div class="timeline-body"> | |
16 | - <div class="article"> | |
17 | - <div class="title"> | |
18 | - <a ui-sref="main.profile.page({profile: ctrl.activity.target.article.profile.identifier, page: ctrl.activity.target.article.path})" | |
19 | - ng-bind="ctrl.activity.target.article.title"></a> | |
20 | - </div> | |
21 | - <div class="lead small"> | |
22 | - <div ng-bind-html="ctrl.activity.target.article.body | stripTags | truncate: 100 : '...': true"></div> | |
23 | - </div> | |
24 | - </div> | |
25 | - </div> | |
26 | -</timeline-panel> |
src/app/components/noosfero-activities/activity/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-activities/activity/new_friendship.html
... | ... | @@ -1,18 +0,0 @@ |
1 | -<timeline-badge class="info"> | |
2 | - <i class="fa fa-user-plus"></i> | |
3 | -</timeline-badge> | |
4 | -<timeline-panel> | |
5 | - <timeline-heading> | |
6 | - <h4 class="timeline-title"> | |
7 | - <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
8 | - <span> {{"activities.new_friendship.description" | translate:{friends: ctrl.activity.params.friend_name.length}:"messageformat" }} </span> | |
9 | - <span class="comma-separated"> | |
10 | - <a class="separated-item" ui-sref="main.profile.info({profile: ctrl.activity.params.friend_url[$index].profile})" ng-repeat="friend in ctrl.activity.params.friend_name"> | |
11 | - <strong ng-bind="friend"></strong> | |
12 | - </a> | |
13 | - </span> | |
14 | - </h4> | |
15 | - <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
16 | - </timeline-heading> | |
17 | - <div class="timeline-body"></div> | |
18 | -</timeline-panel> |
src/app/components/noosfero-activities/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-articles/article/article.html
... | ... | @@ -1,23 +0,0 @@ |
1 | -<div class="article"> | |
2 | - <div class="page-header"> | |
3 | - <h3 ng-bind="ctrl.article.title"></h3> | |
4 | - </div> | |
5 | - | |
6 | - <div class="sub-header clearfix"> | |
7 | - <div class="page-info pull-right small text-muted"> | |
8 | - <span class="time"> | |
9 | - <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> | |
10 | - </span> | |
11 | - <span class="author" ng-if="ctrl.article.author"> | |
12 | - <i class="fa fa-user"></i> | |
13 | - <a ui-sref="main.profile.home({profile: ctrl.article.author.identifier})"> | |
14 | - <span class="author-name" ng-bind="ctrl.article.author.name"></span> | |
15 | - </a> | |
16 | - </span> | |
17 | - </div> | |
18 | - </div> | |
19 | - | |
20 | - <div class="page-body"> | |
21 | - <div ng-bind-html="ctrl.article.body"></div> | |
22 | - </div> | |
23 | -</div> |
src/app/components/noosfero-articles/article/article.scss
src/app/components/noosfero-articles/article/article_view.spec.ts
... | ... | @@ -1,108 +0,0 @@ |
1 | - | |
2 | -import {Input, provide, Component} from 'ng-forward'; | |
3 | -import {ArticleView, ArticleDefaultView} from './article_view'; | |
4 | - | |
5 | -import {createComponentFromClass, quickCreateComponent} from "../../../../spec/helpers"; | |
6 | - | |
7 | -// this htmlTemplate will be re-used between the container components in this spec file | |
8 | -const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-article>'; | |
9 | - | |
10 | - | |
11 | -describe("Components", () => { | |
12 | - | |
13 | - describe("ArticleView Component", () => { | |
14 | - | |
15 | - // the karma preprocessor html2js transform the templates html into js files which put | |
16 | - // the templates to the templateCache into the module templates | |
17 | - // we need to load the module templates here as the template for the | |
18 | - // component Noosfero ArtileView will be load on our tests | |
19 | - beforeEach(angular.mock.module("templates")); | |
20 | - | |
21 | - it("renders the default component when no specific component is found", (done: Function) => { | |
22 | - // Creating a container component (ArticleContainerComponent) to include | |
23 | - // the component under test (ArticleView) | |
24 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleView] }) | |
25 | - class ArticleContainerComponent { | |
26 | - article = { type: 'anyArticleType' }; | |
27 | - profile = { name: 'profile-name' }; | |
28 | - constructor() { | |
29 | - } | |
30 | - } | |
31 | - | |
32 | - createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
33 | - // and here we can inspect and run the test assertions | |
34 | - | |
35 | - // gets the children component of ArticleContainerComponent | |
36 | - let articleView: ArticleView = fixture.debugElement.componentViewChildren[0].componentInstance; | |
37 | - | |
38 | - // and checks if the article View rendered was the Default Article View | |
39 | - expect(articleView.constructor.prototype).toEqual(ArticleDefaultView.prototype); | |
40 | - | |
41 | - // done needs to be called (it isn't really needed, as we can read in | |
42 | - // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
43 | - // because createAsync in ng-forward is not really async, but as the intention | |
44 | - // here is write tests in angular 2 ways, this is recommended | |
45 | - done(); | |
46 | - }); | |
47 | - | |
48 | - }); | |
49 | - | |
50 | - it("receives the article and profile as inputs", (done: Function) => { | |
51 | - | |
52 | - // Creating a container component (ArticleContainerComponent) to include | |
53 | - // the component under test (ArticleView) | |
54 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleView] }) | |
55 | - class ArticleContainerComponent { | |
56 | - article = { type: 'anyArticleType' }; | |
57 | - profile = { name: 'profile-name' }; | |
58 | - constructor() { | |
59 | - } | |
60 | - } | |
61 | - | |
62 | - // uses the TestComponentBuilder instance to initialize the component | |
63 | - createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
64 | - // and here we can inspect and run the test assertions | |
65 | - let articleView: ArticleView = fixture.debugElement.componentViewChildren[0].componentInstance; | |
66 | - | |
67 | - // assure the article object inside the ArticleView matches | |
68 | - // the provided through the parent component | |
69 | - expect(articleView.article.type).toEqual("anyArticleType"); | |
70 | - expect(articleView.profile.name).toEqual("profile-name"); | |
71 | - | |
72 | - // done needs to be called (it isn't really needed, as we can read in | |
73 | - // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
74 | - // because createAsync in ng-forward is not really async, but as the intention | |
75 | - // here is write tests in angular 2 ways, this is recommended | |
76 | - done(); | |
77 | - }); | |
78 | - }); | |
79 | - | |
80 | - | |
81 | - it("renders a article view which matches to the article type", done => { | |
82 | - // NoosferoTinyMceArticle component created to check if it will be used | |
83 | - // when a article with type 'TinyMceArticle' is provided to the noosfero-article (ArticleView) | |
84 | - // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
85 | - @Component({ selector: 'noosfero-tiny-mce-article', template: "<h1>TinyMceArticle</h1>" }) | |
86 | - class TinyMceArticleView { | |
87 | - @Input() article: any; | |
88 | - @Input() profile: any; | |
89 | - } | |
90 | - | |
91 | - // Creating a container component (ArticleContainerComponent) to include our NoosferoTinyMceArticle | |
92 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleView, TinyMceArticleView] }) | |
93 | - class CustomArticleType { | |
94 | - article = { type: 'TinyMceArticle' }; | |
95 | - profile = { name: 'profile-name' }; | |
96 | - constructor() { | |
97 | - } | |
98 | - } | |
99 | - createComponentFromClass(CustomArticleType).then(fixture => { | |
100 | - let myComponent: CustomArticleType = fixture.componentInstance; | |
101 | - expect(myComponent.article.type).toEqual("TinyMceArticle"); | |
102 | - expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("TinyMceArticle"); | |
103 | - done(); | |
104 | - }); | |
105 | - }); | |
106 | - | |
107 | - }); | |
108 | -}); | |
109 | 0 | \ No newline at end of file |
src/app/components/noosfero-articles/article/article_view.ts
... | ... | @@ -1,57 +0,0 @@ |
1 | -import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; | |
2 | -import {ArticleBlog} from "../blog/blog.component"; | |
3 | - | |
4 | -/** | |
5 | - * @ngdoc controller | |
6 | - * @name ArticleDefaultView | |
7 | - * @description | |
8 | - * A default view for Noosfero Articles. If the specific article view is | |
9 | - * not implemented, then this view is used. | |
10 | - */ | |
11 | -@Component({ | |
12 | - selector: 'noosfero-default-article', | |
13 | - templateUrl: 'app/components/noosfero-articles/article/article.html' | |
14 | -}) | |
15 | -export class ArticleDefaultView { | |
16 | - | |
17 | - @Input() article: noosfero.Article; | |
18 | - @Input() profile: noosfero.Profile; | |
19 | - | |
20 | -} | |
21 | - | |
22 | -/** | |
23 | - * @ngdoc controller | |
24 | - * @name ArticleView | |
25 | - * @description | |
26 | - * A dynamic view for articles. It uses the article type to replace | |
27 | - * the default template with the custom article directive. | |
28 | - */ | |
29 | -@Component({ | |
30 | - selector: 'noosfero-article', | |
31 | - template: 'not-used', | |
32 | - directives: [ArticleDefaultView, ArticleBlog] | |
33 | -}) | |
34 | -@Inject("$element", "$scope", "$injector", "$compile") | |
35 | -export class ArticleView { | |
36 | - | |
37 | - @Input() article: noosfero.Article; | |
38 | - @Input() profile: noosfero.Profile; | |
39 | - directiveName: string; | |
40 | - | |
41 | - ngOnInit() { | |
42 | - let specificDirective = 'noosfero' + this.article.type; | |
43 | - this.directiveName = "noosfero-default-article"; | |
44 | - if (this.$injector.has(specificDirective + 'Directive')) { | |
45 | - this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
46 | - } | |
47 | - this.$element.replaceWith(this.$compile('<' + this.directiveName + ' [article]="ctrl.article" [profile]="ctrl.profile"></' + this.directiveName + '>')(this.$scope)); | |
48 | - } | |
49 | - | |
50 | - constructor( | |
51 | - private $element: any, | |
52 | - private $scope: ng.IScope, | |
53 | - private $injector: ng.auto.IInjectorService, | |
54 | - private $compile: ng.ICompileService) { | |
55 | - | |
56 | - } | |
57 | -} |
src/app/components/noosfero-articles/article/index.ts
src/app/components/noosfero-articles/blog/blog.component.spec.ts
... | ... | @@ -1,129 +0,0 @@ |
1 | -import { | |
2 | -providers | |
3 | -} from 'ng-forward/cjs/testing/providers'; | |
4 | - | |
5 | -import { | |
6 | -Input, | |
7 | -Component | |
8 | -} from 'ng-forward'; | |
9 | -import { | |
10 | -ArticleBlog | |
11 | -} from './blog.component'; | |
12 | - | |
13 | -import { | |
14 | -createComponentFromClass, | |
15 | -quickCreateComponent, | |
16 | -provideEmptyObjects, | |
17 | -createProviderToValue, | |
18 | -provideFilters | |
19 | -} from "../../../../spec/helpers.ts"; | |
20 | - | |
21 | -// this htmlTemplate will be re-used between the container components in this spec file | |
22 | -const htmlTemplate: string = '<noosfero-blog [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-blog>'; | |
23 | - | |
24 | -describe("Blog Component", () => { | |
25 | - | |
26 | - function promiseResultTemplate(response?: {}) { | |
27 | - let thenFuncEmpty = (func: Function) => { | |
28 | - // does nothing | |
29 | - }; | |
30 | - if (response) { | |
31 | - return { | |
32 | - then: (func: (response: any) => void) => { | |
33 | - func(response); | |
34 | - } | |
35 | - }; | |
36 | - } else { | |
37 | - return { | |
38 | - then: (func: (response: any) => void) => { | |
39 | - // does nothing | |
40 | - } | |
41 | - }; | |
42 | - } | |
43 | - } | |
44 | - | |
45 | - let articleService = { | |
46 | - getChildren: (article_id: number, filters: {}) => { | |
47 | - return promiseResultTemplate(null); | |
48 | - } | |
49 | - }; | |
50 | - | |
51 | - @Component({ | |
52 | - selector: 'test-container-component', | |
53 | - template: htmlTemplate, | |
54 | - directives: [ArticleBlog], | |
55 | - providers: [ | |
56 | - provideEmptyObjects('Restangular'), | |
57 | - createProviderToValue('ArticleService', articleService), | |
58 | - provideFilters('truncateFilter') | |
59 | - ] | |
60 | - }) | |
61 | - class BlogContainerComponent { | |
62 | - article = { | |
63 | - type: 'anyArticleType' | |
64 | - }; | |
65 | - profile = { | |
66 | - name: 'profile-name' | |
67 | - }; | |
68 | - } | |
69 | - | |
70 | - beforeEach(() => { | |
71 | - | |
72 | - // the karma preprocessor html2js transform the templates html into js files which put | |
73 | - // the templates to the templateCache into the module templates | |
74 | - // we need to load the module templates here as the template for the | |
75 | - // component Noosfero ArtileView will be load on our tests | |
76 | - angular.mock.module("templates"); | |
77 | - | |
78 | - providers((provide: any) => { | |
79 | - return <any>[ | |
80 | - provide('ArticleService', { | |
81 | - useValue: articleService | |
82 | - }) | |
83 | - ]; | |
84 | - }); | |
85 | - }); | |
86 | - | |
87 | - it("renders the blog content", (done: Function) => { | |
88 | - | |
89 | - createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
90 | - | |
91 | - expect(fixture.debugElement.query('div.blog').length).toEqual(1); | |
92 | - | |
93 | - done(); | |
94 | - }); | |
95 | - }); | |
96 | - | |
97 | - it("verify the blog data", (done: Function) => { | |
98 | - | |
99 | - let articles = [{ | |
100 | - id: 1, | |
101 | - title: 'The article test' | |
102 | - }]; | |
103 | - | |
104 | - let result = { data: articles, headers: (name: string) => { return 1; } }; | |
105 | - | |
106 | - // defining a mock result to articleService.getChildren method | |
107 | - articleService.getChildren = (article_id: number, filters: {}) => { | |
108 | - return promiseResultTemplate(result); | |
109 | - }; | |
110 | - | |
111 | - createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
112 | - | |
113 | - // gets the children component of BlogContainerComponent | |
114 | - let articleBlog: BlogContainerComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
115 | - | |
116 | - // check if the component property are the provided by the mocked articleService | |
117 | - let post = { | |
118 | - id: 1, | |
119 | - title: 'The article test' | |
120 | - }; | |
121 | - expect((<any>articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post)); | |
122 | - expect((<any>articleBlog)["totalPosts"]).toEqual(1); | |
123 | - | |
124 | - done(); | |
125 | - }); | |
126 | - | |
127 | - }); | |
128 | - | |
129 | -}); | |
130 | 0 | \ No newline at end of file |
src/app/components/noosfero-articles/blog/blog.component.ts
... | ... | @@ -1,47 +0,0 @@ |
1 | -import {Component, Input, Inject} from "ng-forward"; | |
2 | - | |
3 | -import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
4 | - | |
5 | -/** | |
6 | - * @ngdoc controller | |
7 | - * @name ArticleBlog | |
8 | - * @description | |
9 | - * An specific {@link ArticleView} for Blog articles. | |
10 | - */ | |
11 | -@Component({ | |
12 | - selector: "noosfero-blog", | |
13 | - templateUrl: "app/components/noosfero-articles/blog/blog.html" | |
14 | -}) | |
15 | -@Inject(ArticleService) | |
16 | -export class ArticleBlog { | |
17 | - | |
18 | - @Input() article: noosfero.Article; | |
19 | - @Input() profile: noosfero.Profile; | |
20 | - | |
21 | - private posts: noosfero.Article[]; | |
22 | - private perPage: number = 3; | |
23 | - private currentPage: number; | |
24 | - private totalPosts: number = 0; | |
25 | - | |
26 | - constructor(private articleService: ArticleService) { } | |
27 | - | |
28 | - ngOnInit() { | |
29 | - this.loadPage(); | |
30 | - } | |
31 | - | |
32 | - loadPage() { | |
33 | - let filters = { | |
34 | - content_type: "TinyMceArticle", | |
35 | - per_page: this.perPage, | |
36 | - page: this.currentPage | |
37 | - }; | |
38 | - | |
39 | - this.articleService | |
40 | - .getChildren(this.article, filters) | |
41 | - .then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
42 | - this.totalPosts = <number>result.headers("total"); | |
43 | - this.posts = result.data; | |
44 | - }); | |
45 | - } | |
46 | - | |
47 | -} |
src/app/components/noosfero-articles/blog/blog.html
... | ... | @@ -1,24 +0,0 @@ |
1 | -<div class="blog"> | |
2 | - <div class="blog-cover" ng-show="ctrl.article.image"> | |
3 | - <img ng-src="{{ctrl.article.image.url}}" class="img-responsive"> | |
4 | - <h3 ng-bind="ctrl.article.title"></h3> | |
5 | - </div> | |
6 | - | |
7 | - <div class="page-header" ng-show="!ctrl.article.image"> | |
8 | - <h3 ng-bind="ctrl.article.title"></h3> | |
9 | - </div> | |
10 | - | |
11 | - <div> | |
12 | - <div ng-repeat="child in ctrl.posts | orderBy: 'created_at':true"> | |
13 | - <div class="page-header"> | |
14 | - <a class="title" ui-sref="main.profile.page({profile: ctrl.profile.identifier, page: child.path})"><h4 ng-bind="child.title"></h4></a> | |
15 | - <div class="post-lead" ng-bind-html="child.body | truncate: 500: '...': true"></div> | |
16 | - </div> | |
17 | - </div> | |
18 | - </div> | |
19 | - | |
20 | - <pagination ng-model="ctrl.currentPage" total-items="ctrl.totalPosts" class="pagination-sm center-block" | |
21 | - boundary-links="true" items-per-page="ctrl.perPage" ng-change="ctrl.loadPage()" | |
22 | - first-text="«" last-text="»" previous-text="‹" next-text="›"> | |
23 | - </pagination> | |
24 | -</div> |
src/app/components/noosfero-articles/blog/blog.scss
src/app/components/noosfero-articles/blog/index.ts
src/app/components/noosfero-articles/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/block.component.spec.ts
... | ... | @@ -1,91 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import {Block} from './block.component'; | |
5 | - | |
6 | -const tcb = new TestComponentBuilder(); | |
7 | - | |
8 | -const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block>'; | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - describe("Block Component", () => { | |
12 | - | |
13 | - // the karma preprocessor html2js transform the templates html into js files which put | |
14 | - // the templates to the templateCache into the module templates | |
15 | - // we need to load the module templates here as the template for the | |
16 | - // component Block will be load on our tests | |
17 | - beforeEach(angular.mock.module("templates")); | |
18 | - | |
19 | - it("receives the block and the owner as inputs", done => { | |
20 | - | |
21 | - // Creating a container component (BlockContainerComponent) to include | |
22 | - // the component under test (Block) | |
23 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [Block] }) | |
24 | - class BlockContainerComponent { | |
25 | - block = { type: 'Block' }; | |
26 | - owner = { name: 'profile-name' }; | |
27 | - constructor() { | |
28 | - } | |
29 | - } | |
30 | - | |
31 | - // uses the TestComponentBuilder instance to initialize the component | |
32 | - tcb | |
33 | - .createAsync(BlockContainerComponent).then(fixture => { | |
34 | - // and here we can inspect and run the test assertions | |
35 | - let myComponent: Block = fixture.componentInstance; | |
36 | - | |
37 | - // assure the block object inside the Block matches | |
38 | - // the provided through the parent component | |
39 | - expect(myComponent.block.type).toEqual("Block"); | |
40 | - expect(myComponent.owner.name).toEqual("profile-name"); | |
41 | - done(); | |
42 | - }); | |
43 | - }); | |
44 | - | |
45 | - | |
46 | - it("renders a component which matches to the block type", done => { | |
47 | - // CustomBlock component created to check if it will be used | |
48 | - // when a block with type 'CustomBlock' is provided to the noosfero-block (Block) | |
49 | - // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
50 | - @Component({ selector: 'noosfero-custom-block', template: "<h1>My Custom Block</h1>" }) | |
51 | - class CustomBlock { | |
52 | - @Input() block: any; | |
53 | - @Input() owner: any; | |
54 | - } | |
55 | - | |
56 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [Block, CustomBlock] }) | |
57 | - class CustomBlockType { | |
58 | - block = { type: 'CustomBlock' }; | |
59 | - owner = { name: 'profile-name' }; | |
60 | - constructor() { | |
61 | - } | |
62 | - } | |
63 | - tcb | |
64 | - .createAsync(CustomBlockType).then(fixture => { | |
65 | - let myComponent: CustomBlockType = fixture.componentInstance; | |
66 | - expect(myComponent.block.type).toEqual("CustomBlock"); | |
67 | - expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("My Custom Block"); | |
68 | - done(); | |
69 | - }); | |
70 | - }); | |
71 | - | |
72 | - | |
73 | - it("renders the default block when hasn't defined a block type", done => { | |
74 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [Block] }) | |
75 | - class CustomBlockType { | |
76 | - block: any = { type: null }; | |
77 | - owner: any = { name: 'profile-name' }; | |
78 | - constructor() { | |
79 | - } | |
80 | - } | |
81 | - tcb | |
82 | - .createAsync(CustomBlockType).then(fixture => { | |
83 | - let myComponent: CustomBlockType = fixture.componentInstance; | |
84 | - expect(myComponent.block.type).toBeNull(); | |
85 | - expect(!!fixture.debugElement.nativeElement.querySelector("noosfero-default-block")).toBeTruthy(); | |
86 | - done(); | |
87 | - }); | |
88 | - }); | |
89 | - | |
90 | - }); | |
91 | -}); | |
92 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/block.component.ts
... | ... | @@ -1,20 +0,0 @@ |
1 | -import { Input, Inject, Component } from 'ng-forward'; | |
2 | - | |
3 | -@Component({ | |
4 | - selector: 'noosfero-block', | |
5 | - template: '<div></div>' | |
6 | -}) | |
7 | -@Inject("$element", "$scope", "$injector", "$compile") | |
8 | -export class Block { | |
9 | - | |
10 | - @Input() block: any; | |
11 | - @Input() owner: any; | |
12 | - | |
13 | - ngOnInit() { | |
14 | - let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
15 | - this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); | |
16 | - } | |
17 | - | |
18 | - constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) { | |
19 | - } | |
20 | -} |
src/app/components/noosfero-blocks/block.scss
src/app/components/noosfero-blocks/index.ts
src/app/components/noosfero-blocks/link-list/index.ts
src/app/components/noosfero-blocks/link-list/link-list.component.spec.ts
... | ... | @@ -1,65 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | -import {provideFilters} from '../../../../spec/helpers'; | |
4 | - | |
5 | -import {LinkListBlock} from './link-list.component'; | |
6 | - | |
7 | -const tcb = new TestComponentBuilder(); | |
8 | - | |
9 | -const htmlTemplate: string = '<noosfero-link-list-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-link-list-block>'; | |
10 | - | |
11 | - | |
12 | -describe("Components", () => { | |
13 | - | |
14 | - describe("Link List Block Component", () => { | |
15 | - | |
16 | - beforeEach(angular.mock.module("templates")); | |
17 | - | |
18 | - it("receives the block and the owner as inputs", done => { | |
19 | - | |
20 | - // Creating a container component (BlockContainerComponent) to include | |
21 | - // the component under test (Block) | |
22 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [LinkListBlock] }) | |
23 | - class BlockContainerComponent { | |
24 | - block = { type: 'Block' }; | |
25 | - owner = { name: 'profile-name' }; | |
26 | - constructor() { | |
27 | - } | |
28 | - } | |
29 | - | |
30 | - // uses the TestComponentBuilder instance to initialize the component | |
31 | - // .overrideView(LinkListBlock, { template: 'asdasdasd', pipes: [NoosferoTemplate] }) | |
32 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
33 | - // and here we can inspect and run the test assertions | |
34 | - let myComponent: LinkListBlock = fixture.componentInstance; | |
35 | - | |
36 | - // assure the block object inside the Block matches | |
37 | - // the provided through the parent component | |
38 | - expect(myComponent.block.type).toEqual("Block"); | |
39 | - expect(myComponent.owner.name).toEqual("profile-name"); | |
40 | - done(); | |
41 | - }); | |
42 | - }); | |
43 | - | |
44 | - | |
45 | - it("display links stored in block settings", done => { | |
46 | - | |
47 | - @Component({ | |
48 | - selector: 'test-container-component', | |
49 | - template: htmlTemplate, | |
50 | - directives: [LinkListBlock], | |
51 | - providers: provideFilters("noosferoTemplateFilter") | |
52 | - }) | |
53 | - class CustomBlockType { | |
54 | - block: any = { settings: { links: [{ name: 'link1', address: 'address1' }, { name: 'link2', address: 'address2' }] } }; | |
55 | - owner: any = { name: 'profile-name' }; | |
56 | - } | |
57 | - tcb.createAsync(CustomBlockType).then(fixture => { | |
58 | - expect(fixture.debugElement.queryAll(".link-list-block a").length).toEqual(2); | |
59 | - done(); | |
60 | - }); | |
61 | - }); | |
62 | - | |
63 | - }); | |
64 | - | |
65 | -}); | |
66 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/link-list/link-list.component.ts
... | ... | @@ -1,20 +0,0 @@ |
1 | -import {Component, Input} from "ng-forward"; | |
2 | - | |
3 | -@Component({ | |
4 | - selector: "noosfero-link-list-block", | |
5 | - templateUrl: "app/components/noosfero-blocks/link-list/link-list.html" | |
6 | -}) | |
7 | -export class LinkListBlock { | |
8 | - | |
9 | - @Input() block: any; | |
10 | - @Input() owner: any; | |
11 | - | |
12 | - links: any; | |
13 | - | |
14 | - ngOnInit() { | |
15 | - if (this.block && this.block.settings) { | |
16 | - this.links = this.block.settings.links; | |
17 | - } | |
18 | - } | |
19 | - | |
20 | -} |
src/app/components/noosfero-blocks/link-list/link-list.html
src/app/components/noosfero-blocks/link-list/link-list.scss
... | ... | @@ -1,34 +0,0 @@ |
1 | -.icon-event { | |
2 | - @extend .fa-calendar; | |
3 | -} | |
4 | -.icon-photos { | |
5 | - @extend .fa-photo; | |
6 | -} | |
7 | -.icon-edit { | |
8 | - @extend .fa-edit; | |
9 | -} | |
10 | -.icon-ok { | |
11 | - @extend .fa-check; | |
12 | -} | |
13 | -.icon-send { | |
14 | - @extend .fa-send-o; | |
15 | -} | |
16 | -.icon-menu-people { | |
17 | - @extend .fa-user; | |
18 | -} | |
19 | -.icon-forum { | |
20 | - @extend .fa-users; | |
21 | -} | |
22 | -.icon-new { | |
23 | - @extend .fa-file-o; | |
24 | -} | |
25 | -.icon-save { | |
26 | - @extend .fa-save; | |
27 | -} | |
28 | - | |
29 | -.link-list-block { | |
30 | - a i { | |
31 | - line-height: 25px; | |
32 | - color: #949494; | |
33 | - } | |
34 | -} |
src/app/components/noosfero-blocks/main-block/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/main-block/main-block.component.spec.ts
... | ... | @@ -1,40 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Input, provide, Component, StateConfig} from 'ng-forward'; | |
3 | - | |
4 | -import {MainBlock} from './main-block.component'; | |
5 | -import {NoosferoApp} from '../../../index.module'; | |
6 | - | |
7 | -const tcb = new TestComponentBuilder(); | |
8 | - | |
9 | -const htmlTemplate: string = '<noosfero-main-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-main-block>'; | |
10 | - | |
11 | -describe("Components", () => { | |
12 | - describe("Main Block Component", () => { | |
13 | - | |
14 | - // the karma preprocessor html2js transform the templates html into js files which put | |
15 | - // the templates to the templateCache into the module templates | |
16 | - // we need to load the module templates here as the template for the | |
17 | - // component Block will be load on our tests | |
18 | - beforeEach(angular.mock.module("templates")); | |
19 | - | |
20 | - it("check if the main block has a tag with ui-view attribute", done => { | |
21 | - | |
22 | - // Creating a container component (BlockContainerComponent) to include | |
23 | - // the component under test (Block) | |
24 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MainBlock] }) | |
25 | - class BlockContainerComponent { | |
26 | - } | |
27 | - | |
28 | - // uses the TestComponentBuilder instance to initialize the component | |
29 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
30 | - // and here we can inspect and run the test assertions | |
31 | - // let myComponent: MainBlock = fixture.componentInstance; | |
32 | - | |
33 | - // assure the block object inside the Block matches | |
34 | - // the provided through the parent component | |
35 | - expect(fixture.debugElement.queryAll('[ui-view="mainBlockContent"]').length).toEqual(1); | |
36 | - done(); | |
37 | - }); | |
38 | - }); | |
39 | - }); | |
40 | -}); | |
41 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/main-block/main-block.component.ts
src/app/components/noosfero-blocks/main-block/main-block.html
... | ... | @@ -1 +0,0 @@ |
1 | -<div ui-view="mainBlockContent" autoscroll></div> |
src/app/components/noosfero-blocks/members-block/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/members-block/members-block.component.spec.ts
... | ... | @@ -1,53 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import {MembersBlock} from './members-block.component'; | |
5 | - | |
6 | -const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>'; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - describe("Members Block Component", () => { | |
12 | - | |
13 | - beforeEach(angular.mock.module("templates")); | |
14 | - | |
15 | - let state = jasmine.createSpyObj("state", ["go"]); | |
16 | - let providers = [ | |
17 | - new Provider('truncateFilter', { useValue: () => { } }), | |
18 | - new Provider('stripTagsFilter', { useValue: () => { } }), | |
19 | - new Provider('$state', { useValue: state }), | |
20 | - new Provider('ProfileService', { | |
21 | - useValue: { | |
22 | - getProfileMembers: (profileId: number, filters: any): any => { | |
23 | - return Promise.resolve({ data: { people: [{ identifier: "person1" }] } }); | |
24 | - } | |
25 | - } | |
26 | - }), | |
27 | - ]; | |
28 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MembersBlock], providers: providers }) | |
29 | - class BlockContainerComponent { | |
30 | - block = { type: 'Block', settings: {} }; | |
31 | - owner = { name: 'profile-name' }; | |
32 | - constructor() { | |
33 | - } | |
34 | - } | |
35 | - | |
36 | - it("get members of the block owner", done => { | |
37 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
38 | - let block: MembersBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
39 | - expect(block.members).toEqual([{ identifier: "person1" }]); | |
40 | - done(); | |
41 | - }); | |
42 | - }); | |
43 | - | |
44 | - it("render the profile image for each member", done => { | |
45 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
46 | - fixture.debugElement.getLocal("$rootScope").$apply(); | |
47 | - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1); | |
48 | - done(); | |
49 | - }); | |
50 | - }); | |
51 | - | |
52 | - }); | |
53 | -}); | |
54 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/members-block/members-block.component.ts
... | ... | @@ -1,25 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-members-block", | |
6 | - templateUrl: 'app/components/noosfero-blocks/members-block/members-block.html', | |
7 | -}) | |
8 | -@Inject(ProfileService) | |
9 | -export class MembersBlock { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | - members: any = []; | |
15 | - | |
16 | - constructor(private profileService: ProfileService) { | |
17 | - | |
18 | - } | |
19 | - | |
20 | - ngOnInit() { | |
21 | - this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => { | |
22 | - this.members = response.data.people; | |
23 | - }); | |
24 | - } | |
25 | -} |
src/app/components/noosfero-blocks/members-block/members-block.html
src/app/components/noosfero-blocks/members-block/members-block.scss
... | ... | @@ -1,17 +0,0 @@ |
1 | -.members-block { | |
2 | - .member { | |
3 | - img, i.profile-image { | |
4 | - width: 60px; | |
5 | - } | |
6 | - img { | |
7 | - display: inline-block; | |
8 | - vertical-align: top; | |
9 | - } | |
10 | - i.profile-image { | |
11 | - text-align: center; | |
12 | - background-color: #889DB1; | |
13 | - color: #F1F1F1; | |
14 | - font-size: 4.5em; | |
15 | - } | |
16 | - } | |
17 | -} |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.component.spec.ts
... | ... | @@ -1,46 +0,0 @@ |
1 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import {ProfileImageBlock} from './profile-image-block.component'; | |
5 | - | |
6 | -import * as helpers from "./../../../../spec/helpers"; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -const htmlTemplate: string = '<noosfero-profile-image-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-image-block>'; | |
11 | - | |
12 | -describe("Components", () => { | |
13 | - | |
14 | - describe("Profile Image Block Component", () => { | |
15 | - | |
16 | - beforeEach(angular.mock.module("templates")); | |
17 | - | |
18 | - @Component({ | |
19 | - selector: 'test-container-component', | |
20 | - template: htmlTemplate, | |
21 | - directives: [ProfileImageBlock], | |
22 | - providers: helpers.provideFilters("translateFilter") | |
23 | - }) | |
24 | - class BlockContainerComponent { | |
25 | - block = { type: 'Block' }; | |
26 | - owner = { name: 'profile-name' }; | |
27 | - constructor() { | |
28 | - } | |
29 | - } | |
30 | - | |
31 | - it("show image if present", () => { | |
32 | - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
33 | - let elProfile = fixture.debugElement.componentViewChildren[0]; | |
34 | - expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
35 | - }); | |
36 | - }); | |
37 | - | |
38 | - it("has link to the profile", () => { | |
39 | - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
40 | - let elProfile = fixture.debugElement.componentViewChildren[0]; | |
41 | - expect(elProfile.query('a.settings-link').length).toEqual(1); | |
42 | - }); | |
43 | - }); | |
44 | - | |
45 | - }); | |
46 | -}); |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.component.ts
... | ... | @@ -1,14 +0,0 @@ |
1 | -import {Inject, Input, Component} from "ng-forward"; | |
2 | -import {ProfileImage} from "./../../../components/noosfero/profile-image/profile-image.component"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-profile-image-block", | |
6 | - templateUrl: 'app/components/noosfero-blocks/profile-image-block/profile-image-block.html', | |
7 | - directives: [ProfileImage] | |
8 | -}) | |
9 | -export class ProfileImageBlock { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | -} |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.html
... | ... | @@ -1,6 +0,0 @@ |
1 | -<div class="center-block text-center profile-image-block"> | |
2 | - <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})"> | |
3 | - <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> | |
4 | - </a> | |
5 | - <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> | |
6 | -</div> |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.scss
src/app/components/noosfero-blocks/raw-html/raw-html.component.spec.ts
... | ... | @@ -1,36 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Component} from 'ng-forward'; | |
3 | - | |
4 | -import {RawHTMLBlock} from './raw-html.component'; | |
5 | - | |
6 | -const tcb = new TestComponentBuilder(); | |
7 | - | |
8 | -const htmlTemplate: string = '<noosfero-raw-htmlblock [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-raw-htmlblock>'; | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - | |
12 | - describe("Raw Html Block Component", () => { | |
13 | - | |
14 | - beforeEach(angular.mock.module("templates")); | |
15 | - beforeEach(angular.mock.module("ngSanitize")); | |
16 | - | |
17 | - it("display html stored in block settings", done => { | |
18 | - | |
19 | - @Component({ | |
20 | - selector: 'test-container-component', | |
21 | - template: htmlTemplate, | |
22 | - directives: [RawHTMLBlock], | |
23 | - }) | |
24 | - class CustomBlockType { | |
25 | - block: any = { settings: { html: '<em>block content</em>' } }; | |
26 | - owner: any = { name: 'profile-name' }; | |
27 | - } | |
28 | - tcb.createAsync(CustomBlockType).then(fixture => { | |
29 | - expect(fixture.debugElement.query(".raw-html-block em").text().trim()).toEqual('block content'); | |
30 | - done(); | |
31 | - }); | |
32 | - }); | |
33 | - | |
34 | - }); | |
35 | - | |
36 | -}); |
src/app/components/noosfero-blocks/raw-html/raw-html.component.ts
... | ... | @@ -1,18 +0,0 @@ |
1 | -import {Component, Input} from "ng-forward"; | |
2 | - | |
3 | -@Component({ | |
4 | - selector: "noosfero-raw-htmlblock", | |
5 | - templateUrl: 'app/components/noosfero-blocks/raw-html/raw-html.html' | |
6 | -}) | |
7 | - | |
8 | -export class RawHTMLBlock { | |
9 | - | |
10 | - @Input() block: any; | |
11 | - @Input() owner: any; | |
12 | - | |
13 | - html: string; | |
14 | - | |
15 | - ngOnInit() { | |
16 | - this.html = this.block.settings.html; | |
17 | - } | |
18 | -} |
src/app/components/noosfero-blocks/raw-html/raw-html.html
src/app/components/noosfero-blocks/recent-documents/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/recent-documents/recent-documents.component.spec.ts
... | ... | @@ -1,80 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | -import {provideFilters} from '../../../../spec/helpers'; | |
4 | -import {RecentDocumentsBlock} from './recent-documents.component'; | |
5 | - | |
6 | -const htmlTemplate: string = '<noosfero-recent-documents-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-recent-documents-block>'; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - describe("Recent Documents Block Component", () => { | |
12 | - | |
13 | - let settingsObj = {}; | |
14 | - let mockedArticleService = { | |
15 | - getByProfile: (profile: noosfero.Profile, filters: any): any => { | |
16 | - return Promise.resolve({ data: [{ name: "article1" }], headers: (name: string) => { return name; } }); | |
17 | - } | |
18 | - }; | |
19 | - let profile = { name: 'profile-name' }; | |
20 | - beforeEach(angular.mock.module("templates")); | |
21 | - | |
22 | - let state = jasmine.createSpyObj("state", ["go"]); | |
23 | - | |
24 | - | |
25 | - function getProviders() { | |
26 | - return [ | |
27 | - new Provider('$state', { useValue: state }), | |
28 | - new Provider('ArticleService', { | |
29 | - useValue: mockedArticleService | |
30 | - }), | |
31 | - ].concat(provideFilters("truncateFilter", "stripTagsFilter")); | |
32 | - } | |
33 | - let componentClass: any = null; | |
34 | - | |
35 | - function getComponent() { | |
36 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [RecentDocumentsBlock], providers: getProviders() }) | |
37 | - class BlockContainerComponent { | |
38 | - block = { type: 'Block', settings: settingsObj }; | |
39 | - owner = profile; | |
40 | - constructor() { | |
41 | - } | |
42 | - } | |
43 | - return BlockContainerComponent; | |
44 | - } | |
45 | - | |
46 | - | |
47 | - it("get recent documents from the article service", done => { | |
48 | - tcb.createAsync(getComponent()).then(fixture => { | |
49 | - let recentDocumentsBlock: RecentDocumentsBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
50 | - expect(recentDocumentsBlock.documents).toEqual([{ name: "article1" }]); | |
51 | - done(); | |
52 | - }); | |
53 | - }); | |
54 | - | |
55 | - it("go to article page when open a document", done => { | |
56 | - tcb.createAsync(getComponent()).then(fixture => { | |
57 | - let recentDocumentsBlock: RecentDocumentsBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
58 | - recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
59 | - expect(state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "identifier" }); | |
60 | - done(); | |
61 | - }); | |
62 | - }); | |
63 | - | |
64 | - it("it uses default limit 5 if not defined on block", done => { | |
65 | - settingsObj = null; | |
66 | - mockedArticleService = jasmine.createSpyObj("mockedArticleService", ["getByProfile"]); | |
67 | - (<any>mockedArticleService).mocked = true; | |
68 | - let thenMocked = jasmine.createSpy("then"); | |
69 | - mockedArticleService.getByProfile = jasmine.createSpy("getByProfile").and.returnValue({then: thenMocked}); | |
70 | - let getByProfileFunct = mockedArticleService.getByProfile; | |
71 | - tcb.createAsync(getComponent()).then(fixture => { | |
72 | - let recentDocumentsBlock: RecentDocumentsBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
73 | - recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
74 | - expect(getByProfileFunct).toHaveBeenCalledWith(profile, { content_type: 'TinyMceArticle', per_page: 5 }); | |
75 | - done(); | |
76 | - }); | |
77 | - }); | |
78 | - | |
79 | - }); | |
80 | -}); | |
81 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/recent-documents/recent-documents.component.ts
... | ... | @@ -1,41 +0,0 @@ |
1 | -import {Component, Inject, Input} from "ng-forward"; | |
2 | -import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-recent-documents-block", | |
6 | - templateUrl: 'app/components/noosfero-blocks/recent-documents/recent-documents.html' | |
7 | -}) | |
8 | -@Inject(ArticleService, "$state") | |
9 | -export class RecentDocumentsBlock { | |
10 | - | |
11 | - @Input() block: any; | |
12 | - @Input() owner: any; | |
13 | - | |
14 | - profile: any; | |
15 | - documents: any; | |
16 | - | |
17 | - documentsLoaded: boolean = false; | |
18 | - | |
19 | - constructor(private articleService: ArticleService, private $state: any) { | |
20 | - } | |
21 | - | |
22 | - ngOnInit() { | |
23 | - this.profile = this.owner; | |
24 | - this.documents = []; | |
25 | - | |
26 | - let limit = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5; | |
27 | - // FIXME get all text articles | |
28 | - // FIXME make the getByProfile a generic method where we tell the type passing a class TinyMceArticle | |
29 | - // and the promise should be of type TinyMceArticle[], per example | |
30 | - this.articleService.getByProfile(this.profile, { content_type: 'TinyMceArticle', per_page: limit }) | |
31 | - .then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
32 | - this.documents = <noosfero.Article[]>result.data; | |
33 | - this.documentsLoaded = true; | |
34 | - }); | |
35 | - } | |
36 | - | |
37 | - openDocument(article: any) { | |
38 | - this.$state.go("main.profile.page", { page: article.path, profile: article.profile.identifier }); | |
39 | - } | |
40 | - | |
41 | -} |
src/app/components/noosfero-blocks/recent-documents/recent-documents.html
... | ... | @@ -1,18 +0,0 @@ |
1 | -<div deckgrid source="ctrl.documents" class="deckgrid"> | |
2 | - <div class="a-card panel media" ng-click="mother.ctrl.openDocument(card);"> | |
3 | - <div class="author media-left" ng-show="card.author.image"> | |
4 | - <img ng-src="{{card.author.image.url}}" class="img-circle"> | |
5 | - </div> | |
6 | - <div class="header media-body"> | |
7 | - <h5 class="title media-heading" ng-bind="card.title"></h5> | |
8 | - | |
9 | - <div class="subheader"> | |
10 | - <span class="time"> | |
11 | - <i class="fa fa-clock-o"></i> <span am-time-ago="card.created_at | dateFormat"></span> | |
12 | - </span> | |
13 | - </div> | |
14 | - </div> | |
15 | - <img ng-show="card.image" ng-src="{{card.image.url}}" class="img-responsive article-image"> | |
16 | - <div class="post-lead" ng-bind-html="card.body | stripTags | truncate: 100: '...': true"></div> | |
17 | - </div> | |
18 | -</div> |
src/app/components/noosfero-blocks/recent-documents/recent-documents.scss
... | ... | @@ -1,65 +0,0 @@ |
1 | -.block.recentdocumentsblock { | |
2 | - .deckgrid[deckgrid]::before { | |
3 | - font-size: 0; /* See https://github.com/akoenig/angular-deckgrid/issues/14#issuecomment-35728861 */ | |
4 | - visibility: hidden; | |
5 | - } | |
6 | - .author { | |
7 | - img { | |
8 | - width: 30px; | |
9 | - height: 30px; | |
10 | - } | |
11 | - } | |
12 | - .header { | |
13 | - .subheader { | |
14 | - color: #C1C1C1; | |
15 | - font-size: 10px; | |
16 | - } | |
17 | - } | |
18 | - .post-lead { | |
19 | - color: #8E8E8E; | |
20 | - font-size: 14px; | |
21 | - } | |
22 | - .article-image { | |
23 | - margin: 10px 0; | |
24 | - } | |
25 | -} | |
26 | - | |
27 | -.col-md-2-5 { | |
28 | - .deckgrid[deckgrid]::before { | |
29 | - content: '1 .deck-column'; | |
30 | - } | |
31 | -} | |
32 | - | |
33 | -.col-md-7 { | |
34 | - .block.recentdocumentsblock { | |
35 | - background-color: transparent; | |
36 | - border: 0; | |
37 | - | |
38 | - .deckgrid[deckgrid]::before { | |
39 | - content: '3 .deck-column'; | |
40 | - } | |
41 | - | |
42 | - .panel-heading { | |
43 | - display: none; | |
44 | - } | |
45 | - .panel-body { | |
46 | - padding: 0; | |
47 | - } | |
48 | - | |
49 | - .deckgrid { | |
50 | - .column { | |
51 | - float: left; | |
52 | - } | |
53 | - | |
54 | - .deck-column { | |
55 | - @extend .col-md-4; | |
56 | - padding: 0; | |
57 | - | |
58 | - .a-card { | |
59 | - padding: 10px; | |
60 | - margin: 3px; | |
61 | - } | |
62 | - } | |
63 | - } | |
64 | - } | |
65 | -} |
src/app/components/noosfero-boxes/box.html
... | ... | @@ -1,10 +0,0 @@ |
1 | -<div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> | |
2 | - <div ng-repeat="block in box.blocks | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > | |
3 | - <div class="panel-heading" ng-show="block.title"> | |
4 | - <h3 class="panel-title">{{block.title}}</h3> | |
5 | - </div> | |
6 | - <div class="panel-body"> | |
7 | - <noosfero-block [block]="block" [owner]="ctrl.owner"></noosfero-block> | |
8 | - </div> | |
9 | - </div> | |
10 | -</div> |
src/app/components/noosfero-boxes/boxes.component.spec.ts
... | ... | @@ -1,65 +0,0 @@ |
1 | -import {Component} from 'ng-forward'; | |
2 | - | |
3 | -import {Boxes} from './boxes.component'; | |
4 | - | |
5 | -import { | |
6 | - createComponentFromClass, | |
7 | - quickCreateComponent, | |
8 | - provideEmptyObjects, | |
9 | - createProviderToValue, | |
10 | - getAngularServiceFactory, | |
11 | - provideFilters | |
12 | -} from "../../../spec/helpers"; | |
13 | - | |
14 | -// this htmlTemplate will be re-used between the container components in this spec file | |
15 | -const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl.profile"></noosfero-blog>'; | |
16 | - | |
17 | - | |
18 | -describe("Boxes Component", () => { | |
19 | - | |
20 | - beforeEach(() => { | |
21 | - angular.mock.module("templates"); | |
22 | - }); | |
23 | - | |
24 | - @Component({ | |
25 | - selector: 'test-container-component', | |
26 | - template: htmlTemplate, | |
27 | - directives: [Boxes], | |
28 | - providers: [] | |
29 | - }) | |
30 | - class BoxesContainerComponent { | |
31 | - boxes: noosfero.Box[] = [ | |
32 | - { id: 1, position: 1 }, | |
33 | - { id: 2, position: 2 } | |
34 | - ]; | |
35 | - | |
36 | - owner: noosfero.Profile = <noosfero.Profile> { | |
37 | - id: 1, | |
38 | - identifier: 'profile-name', | |
39 | - type: 'Person' | |
40 | - }; | |
41 | - } | |
42 | - | |
43 | - it("renders boxes into a container", (done: Function) => { | |
44 | - createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
45 | - let boxesHtml = fixture.debugElement; | |
46 | - expect(boxesHtml.query('div.col-md-7').length).toEqual(1); | |
47 | - expect(boxesHtml.query('div.col-md-2-5').length).toEqual(1); | |
48 | - | |
49 | - done(); | |
50 | - }); | |
51 | - }); | |
52 | - | |
53 | - it("check the boxes order", (done: Function) => { | |
54 | - createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
55 | - | |
56 | - let boxesComponent: Boxes = fixture.debugElement.componentViewChildren[0].componentInstance; | |
57 | - let boxesContainer: BoxesContainerComponent = fixture.componentInstance; | |
58 | - | |
59 | - expect(boxesComponent.boxesOrder(boxesContainer.boxes[0])).toEqual(1); | |
60 | - expect(boxesComponent.boxesOrder(boxesContainer.boxes[1])).toEqual(0); | |
61 | - | |
62 | - done(); | |
63 | - }); | |
64 | - }); | |
65 | -}); |
src/app/components/noosfero-boxes/boxes.component.ts
... | ... | @@ -1,16 +0,0 @@ |
1 | -import {Input, Inject, Component} from 'ng-forward'; | |
2 | - | |
3 | -@Component({ | |
4 | - selector: "noosfero-boxes", | |
5 | - templateUrl: "app/components/noosfero-boxes/boxes.html" | |
6 | -}) | |
7 | -export class Boxes { | |
8 | - | |
9 | - @Input() boxes: noosfero.Box[]; | |
10 | - @Input() owner: noosfero.Profile; | |
11 | - | |
12 | - boxesOrder(box: noosfero.Box) { | |
13 | - if (box.position === 2) return 0; | |
14 | - return box.position; | |
15 | - } | |
16 | -} |
src/app/components/noosfero-boxes/boxes.html
... | ... | @@ -1 +0,0 @@ |
1 | -<ng-include ng-repeat="box in ctrl.boxes | orderBy: ctrl.boxesOrder" src="'app/components/noosfero-boxes/box.html'"></ng-include> |
src/app/components/noosfero-boxes/boxes.scss
src/app/components/noosfero-boxes/index.ts
src/app/components/noosfero/date-format/date-format.filter.spec.ts
... | ... | @@ -1,20 +0,0 @@ |
1 | -import {quickCreateComponent} from "../../../../spec/helpers"; | |
2 | -import {DateFormat} from './date-format.filter'; | |
3 | - | |
4 | -describe("Filters", () => { | |
5 | - describe("Date Format Filter", () => { | |
6 | - | |
7 | - beforeEach(angular.mock.module("templates")); | |
8 | - beforeEach(angular.mock.module("angularMoment")); | |
9 | - | |
10 | - it("convert date from the format returned by noosfero api to an ISO format", done => { | |
11 | - let date = "2016/03/10 10:46:47"; | |
12 | - let htmlTemplate = `{{ '${date}' | dateFormat }}`; | |
13 | - quickCreateComponent({ providers: [DateFormat], template: htmlTemplate }).then(fixture => { | |
14 | - expect(fixture.debugElement.text()).toEqual('2016-03-10T13:46:47.000Z'); | |
15 | - done(); | |
16 | - }); | |
17 | - }); | |
18 | - | |
19 | - }); | |
20 | -}); |
src/app/components/noosfero/date-format/date-format.filter.ts
... | ... | @@ -1,13 +0,0 @@ |
1 | -import {Pipe, Inject} from "ng-forward"; | |
2 | - | |
3 | -@Pipe("dateFormat") | |
4 | -@Inject("amParseFilter") | |
5 | -export class DateFormat { | |
6 | - | |
7 | - constructor(private amParseFilter: any) { } | |
8 | - | |
9 | - transform(date: string, options: any) { | |
10 | - return this.amParseFilter(date, "YYYY/MM/DD HH:mm:ss").toISOString(); | |
11 | - } | |
12 | - | |
13 | -} |
src/app/components/noosfero/index.ts
... | ... | @@ -1 +0,0 @@ |
1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero/noosfero-template.filter.spec.ts
... | ... | @@ -1,19 +0,0 @@ |
1 | -import {quickCreateComponent} from "../../../spec/helpers"; | |
2 | -import {NoosferoTemplate} from './noosfero-template.filter'; | |
3 | - | |
4 | -describe("Filters", () => { | |
5 | - describe("Noosfero Template Filter", () => { | |
6 | - | |
7 | - beforeEach(angular.mock.module("templates")); | |
8 | - | |
9 | - it("replace the options in text with the values passed on options", done => { | |
10 | - let text = 'profile: {profile}, other: {other}'; | |
11 | - let htmlTemplate = `{{ '${text}' | noosferoTemplate: {profile: 'profile1', other: 'other value'} }}`; | |
12 | - quickCreateComponent({ providers: [NoosferoTemplate], template: htmlTemplate }).then(fixture => { | |
13 | - expect(fixture.debugElement.text()).toEqual("profile: profile1, other: other value"); | |
14 | - done(); | |
15 | - }); | |
16 | - }); | |
17 | - | |
18 | - }); | |
19 | -}); |
src/app/components/noosfero/noosfero-template.filter.ts
... | ... | @@ -1,13 +0,0 @@ |
1 | -import {Pipe} from "ng-forward"; | |
2 | - | |
3 | -@Pipe("noosferoTemplate") | |
4 | -export class NoosferoTemplate { | |
5 | - | |
6 | - transform(text: string, options: any) { | |
7 | - for (let option in options) { | |
8 | - text = text.replace('{' + option + '}', options[option]); | |
9 | - } | |
10 | - return text; | |
11 | - } | |
12 | - | |
13 | -} |
src/app/components/noosfero/profile-image/index.ts
src/app/components/noosfero/profile-image/profile-image.component.spec.ts
... | ... | @@ -1,51 +0,0 @@ |
1 | -/** | |
2 | - * @ngdoc overview | |
3 | - * @name components.noosfero.profile-image.ProfileImageSpec | |
4 | - * @description | |
5 | - * This file contains the tests for the {@link components.noosfero.profile-image.ProfileImage} component. | |
6 | - */ | |
7 | - | |
8 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
9 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
10 | - | |
11 | -import * as helpers from "./../../../../spec/helpers"; | |
12 | - | |
13 | -import {ProfileImage} from "./profile-image.component"; | |
14 | - | |
15 | -const tcb = new TestComponentBuilder(); | |
16 | - | |
17 | -describe("Components", () => { | |
18 | - | |
19 | - describe("Profile Image Component", () => { | |
20 | - | |
21 | - beforeEach(angular.mock.module("templates")); | |
22 | - | |
23 | - it("show community users image if profile is not Person", done => { | |
24 | - helpers.tcb.createAsync(ProfileImage).then(fixture => { | |
25 | - let profileImageComponent: ProfileImage = fixture.componentInstance; | |
26 | - let profile = <noosfero.Profile>{ id: 1, identifier: "myprofile", type: "Community" }; | |
27 | - profileImageComponent.profile = profile; | |
28 | - profileImageComponent.ngOnInit(); | |
29 | - | |
30 | - // Check the attribute | |
31 | - expect(profileImageComponent.defaultIcon).toBe("fa-users", "The default icon should be community users"); | |
32 | - // var elProfile = fixture.debugElement.componentViewChildren[0]; | |
33 | - // expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
34 | - done(); | |
35 | - }); | |
36 | - }); | |
37 | - | |
38 | - it("show Person image if profile is Person", done => { | |
39 | - tcb.createAsync(ProfileImage).then(fixture => { | |
40 | - let profileImageComponent: ProfileImage = fixture.componentInstance; | |
41 | - let profile = <noosfero.Profile>{ id: 1, identifier: "myprofile", type: "Person" }; | |
42 | - profileImageComponent.profile = profile; | |
43 | - profileImageComponent.ngOnInit(); | |
44 | - // Check the attribute | |
45 | - expect(profileImageComponent.defaultIcon).toEqual("fa-user", "The default icon should be person user"); | |
46 | - done(); | |
47 | - }); | |
48 | - }); | |
49 | - | |
50 | - }); | |
51 | -}); | |
52 | 0 | \ No newline at end of file |
src/app/components/noosfero/profile-image/profile-image.component.ts
... | ... | @@ -1,47 +0,0 @@ |
1 | -import {Inject, Input, Component} from "ng-forward"; | |
2 | - | |
3 | - | |
4 | -/** | |
5 | - * @ngdoc controller | |
6 | - * @name components.noosfero.profile-image.ProfileImage | |
7 | - * @description The component responsible for rendering the profile image | |
8 | - * @exports ProfileImage | |
9 | - */ | |
10 | -@Component({ | |
11 | - selector: "noosfero-profile-image", | |
12 | - templateUrl: 'app/components/noosfero/profile-image/profile-image.html', | |
13 | -}) | |
14 | -export class ProfileImage { | |
15 | - | |
16 | - /** | |
17 | - * @ngdoc property | |
18 | - * @name profile | |
19 | - * @propertyOf components.noosfero.profile-image.ProfileImage | |
20 | - * @description | |
21 | - * The Noosfero {@link models.Profile} holding the image. | |
22 | - */ | |
23 | - @Input() profile: noosfero.Profile; | |
24 | - /** | |
25 | - * @ngdoc property | |
26 | - * @name defaultIcon | |
27 | - * @propertyOf components.noosfero.profile-image.ProfileImage | |
28 | - * @descritpion | |
29 | - * The default icon used by this profile | |
30 | - */ | |
31 | - defaultIcon: string; | |
32 | - | |
33 | - /** | |
34 | - * @ngdoc method | |
35 | - * @name ngOnInit | |
36 | - * @methodOf components.noosfero.profile-image.ProfileImage | |
37 | - * @description | |
38 | - * Initializes the icon names to their corresponding values depending on the profile type passed to the controller | |
39 | - */ | |
40 | - ngOnInit() { | |
41 | - this.defaultIcon = 'fa-users'; | |
42 | - if (this.profile && this.profile.type === 'Person') { | |
43 | - this.defaultIcon = 'fa-user'; | |
44 | - } | |
45 | - } | |
46 | -} | |
47 | - |
src/app/components/noosfero/profile-image/profile-image.html
src/app/components/noosfero/profile-image/profile-image.scss
src/app/components/notification/notification.component.spec.ts
... | ... | @@ -1,80 +0,0 @@ |
1 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import * as helpers from "../../../spec/helpers"; | |
5 | - | |
6 | -import {Notification} from "./notification.component"; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - | |
12 | - describe("Profile Image Component", () => { | |
13 | - | |
14 | - beforeEach(angular.mock.module("templates")); | |
15 | - | |
16 | - it("display an error message when notify an error", done => { | |
17 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
18 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
19 | - | |
20 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
21 | - component.error("message", "title"); | |
22 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
23 | - text: "message", | |
24 | - title: "title", | |
25 | - type: "error" | |
26 | - })); | |
27 | - done(); | |
28 | - }); | |
29 | - | |
30 | - it("use the default message when call notification component without a message", done => { | |
31 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
32 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
33 | - | |
34 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
35 | - component.error(); | |
36 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
37 | - text: Notification.DEFAULT_ERROR_MESSAGE, | |
38 | - type: "error" | |
39 | - })); | |
40 | - done(); | |
41 | - }); | |
42 | - | |
43 | - it("display a success message when call notification success", done => { | |
44 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
45 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
46 | - | |
47 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
48 | - component.success("title", "message", 1000); | |
49 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
50 | - type: "success" | |
51 | - })); | |
52 | - done(); | |
53 | - }); | |
54 | - | |
55 | - it("display a message relative to the http error code", done => { | |
56 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
57 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
58 | - | |
59 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
60 | - component.httpError(500, {}); | |
61 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
62 | - text: "notification.http_error.500.message" | |
63 | - })); | |
64 | - done(); | |
65 | - }); | |
66 | - | |
67 | - it("set the default timer in success messages", done => { | |
68 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
69 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
70 | - | |
71 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
72 | - component.success("title", "message"); | |
73 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
74 | - type: "success", | |
75 | - timer: Notification.DEFAULT_SUCCESS_TIMER | |
76 | - })); | |
77 | - done(); | |
78 | - }); | |
79 | - }); | |
80 | -}); |
src/app/components/notification/notification.component.ts
... | ... | @@ -1,41 +0,0 @@ |
1 | -import {Injectable, Inject} from "ng-forward"; | |
2 | -import {TranslatorService} from "../translator/translator.service"; | |
3 | - | |
4 | -@Injectable() | |
5 | -@Inject("$log", "SweetAlert", TranslatorService) | |
6 | -export class Notification { | |
7 | - | |
8 | - constructor( | |
9 | - private $log: ng.ILogService, | |
10 | - private SweetAlert: any, | |
11 | - private translatorService: TranslatorService | |
12 | - ) { } | |
13 | - | |
14 | - public static DEFAULT_ERROR_TITLE = "notification.error.default.title"; | |
15 | - public static DEFAULT_ERROR_MESSAGE = "notification.error.default.message"; | |
16 | - public static DEFAULT_SUCCESS_TIMER = 1000; | |
17 | - | |
18 | - error(message: string = Notification.DEFAULT_ERROR_MESSAGE, title: string = Notification.DEFAULT_ERROR_TITLE) { | |
19 | - this.$log.debug("Notification error:", title, message, this.translatorService.currentLanguage()); | |
20 | - this.SweetAlert.swal({ | |
21 | - title: this.translatorService.translate(title), | |
22 | - text: this.translatorService.translate(message), | |
23 | - type: "error" | |
24 | - }); | |
25 | - } | |
26 | - | |
27 | - httpError(status: number, data: any): boolean { | |
28 | - this.error(`notification.http_error.${status}.message`); | |
29 | - return true; // return true to indicate that the error was already handled | |
30 | - } | |
31 | - | |
32 | - success(title: string, text: string, timer: number = Notification.DEFAULT_SUCCESS_TIMER) { | |
33 | - this.SweetAlert.swal({ | |
34 | - title: title, | |
35 | - text: text, | |
36 | - type: "success", | |
37 | - timer: timer | |
38 | - }); | |
39 | - } | |
40 | - | |
41 | -} |
src/app/components/translator/translator.service.spec.ts
... | ... | @@ -1,116 +0,0 @@ |
1 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {provide} from 'ng-forward'; | |
3 | - | |
4 | -import {TranslatorService} from './translator.service'; | |
5 | - | |
6 | -import * as helpers from "../../../spec/helpers"; | |
7 | - | |
8 | -describe("Services", () => { | |
9 | - | |
10 | - describe("Translator 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 | - function createComponent() { | |
21 | - return new TranslatorService( | |
22 | - <any>helpers.mocks.$translate, | |
23 | - <any>helpers.mocks.tmhDynamicLocale, | |
24 | - <any>helpers.mocks.amMoment, | |
25 | - <any>helpers.mocks.angularLoad, | |
26 | - $rootScope | |
27 | - ); | |
28 | - } | |
29 | - | |
30 | - it("set available languages when change language", (done) => { | |
31 | - let component: TranslatorService = createComponent(); | |
32 | - component.availableLanguages = null; | |
33 | - expect(component.availableLanguages).toBeNull(); | |
34 | - $rootScope.$emit("$translateChangeSuccess"); | |
35 | - expect(component.availableLanguages).not.toBeNull(); | |
36 | - done(); | |
37 | - }); | |
38 | - | |
39 | - it("change the language", (done) => { | |
40 | - let component: TranslatorService = createComponent(); | |
41 | - let loadScripPromise = $q.defer(); | |
42 | - loadScripPromise.resolve(); | |
43 | - component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue(loadScripPromise.promise); | |
44 | - component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
45 | - component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue("en"); | |
46 | - component["$translate"].use = jasmine.createSpy("use"); | |
47 | - | |
48 | - component.changeLanguage('pt'); | |
49 | - $rootScope.$digest(); | |
50 | - | |
51 | - expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
52 | - expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/messageformat/locale/pt.js"); | |
53 | - expect(component["tmhDynamicLocale"].set).toHaveBeenCalledWith("pt"); | |
54 | - expect(component["$translate"].use).toHaveBeenCalledWith("pt"); | |
55 | - done(); | |
56 | - }); | |
57 | - | |
58 | - it("do not load moment locale when change the language to english", (done) => { | |
59 | - let component: TranslatorService = createComponent(); | |
60 | - component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue($q.defer().promise); | |
61 | - component.changeLanguage('en'); | |
62 | - expect(component["angularLoad"].loadScript).not.toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
63 | - done(); | |
64 | - }); | |
65 | - | |
66 | - it("do nothing when call change language with null", (done) => { | |
67 | - let component: TranslatorService = createComponent(); | |
68 | - component["angularLoad"].loadScript = jasmine.createSpy("loadScript"); | |
69 | - component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
70 | - component["$translate"].use = jasmine.createSpy("use"); | |
71 | - | |
72 | - component.changeLanguage(null); | |
73 | - | |
74 | - expect(component["angularLoad"].loadScript).not.toHaveBeenCalled(); | |
75 | - expect(component["tmhDynamicLocale"].set).not.toHaveBeenCalled(); | |
76 | - expect(component["$translate"].use).not.toHaveBeenCalled(); | |
77 | - done(); | |
78 | - }); | |
79 | - | |
80 | - it("return the current language used by the translator", (done) => { | |
81 | - let component: TranslatorService = createComponent(); | |
82 | - component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
83 | - expect(component.currentLanguage()).toEqual("en"); | |
84 | - expect(component["$translate"].use).toHaveBeenCalled(); | |
85 | - done(); | |
86 | - }); | |
87 | - | |
88 | - it("call translate service when translate a text", (done) => { | |
89 | - let component: TranslatorService = createComponent(); | |
90 | - component["$translate"].instant = jasmine.createSpy("instant"); | |
91 | - component.translate("text"); | |
92 | - expect(component["$translate"].instant).toHaveBeenCalledWith("text"); | |
93 | - done(); | |
94 | - }); | |
95 | - | |
96 | - it("change the language when receive an event", (done) => { | |
97 | - let component: TranslatorService = createComponent(); | |
98 | - component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
99 | - $rootScope.$emit("$localeChangeSuccess"); | |
100 | - expect(component.changeLanguage).toHaveBeenCalled(); | |
101 | - done(); | |
102 | - }); | |
103 | - | |
104 | - it("use the translate language when receive a change language event and there is no language previously selected", (done) => { | |
105 | - let component: TranslatorService = createComponent(); | |
106 | - component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
107 | - component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue(null); | |
108 | - component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
109 | - | |
110 | - $rootScope.$emit("$localeChangeSuccess"); | |
111 | - expect(component["$translate"].use).toHaveBeenCalled(); | |
112 | - expect(component.changeLanguage).toHaveBeenCalledWith("en"); | |
113 | - done(); | |
114 | - }); | |
115 | - }); | |
116 | -}); |
src/app/components/translator/translator.service.ts
... | ... | @@ -1,59 +0,0 @@ |
1 | -import {Injectable, Inject} from "ng-forward"; | |
2 | - | |
3 | -@Injectable() | |
4 | -@Inject("$translate", "tmhDynamicLocale", "amMoment", "angularLoad", "$rootScope") | |
5 | -export class TranslatorService { | |
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.$rootScope.$on("$localeChangeSuccess", () => { | |
16 | - this.changeLanguage(tmhDynamicLocale.get() || $translate.use()); | |
17 | - }); | |
18 | - this.$rootScope.$on("$translateChangeSuccess", () => { | |
19 | - this.configAvailableLanguages(); | |
20 | - }); | |
21 | - } | |
22 | - | |
23 | - currentLanguage() { | |
24 | - return this.$translate.use(); | |
25 | - } | |
26 | - | |
27 | - changeLanguage(language: string) { | |
28 | - if (!language) { | |
29 | - console.log("WARN: language undefined"); | |
30 | - return; | |
31 | - } | |
32 | - this.changeMomentLocale(language); | |
33 | - this.tmhDynamicLocale.set(language); | |
34 | - this.angularLoad.loadScript(`/bower_components/messageformat/locale/${language}.js`).then(() => { | |
35 | - return this.$translate.use(language); | |
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/content-viewer/content-viewer-actions.component.spec.ts
... | ... | @@ -1,67 +0,0 @@ |
1 | -import {providers} from 'ng-forward/cjs/testing/providers'; | |
2 | - | |
3 | -import {Input, Component, provide} from 'ng-forward'; | |
4 | - | |
5 | -import * as helpers from "../../spec/helpers"; | |
6 | - | |
7 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
8 | -import {ContentViewerActions} from './content-viewer-actions.component'; | |
9 | - | |
10 | -// this htmlTemplate will be re-used between the container components in this spec file | |
11 | -const htmlTemplate: string = '<content-viewer-actions [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer-actions>'; | |
12 | - | |
13 | -describe('Content Viewer Actions Component', () => { | |
14 | - | |
15 | - beforeEach(() => { | |
16 | - | |
17 | - angular.mock.module("templates"); | |
18 | - | |
19 | - providers((provide: any) => { | |
20 | - return <any>[ | |
21 | - provide('ProfileService', { | |
22 | - useValue: helpers.mocks.profileService | |
23 | - }) | |
24 | - ]; | |
25 | - }); | |
26 | - }); | |
27 | - | |
28 | - let buildComponent = (): Promise<ComponentFixture> => { | |
29 | - return helpers.quickCreateComponent({ | |
30 | - providers: [ | |
31 | - helpers.provideEmptyObjects('Restangular'), | |
32 | - helpers.provideFilters('translateFilter') | |
33 | - ], | |
34 | - directives: [ContentViewerActions], | |
35 | - template: htmlTemplate | |
36 | - }); | |
37 | - }; | |
38 | - | |
39 | - it('renders content viewer actions directive', (done: Function) => { | |
40 | - buildComponent().then((fixture: ComponentFixture) => { | |
41 | - expect(fixture.debugElement.query('content-viewer-actions').length).toEqual(1); | |
42 | - | |
43 | - done(); | |
44 | - }); | |
45 | - }); | |
46 | - | |
47 | - it('check if profile was loaded', (done: Function) => { | |
48 | - let profile: any = { | |
49 | - id: 1, | |
50 | - identifier: 'the-profile-test', | |
51 | - type: 'Person' | |
52 | - }; | |
53 | - | |
54 | - helpers.mocks.profileService.getCurrentProfile = () => { | |
55 | - return helpers.mocks.promiseResultTemplate(profile); | |
56 | - }; | |
57 | - | |
58 | - buildComponent().then((fixture: ComponentFixture) => { | |
59 | - let contentViewerComp: ContentViewerActions = fixture.debugElement.componentViewChildren[0].componentInstance; | |
60 | - | |
61 | - expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile)); | |
62 | - | |
63 | - done(); | |
64 | - }); | |
65 | - }); | |
66 | - | |
67 | -}); |
src/app/content-viewer/content-viewer-actions.component.ts
... | ... | @@ -1,20 +0,0 @@ |
1 | -import {Component, Inject, provide} from "ng-forward"; | |
2 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "content-viewer-actions", | |
6 | - templateUrl: "app/content-viewer/navbar-actions.html", | |
7 | - providers: [provide('profileService', { useClass: ProfileService })] | |
8 | -}) | |
9 | -@Inject(ProfileService) | |
10 | -export class ContentViewerActions { | |
11 | - | |
12 | - article: noosfero.Article; | |
13 | - profile: noosfero.Profile; | |
14 | - | |
15 | - constructor(profileService: ProfileService) { | |
16 | - profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
17 | - this.profile = profile; | |
18 | - }); | |
19 | - } | |
20 | -} |
src/app/content-viewer/content-viewer.component.spec.ts
... | ... | @@ -1,88 +0,0 @@ |
1 | -import {providers} from 'ng-forward/cjs/testing/providers'; | |
2 | - | |
3 | -import {Input, Component, provide} from 'ng-forward'; | |
4 | - | |
5 | -import * as helpers from "../../spec/helpers"; | |
6 | - | |
7 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
8 | -import {ContentViewer} from './content-viewer.component'; | |
9 | - | |
10 | -// this htmlTemplate will be re-used between the container components in this spec file | |
11 | -const htmlTemplate: string = '<content-viewer [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer>'; | |
12 | - | |
13 | -describe('Content Viewer Component', () => { | |
14 | - | |
15 | - let stateParamsService: any; | |
16 | - | |
17 | - // loading the templates | |
18 | - beforeEach(() => { | |
19 | - angular.mock.module("templates"); | |
20 | - | |
21 | - stateParamsService = { page: 1 }; | |
22 | - | |
23 | - providers((provide: any) => { | |
24 | - return <any>[ | |
25 | - provide('ArticleService', { | |
26 | - useValue: helpers.mocks.articleService | |
27 | - }), | |
28 | - provide('ProfileService', { | |
29 | - useValue: helpers.mocks.profileService | |
30 | - }), | |
31 | - // TODO: Como criar um mock do atributo "page" de stateParams | |
32 | - provide('$stateParams', { | |
33 | - useValue: stateParamsService | |
34 | - }) | |
35 | - ]; | |
36 | - }); | |
37 | - }); | |
38 | - | |
39 | - let buildComponent = (): Promise<ComponentFixture> => { | |
40 | - return helpers.quickCreateComponent({ | |
41 | - providers: [ | |
42 | - helpers.provideEmptyObjects('Restangular') | |
43 | - ], | |
44 | - directives: [ContentViewer], | |
45 | - template: htmlTemplate | |
46 | - }); | |
47 | - }; | |
48 | - | |
49 | - it('renders content viewer directive', (done: Function) => { | |
50 | - buildComponent().then((fixture: ComponentFixture) => { | |
51 | - expect(fixture.debugElement.query('content-viewer').length).toEqual(1); | |
52 | - | |
53 | - done(); | |
54 | - }); | |
55 | - }); | |
56 | - | |
57 | - it('check if article was loaded', (done: Function) => { | |
58 | - let article: any = { | |
59 | - id: 1, | |
60 | - title: 'The article test' | |
61 | - }; | |
62 | - let profile: any = { | |
63 | - id: 1, | |
64 | - identifier: 'the-profile-test', | |
65 | - type: 'Person' | |
66 | - }; | |
67 | - | |
68 | - helpers.mocks.profileService.getCurrentProfile = () => { | |
69 | - return helpers.mocks.promiseResultTemplate(profile); | |
70 | - }; | |
71 | - | |
72 | - helpers.mocks.articleService.getArticleByProfileAndPath = (profile: noosfero.Profile, path: string) => { | |
73 | - return helpers.mocks.promiseResultTemplate({ | |
74 | - data: article | |
75 | - }); | |
76 | - }; | |
77 | - | |
78 | - | |
79 | - buildComponent().then((fixture: ComponentFixture) => { | |
80 | - let contentViewerComp: ContentViewer = fixture.debugElement.componentViewChildren[0].componentInstance; | |
81 | - | |
82 | - expect(contentViewerComp.profile).toEqual(profile); | |
83 | - expect(contentViewerComp.article).toEqual(article); | |
84 | - | |
85 | - done(); | |
86 | - }); | |
87 | - }); | |
88 | -}); |
src/app/content-viewer/content-viewer.component.ts
... | ... | @@ -1,38 +0,0 @@ |
1 | -import {ArticleView} from "../components/noosfero-articles/article/article_view"; | |
2 | -import {Input, Component, StateConfig, Inject, provide} from "ng-forward"; | |
3 | - | |
4 | -import {ArticleBlog} from "./../components/noosfero-articles/blog/blog.component"; | |
5 | -import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
6 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
7 | - | |
8 | -@Component({ | |
9 | - selector: "content-viewer", | |
10 | - templateUrl: "app/content-viewer/page.html", | |
11 | - directives: [ArticleBlog, ArticleView], | |
12 | - providers: [ | |
13 | - provide('articleService', { useClass: ArticleService }), | |
14 | - provide('profileService', { useClass: ProfileService }) | |
15 | - ] | |
16 | -}) | |
17 | -@Inject(ArticleService, ProfileService, "$log", "$stateParams") | |
18 | -export class ContentViewer { | |
19 | - | |
20 | - @Input() | |
21 | - article: noosfero.Article = null; | |
22 | - | |
23 | - @Input() | |
24 | - profile: noosfero.Profile = null; | |
25 | - | |
26 | - constructor(private articleService: ArticleService, private profileService: ProfileService, private $log: ng.ILogService, private $stateParams: angular.ui.IStateParamsService) { | |
27 | - this.activate(); | |
28 | - } | |
29 | - | |
30 | - activate() { | |
31 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
32 | - this.profile = profile; | |
33 | - return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]); | |
34 | - }).then((result: noosfero.RestResult<any>) => { | |
35 | - this.article = <noosfero.Article>result.data; | |
36 | - }); | |
37 | - } | |
38 | -} |
src/app/content-viewer/index.ts
src/app/content-viewer/navbar-actions.html
... | ... | @@ -1,7 +0,0 @@ |
1 | -<ul class="nav navbar-nav"> | |
2 | - <li ng-show="vm.profile"> | |
3 | - <a href="#" role="button" ui-sref="main.profile.cms({profile: vm.profile.identifier})"> | |
4 | - <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | |
5 | - </a> | |
6 | - </li> | |
7 | -</ul> |
src/app/content-viewer/page.html
... | ... | @@ -1 +0,0 @@ |
1 | -<noosfero-article ng-if="vm.article" [article]="vm.article" [profile]="vm.profile"></noosfero-article> |
src/app/index.run.ts
1 | -import {Session} from "./components/auth/session"; | |
2 | -import {Notification} from "./components/notification/notification.component"; | |
1 | +import {SessionService} from "./login"; | |
2 | +import {NotificationService} from "./shared/services/notification.service"; | |
3 | 3 | |
4 | 4 | /** @ngInject */ |
5 | 5 | export function noosferoAngularRunBlock( |
6 | 6 | $log: ng.ILogService, |
7 | 7 | Restangular: restangular.IService, |
8 | - Session: Session, | |
9 | - Notification: Notification | |
8 | + SessionService: SessionService, | |
9 | + NotificationService: NotificationService | |
10 | 10 | ) { |
11 | 11 | |
12 | 12 | Restangular.addFullRequestInterceptor((element: any, operation: string, route: string, url: string, headers: string) => { |
13 | - if (Session.currentUser()) { | |
14 | - (<any>headers)["Private-Token"] = Session.currentUser().private_token; | |
13 | + if (SessionService.currentUser()) { | |
14 | + (<any>headers)["Private-Token"] = SessionService.currentUser().private_token; | |
15 | 15 | } |
16 | 16 | return <any>{ headers: <any>headers }; |
17 | 17 | }); |
18 | 18 | Restangular.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>) => { |
19 | 19 | // return false to break the promise chain and don't call catch |
20 | - return !Notification.httpError(response.status, response.data); | |
20 | + return !NotificationService.httpError(response.status, response.data); | |
21 | 21 | }); |
22 | 22 | } | ... | ... |
src/app/index.ts
... | ... | @@ -5,10 +5,10 @@ import {noosferoAngularRunBlock} from "./index.run"; |
5 | 5 | import {Main} from "./main/main.component"; |
6 | 6 | import {bootstrap, bundle} from "ng-forward"; |
7 | 7 | |
8 | -import {AUTH_EVENTS} from "./components/auth/auth_events"; | |
9 | -import {AuthController} from "./components/auth/auth_controller"; | |
8 | +import {AUTH_EVENTS} from "./login/auth-events"; | |
9 | +import {AuthController} from "./login/auth.controller"; | |
10 | 10 | |
11 | -import {Navbar} from "./components/navbar/navbar"; | |
11 | +import {Navbar} from "./layout/navbar/navbar"; | |
12 | 12 | |
13 | 13 | declare var moment: any; |
14 | 14 | ... | ... |
... | ... | @@ -0,0 +1,91 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Input, provide, Component} from 'ng-forward'; | |
3 | + | |
4 | +import {BlockComponent} from './block.component'; | |
5 | + | |
6 | +const tcb = new TestComponentBuilder(); | |
7 | + | |
8 | +const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block>'; | |
9 | + | |
10 | +describe("Components", () => { | |
11 | + describe("Block Component", () => { | |
12 | + | |
13 | + // the karma preprocessor html2js transform the templates html into js files which put | |
14 | + // the templates to the templateCache into the module templates | |
15 | + // we need to load the module templates here as the template for the | |
16 | + // component Block will be load on our tests | |
17 | + beforeEach(angular.mock.module("templates")); | |
18 | + | |
19 | + it("receives the block and the owner as inputs", done => { | |
20 | + | |
21 | + // Creating a container component (BlockContainerComponent) to include | |
22 | + // the component under test (Block) | |
23 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent] }) | |
24 | + class BlockContainerComponent { | |
25 | + block = { type: 'Block' }; | |
26 | + owner = { name: 'profile-name' }; | |
27 | + constructor() { | |
28 | + } | |
29 | + } | |
30 | + | |
31 | + // uses the TestComponentBuilder instance to initialize the component | |
32 | + tcb | |
33 | + .createAsync(BlockContainerComponent).then(fixture => { | |
34 | + // and here we can inspect and run the test assertions | |
35 | + let myComponent: BlockComponent = fixture.componentInstance; | |
36 | + | |
37 | + // assure the block object inside the Block matches | |
38 | + // the provided through the parent component | |
39 | + expect(myComponent.block.type).toEqual("Block"); | |
40 | + expect(myComponent.owner.name).toEqual("profile-name"); | |
41 | + done(); | |
42 | + }); | |
43 | + }); | |
44 | + | |
45 | + | |
46 | + it("renders a component which matches to the block type", done => { | |
47 | + // CustomBlock component created to check if it will be used | |
48 | + // when a block with type 'CustomBlock' is provided to the noosfero-block (Block) | |
49 | + // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
50 | + @Component({ selector: 'noosfero-custom-block', template: "<h1>My Custom Block</h1>" }) | |
51 | + class CustomBlock { | |
52 | + @Input() block: any; | |
53 | + @Input() owner: any; | |
54 | + } | |
55 | + | |
56 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent, CustomBlock] }) | |
57 | + class CustomBlockType { | |
58 | + block = { type: 'CustomBlock' }; | |
59 | + owner = { name: 'profile-name' }; | |
60 | + constructor() { | |
61 | + } | |
62 | + } | |
63 | + tcb | |
64 | + .createAsync(CustomBlockType).then(fixture => { | |
65 | + let myComponent: CustomBlockType = fixture.componentInstance; | |
66 | + expect(myComponent.block.type).toEqual("CustomBlock"); | |
67 | + expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("My Custom Block"); | |
68 | + done(); | |
69 | + }); | |
70 | + }); | |
71 | + | |
72 | + | |
73 | + it("renders the default block when hasn't defined a block type", done => { | |
74 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent] }) | |
75 | + class CustomBlockType { | |
76 | + block: any = { type: null }; | |
77 | + owner: any = { name: 'profile-name' }; | |
78 | + constructor() { | |
79 | + } | |
80 | + } | |
81 | + tcb | |
82 | + .createAsync(CustomBlockType).then(fixture => { | |
83 | + let myComponent: CustomBlockType = fixture.componentInstance; | |
84 | + expect(myComponent.block.type).toBeNull(); | |
85 | + expect(!!fixture.debugElement.nativeElement.querySelector("noosfero-default-block")).toBeTruthy(); | |
86 | + done(); | |
87 | + }); | |
88 | + }); | |
89 | + | |
90 | + }); | |
91 | +}); | |
0 | 92 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +import { Input, Inject, Component } from 'ng-forward'; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: 'noosfero-block', | |
5 | + template: '<div></div>' | |
6 | +}) | |
7 | +@Inject("$element", "$scope", "$injector", "$compile") | |
8 | +export class BlockComponent { | |
9 | + | |
10 | + @Input() block: any; | |
11 | + @Input() owner: any; | |
12 | + | |
13 | + ngOnInit() { | |
14 | + let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
15 | + this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); | |
16 | + } | |
17 | + | |
18 | + constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) { | |
19 | + } | |
20 | +} | ... | ... |
src/app/layout/blocks/link-list/link-list.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,65 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | +import {provideFilters} from '../../../../spec/helpers'; | |
4 | + | |
5 | +import {LinkListBlockComponent} from './link-list.component'; | |
6 | + | |
7 | +const tcb = new TestComponentBuilder(); | |
8 | + | |
9 | +const htmlTemplate: string = '<noosfero-link-list-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-link-list-block>'; | |
10 | + | |
11 | + | |
12 | +describe("Components", () => { | |
13 | + | |
14 | + describe("Link List Block Component", () => { | |
15 | + | |
16 | + beforeEach(angular.mock.module("templates")); | |
17 | + | |
18 | + it("receives the block and the owner as inputs", done => { | |
19 | + | |
20 | + // Creating a container component (BlockContainerComponent) to include | |
21 | + // the component under test (Block) | |
22 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [LinkListBlockComponent] }) | |
23 | + class BlockContainerComponent { | |
24 | + block = { type: 'Block' }; | |
25 | + owner = { name: 'profile-name' }; | |
26 | + constructor() { | |
27 | + } | |
28 | + } | |
29 | + | |
30 | + // uses the TestComponentBuilder instance to initialize the component | |
31 | + // .overrideView(LinkListBlock, { template: 'asdasdasd', pipes: [NoosferoTemplate] }) | |
32 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
33 | + // and here we can inspect and run the test assertions | |
34 | + let myComponent: LinkListBlockComponent = fixture.componentInstance; | |
35 | + | |
36 | + // assure the block object inside the Block matches | |
37 | + // the provided through the parent component | |
38 | + expect(myComponent.block.type).toEqual("Block"); | |
39 | + expect(myComponent.owner.name).toEqual("profile-name"); | |
40 | + done(); | |
41 | + }); | |
42 | + }); | |
43 | + | |
44 | + | |
45 | + it("display links stored in block settings", done => { | |
46 | + | |
47 | + @Component({ | |
48 | + selector: 'test-container-component', | |
49 | + template: htmlTemplate, | |
50 | + directives: [LinkListBlockComponent], | |
51 | + providers: provideFilters("noosferoTemplateFilter") | |
52 | + }) | |
53 | + class CustomBlockType { | |
54 | + block: any = { settings: { links: [{ name: 'link1', address: 'address1' }, { name: 'link2', address: 'address2' }] } }; | |
55 | + owner: any = { name: 'profile-name' }; | |
56 | + } | |
57 | + tcb.createAsync(CustomBlockType).then(fixture => { | |
58 | + expect(fixture.debugElement.queryAll(".link-list-block a").length).toEqual(2); | |
59 | + done(); | |
60 | + }); | |
61 | + }); | |
62 | + | |
63 | + }); | |
64 | + | |
65 | +}); | |
0 | 66 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +import {Component, Input} from "ng-forward"; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: "noosfero-link-list-block", | |
5 | + templateUrl: "app/layout/blocks/link-list/link-list.html" | |
6 | +}) | |
7 | +export class LinkListBlockComponent { | |
8 | + | |
9 | + @Input() block: any; | |
10 | + @Input() owner: any; | |
11 | + | |
12 | + links: any; | |
13 | + | |
14 | + ngOnInit() { | |
15 | + if (this.block && this.block.settings) { | |
16 | + this.links = this.block.settings.links; | |
17 | + } | |
18 | + } | |
19 | + | |
20 | +} | ... | ... |
... | ... | @@ -0,0 +1,34 @@ |
1 | +.icon-event { | |
2 | + @extend .fa-calendar; | |
3 | +} | |
4 | +.icon-photos { | |
5 | + @extend .fa-photo; | |
6 | +} | |
7 | +.icon-edit { | |
8 | + @extend .fa-edit; | |
9 | +} | |
10 | +.icon-ok { | |
11 | + @extend .fa-check; | |
12 | +} | |
13 | +.icon-send { | |
14 | + @extend .fa-send-o; | |
15 | +} | |
16 | +.icon-menu-people { | |
17 | + @extend .fa-user; | |
18 | +} | |
19 | +.icon-forum { | |
20 | + @extend .fa-users; | |
21 | +} | |
22 | +.icon-new { | |
23 | + @extend .fa-file-o; | |
24 | +} | |
25 | +.icon-save { | |
26 | + @extend .fa-save; | |
27 | +} | |
28 | + | |
29 | +.link-list-block { | |
30 | + a i { | |
31 | + line-height: 25px; | |
32 | + color: #949494; | |
33 | + } | |
34 | +} | ... | ... |
src/app/layout/blocks/main-block/main-block.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,40 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Input, provide, Component, StateConfig} from 'ng-forward'; | |
3 | + | |
4 | +import {MainBlockComponent} from './main-block.component'; | |
5 | +import {NoosferoApp} from '../../../index.module'; | |
6 | + | |
7 | +const tcb = new TestComponentBuilder(); | |
8 | + | |
9 | +const htmlTemplate: string = '<noosfero-main-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-main-block>'; | |
10 | + | |
11 | +describe("Components", () => { | |
12 | + describe("Main Block Component", () => { | |
13 | + | |
14 | + // the karma preprocessor html2js transform the templates html into js files which put | |
15 | + // the templates to the templateCache into the module templates | |
16 | + // we need to load the module templates here as the template for the | |
17 | + // component Block will be load on our tests | |
18 | + beforeEach(angular.mock.module("templates")); | |
19 | + | |
20 | + it("check if the main block has a tag with ui-view attribute", done => { | |
21 | + | |
22 | + // Creating a container component (BlockContainerComponent) to include | |
23 | + // the component under test (Block) | |
24 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MainBlockComponent] }) | |
25 | + class BlockContainerComponent { | |
26 | + } | |
27 | + | |
28 | + // uses the TestComponentBuilder instance to initialize the component | |
29 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
30 | + // and here we can inspect and run the test assertions | |
31 | + // let myComponent: MainBlockComponent = fixture.componentInstance; | |
32 | + | |
33 | + // assure the block object inside the Block matches | |
34 | + // the provided through the parent component | |
35 | + expect(fixture.debugElement.queryAll('[ui-view="mainBlockContent"]').length).toEqual(1); | |
36 | + done(); | |
37 | + }); | |
38 | + }); | |
39 | + }); | |
40 | +}); | |
0 | 41 | \ No newline at end of file | ... | ... |
src/app/layout/blocks/main-block/main-block.component.ts
0 → 100644
... | ... | @@ -0,0 +1,10 @@ |
1 | +import {Component, Input} from 'ng-forward'; | |
2 | +import {BlockComponent} from '../block.component'; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: 'noosfero-main-block', | |
6 | + templateUrl: 'app/layout/blocks/main-block/main-block.html' | |
7 | +}) | |
8 | +export class MainBlockComponent { | |
9 | + | |
10 | +} | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<div ui-view="mainBlockContent" autoscroll></div> | ... | ... |
src/app/layout/blocks/members-block/members-block.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,53 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | + | |
4 | +import {MembersBlockComponent} from './members-block.component'; | |
5 | + | |
6 | +const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>'; | |
7 | + | |
8 | +const tcb = new TestComponentBuilder(); | |
9 | + | |
10 | +describe("Components", () => { | |
11 | + describe("Members Block Component", () => { | |
12 | + | |
13 | + beforeEach(angular.mock.module("templates")); | |
14 | + | |
15 | + let state = jasmine.createSpyObj("state", ["go"]); | |
16 | + let providers = [ | |
17 | + new Provider('truncateFilter', { useValue: () => { } }), | |
18 | + new Provider('stripTagsFilter', { useValue: () => { } }), | |
19 | + new Provider('$state', { useValue: state }), | |
20 | + new Provider('ProfileService', { | |
21 | + useValue: { | |
22 | + getProfileMembers: (profileId: number, filters: any): any => { | |
23 | + return Promise.resolve({ data: { people: [{ identifier: "person1" }] } }); | |
24 | + } | |
25 | + } | |
26 | + }), | |
27 | + ]; | |
28 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MembersBlockComponent], providers: providers }) | |
29 | + class BlockContainerComponent { | |
30 | + block = { type: 'Block', settings: {} }; | |
31 | + owner = { name: 'profile-name' }; | |
32 | + constructor() { | |
33 | + } | |
34 | + } | |
35 | + | |
36 | + it("get members of the block owner", done => { | |
37 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
38 | + let block: MembersBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
39 | + expect(block.members).toEqual([{ identifier: "person1" }]); | |
40 | + done(); | |
41 | + }); | |
42 | + }); | |
43 | + | |
44 | + it("render the profile image for each member", done => { | |
45 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
46 | + fixture.debugElement.getLocal("$rootScope").$apply(); | |
47 | + expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1); | |
48 | + done(); | |
49 | + }); | |
50 | + }); | |
51 | + | |
52 | + }); | |
53 | +}); | |
0 | 54 | \ No newline at end of file | ... | ... |
src/app/layout/blocks/members-block/members-block.component.ts
0 → 100644
... | ... | @@ -0,0 +1,25 @@ |
1 | +import {Input, Inject, Component} from "ng-forward"; | |
2 | +import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: "noosfero-members-block", | |
6 | + templateUrl: 'app/layout/blocks/members-block/members-block.html', | |
7 | +}) | |
8 | +@Inject(ProfileService) | |
9 | +export class MembersBlockComponent { | |
10 | + | |
11 | + @Input() block: noosfero.Block; | |
12 | + @Input() owner: noosfero.Profile; | |
13 | + | |
14 | + members: any = []; | |
15 | + | |
16 | + constructor(private profileService: ProfileService) { | |
17 | + | |
18 | + } | |
19 | + | |
20 | + ngOnInit() { | |
21 | + this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => { | |
22 | + this.members = response.data.people; | |
23 | + }); | |
24 | + } | |
25 | +} | ... | ... |
... | ... | @@ -0,0 +1,17 @@ |
1 | +.members-block { | |
2 | + .member { | |
3 | + img, i.profile-image { | |
4 | + width: 60px; | |
5 | + } | |
6 | + img { | |
7 | + display: inline-block; | |
8 | + vertical-align: top; | |
9 | + } | |
10 | + i.profile-image { | |
11 | + text-align: center; | |
12 | + background-color: #889DB1; | |
13 | + color: #F1F1F1; | |
14 | + font-size: 4.5em; | |
15 | + } | |
16 | + } | |
17 | +} | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,46 @@ |
1 | +import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | + | |
4 | +import {ProfileImageBlockComponent} from './profile-image-block.component'; | |
5 | + | |
6 | +import * as helpers from "./../../../../spec/helpers"; | |
7 | + | |
8 | +const tcb = new TestComponentBuilder(); | |
9 | + | |
10 | +const htmlTemplate: string = '<noosfero-profile-image-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-image-block>'; | |
11 | + | |
12 | +describe("Components", () => { | |
13 | + | |
14 | + describe("Profile Image Block Component", () => { | |
15 | + | |
16 | + beforeEach(angular.mock.module("templates")); | |
17 | + | |
18 | + @Component({ | |
19 | + selector: 'test-container-component', | |
20 | + template: htmlTemplate, | |
21 | + directives: [ProfileImageBlockComponent], | |
22 | + providers: helpers.provideFilters("translateFilter") | |
23 | + }) | |
24 | + class BlockContainerComponent { | |
25 | + block = { type: 'Block' }; | |
26 | + owner = { name: 'profile-name' }; | |
27 | + constructor() { | |
28 | + } | |
29 | + } | |
30 | + | |
31 | + it("show image if present", () => { | |
32 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
33 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | |
34 | + expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
35 | + }); | |
36 | + }); | |
37 | + | |
38 | + it("has link to the profile", () => { | |
39 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
40 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | |
41 | + expect(elProfile.query('a.settings-link').length).toEqual(1); | |
42 | + }); | |
43 | + }); | |
44 | + | |
45 | + }); | |
46 | +}); | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.component.ts
0 → 100644
... | ... | @@ -0,0 +1,14 @@ |
1 | +import {Inject, Input, Component} from "ng-forward"; | |
2 | +import {ProfileImageComponent} from "./../../../profile/image/image.component"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: "noosfero-profile-image-block", | |
6 | + templateUrl: 'app/layout/blocks/profile-image-block/profile-image-block.html', | |
7 | + directives: [ProfileImageComponent] | |
8 | +}) | |
9 | +export class ProfileImageBlockComponent { | |
10 | + | |
11 | + @Input() block: noosfero.Block; | |
12 | + @Input() owner: noosfero.Profile; | |
13 | + | |
14 | +} | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.html
0 → 100644
... | ... | @@ -0,0 +1,6 @@ |
1 | +<div class="center-block text-center profile-image-block"> | |
2 | + <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})"> | |
3 | + <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> | |
4 | + </a> | |
5 | + <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> | |
6 | +</div> | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.scss
0 → 100644
src/app/layout/blocks/raw-html/raw-html.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,36 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Component} from 'ng-forward'; | |
3 | + | |
4 | +import {RawHTMLBlockComponent} from './raw-html.component'; | |
5 | + | |
6 | +const tcb = new TestComponentBuilder(); | |
7 | + | |
8 | +const htmlTemplate: string = '<noosfero-raw-htmlblock [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-raw-htmlblock>'; | |
9 | + | |
10 | +describe("Components", () => { | |
11 | + | |
12 | + describe("Raw Html Block Component", () => { | |
13 | + | |
14 | + beforeEach(angular.mock.module("templates")); | |
15 | + beforeEach(angular.mock.module("ngSanitize")); | |
16 | + | |
17 | + it("display html stored in block settings", done => { | |
18 | + | |
19 | + @Component({ | |
20 | + selector: 'test-container-component', | |
21 | + template: htmlTemplate, | |
22 | + directives: [RawHTMLBlockComponent], | |
23 | + }) | |
24 | + class CustomBlockType { | |
25 | + block: any = { settings: { html: '<em>block content</em>' } }; | |
26 | + owner: any = { name: 'profile-name' }; | |
27 | + } | |
28 | + tcb.createAsync(CustomBlockType).then(fixture => { | |
29 | + expect(fixture.debugElement.query(".raw-html-block em").text().trim()).toEqual('block content'); | |
30 | + done(); | |
31 | + }); | |
32 | + }); | |
33 | + | |
34 | + }); | |
35 | + | |
36 | +}); | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +import {Component, Input} from "ng-forward"; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: "noosfero-raw-htmlblock", | |
5 | + templateUrl: 'app/layout/blocks/raw-html/raw-html.html' | |
6 | +}) | |
7 | + | |
8 | +export class RawHTMLBlockComponent { | |
9 | + | |
10 | + @Input() block: any; | |
11 | + @Input() owner: any; | |
12 | + | |
13 | + html: string; | |
14 | + | |
15 | + ngOnInit() { | |
16 | + this.html = this.block.settings.html; | |
17 | + } | |
18 | +} | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,80 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | +import {provideFilters} from '../../../../spec/helpers'; | |
4 | +import {RecentDocumentsBlockComponent} from './recent-documents.component'; | |
5 | + | |
6 | +const htmlTemplate: string = '<noosfero-recent-documents-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-recent-documents-block>'; | |
7 | + | |
8 | +const tcb = new TestComponentBuilder(); | |
9 | + | |
10 | +describe("Components", () => { | |
11 | + describe("Recent Documents Block Component", () => { | |
12 | + | |
13 | + let settingsObj = {}; | |
14 | + let mockedArticleService = { | |
15 | + getByProfile: (profile: noosfero.Profile, filters: any): any => { | |
16 | + return Promise.resolve({ data: [{ name: "article1" }], headers: (name: string) => { return name; } }); | |
17 | + } | |
18 | + }; | |
19 | + let profile = { name: 'profile-name' }; | |
20 | + beforeEach(angular.mock.module("templates")); | |
21 | + | |
22 | + let state = jasmine.createSpyObj("state", ["go"]); | |
23 | + | |
24 | + | |
25 | + function getProviders() { | |
26 | + return [ | |
27 | + new Provider('$state', { useValue: state }), | |
28 | + new Provider('ArticleService', { | |
29 | + useValue: mockedArticleService | |
30 | + }), | |
31 | + ].concat(provideFilters("truncateFilter", "stripTagsFilter")); | |
32 | + } | |
33 | + let componentClass: any = null; | |
34 | + | |
35 | + function getComponent() { | |
36 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [RecentDocumentsBlockComponent], providers: getProviders() }) | |
37 | + class BlockContainerComponent { | |
38 | + block = { type: 'Block', settings: settingsObj }; | |
39 | + owner = profile; | |
40 | + constructor() { | |
41 | + } | |
42 | + } | |
43 | + return BlockContainerComponent; | |
44 | + } | |
45 | + | |
46 | + | |
47 | + it("get recent documents from the article service", done => { | |
48 | + tcb.createAsync(getComponent()).then(fixture => { | |
49 | + let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
50 | + expect(recentDocumentsBlock.documents).toEqual([{ name: "article1" }]); | |
51 | + done(); | |
52 | + }); | |
53 | + }); | |
54 | + | |
55 | + it("go to article page when open a document", done => { | |
56 | + tcb.createAsync(getComponent()).then(fixture => { | |
57 | + let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
58 | + recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
59 | + expect(state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "identifier" }); | |
60 | + done(); | |
61 | + }); | |
62 | + }); | |
63 | + | |
64 | + it("it uses default limit 5 if not defined on block", done => { | |
65 | + settingsObj = null; | |
66 | + mockedArticleService = jasmine.createSpyObj("mockedArticleService", ["getByProfile"]); | |
67 | + (<any>mockedArticleService).mocked = true; | |
68 | + let thenMocked = jasmine.createSpy("then"); | |
69 | + mockedArticleService.getByProfile = jasmine.createSpy("getByProfile").and.returnValue({then: thenMocked}); | |
70 | + let getByProfileFunct = mockedArticleService.getByProfile; | |
71 | + tcb.createAsync(getComponent()).then(fixture => { | |
72 | + let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
73 | + recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
74 | + expect(getByProfileFunct).toHaveBeenCalledWith(profile, { content_type: 'TinyMceArticle', per_page: 5 }); | |
75 | + done(); | |
76 | + }); | |
77 | + }); | |
78 | + | |
79 | + }); | |
80 | +}); | |
0 | 81 | \ No newline at end of file | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.component.ts
0 → 100644
... | ... | @@ -0,0 +1,41 @@ |
1 | +import {Component, Inject, Input} from "ng-forward"; | |
2 | +import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: "noosfero-recent-documents-block", | |
6 | + templateUrl: 'app/layout/blocks/recent-documents/recent-documents.html' | |
7 | +}) | |
8 | +@Inject(ArticleService, "$state") | |
9 | +export class RecentDocumentsBlockComponent { | |
10 | + | |
11 | + @Input() block: any; | |
12 | + @Input() owner: any; | |
13 | + | |
14 | + profile: any; | |
15 | + documents: any; | |
16 | + | |
17 | + documentsLoaded: boolean = false; | |
18 | + | |
19 | + constructor(private articleService: ArticleService, private $state: any) { | |
20 | + } | |
21 | + | |
22 | + ngOnInit() { | |
23 | + this.profile = this.owner; | |
24 | + this.documents = []; | |
25 | + | |
26 | + let limit = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5; | |
27 | + // FIXME get all text articles | |
28 | + // FIXME make the getByProfile a generic method where we tell the type passing a class TinyMceArticle | |
29 | + // and the promise should be of type TinyMceArticle[], per example | |
30 | + this.articleService.getByProfile(this.profile, { content_type: 'TinyMceArticle', per_page: limit }) | |
31 | + .then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
32 | + this.documents = <noosfero.Article[]>result.data; | |
33 | + this.documentsLoaded = true; | |
34 | + }); | |
35 | + } | |
36 | + | |
37 | + openDocument(article: any) { | |
38 | + this.$state.go("main.profile.page", { page: article.path, profile: article.profile.identifier }); | |
39 | + } | |
40 | + | |
41 | +} | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.html
0 → 100644
... | ... | @@ -0,0 +1,18 @@ |
1 | +<div deckgrid source="ctrl.documents" class="deckgrid"> | |
2 | + <div class="a-card panel media" ng-click="mother.ctrl.openDocument(card);"> | |
3 | + <div class="author media-left" ng-show="card.author.image"> | |
4 | + <img ng-src="{{card.author.image.url}}" class="img-circle"> | |
5 | + </div> | |
6 | + <div class="header media-body"> | |
7 | + <h5 class="title media-heading" ng-bind="card.title"></h5> | |
8 | + | |
9 | + <div class="subheader"> | |
10 | + <span class="time"> | |
11 | + <i class="fa fa-clock-o"></i> <span am-time-ago="card.created_at | dateFormat"></span> | |
12 | + </span> | |
13 | + </div> | |
14 | + </div> | |
15 | + <img ng-show="card.image" ng-src="{{card.image.url}}" class="img-responsive article-image"> | |
16 | + <div class="post-lead" ng-bind-html="card.body | stripTags | truncate: 100: '...': true"></div> | |
17 | + </div> | |
18 | +</div> | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.scss
0 → 100644
... | ... | @@ -0,0 +1,65 @@ |
1 | +.block.recentdocumentsblock { | |
2 | + .deckgrid[deckgrid]::before { | |
3 | + font-size: 0; /* See https://github.com/akoenig/angular-deckgrid/issues/14#issuecomment-35728861 */ | |
4 | + visibility: hidden; | |
5 | + } | |
6 | + .author { | |
7 | + img { | |
8 | + width: 30px; | |
9 | + height: 30px; | |
10 | + } | |
11 | + } | |
12 | + .header { | |
13 | + .subheader { | |
14 | + color: #C1C1C1; | |
15 | + font-size: 10px; | |
16 | + } | |
17 | + } | |
18 | + .post-lead { | |
19 | + color: #8E8E8E; | |
20 | + font-size: 14px; | |
21 | + } | |
22 | + .article-image { | |
23 | + margin: 10px 0; | |
24 | + } | |
25 | +} | |
26 | + | |
27 | +.col-md-2-5 { | |
28 | + .deckgrid[deckgrid]::before { | |
29 | + content: '1 .deck-column'; | |
30 | + } | |
31 | +} | |
32 | + | |
33 | +.col-md-7 { | |
34 | + .block.recentdocumentsblock { | |
35 | + background-color: transparent; | |
36 | + border: 0; | |
37 | + | |
38 | + .deckgrid[deckgrid]::before { | |
39 | + content: '3 .deck-column'; | |
40 | + } | |
41 | + | |
42 | + .panel-heading { | |
43 | + display: none; | |
44 | + } | |
45 | + .panel-body { | |
46 | + padding: 0; | |
47 | + } | |
48 | + | |
49 | + .deckgrid { | |
50 | + .column { | |
51 | + float: left; | |
52 | + } | |
53 | + | |
54 | + .deck-column { | |
55 | + @extend .col-md-4; | |
56 | + padding: 0; | |
57 | + | |
58 | + .a-card { | |
59 | + padding: 10px; | |
60 | + margin: 3px; | |
61 | + } | |
62 | + } | |
63 | + } | |
64 | + } | |
65 | +} | ... | ... |
... | ... | @@ -0,0 +1,10 @@ |
1 | +<div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> | |
2 | + <div ng-repeat="block in box.blocks | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > | |
3 | + <div class="panel-heading" ng-show="block.title"> | |
4 | + <h3 class="panel-title">{{block.title}}</h3> | |
5 | + </div> | |
6 | + <div class="panel-body"> | |
7 | + <noosfero-block [block]="block" [owner]="ctrl.owner"></noosfero-block> | |
8 | + </div> | |
9 | + </div> | |
10 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,65 @@ |
1 | +import {Component} from 'ng-forward'; | |
2 | + | |
3 | +import {BoxesComponent} from './boxes.component'; | |
4 | + | |
5 | +import { | |
6 | + createComponentFromClass, | |
7 | + quickCreateComponent, | |
8 | + provideEmptyObjects, | |
9 | + createProviderToValue, | |
10 | + getAngularServiceFactory, | |
11 | + provideFilters | |
12 | +} from "../../../spec/helpers"; | |
13 | + | |
14 | +// this htmlTemplate will be re-used between the container components in this spec file | |
15 | +const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl.profile"></noosfero-blog>'; | |
16 | + | |
17 | + | |
18 | +describe("Boxes Component", () => { | |
19 | + | |
20 | + beforeEach(() => { | |
21 | + angular.mock.module("templates"); | |
22 | + }); | |
23 | + | |
24 | + @Component({ | |
25 | + selector: 'test-container-component', | |
26 | + template: htmlTemplate, | |
27 | + directives: [BoxesComponent], | |
28 | + providers: [] | |
29 | + }) | |
30 | + class BoxesContainerComponent { | |
31 | + boxes: noosfero.Box[] = [ | |
32 | + { id: 1, position: 1 }, | |
33 | + { id: 2, position: 2 } | |
34 | + ]; | |
35 | + | |
36 | + owner: noosfero.Profile = <noosfero.Profile> { | |
37 | + id: 1, | |
38 | + identifier: 'profile-name', | |
39 | + type: 'Person' | |
40 | + }; | |
41 | + } | |
42 | + | |
43 | + it("renders boxes into a container", (done: Function) => { | |
44 | + createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
45 | + let boxesHtml = fixture.debugElement; | |
46 | + expect(boxesHtml.query('div.col-md-7').length).toEqual(1); | |
47 | + expect(boxesHtml.query('div.col-md-2-5').length).toEqual(1); | |
48 | + | |
49 | + done(); | |
50 | + }); | |
51 | + }); | |
52 | + | |
53 | + it("check the boxes order", (done: Function) => { | |
54 | + createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
55 | + | |
56 | + let boxesComponent: BoxesComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
57 | + let boxesContainer: BoxesContainerComponent = fixture.componentInstance; | |
58 | + | |
59 | + expect(boxesComponent.boxesOrder(boxesContainer.boxes[0])).toEqual(1); | |
60 | + expect(boxesComponent.boxesOrder(boxesContainer.boxes[1])).toEqual(0); | |
61 | + | |
62 | + done(); | |
63 | + }); | |
64 | + }); | |
65 | +}); | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +import {Input, Inject, Component} from 'ng-forward'; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: "noosfero-boxes", | |
5 | + templateUrl: "app/layout/boxes/boxes.html" | |
6 | +}) | |
7 | +export class BoxesComponent { | |
8 | + | |
9 | + @Input() boxes: noosfero.Box[]; | |
10 | + @Input() owner: noosfero.Profile; | |
11 | + | |
12 | + boxesOrder(box: noosfero.Box) { | |
13 | + if (box.position === 2) return 0; | |
14 | + return box.position; | |
15 | + } | |
16 | +} | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<ng-include ng-repeat="box in ctrl.boxes | orderBy: ctrl.boxesOrder" src="'app/layout/boxes/box.html'"></ng-include> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
src/app/layout/language-selector/language-selector.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,46 @@ |
1 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {provide} from 'ng-forward'; | |
3 | + | |
4 | +import {LanguageSelectorService} from './language-selector.component'; | |
5 | + | |
6 | +import * as helpers from "../../../spec/helpers"; | |
7 | + | |
8 | +describe("Components", () => { | |
9 | + | |
10 | + describe("Language Selector Component", () => { | |
11 | + | |
12 | + beforeEach(angular.mock.module("templates")); | |
13 | + | |
14 | + let translatorService: any; | |
15 | + | |
16 | + let buildComponent = (): Promise<ComponentFixture> => { | |
17 | + translatorService = jasmine.createSpyObj("translatorService", ["availableLanguages", "currentLanguage"]); | |
18 | + return helpers.quickCreateComponent({ | |
19 | + template: "<language-selector></language-selector>", | |
20 | + directives: [LanguageSelectorService], | |
21 | + providers: [ | |
22 | + provide('TranslatorService', { | |
23 | + useValue: translatorService | |
24 | + }) | |
25 | + ].concat(helpers.provideFilters("translateFilter")) | |
26 | + }); | |
27 | + }; | |
28 | + | |
29 | + it("display language options", (done) => { | |
30 | + buildComponent().then(fixture => { | |
31 | + fixture.debugElement.getLocal("$rootScope").$apply(); | |
32 | + expect(fixture.debugElement.queryAll('li.language').length).toEqual(2); | |
33 | + done(); | |
34 | + }); | |
35 | + }); | |
36 | + | |
37 | + it("call the translator service when change the language", (done) => { | |
38 | + let translatorService = jasmine.createSpyObj("translatorService", ["changeLanguage"]); | |
39 | + let languageSelector = new LanguageSelectorService(<any>translatorService); | |
40 | + languageSelector.changeLanguage("en"); | |
41 | + expect(translatorService.changeLanguage).toHaveBeenCalledWith("en"); | |
42 | + done(); | |
43 | + }); | |
44 | + | |
45 | + }); | |
46 | +}); | ... | ... |
src/app/layout/language-selector/language-selector.component.ts
0 → 100644
... | ... | @@ -0,0 +1,24 @@ |
1 | +import {Component, Inject} from "ng-forward"; | |
2 | +import {TranslatorService} from "../../shared/services/translator.service"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: "language-selector", | |
6 | + templateUrl: "app/layout/language-selector/language-selector.html" | |
7 | +}) | |
8 | +@Inject(TranslatorService) | |
9 | +export class LanguageSelectorService { | |
10 | + | |
11 | + constructor(private translatorService: TranslatorService) { } | |
12 | + | |
13 | + currentLanguage() { | |
14 | + return this.translatorService.currentLanguage(); | |
15 | + } | |
16 | + | |
17 | + changeLanguage(language: string) { | |
18 | + this.translatorService.changeLanguage(language); | |
19 | + } | |
20 | + | |
21 | + availableLanguages() { | |
22 | + return this.translatorService.availableLanguages; | |
23 | + } | |
24 | +} | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<li class="dropdown profile-menu" dropdown> | |
2 | + <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
3 | + <span>{{"language.selector" | translate}}</span> <b class="caret"></b> | |
4 | + </a> | |
5 | + <ul class="dropdown-menu" dropdown-menu> | |
6 | + <li ng-repeat="(language, description) in ctrl.availableLanguages()" | |
7 | + class="language language-{{language}}" ng-class="{'active': language==ctrl.currentLanguage()}"> | |
8 | + <a href="#" ng-click="ctrl.changeLanguage(language)"> | |
9 | + {{description}} | |
10 | + </a> | |
11 | + </li> | |
12 | + </ul> | |
13 | +</li> | ... | ... |
... | ... | @@ -0,0 +1,45 @@ |
1 | +(function() { | |
2 | + 'use strict'; | |
3 | + | |
4 | + describe('directive navbar', function() { | |
5 | + var vm; | |
6 | + var el; | |
7 | + var AUTH_EVENTS; | |
8 | + var $state; | |
9 | + | |
10 | + beforeEach(module('angular')); | |
11 | + beforeEach(inject(function($compile, $rootScope, $httpBackend, _AUTH_EVENTS_, _$state_) { | |
12 | + $state = _$state_; | |
13 | + AUTH_EVENTS = _AUTH_EVENTS_; | |
14 | + $httpBackend.when('POST','/api/v1/login_from_cookie').respond({}); | |
15 | + | |
16 | + el = angular.element('<acme-navbar></acme-navbar>'); | |
17 | + | |
18 | + $compile(el)($rootScope.$new()); | |
19 | + $rootScope.$digest(); | |
20 | + vm = el.isolateScope().vm; | |
21 | + })); | |
22 | + | |
23 | + it('should be compiled', function() { | |
24 | + expect(el.html()).not.toEqual(null); | |
25 | + }); | |
26 | + | |
27 | + it('should have isolate scope object with instanciate members', function() { | |
28 | + expect(vm).toEqual(jasmine.any(Object)); | |
29 | + expect(vm.currentUser).toEqual(undefined); | |
30 | + }); | |
31 | + | |
32 | + it('should reload current state after login', function() { | |
33 | + spyOn($state, 'go'); | |
34 | + el.isolateScope().$broadcast(AUTH_EVENTS.loginSuccess, {}); | |
35 | + expect($state.go).toHaveBeenCalled(); | |
36 | + }); | |
37 | + | |
38 | + it('should open login when not logged in', function() { | |
39 | + spyOn(vm, 'openLogin'); | |
40 | + vm.activate(); | |
41 | + expect(vm.openLogin).toHaveBeenCalled(); | |
42 | + }); | |
43 | + | |
44 | + }); | |
45 | +})(); | ... | ... |
... | ... | @@ -0,0 +1,49 @@ |
1 | +<nav class="navbar navbar-static-top navbar-inverse"> | |
2 | + <div class="container-fluid"> | |
3 | + <div class="navbar-header"> | |
4 | + <button type="button" class="navbar-toggle collapsed" ng-click="isCollapsed = !isCollapsed"> | |
5 | + <span class="sr-only">{{"navbar.toggle_menu" | translate}}</span> | |
6 | + <span class="icon-bar"></span> | |
7 | + <span class="icon-bar"></span> | |
8 | + <span class="icon-bar"></span> | |
9 | + </button> | |
10 | + <a class="navbar-brand" ui-sref="main"> | |
11 | + <span class="noosfero-logo"><img src="assets/images/logo-noosfero.png"></span> {{"noosfero.name" | translate}} | |
12 | + </a> | |
13 | + </div> | |
14 | + | |
15 | + <div class="collapse navbar-collapse" id="navbar-collapse" collapse="isCollapsed"> | |
16 | + <ul class="nav navbar-nav"> | |
17 | + </ul> | |
18 | + | |
19 | + <ul class="nav navbar-nav navbar-right"> | |
20 | + <li ng-show="!ctrl.currentUser"> | |
21 | + <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a> | |
22 | + </li> | |
23 | + | |
24 | + <li class="dropdown profile-menu" ng-show="ctrl.currentUser" dropdown> | |
25 | + <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
26 | + <noosfero-profile-image [profile]="ctrl.currentUser.person" class="profile-image"></noosfero-profile-image> | |
27 | + <span ng-bind="ctrl.currentUser.person.name"></span> <b class="caret"></b> | |
28 | + </a> | |
29 | + <ul class="dropdown-menu" dropdown-menu> | |
30 | + <li> | |
31 | + <a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-user"></i> {{"navbar.profile" | translate}}</a> | |
32 | + </li> | |
33 | + <li> | |
34 | + <a target="_self" ui-sref="main.profile.settings({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-gear"></i> {{"navbar.settings" | translate}}</a> | |
35 | + </li> | |
36 | + <li class="divider"></li> | |
37 | + <li> | |
38 | + <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a> | |
39 | + </li> | |
40 | + </ul> | |
41 | + </li> | |
42 | + </ul> | |
43 | + <ul class="nav navbar-nav navbar-right"> | |
44 | + <language-selector class="nav navbar-nav navbar-right"></language-selector> | |
45 | + </ul> | |
46 | + <div ui-view="actions"></div> | |
47 | + </div> | |
48 | + </div> | |
49 | +</nav> | ... | ... |
... | ... | @@ -0,0 +1,31 @@ |
1 | +.navbar { | |
2 | + | |
3 | + .container-fluid { | |
4 | + padding-right: 12%; | |
5 | + padding-left: 12%; | |
6 | + @media (max-width: 978px) { | |
7 | + padding-right: 2%; | |
8 | + padding-left: 2%; | |
9 | + } | |
10 | + | |
11 | + .navbar-brand { | |
12 | + .noosfero-logo img { | |
13 | + height: 35px; | |
14 | + } | |
15 | + } | |
16 | + | |
17 | + .navbar-nav { | |
18 | + .profile-menu .profile-image { | |
19 | + img { | |
20 | + height: 30px; | |
21 | + width: 30px; | |
22 | + display: inline-block; | |
23 | + @extend .img-circle; | |
24 | + } | |
25 | + i { | |
26 | + font-size: 1.7em; | |
27 | + } | |
28 | + } | |
29 | + } | |
30 | + } | |
31 | +} | ... | ... |
... | ... | @@ -0,0 +1,187 @@ |
1 | +import * as helpers from "./../../../spec/helpers"; | |
2 | +import {Navbar} from "./navbar"; | |
3 | + | |
4 | +import {Injectable, Provider, provide} from "ng-forward"; | |
5 | + | |
6 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
7 | + | |
8 | +import {SessionService, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../../login"; | |
9 | + | |
10 | + | |
11 | +describe("Components", () => { | |
12 | + | |
13 | + describe("Navbar Component", () => { | |
14 | + | |
15 | + let user: noosfero.User = null; | |
16 | + let scope: any; | |
17 | + let $rootScope: ng.IRootScopeService; | |
18 | + | |
19 | + let modalInstance: any; | |
20 | + let $modal: any; | |
21 | + let authService: any; | |
22 | + let stateService: any; | |
23 | + let sessionService: SessionService; | |
24 | + | |
25 | + let provideFunc = provide; | |
26 | + | |
27 | + // before Each -> loading mocks on locals variables | |
28 | + beforeEach(() => { | |
29 | + user = <noosfero.User>{ | |
30 | + id: 1, | |
31 | + login: "user" | |
32 | + }; | |
33 | + scope = helpers.mocks.scopeWithEvents; | |
34 | + modalInstance = helpers.mocks.modalInstance; | |
35 | + $modal = helpers.mocks.$modal; | |
36 | + authService = helpers.mocks.authService; | |
37 | + stateService = jasmine.createSpyObj("$state", ["go"]); | |
38 | + sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); | |
39 | + }); | |
40 | + | |
41 | + | |
42 | + // loading the templates | |
43 | + beforeEach(angular.mock.module("templates")); | |
44 | + | |
45 | + | |
46 | + // this function allow build the fixture of the container component | |
47 | + // and is reused in each test | |
48 | + // The main idea behing not prebuild it on a general beforeEach block is | |
49 | + // to allow tests configure the mock services accordilly their own needs | |
50 | + let buildComponent = (): Promise<ComponentFixture> => { | |
51 | + return helpers.quickCreateComponent({ | |
52 | + providers: [ | |
53 | + provide('$modal', { | |
54 | + useValue: $modal | |
55 | + }), | |
56 | + provide('AuthService', { | |
57 | + useValue: authService | |
58 | + }), | |
59 | + helpers.provideEmptyObjects('moment'), | |
60 | + provide('$state', { | |
61 | + useValue: stateService | |
62 | + }), | |
63 | + provide("$scope", { | |
64 | + useValue: scope | |
65 | + }), | |
66 | + provide('SessionService', { | |
67 | + useValue: sessionService | |
68 | + }), | |
69 | + provide('AUTH_EVENTS', { | |
70 | + useValue: { | |
71 | + AUTH_EVENTS | |
72 | + } | |
73 | + }), | |
74 | + provide('TranslatorService', { | |
75 | + useValue: helpers.mocks.translatorService | |
76 | + }) | |
77 | + ].concat(helpers.provideFilters("translateFilter")), | |
78 | + directives: [Navbar], | |
79 | + template: '<acme-navbar></acme-navbar>' | |
80 | + }); | |
81 | + }; | |
82 | + | |
83 | + | |
84 | + it('should get the loggedIn user', (done: Function) => { | |
85 | + buildComponent().then((fixture: ComponentFixture) => { | |
86 | + let navbarInstance: Navbar = fixture.debugElement.componentViewChildren[0].componentInstance; | |
87 | + expect(navbarInstance).toBeDefined(); | |
88 | + expect(navbarInstance["currentUser"]).toEqual(user); | |
89 | + done(); | |
90 | + }); | |
91 | + }); | |
92 | + | |
93 | + it('should open on click', (done: Function) => { | |
94 | + spyOn($modal, "open"); | |
95 | + buildComponent().then((fixture: ComponentFixture) => { | |
96 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
97 | + navbarComp.openLogin(); | |
98 | + expect($modal.open).toHaveBeenCalled(); | |
99 | + expect($modal.open).toHaveBeenCalledWith({ | |
100 | + templateUrl: 'app/components/auth/login.html', | |
101 | + controller: AuthController, | |
102 | + controllerAs: 'vm', | |
103 | + bindToController: true | |
104 | + }); | |
105 | + done(); | |
106 | + }); | |
107 | + }); | |
108 | + | |
109 | + it('should logout', (done: Function) => { | |
110 | + buildComponent().then((fixture: ComponentFixture) => { | |
111 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
112 | + spyOn(authService, "logout"); | |
113 | + try { | |
114 | + navbarComp.logout(); | |
115 | + expect(authService.logout).toHaveBeenCalled(); | |
116 | + done(); | |
117 | + } catch (e) { | |
118 | + console.error(e); | |
119 | + fail(e.message); | |
120 | + done(); | |
121 | + } | |
122 | + }); | |
123 | + }); | |
124 | + | |
125 | + | |
126 | + it('should not activate user when logged in', (done: Function) => { | |
127 | + buildComponent().then((fixture: ComponentFixture) => { | |
128 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
129 | + spyOn(navbarComp, "openLogin"); | |
130 | + navbarComp.activate(); | |
131 | + expect((<any>navbarComp.openLogin).calls.count()).toBe(0); | |
132 | + done(); | |
133 | + }); | |
134 | + }); | |
135 | + | |
136 | + it('should activate when user not logged in', (done: Function) => { | |
137 | + spyOn(sessionService, 'currentUser').and.returnValue(null); | |
138 | + buildComponent().then((fixture: ComponentFixture) => { | |
139 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
140 | + spyOn(navbarComp, "openLogin"); | |
141 | + navbarComp.activate(); | |
142 | + expect(navbarComp.openLogin).toHaveBeenCalled(); | |
143 | + done(); | |
144 | + }); | |
145 | + }); | |
146 | + | |
147 | + | |
148 | + it('closes the modal after login', (done: Function) => { | |
149 | + modalInstance = jasmine.createSpyObj("modalInstance", ["close"]); | |
150 | + modalInstance.close = jasmine.createSpy("close"); | |
151 | + | |
152 | + $modal.open = () => { | |
153 | + return modalInstance; | |
154 | + }; | |
155 | + | |
156 | + buildComponent().then((fixture: ComponentFixture) => { | |
157 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
158 | + let localScope: ng.IScope = navbarComp["$scope"]; | |
159 | + | |
160 | + navbarComp.openLogin(); | |
161 | + localScope.$emit(AUTH_EVENTS.loginSuccess); | |
162 | + expect(modalInstance.close).toHaveBeenCalled(); | |
163 | + done(); | |
164 | + }); | |
165 | + }); | |
166 | + | |
167 | + it('updates current user on logout', (done: Function) => { | |
168 | + buildComponent().then((fixture: ComponentFixture) => { | |
169 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
170 | + let localScope: ng.IScope = navbarComp["$scope"]; | |
171 | + | |
172 | + // init navbar currentUser with some user | |
173 | + navbarComp["currentUser"] = user; | |
174 | + | |
175 | + // changes the current User to return null, | |
176 | + // and emmit the 'logoutSuccess' event | |
177 | + // just what happens when user logsout | |
178 | + sessionService.currentUser = () => { return null; }; | |
179 | + localScope.$emit(AUTH_EVENTS.logoutSuccess); | |
180 | + expect(navbarComp["currentUser"]).toBeNull(); | |
181 | + done(); | |
182 | + }); | |
183 | + }); | |
184 | + | |
185 | + | |
186 | + }); | |
187 | +}); | ... | ... |
... | ... | @@ -0,0 +1,66 @@ |
1 | +import {Component, Inject} from "ng-forward"; | |
2 | +import {LanguageSelectorService} from "../language-selector/language-selector.component"; | |
3 | + | |
4 | + | |
5 | +import {SessionService, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../../login"; | |
6 | + | |
7 | +@Component({ | |
8 | + selector: "acme-navbar", | |
9 | + templateUrl: "app/layout/navbar/navbar.html", | |
10 | + directives: [LanguageSelectorService], | |
11 | + providers: [AuthService, SessionService] | |
12 | +}) | |
13 | +@Inject("$modal", AuthService, "SessionService", "$scope", "$state") | |
14 | +export class Navbar { | |
15 | + | |
16 | + private currentUser: noosfero.User; | |
17 | + private modalInstance: any = null; | |
18 | + /** | |
19 | + * | |
20 | + */ | |
21 | + constructor( | |
22 | + private $modal: any, | |
23 | + private authService: AuthService, | |
24 | + private session: SessionService, | |
25 | + private $scope: ng.IScope, | |
26 | + private $state: ng.ui.IStateService | |
27 | + ) { | |
28 | + this.currentUser = this.session.currentUser(); | |
29 | + | |
30 | + this.$scope.$on(AUTH_EVENTS.loginSuccess, () => { | |
31 | + if (this.modalInstance) { | |
32 | + this.modalInstance.close(); | |
33 | + this.modalInstance = null; | |
34 | + } | |
35 | + | |
36 | + this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
37 | + }); | |
38 | + | |
39 | + this.$scope.$on(AUTH_EVENTS.logoutSuccess, () => { | |
40 | + this.currentUser = this.session.currentUser(); | |
41 | + }); | |
42 | + } | |
43 | + | |
44 | + openLogin() { | |
45 | + this.modalInstance = this.$modal.open({ | |
46 | + templateUrl: 'app/components/auth/login.html', | |
47 | + controller: AuthController, | |
48 | + controllerAs: 'vm', | |
49 | + bindToController: true | |
50 | + }); | |
51 | + }; | |
52 | + | |
53 | + logout() { | |
54 | + this.authService.logout(); | |
55 | + this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
56 | + }; | |
57 | + | |
58 | + | |
59 | + | |
60 | + activate() { | |
61 | + if (!this.currentUser) { | |
62 | + this.openLogin(); | |
63 | + } | |
64 | + } | |
65 | + | |
66 | +} | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +export interface IAuthEvents { | |
2 | + loginSuccess: string; | |
3 | + loginFailed: string; | |
4 | + logoutSuccess: string; | |
5 | +} | |
6 | + | |
7 | +export const AUTH_EVENTS: IAuthEvents = { | |
8 | + loginSuccess: "auth-login-success", | |
9 | + loginFailed: "auth-login-failed", | |
10 | + logoutSuccess: "auth-logout-success" | |
11 | +}; | |
0 | 12 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,33 @@ |
1 | +import {AuthController} from "./auth.controller"; | |
2 | +import {AuthService} from "./auth.service"; | |
3 | + | |
4 | +describe("Controllers", () => { | |
5 | + | |
6 | + | |
7 | + describe("AuthController", () => { | |
8 | + | |
9 | + it("calls authenticate on AuthService when login called", () => { | |
10 | + | |
11 | + // creating a Mock AuthService | |
12 | + let AuthServiceMock: AuthService = jasmine.createSpyObj("AuthService", ["login"]); | |
13 | + | |
14 | + // pass AuthServiceMock into the constructor | |
15 | + let authController = new AuthController(null, null, AuthServiceMock); | |
16 | + | |
17 | + // setup of authController -> set the credentials instance property | |
18 | + let credentials = { username: "username", password: "password" }; | |
19 | + | |
20 | + authController.credentials = credentials; | |
21 | + | |
22 | + // calls the authController login method | |
23 | + authController.login(); | |
24 | + | |
25 | + // checks if the method login of the injected AuthService has been called | |
26 | + expect(AuthServiceMock.login).toHaveBeenCalledWith(credentials); | |
27 | + | |
28 | + }); | |
29 | + | |
30 | + | |
31 | + | |
32 | + }); | |
33 | +}); | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +import {AuthService} from "./auth.service"; | |
2 | + | |
3 | +export class AuthController { | |
4 | + | |
5 | + static $inject = ["$log", "$stateParams", "AuthService"]; | |
6 | + | |
7 | + constructor( | |
8 | + private $log: ng.ILogService, | |
9 | + private $stateParams: any, | |
10 | + private AuthService: AuthService | |
11 | + ) { | |
12 | + | |
13 | + } | |
14 | + | |
15 | + credentials: noosfero.Credentials; | |
16 | + | |
17 | + login() { | |
18 | + this.AuthService.login(this.credentials); | |
19 | + } | |
20 | +} | ... | ... |
... | ... | @@ -0,0 +1,81 @@ |
1 | + | |
2 | + | |
3 | +import {AuthService, AUTH_EVENTS} from "./"; | |
4 | + | |
5 | +describe("Services", () => { | |
6 | + | |
7 | + | |
8 | + describe("Auth Service", () => { | |
9 | + | |
10 | + let $httpBackend: ng.IHttpBackendService; | |
11 | + let authService: AuthService; | |
12 | + let credentials: noosfero.Credentials; | |
13 | + let $rootScope: ng.IRootScopeService; | |
14 | + let user: noosfero.User; | |
15 | + | |
16 | + beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => { | |
17 | + $translateProvider.translations('en', {}); | |
18 | + })); | |
19 | + | |
20 | + beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService, _AuthService_: AuthService) => { | |
21 | + $httpBackend = _$httpBackend_; | |
22 | + authService = _AuthService_; | |
23 | + $rootScope = _$rootScope_; | |
24 | + | |
25 | + user = <noosfero.User>{ | |
26 | + id: 1, | |
27 | + login: "user" | |
28 | + }; | |
29 | + })); | |
30 | + | |
31 | + | |
32 | + describe("Succesffull login", () => { | |
33 | + | |
34 | + beforeEach(() => { | |
35 | + credentials = { username: "user", password: "password" }; | |
36 | + | |
37 | + $httpBackend.expectPOST("/api/v1/login", "login=user&password=password").respond(200, { user: user }); | |
38 | + }); | |
39 | + | |
40 | + it("should return loggedUser", (done) => { | |
41 | + authService.login(credentials).then((loggedUser) => { | |
42 | + expect(loggedUser).toBeDefined(); | |
43 | + done(); | |
44 | + }); | |
45 | + $httpBackend.flush(); | |
46 | + expect($httpBackend.verifyNoOutstandingRequest()); | |
47 | + }); | |
48 | + | |
49 | + | |
50 | + it("should emit event loggin successful with user logged data", () => { | |
51 | + | |
52 | + authService.login(credentials); | |
53 | + | |
54 | + let eventEmmited: boolean = false; | |
55 | + $rootScope.$on(AUTH_EVENTS.loginSuccess, (event: ng.IAngularEvent, userThroughEvent: noosfero.User) => { | |
56 | + eventEmmited = true; | |
57 | + expect(userThroughEvent).toEqual(user); | |
58 | + }); | |
59 | + | |
60 | + $httpBackend.flush(); | |
61 | + | |
62 | + expect(eventEmmited).toBeTruthy(AUTH_EVENTS.loginSuccess + " was not emmited!"); | |
63 | + }); | |
64 | + | |
65 | + it("should return the current logged in user", () => { | |
66 | + authService.login(credentials); | |
67 | + $httpBackend.flush(); | |
68 | + let actual: noosfero.User = authService.currentUser(); | |
69 | + expect(actual).toEqual(user, "The returned user must be present"); | |
70 | + }); | |
71 | + | |
72 | + it("should not return the current user after logout", () => { | |
73 | + authService.logout(); | |
74 | + let actual: any = authService.currentUser(); | |
75 | + expect(actual).toEqual(undefined, "The returned user must not be defined"); | |
76 | + }); | |
77 | + }); | |
78 | + | |
79 | + | |
80 | + }); | |
81 | +}); | ... | ... |
... | ... | @@ -0,0 +1,69 @@ |
1 | +import {Injectable, Inject} from "ng-forward"; | |
2 | + | |
3 | +import {NoosferoRootScope, UserResponse} from "./../shared/models/interfaces"; | |
4 | +import {SessionService} from "./session.service"; | |
5 | + | |
6 | +import {AUTH_EVENTS, IAuthEvents} from "./auth-events"; | |
7 | + | |
8 | +@Injectable() | |
9 | +@Inject("$q", "$http", "$rootScope", "SessionService", "$log", "AUTH_EVENTS") | |
10 | +export class AuthService { | |
11 | + | |
12 | + constructor(private $q: ng.IQService, | |
13 | + private $http: ng.IHttpService, | |
14 | + private $rootScope: NoosferoRootScope, | |
15 | + private sessionService: SessionService, | |
16 | + private $log: ng.ILogService, | |
17 | + private auth_events: IAuthEvents) { | |
18 | + | |
19 | + } | |
20 | + | |
21 | + loginFromCookie() { | |
22 | + let url: string = '/api/v1/login_from_cookie'; | |
23 | + return this.$http.post(url, null).then(this.loginSuccessCallback.bind(this), this.loginFailedCallback.bind(this)); | |
24 | + } | |
25 | + | |
26 | + | |
27 | + private loginSuccessCallback(response: ng.IHttpPromiseCallbackArg<UserResponse>) { | |
28 | + this.$log.debug('AuthService.login [SUCCESS] response', response); | |
29 | + let currentUser: noosfero.User = this.sessionService.create(response.data); | |
30 | + this.$rootScope.currentUser = currentUser; | |
31 | + this.$rootScope.$broadcast(this.auth_events.loginSuccess, currentUser); | |
32 | + return currentUser; | |
33 | + } | |
34 | + | |
35 | + login(credentials: noosfero.Credentials): ng.IPromise<noosfero.User> { | |
36 | + let url = '/api/v1/login'; | |
37 | + let encodedData = 'login=' + credentials.username + '&password=' + credentials.password; | |
38 | + return this.$http.post(url, encodedData).then(this.loginSuccessCallback.bind(this), this.loginFailedCallback.bind(this)); | |
39 | + } | |
40 | + | |
41 | + private loginFailedCallback(response: ng.IHttpPromiseCallbackArg<any>): any { | |
42 | + this.$log.debug('AuthService.login [FAIL] response', response); | |
43 | + this.$rootScope.$broadcast(this.auth_events.loginFailed); | |
44 | + // return $q.reject(response); | |
45 | + return null; | |
46 | + } | |
47 | + | |
48 | + public logout() { | |
49 | + this.sessionService.destroy(); | |
50 | + this.$rootScope.currentUser = undefined; | |
51 | + this.$rootScope.$broadcast(this.auth_events.logoutSuccess); | |
52 | + this.$http.jsonp('/account/logout'); // FIXME logout from noosfero to sync login state | |
53 | + } | |
54 | + | |
55 | + public isAuthenticated() { | |
56 | + return !!this.sessionService.currentUser(); | |
57 | + } | |
58 | + | |
59 | + public currentUser(): noosfero.User { | |
60 | + return this.sessionService.currentUser(); | |
61 | + } | |
62 | + | |
63 | + public isAuthorized(authorizedRoles: string | string[]) { | |
64 | + if (!angular.isArray(authorizedRoles)) { | |
65 | + authorizedRoles = [<string>authorizedRoles]; | |
66 | + } | |
67 | + return (this.isAuthenticated() && authorizedRoles.indexOf(this.sessionService.currentUser().userRole) !== -1); | |
68 | + } | |
69 | +} | |
0 | 70 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +<div class="modal-header"> | |
2 | + <h3 class="modal-title">{{"auth.title" | translate}}</h3> | |
3 | +</div> | |
4 | +<div class="modal-body"> | |
5 | + <form> | |
6 | + <div class="form-group"> | |
7 | + <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label> | |
8 | + <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="vm.credentials.username"> | |
9 | + </div> | |
10 | + <div class="form-group"> | |
11 | + <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label> | |
12 | + <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="vm.credentials.password"> | |
13 | + </div> | |
14 | + <button type="submit" class="btn btn-default" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> | |
15 | + </form> | |
16 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,49 @@ |
1 | +import {Component} from "ng-forward"; | |
2 | +import {SessionService} from "./session.service"; | |
3 | +import {fixtures, createComponentFromClass, createProviderToValue} from "./../../spec/helpers"; | |
4 | +import {UserResponse, INoosferoLocalStorage} from "./../shared/models/interfaces"; | |
5 | + | |
6 | + | |
7 | +describe("Services", () => { | |
8 | + | |
9 | + | |
10 | + describe("Session Service", () => { | |
11 | + | |
12 | + let $localStorage: INoosferoLocalStorage = null; | |
13 | + let $log: any; | |
14 | + | |
15 | + beforeEach(() => { | |
16 | + $localStorage = <INoosferoLocalStorage>{ currentUser: null }; | |
17 | + $log = jasmine.createSpyObj('$log', ['debug']); | |
18 | + }); | |
19 | + | |
20 | + it("method 'create()' saves the current user on $localstorage service", () => { | |
21 | + let session = new SessionService($localStorage, $log); | |
22 | + let userResponse = <UserResponse>{ | |
23 | + user: fixtures.user | |
24 | + }; | |
25 | + session.create(userResponse); | |
26 | + expect($localStorage.currentUser).toEqual(userResponse.user); | |
27 | + }); | |
28 | + | |
29 | + it("method 'destroy()' clean the currentUser on $localstorage", () => { | |
30 | + let session = new SessionService($localStorage, $log); | |
31 | + let userResponse = <UserResponse>{ | |
32 | + user: fixtures.user | |
33 | + }; | |
34 | + $localStorage.currentUser = fixtures.user; | |
35 | + session.destroy(); | |
36 | + expect($localStorage.currentUser).toBeUndefined(); | |
37 | + }); | |
38 | + | |
39 | + it("method 'currentUser()' returns the user recorded on $localstorage service", () => { | |
40 | + let session = new SessionService($localStorage, $log); | |
41 | + let userResponse = <UserResponse>{ | |
42 | + user: fixtures.user | |
43 | + }; | |
44 | + $localStorage.currentUser = fixtures.user; | |
45 | + expect(session.currentUser()).toEqual($localStorage.currentUser); | |
46 | + }); | |
47 | + }); | |
48 | + | |
49 | +}); | |
0 | 50 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +import {Injectable, Inject} from "ng-forward"; | |
2 | +import {UserResponse, INoosferoLocalStorage} from "./../shared/models/interfaces"; | |
3 | + | |
4 | +@Injectable() | |
5 | +@Inject("$localStorage", "$log") | |
6 | +export class SessionService { | |
7 | + | |
8 | + constructor(private $localStorage: INoosferoLocalStorage, private $log: ng.ILogService) { | |
9 | + | |
10 | + } | |
11 | + | |
12 | + create(data: UserResponse): noosfero.User { | |
13 | + this.$localStorage.currentUser = data.user; | |
14 | + this.$log.debug('User session created.', this.$localStorage.currentUser); | |
15 | + return this.$localStorage.currentUser; | |
16 | + }; | |
17 | + | |
18 | + destroy() { | |
19 | + delete this.$localStorage.currentUser; | |
20 | + this.$log.debug('User session destroyed.'); | |
21 | + }; | |
22 | + | |
23 | + currentUser(): noosfero.User { | |
24 | + return this.$localStorage.currentUser; | |
25 | + }; | |
26 | + | |
27 | +} | |
0 | 28 | \ No newline at end of file | ... | ... |
src/app/main/main.component.ts
1 | 1 | import {bundle, Component, StateConfig} from "ng-forward"; |
2 | -import {ArticleBlog} from "./../components/noosfero-articles/blog/blog.component"; | |
2 | +import {ArticleBlogComponent} from "./../article/types/blog/blog.component"; | |
3 | 3 | |
4 | -import {ArticleView} from "../components/noosfero-articles/article/article_view"; | |
4 | +import {ArticleViewComponent} from "./../article/article-default-view.component"; | |
5 | 5 | |
6 | -import {Profile} from "../profile/profile.component"; | |
7 | -import {Boxes} from "../components/noosfero-boxes/boxes.component"; | |
8 | -import {Block} from "../components/noosfero-blocks/block.component"; | |
9 | -import {LinkListBlock} from "../components/noosfero-blocks/link-list/link-list.component"; | |
10 | -import {RecentDocumentsBlock} from "../components/noosfero-blocks/recent-documents/recent-documents.component"; | |
11 | -import {ProfileImageBlock} from "../components/noosfero-blocks/profile-image-block/profile-image-block.component"; | |
12 | -import {RawHTMLBlock} from "../components/noosfero-blocks/raw-html/raw-html.component"; | |
6 | +import {ProfileComponent} from "../profile/profile.component"; | |
7 | +import {BoxesComponent} from "../layout/boxes/boxes.component"; | |
8 | +import {BlockComponent} from "../layout/blocks/block.component"; | |
9 | +import {LinkListBlockComponent} from "./../layout/blocks/link-list/link-list.component"; | |
10 | +import {RecentDocumentsBlockComponent} from "../layout/blocks/recent-documents/recent-documents.component"; | |
11 | +import {ProfileImageBlockComponent} from "../layout/blocks/profile-image-block/profile-image-block.component"; | |
12 | +import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html.component"; | |
13 | 13 | |
14 | -import {MembersBlock} from "../components/noosfero-blocks/members-block/members-block.component"; | |
15 | -import {NoosferoTemplate} from "../components/noosfero/noosfero-template.filter"; | |
16 | -import {DateFormat} from "../components/noosfero/date-format/date-format.filter"; | |
14 | +import {MembersBlockComponent} from "./../layout/blocks/members-block/members-block.component"; | |
15 | +import {NoosferoTemplate} from "../shared/pipes/noosfero-template.filter"; | |
16 | +import {DateFormat} from "../shared/pipes/date-format.filter"; | |
17 | 17 | |
18 | -import {AuthService} from "./../components/auth/auth_service"; | |
19 | -import {Session} from "./../components/auth/session"; | |
20 | -import {Notification} from "./../components/notification/notification.component"; | |
18 | +import {AuthService} from "../login/auth.service"; | |
19 | +import {SessionService} from "../login/session.service"; | |
21 | 20 | |
21 | +import {NotificationService} from "../shared/services/notification.service"; | |
22 | 22 | |
23 | -import {Navbar} from "../components/navbar/navbar"; | |
24 | 23 | |
25 | -import {MainBlock} from "../components/noosfero-blocks/main-block/main-block.component"; | |
24 | +import {Navbar} from "../layout/navbar/navbar"; | |
25 | + | |
26 | +import {MainBlockComponent} from "../layout/blocks/main-block/main-block.component"; | |
26 | 27 | |
27 | 28 | |
28 | 29 | /** |
... | ... | @@ -38,7 +39,7 @@ import {MainBlock} from "../components/noosfero-blocks/main-block/main-block.com |
38 | 39 | @Component({ |
39 | 40 | selector: 'main-content', |
40 | 41 | templateUrl: "app/main/main.html", |
41 | - providers: [AuthService, Session] | |
42 | + providers: [AuthService, SessionService] | |
42 | 43 | }) |
43 | 44 | export class MainContent { |
44 | 45 | |
... | ... | @@ -62,11 +63,11 @@ export class MainContent { |
62 | 63 | selector: 'main', |
63 | 64 | template: '<div ng-view></div>', |
64 | 65 | directives: [ |
65 | - ArticleBlog, ArticleView, Boxes, Block, LinkListBlock, | |
66 | - MainBlock, RecentDocumentsBlock, Navbar, ProfileImageBlock, | |
67 | - MembersBlock, NoosferoTemplate, DateFormat, RawHTMLBlock | |
66 | + ArticleBlogComponent, ArticleViewComponent, BoxesComponent, BlockComponent, LinkListBlockComponent, | |
67 | + MainBlockComponent, RecentDocumentsBlockComponent, Navbar, ProfileImageBlockComponent, | |
68 | + MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent | |
68 | 69 | ], |
69 | - providers: [AuthService, Session, Notification] | |
70 | + providers: [AuthService, SessionService, NotificationService] | |
70 | 71 | }) |
71 | 72 | @StateConfig([ |
72 | 73 | { |
... | ... | @@ -77,12 +78,12 @@ export class MainContent { |
77 | 78 | { |
78 | 79 | url: "^/:profile", |
79 | 80 | abstract: true, |
80 | - component: Profile, | |
81 | + component: ProfileComponent, | |
81 | 82 | name: 'main.profile', |
82 | 83 | views: { |
83 | 84 | "content": { |
84 | 85 | templateUrl: "app/profile/profile.html", |
85 | - controller: Profile, | |
86 | + controller: ProfileComponent, | |
86 | 87 | controllerAs: "vm" |
87 | 88 | } |
88 | 89 | } | ... | ... |
src/app/models/index.ts
src/app/models/interfaces.ts
... | ... | @@ -1,12 +0,0 @@ |
1 | -export interface NoosferoRootScope extends ng.IScope { | |
2 | - currentUser: noosfero.User; | |
3 | -} | |
4 | - | |
5 | -export interface UserResponse { | |
6 | - user: noosfero.User; | |
7 | -} | |
8 | - | |
9 | - | |
10 | -export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { | |
11 | - currentUser: noosfero.User; | |
12 | -} |
src/app/profile-info/index.ts
src/app/profile-info/profile-info.component.spec.ts
... | ... | @@ -1,40 +0,0 @@ |
1 | -import {quickCreateComponent} from "../../spec/helpers"; | |
2 | -import {ProfileInfo} from "./profile-info.component"; | |
3 | - | |
4 | -describe("Components", () => { | |
5 | - describe("Profile Info Component", () => { | |
6 | - | |
7 | - let $rootScope: ng.IRootScopeService; | |
8 | - let $q: ng.IQService; | |
9 | - let profileServiceMock: any; | |
10 | - let $stateParams: any; | |
11 | - | |
12 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
13 | - $rootScope = _$rootScope_; | |
14 | - $q = _$q_; | |
15 | - })); | |
16 | - | |
17 | - beforeEach(() => { | |
18 | - $stateParams = jasmine.createSpyObj("$stateParams", ["profile"]); | |
19 | - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile", "getActivities"]); | |
20 | - | |
21 | - let getCurrentProfileResponse = $q.defer(); | |
22 | - getCurrentProfileResponse.resolve({ id: 1 }); | |
23 | - | |
24 | - let getActivitiesResponse = $q.defer(); | |
25 | - getActivitiesResponse.resolve({ data: { activities: [{ id: 1 }, { id: 2 }] } }); | |
26 | - | |
27 | - profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
28 | - profileServiceMock.getActivities = jasmine.createSpy("getActivities").and.returnValue(getActivitiesResponse.promise); | |
29 | - }); | |
30 | - | |
31 | - it("get the profile activities", done => { | |
32 | - let component: ProfileInfo = new ProfileInfo(profileServiceMock); | |
33 | - $rootScope.$apply(); | |
34 | - expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
35 | - expect(profileServiceMock.getActivities).toHaveBeenCalled(); | |
36 | - expect(component.activities).toEqual([{ id: 1 }, { id: 2 }]); | |
37 | - done(); | |
38 | - }); | |
39 | - }); | |
40 | -}); |
src/app/profile-info/profile-info.component.ts
... | ... | @@ -1,27 +0,0 @@ |
1 | -import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
2 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: 'profile', | |
6 | - templateUrl: "app/profile-info/profile-info.html", | |
7 | - providers: [provide('profileService', { useClass: ProfileService })] | |
8 | -}) | |
9 | -@Inject(ProfileService) | |
10 | -export class ProfileInfo { | |
11 | - | |
12 | - activities: any; | |
13 | - profile: noosfero.Profile; | |
14 | - | |
15 | - constructor(private profileService: ProfileService) { | |
16 | - this.activate(); | |
17 | - } | |
18 | - | |
19 | - activate() { | |
20 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
21 | - this.profile = profile; | |
22 | - return this.profileService.getActivities(<number>this.profile.id); | |
23 | - }).then((response: restangular.IResponse) => { | |
24 | - this.activities = response.data.activities; | |
25 | - }); | |
26 | - } | |
27 | -} |
src/app/profile-info/profile-info.html
... | ... | @@ -0,0 +1,36 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | +import {provideFilters} from '../../../spec/helpers'; | |
4 | + | |
5 | +import {ActivitiesComponent} from './activities.component'; | |
6 | + | |
7 | +const tcb = new TestComponentBuilder(); | |
8 | + | |
9 | +const htmlTemplate: string = '<noosfero-activities [activities]="ctrl.activities"></noosfero-activities>'; | |
10 | + | |
11 | + | |
12 | +describe("Components", () => { | |
13 | + | |
14 | + describe("Noosfero Activities", () => { | |
15 | + | |
16 | + beforeEach(angular.mock.module("templates")); | |
17 | + | |
18 | + @Component({ | |
19 | + selector: 'test-container-component', | |
20 | + template: htmlTemplate, | |
21 | + directives: [ActivitiesComponent], | |
22 | + providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
23 | + }) | |
24 | + class BlockContainerComponent { | |
25 | + activities = [{ name: "activity1", verb: "create_article" }, { name: "activity2", verb: "create_article" }]; | |
26 | + } | |
27 | + | |
28 | + it("render a noosfero activity tag for each activity", done => { | |
29 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
30 | + expect(fixture.debugElement.queryAll("noosfero-activity").length).toEqual(2); | |
31 | + done(); | |
32 | + }); | |
33 | + }); | |
34 | + }); | |
35 | + | |
36 | +}); | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +import {Component, Input} from "ng-forward"; | |
2 | +import {ActivityComponent} from "./activity/activity.component"; | |
3 | + | |
4 | +/** | |
5 | + * @ngdoc controller | |
6 | + * @name NoosferoActivities | |
7 | + * @description | |
8 | + * The controller responsible to retreive profile activities. | |
9 | + */ | |
10 | + | |
11 | +@Component({ | |
12 | + selector: "noosfero-activities", | |
13 | + templateUrl: 'app/profile/activities/activities.html', | |
14 | + directives: [ActivityComponent] | |
15 | +}) | |
16 | +export class ActivitiesComponent { | |
17 | + | |
18 | + /** | |
19 | + * @ngdoc property | |
20 | + * @propertyOf NoosferoActivities | |
21 | + * @name activities | |
22 | + * @returns {Activity[]} An array of {@link Activity}. | |
23 | + */ | |
24 | + @Input() activities: noosfero.Activity[]; | |
25 | + | |
26 | + | |
27 | +} | ... | ... |
src/app/profile/activities/activity/activity.component.spec.ts
0 → 100644
... | ... | @@ -0,0 +1,38 @@ |
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | +import {provideFilters} from '../../../../spec/helpers'; | |
4 | + | |
5 | +import {ActivityComponent} from './activity.component'; | |
6 | + | |
7 | +const tcb = new TestComponentBuilder(); | |
8 | + | |
9 | +const htmlTemplate: string = '<noosfero-activity [activity]="ctrl.activity"></noosfero-activity>'; | |
10 | + | |
11 | + | |
12 | +describe("Components", () => { | |
13 | + | |
14 | + describe("Noosfero Activity", () => { | |
15 | + | |
16 | + beforeEach(angular.mock.module("templates")); | |
17 | + | |
18 | + @Component({ | |
19 | + selector: 'test-container-component', | |
20 | + template: htmlTemplate, | |
21 | + directives: [ActivityComponent], | |
22 | + providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
23 | + }) | |
24 | + class BlockContainerComponent { | |
25 | + activity = { name: "activity1", verb: "create_article" }; | |
26 | + } | |
27 | + | |
28 | + it("render the specific template for an activity verb", done => { | |
29 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
30 | + let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
31 | + expect(component.getActivityTemplate()).toEqual('app/profile/activities/activity/create_article.html'); | |
32 | + expect(fixture.debugElement.queryAll(".activity.create_article").length).toEqual(1); | |
33 | + done(); | |
34 | + }); | |
35 | + }); | |
36 | + }); | |
37 | + | |
38 | +}); | ... | ... |
src/app/profile/activities/activity/activity.component.ts
0 → 100644
... | ... | @@ -0,0 +1,15 @@ |
1 | +import {Component, Input} from "ng-forward"; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: "noosfero-activity", | |
5 | + templateUrl: 'app/profile/activities/activity/activity.html' | |
6 | +}) | |
7 | +export class ActivityComponent { | |
8 | + | |
9 | + @Input() activity: noosfero.Activity; | |
10 | + | |
11 | + getActivityTemplate() { | |
12 | + return 'app/profile/activities/activity/' + this.activity.verb + '.html'; | |
13 | + } | |
14 | + | |
15 | +} | ... | ... |
src/app/profile/activities/activity/add_member_in_community.html
0 → 100644
... | ... | @@ -0,0 +1,13 @@ |
1 | +<timeline-badge class="info"> | |
2 | + <i class="fa fa-user-plus"></i> | |
3 | +</timeline-badge> | |
4 | +<timeline-panel> | |
5 | + <timeline-heading> | |
6 | + <h4 class="timeline-title"> | |
7 | + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
8 | + <span> {{"activities.add_member_in_community.description" | translate}}</span> | |
9 | + </h4> | |
10 | + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
11 | + </timeline-heading> | |
12 | + <div class="timeline-body"></div> | |
13 | +</timeline-panel> | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
1 | +<timeline-badge class="success"> | |
2 | + <i class="fa fa-file-text"></i> | |
3 | +</timeline-badge> | |
4 | +<timeline-panel> | |
5 | + <timeline-heading> | |
6 | + <h4 class="timeline-title"> | |
7 | + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
8 | + <span> {{"activities.create_article.description" | translate}} </span> | |
9 | + <a ui-sref="main.profile.info({profile: ctrl.activity.target.article.profile.identifier})"> | |
10 | + <strong ng-bind="ctrl.activity.target.article.profile.name"></strong></span> | |
11 | + </a> | |
12 | + </h4> | |
13 | + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
14 | + </timeline-heading> | |
15 | + <div class="timeline-body"> | |
16 | + <div class="article"> | |
17 | + <div class="title"> | |
18 | + <a ui-sref="main.profile.page({profile: ctrl.activity.target.article.profile.identifier, page: ctrl.activity.target.article.path})" | |
19 | + ng-bind="ctrl.activity.target.article.title"></a> | |
20 | + </div> | |
21 | + <div class="lead small"> | |
22 | + <div ng-bind-html="ctrl.activity.target.article.body | stripTags | truncate: 100 : '...': true"></div> | |
23 | + </div> | |
24 | + </div> | |
25 | + </div> | |
26 | +</timeline-panel> | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +<timeline-badge class="info"> | |
2 | + <i class="fa fa-user-plus"></i> | |
3 | +</timeline-badge> | |
4 | +<timeline-panel> | |
5 | + <timeline-heading> | |
6 | + <h4 class="timeline-title"> | |
7 | + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
8 | + <span> {{"activities.new_friendship.description" | translate:{friends: ctrl.activity.params.friend_name.length}:"messageformat" }} </span> | |
9 | + <span class="comma-separated"> | |
10 | + <a class="separated-item" ui-sref="main.profile.info({profile: ctrl.activity.params.friend_url[$index].profile})" ng-repeat="friend in ctrl.activity.params.friend_name"> | |
11 | + <strong ng-bind="friend"></strong> | |
12 | + </a> | |
13 | + </span> | |
14 | + </h4> | |
15 | + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
16 | + </timeline-heading> | |
17 | + <div class="timeline-body"></div> | |
18 | +</timeline-panel> | ... | ... |
... | ... | @@ -0,0 +1,51 @@ |
1 | +/** | |
2 | + * @ngdoc overview | |
3 | + * @name components.noosfero.profile-image.ProfileImageSpec | |
4 | + * @description | |
5 | + * This file contains the tests for the {@link components.noosfero.profile-image.ProfileImage} component. | |
6 | + */ | |
7 | + | |
8 | +import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
9 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
10 | + | |
11 | +import * as helpers from "../../../spec/helpers"; | |
12 | + | |
13 | +import {ProfileImageComponent} from "./image.component"; | |
14 | + | |
15 | +const tcb = new TestComponentBuilder(); | |
16 | + | |
17 | +describe("Components", () => { | |
18 | + | |
19 | + describe("Profile Image Component", () => { | |
20 | + | |
21 | + beforeEach(angular.mock.module("templates")); | |
22 | + | |
23 | + it("show community users image if profile is not Person", done => { | |
24 | + helpers.tcb.createAsync(ProfileImageComponent).then(fixture => { | |
25 | + let profileImageComponent: ProfileImageComponent = fixture.componentInstance; | |
26 | + let profile = <noosfero.Profile>{ id: 1, identifier: "myprofile", type: "Community" }; | |
27 | + profileImageComponent.profile = profile; | |
28 | + profileImageComponent.ngOnInit(); | |
29 | + | |
30 | + // Check the attribute | |
31 | + expect(profileImageComponent.defaultIcon).toBe("fa-users", "The default icon should be community users"); | |
32 | + // var elProfile = fixture.debugElement.componentViewChildren[0]; | |
33 | + // expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
34 | + done(); | |
35 | + }); | |
36 | + }); | |
37 | + | |
38 | + it("show Person image if profile is Person", done => { | |
39 | + tcb.createAsync(ProfileImageComponent).then(fixture => { | |
40 | + let profileImageComponent: ProfileImageComponent = fixture.componentInstance; | |
41 | + let profile = <noosfero.Profile>{ id: 1, identifier: "myprofile", type: "Person" }; | |
42 | + profileImageComponent.profile = profile; | |
43 | + profileImageComponent.ngOnInit(); | |
44 | + // Check the attribute | |
45 | + expect(profileImageComponent.defaultIcon).toEqual("fa-user", "The default icon should be person user"); | |
46 | + done(); | |
47 | + }); | |
48 | + }); | |
49 | + | |
50 | + }); | |
51 | +}); | |
0 | 52 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,47 @@ |
1 | +import {Inject, Input, Component} from "ng-forward"; | |
2 | + | |
3 | + | |
4 | +/** | |
5 | + * @ngdoc controller | |
6 | + * @name components.noosfero.profile-image.ProfileImage | |
7 | + * @description The component responsible for rendering the profile image | |
8 | + * @exports ProfileImage | |
9 | + */ | |
10 | +@Component({ | |
11 | + selector: "noosfero-profile-image", | |
12 | + templateUrl: 'app/profile/image/image.html', | |
13 | +}) | |
14 | +export class ProfileImageComponent { | |
15 | + | |
16 | + /** | |
17 | + * @ngdoc property | |
18 | + * @name profile | |
19 | + * @propertyOf components.noosfero.profile-image.ProfileImage | |
20 | + * @description | |
21 | + * The Noosfero {@link models.Profile} holding the image. | |
22 | + */ | |
23 | + @Input() profile: noosfero.Profile; | |
24 | + /** | |
25 | + * @ngdoc property | |
26 | + * @name defaultIcon | |
27 | + * @propertyOf components.noosfero.profile-image.ProfileImage | |
28 | + * @descritpion | |
29 | + * The default icon used by this profile | |
30 | + */ | |
31 | + defaultIcon: string; | |
32 | + | |
33 | + /** | |
34 | + * @ngdoc method | |
35 | + * @name ngOnInit | |
36 | + * @methodOf components.noosfero.profile-image.ProfileImage | |
37 | + * @description | |
38 | + * Initializes the icon names to their corresponding values depending on the profile type passed to the controller | |
39 | + */ | |
40 | + ngOnInit() { | |
41 | + this.defaultIcon = 'fa-users'; | |
42 | + if (this.profile && this.profile.type === 'Person') { | |
43 | + this.defaultIcon = 'fa-user'; | |
44 | + } | |
45 | + } | |
46 | +} | |
47 | + | ... | ... |
src/app/profile/index.ts
... | ... | @@ -0,0 +1,40 @@ |
1 | +import {quickCreateComponent} from "../../../spec/helpers"; | |
2 | +import {ProfileInfoComponent} from "./profile-info.component"; | |
3 | + | |
4 | +describe("Components", () => { | |
5 | + describe("Profile Info Component", () => { | |
6 | + | |
7 | + let $rootScope: ng.IRootScopeService; | |
8 | + let $q: ng.IQService; | |
9 | + let profileServiceMock: any; | |
10 | + let $stateParams: any; | |
11 | + | |
12 | + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
13 | + $rootScope = _$rootScope_; | |
14 | + $q = _$q_; | |
15 | + })); | |
16 | + | |
17 | + beforeEach(() => { | |
18 | + $stateParams = jasmine.createSpyObj("$stateParams", ["profile"]); | |
19 | + profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile", "getActivities"]); | |
20 | + | |
21 | + let getCurrentProfileResponse = $q.defer(); | |
22 | + getCurrentProfileResponse.resolve({ id: 1 }); | |
23 | + | |
24 | + let getActivitiesResponse = $q.defer(); | |
25 | + getActivitiesResponse.resolve({ data: { activities: [{ id: 1 }, { id: 2 }] } }); | |
26 | + | |
27 | + profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
28 | + profileServiceMock.getActivities = jasmine.createSpy("getActivities").and.returnValue(getActivitiesResponse.promise); | |
29 | + }); | |
30 | + | |
31 | + it("get the profile activities", done => { | |
32 | + let component: ProfileInfoComponent = new ProfileInfoComponent(profileServiceMock); | |
33 | + $rootScope.$apply(); | |
34 | + expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
35 | + expect(profileServiceMock.getActivities).toHaveBeenCalled(); | |
36 | + expect(component.activities).toEqual([{ id: 1 }, { id: 2 }]); | |
37 | + done(); | |
38 | + }); | |
39 | + }); | |
40 | +}); | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
2 | +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
3 | + | |
4 | +@Component({ | |
5 | + selector: 'profile', | |
6 | + templateUrl: "app/profile-info/profile-info.html", | |
7 | + providers: [provide('profileService', { useClass: ProfileService })] | |
8 | +}) | |
9 | +@Inject(ProfileService) | |
10 | +export class ProfileInfoComponent { | |
11 | + | |
12 | + activities: any; | |
13 | + profile: noosfero.Profile; | |
14 | + | |
15 | + constructor(private profileService: ProfileService) { | |
16 | + this.activate(); | |
17 | + } | |
18 | + | |
19 | + activate() { | |
20 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
21 | + this.profile = profile; | |
22 | + return this.profileService.getActivities(<number>this.profile.id); | |
23 | + }).then((response: restangular.IResponse) => { | |
24 | + this.activities = response.data.activities; | |
25 | + }); | |
26 | + } | |
27 | +} | ... | ... |
src/app/profile/profile.component.spec.ts
1 | 1 | import {quickCreateComponent} from "../../spec/helpers"; |
2 | -import {Profile} from "./profile.component"; | |
2 | +import {ProfileComponent} from "./profile.component"; | |
3 | 3 | |
4 | 4 | describe("Components", () => { |
5 | 5 | describe("Profile Component", () => { |
... | ... | @@ -30,7 +30,7 @@ describe("Components", () => { |
30 | 30 | }); |
31 | 31 | |
32 | 32 | it("get the profile and store in profile service", done => { |
33 | - let component: Profile = new Profile(profileServiceMock, $stateParams, notificationMock); | |
33 | + let component: ProfileComponent = new ProfileComponent(profileServiceMock, $stateParams, notificationMock); | |
34 | 34 | $rootScope.$apply(); |
35 | 35 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); |
36 | 36 | expect(component.profile).toEqual({ id: 1 }); |
... | ... | @@ -38,7 +38,7 @@ describe("Components", () => { |
38 | 38 | }); |
39 | 39 | |
40 | 40 | it("get the profile boxes", done => { |
41 | - let component: Profile = new Profile(profileServiceMock, $stateParams, notificationMock); | |
41 | + let component: ProfileComponent = new ProfileComponent(profileServiceMock, $stateParams, notificationMock); | |
42 | 42 | $rootScope.$apply(); |
43 | 43 | expect(profileServiceMock.getBoxes).toHaveBeenCalled(); |
44 | 44 | expect(component.boxes).toEqual([{ id: 2 }]); |
... | ... | @@ -50,7 +50,7 @@ describe("Components", () => { |
50 | 50 | profileResponse.reject(); |
51 | 51 | profileServiceMock.setCurrentProfileByIdentifier = jasmine.createSpy("setCurrentProfileByIdentifier").and.returnValue(profileResponse.promise); |
52 | 52 | |
53 | - let component: Profile = new Profile(profileServiceMock, $stateParams, notificationMock); | |
53 | + let component: ProfileComponent = new ProfileComponent(profileServiceMock, $stateParams, notificationMock); | |
54 | 54 | $rootScope.$apply(); |
55 | 55 | |
56 | 56 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); | ... | ... |
src/app/profile/profile.component.ts
1 | 1 | import {StateConfig, Component, Inject, provide} from 'ng-forward'; |
2 | -import {ProfileInfo} from '../profile-info/profile-info.component'; | |
3 | -import {ProfileHome} from '../profile/profile-home.component'; | |
4 | -import {Cms} from '../cms/cms.component'; | |
5 | -import {ContentViewer} from "../content-viewer/content-viewer.component"; | |
6 | -import {ContentViewerActions} from "../content-viewer/content-viewer-actions.component"; | |
7 | -import {NoosferoActivities} from "../components/noosfero-activities/activities.component"; | |
2 | +import {ProfileInfoComponent} from './info/profile-info.component'; | |
3 | +import {ProfileHome} from './profile-home.component'; | |
4 | +import {BasicEditorComponent} from '../article/basic-editor.component'; | |
5 | +import {ContentViewerComponent} from "../article/content-viewer/content-viewer.component"; | |
6 | +import {ContentViewerActions} from "../article/content-viewer/content-viewer-actions.component"; | |
7 | +import {ActivitiesComponent} from "./activities/activities.component"; | |
8 | 8 | import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; |
9 | -import {Notification} from "../components/notification/notification.component"; | |
9 | +import {NotificationService} from "../shared/services/notification.service"; | |
10 | 10 | import {MyProfile} from "./myprofile.component"; |
11 | 11 | |
12 | 12 | |
... | ... | @@ -20,21 +20,21 @@ import {MyProfile} from "./myprofile.component"; |
20 | 20 | @Component({ |
21 | 21 | selector: 'profile', |
22 | 22 | templateUrl: "app/profile/profile.html", |
23 | - directives: [NoosferoActivities], | |
23 | + directives: [ActivitiesComponent], | |
24 | 24 | providers: [ |
25 | 25 | provide('profileService', { useClass: ProfileService }), |
26 | - provide('notification', { useClass: Notification }) | |
26 | + provide('notificationService', { useClass: NotificationService }) | |
27 | 27 | ] |
28 | 28 | }) |
29 | 29 | @StateConfig([ |
30 | 30 | { |
31 | 31 | name: 'main.profile.info', |
32 | 32 | url: "^/profile/:profile", |
33 | - component: ProfileInfo, | |
33 | + component: ProfileInfoComponent, | |
34 | 34 | views: { |
35 | 35 | "mainBlockContent": { |
36 | 36 | templateUrl: "app/profile-info/profile-info.html", |
37 | - controller: ProfileInfo, | |
37 | + controller: ProfileInfoComponent, | |
38 | 38 | controllerAs: "vm" |
39 | 39 | } |
40 | 40 | } |
... | ... | @@ -47,11 +47,11 @@ import {MyProfile} from "./myprofile.component"; |
47 | 47 | { |
48 | 48 | name: 'main.profile.cms', |
49 | 49 | url: "^/myprofile/:profile/cms", |
50 | - component: Cms, | |
50 | + component: BasicEditorComponent, | |
51 | 51 | views: { |
52 | 52 | "mainBlockContent": { |
53 | - templateUrl: "app/cms/cms.html", | |
54 | - controller: Cms, | |
53 | + templateUrl: "app/article/basic-editor.html", | |
54 | + controller: BasicEditorComponent, | |
55 | 55 | controllerAs: "vm" |
56 | 56 | } |
57 | 57 | } |
... | ... | @@ -70,11 +70,11 @@ import {MyProfile} from "./myprofile.component"; |
70 | 70 | { |
71 | 71 | name: 'main.profile.page', |
72 | 72 | url: "/{page:any}", |
73 | - component: ContentViewer, | |
73 | + component: ContentViewerComponent, | |
74 | 74 | views: { |
75 | 75 | "mainBlockContent": { |
76 | 76 | templateUrl: "app/content-viewer/page.html", |
77 | - controller: ContentViewer, | |
77 | + controller: ContentViewerComponent, | |
78 | 78 | controllerAs: "vm" |
79 | 79 | }, |
80 | 80 | "actions@main": { |
... | ... | @@ -86,19 +86,19 @@ import {MyProfile} from "./myprofile.component"; |
86 | 86 | } |
87 | 87 | ]) |
88 | 88 | @Inject(ProfileService, "$stateParams") |
89 | -export class Profile { | |
89 | +export class ProfileComponent { | |
90 | 90 | |
91 | 91 | boxes: noosfero.Box[]; |
92 | 92 | profile: noosfero.Profile; |
93 | 93 | |
94 | - constructor(profileService: ProfileService, $stateParams: ng.ui.IStateParamsService, notification: Notification) { | |
94 | + constructor(profileService: ProfileService, $stateParams: ng.ui.IStateParamsService, notificationService: NotificationService) { | |
95 | 95 | profileService.setCurrentProfileByIdentifier($stateParams["profile"]).then((profile: noosfero.Profile) => { |
96 | 96 | this.profile = profile; |
97 | 97 | return profileService.getBoxes(<number>this.profile.id); |
98 | 98 | }).then((response: restangular.IResponse) => { |
99 | 99 | this.boxes = response.data.boxes; |
100 | 100 | }).catch(() => { |
101 | - notification.error("notification.profile.not_found"); | |
101 | + notificationService.error("notification.profile.not_found"); | |
102 | 102 | }); |
103 | 103 | } |
104 | 104 | } | ... | ... |
src/app/profile/profile.scss
... | ... | @@ -0,0 +1 @@ |
1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
... | ... | @@ -0,0 +1,12 @@ |
1 | +export interface NoosferoRootScope extends ng.IScope { | |
2 | + currentUser: noosfero.User; | |
3 | +} | |
4 | + | |
5 | +export interface UserResponse { | |
6 | + user: noosfero.User; | |
7 | +} | |
8 | + | |
9 | + | |
10 | +export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { | |
11 | + currentUser: noosfero.User; | |
12 | +} | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +import {quickCreateComponent} from "../../../spec/helpers"; | |
2 | +import {DateFormat} from './date-format.filter'; | |
3 | + | |
4 | +describe("Filters", () => { | |
5 | + describe("Date Format Filter", () => { | |
6 | + | |
7 | + beforeEach(angular.mock.module("templates")); | |
8 | + beforeEach(angular.mock.module("angularMoment")); | |
9 | + | |
10 | + it("convert date from the format returned by noosfero api to an ISO format", done => { | |
11 | + let date = "2016/03/10 10:46:47"; | |
12 | + let htmlTemplate = `{{ '${date}' | dateFormat }}`; | |
13 | + quickCreateComponent({ providers: [DateFormat], template: htmlTemplate }).then(fixture => { | |
14 | + expect(fixture.debugElement.text()).toEqual('2016-03-10T13:46:47.000Z'); | |
15 | + done(); | |
16 | + }); | |
17 | + }); | |
18 | + | |
19 | + }); | |
20 | +}); | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +import {Pipe, Inject} from "ng-forward"; | |
2 | + | |
3 | +@Pipe("dateFormat") | |
4 | +@Inject("amParseFilter") | |
5 | +export class DateFormat { | |
6 | + | |
7 | + constructor(private amParseFilter: any) { } | |
8 | + | |
9 | + transform(date: string, options: any) { | |
10 | + return this.amParseFilter(date, "YYYY/MM/DD HH:mm:ss").toISOString(); | |
11 | + } | |
12 | + | |
13 | +} | ... | ... |
... | ... | @@ -0,0 +1,19 @@ |
1 | +import {quickCreateComponent} from "../../../spec/helpers"; | |
2 | +import {NoosferoTemplate} from './noosfero-template.filter'; | |
3 | + | |
4 | +describe("Filters", () => { | |
5 | + describe("Noosfero Template Filter", () => { | |
6 | + | |
7 | + beforeEach(angular.mock.module("templates")); | |
8 | + | |
9 | + it("replace the options in text with the values passed on options", done => { | |
10 | + let text = 'profile: {profile}, other: {other}'; | |
11 | + let htmlTemplate = `{{ '${text}' | noosferoTemplate: {profile: 'profile1', other: 'other value'} }}`; | |
12 | + quickCreateComponent({ providers: [NoosferoTemplate], template: htmlTemplate }).then(fixture => { | |
13 | + expect(fixture.debugElement.text()).toEqual("profile: profile1, other: other value"); | |
14 | + done(); | |
15 | + }); | |
16 | + }); | |
17 | + | |
18 | + }); | |
19 | +}); | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +import {Pipe} from "ng-forward"; | |
2 | + | |
3 | +@Pipe("noosferoTemplate") | |
4 | +export class NoosferoTemplate { | |
5 | + | |
6 | + transform(text: string, options: any) { | |
7 | + for (let option in options) { | |
8 | + text = text.replace('{' + option + '}', options[option]); | |
9 | + } | |
10 | + return text; | |
11 | + } | |
12 | + | |
13 | +} | ... | ... |
... | ... | @@ -0,0 +1,80 @@ |
1 | +import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | + | |
4 | +import * as helpers from "../../../spec/helpers"; | |
5 | + | |
6 | +import {NotificationService} from "./notification.service"; | |
7 | + | |
8 | +const tcb = new TestComponentBuilder(); | |
9 | + | |
10 | +describe("Components", () => { | |
11 | + | |
12 | + describe("Profile Image Component", () => { | |
13 | + | |
14 | + beforeEach(angular.mock.module("templates")); | |
15 | + | |
16 | + it("display an error message when notify an error", done => { | |
17 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
18 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
19 | + | |
20 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
21 | + component.error("message", "title"); | |
22 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
23 | + text: "message", | |
24 | + title: "title", | |
25 | + type: "error" | |
26 | + })); | |
27 | + done(); | |
28 | + }); | |
29 | + | |
30 | + it("use the default message when call notification component without a message", done => { | |
31 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
32 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
33 | + | |
34 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
35 | + component.error(); | |
36 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
37 | + text: NotificationService.DEFAULT_ERROR_MESSAGE, | |
38 | + type: "error" | |
39 | + })); | |
40 | + done(); | |
41 | + }); | |
42 | + | |
43 | + it("display a success message when call notification success", done => { | |
44 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
45 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
46 | + | |
47 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
48 | + component.success("title", "message", 1000); | |
49 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
50 | + type: "success" | |
51 | + })); | |
52 | + done(); | |
53 | + }); | |
54 | + | |
55 | + it("display a message relative to the http error code", done => { | |
56 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
57 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
58 | + | |
59 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
60 | + component.httpError(500, {}); | |
61 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
62 | + text: "notification.http_error.500.message" | |
63 | + })); | |
64 | + done(); | |
65 | + }); | |
66 | + | |
67 | + it("set the default timer in success messages", done => { | |
68 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
69 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
70 | + | |
71 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
72 | + component.success("title", "message"); | |
73 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
74 | + type: "success", | |
75 | + timer: NotificationService.DEFAULT_SUCCESS_TIMER | |
76 | + })); | |
77 | + done(); | |
78 | + }); | |
79 | + }); | |
80 | +}); | ... | ... |
... | ... | @@ -0,0 +1,41 @@ |
1 | +import {Injectable, Inject} from "ng-forward"; | |
2 | +import {TranslatorService} from "./translator.service"; | |
3 | + | |
4 | +@Injectable() | |
5 | +@Inject("$log", "SweetAlert", TranslatorService) | |
6 | +export class NotificationService { | |
7 | + | |
8 | + constructor( | |
9 | + private $log: ng.ILogService, | |
10 | + private SweetAlert: any, | |
11 | + private translatorService: TranslatorService | |
12 | + ) { } | |
13 | + | |
14 | + public static DEFAULT_ERROR_TITLE = "notification.error.default.title"; | |
15 | + public static DEFAULT_ERROR_MESSAGE = "notification.error.default.message"; | |
16 | + public static DEFAULT_SUCCESS_TIMER = 1000; | |
17 | + | |
18 | + error(message: string = NotificationService.DEFAULT_ERROR_MESSAGE, title: string = NotificationService.DEFAULT_ERROR_TITLE) { | |
19 | + this.$log.debug("Notification error:", title, message, this.translatorService.currentLanguage()); | |
20 | + this.SweetAlert.swal({ | |
21 | + title: this.translatorService.translate(title), | |
22 | + text: this.translatorService.translate(message), | |
23 | + type: "error" | |
24 | + }); | |
25 | + } | |
26 | + | |
27 | + httpError(status: number, data: any): boolean { | |
28 | + this.error(`notification.http_error.${status}.message`); | |
29 | + return true; // return true to indicate that the error was already handled | |
30 | + } | |
31 | + | |
32 | + success(title: string, text: string, timer: number = NotificationService.DEFAULT_SUCCESS_TIMER) { | |
33 | + this.SweetAlert.swal({ | |
34 | + title: title, | |
35 | + text: text, | |
36 | + type: "success", | |
37 | + timer: timer | |
38 | + }); | |
39 | + } | |
40 | + | |
41 | +} | ... | ... |
... | ... | @@ -0,0 +1,116 @@ |
1 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | +import {provide} from 'ng-forward'; | |
3 | + | |
4 | +import {TranslatorService} from './translator.service'; | |
5 | + | |
6 | +import * as helpers from "../../../spec/helpers"; | |
7 | + | |
8 | +describe("Services", () => { | |
9 | + | |
10 | + describe("Translator 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 | + function createComponent() { | |
21 | + return new TranslatorService( | |
22 | + <any>helpers.mocks.$translate, | |
23 | + <any>helpers.mocks.tmhDynamicLocale, | |
24 | + <any>helpers.mocks.amMoment, | |
25 | + <any>helpers.mocks.angularLoad, | |
26 | + $rootScope | |
27 | + ); | |
28 | + } | |
29 | + | |
30 | + it("set available languages when change language", (done) => { | |
31 | + let component: TranslatorService = createComponent(); | |
32 | + component.availableLanguages = null; | |
33 | + expect(component.availableLanguages).toBeNull(); | |
34 | + $rootScope.$emit("$translateChangeSuccess"); | |
35 | + expect(component.availableLanguages).not.toBeNull(); | |
36 | + done(); | |
37 | + }); | |
38 | + | |
39 | + it("change the language", (done) => { | |
40 | + let component: TranslatorService = createComponent(); | |
41 | + let loadScripPromise = $q.defer(); | |
42 | + loadScripPromise.resolve(); | |
43 | + component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue(loadScripPromise.promise); | |
44 | + component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
45 | + component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue("en"); | |
46 | + component["$translate"].use = jasmine.createSpy("use"); | |
47 | + | |
48 | + component.changeLanguage('pt'); | |
49 | + $rootScope.$digest(); | |
50 | + | |
51 | + expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
52 | + expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/messageformat/locale/pt.js"); | |
53 | + expect(component["tmhDynamicLocale"].set).toHaveBeenCalledWith("pt"); | |
54 | + expect(component["$translate"].use).toHaveBeenCalledWith("pt"); | |
55 | + done(); | |
56 | + }); | |
57 | + | |
58 | + it("do not load moment locale when change the language to english", (done) => { | |
59 | + let component: TranslatorService = createComponent(); | |
60 | + component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue($q.defer().promise); | |
61 | + component.changeLanguage('en'); | |
62 | + expect(component["angularLoad"].loadScript).not.toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
63 | + done(); | |
64 | + }); | |
65 | + | |
66 | + it("do nothing when call change language with null", (done) => { | |
67 | + let component: TranslatorService = createComponent(); | |
68 | + component["angularLoad"].loadScript = jasmine.createSpy("loadScript"); | |
69 | + component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
70 | + component["$translate"].use = jasmine.createSpy("use"); | |
71 | + | |
72 | + component.changeLanguage(null); | |
73 | + | |
74 | + expect(component["angularLoad"].loadScript).not.toHaveBeenCalled(); | |
75 | + expect(component["tmhDynamicLocale"].set).not.toHaveBeenCalled(); | |
76 | + expect(component["$translate"].use).not.toHaveBeenCalled(); | |
77 | + done(); | |
78 | + }); | |
79 | + | |
80 | + it("return the current language used by the translator", (done) => { | |
81 | + let component: TranslatorService = createComponent(); | |
82 | + component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
83 | + expect(component.currentLanguage()).toEqual("en"); | |
84 | + expect(component["$translate"].use).toHaveBeenCalled(); | |
85 | + done(); | |
86 | + }); | |
87 | + | |
88 | + it("call translate service when translate a text", (done) => { | |
89 | + let component: TranslatorService = createComponent(); | |
90 | + component["$translate"].instant = jasmine.createSpy("instant"); | |
91 | + component.translate("text"); | |
92 | + expect(component["$translate"].instant).toHaveBeenCalledWith("text"); | |
93 | + done(); | |
94 | + }); | |
95 | + | |
96 | + it("change the language when receive an event", (done) => { | |
97 | + let component: TranslatorService = createComponent(); | |
98 | + component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
99 | + $rootScope.$emit("$localeChangeSuccess"); | |
100 | + expect(component.changeLanguage).toHaveBeenCalled(); | |
101 | + done(); | |
102 | + }); | |
103 | + | |
104 | + it("use the translate language when receive a change language event and there is no language previously selected", (done) => { | |
105 | + let component: TranslatorService = createComponent(); | |
106 | + component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
107 | + component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue(null); | |
108 | + component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
109 | + | |
110 | + $rootScope.$emit("$localeChangeSuccess"); | |
111 | + expect(component["$translate"].use).toHaveBeenCalled(); | |
112 | + expect(component.changeLanguage).toHaveBeenCalledWith("en"); | |
113 | + done(); | |
114 | + }); | |
115 | + }); | |
116 | +}); | ... | ... |
... | ... | @@ -0,0 +1,59 @@ |
1 | +import {Injectable, Inject} from "ng-forward"; | |
2 | + | |
3 | +@Injectable() | |
4 | +@Inject("$translate", "tmhDynamicLocale", "amMoment", "angularLoad", "$rootScope") | |
5 | +export class TranslatorService { | |
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.$rootScope.$on("$localeChangeSuccess", () => { | |
16 | + this.changeLanguage(tmhDynamicLocale.get() || $translate.use()); | |
17 | + }); | |
18 | + this.$rootScope.$on("$translateChangeSuccess", () => { | |
19 | + this.configAvailableLanguages(); | |
20 | + }); | |
21 | + } | |
22 | + | |
23 | + currentLanguage() { | |
24 | + return this.$translate.use(); | |
25 | + } | |
26 | + | |
27 | + changeLanguage(language: string) { | |
28 | + if (!language) { | |
29 | + console.log("WARN: language undefined"); | |
30 | + return; | |
31 | + } | |
32 | + this.changeMomentLocale(language); | |
33 | + this.tmhDynamicLocale.set(language); | |
34 | + this.angularLoad.loadScript(`/bower_components/messageformat/locale/${language}.js`).then(() => { | |
35 | + return this.$translate.use(language); | |
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 | +} | ... | ... |