Commit 7044b1ae115fc6d86408eeb89aa97a8e2b356ebc
1 parent
5ef3ec06
Exists in
master
and in
1 other branch
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 | +} | ... | ... |