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
@@ -3,7 +3,9 @@ | @@ -3,7 +3,9 @@ | ||
3 | "files.exclude": { | 3 | "files.exclude": { |
4 | "**/.git": true, | 4 | "**/.git": true, |
5 | "**/.DS_Store": true, | 5 | "**/.DS_Store": true, |
6 | - "src/app/*.js": false | 6 | + "src/**/*.js": true, |
7 | + "src/**/*.js.map": true, | ||
8 | + "coverage": true | ||
7 | }, | 9 | }, |
8 | "editor.fontSize": 14, | 10 | "editor.fontSize": 14, |
9 | "typescript.useCodeSnippetsOnMethodSuggest": true | 11 | "typescript.useCodeSnippetsOnMethodSuggest": true |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +/* Module Index Entry - generated using the script npm run generate-index */ |
@@ -0,0 +1,108 @@ | @@ -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 | \ No newline at end of file | 109 | \ No newline at end of file |
@@ -0,0 +1,57 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -0,0 +1 @@ | ||
1 | +<noosfero-article ng-if="vm.article" [article]="vm.article" [profile]="vm.profile"></noosfero-article> |
@@ -0,0 +1,129 @@ | @@ -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 | \ No newline at end of file | 130 | \ No newline at end of file |
@@ -0,0 +1,47 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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,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,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,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,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,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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/auth/auth_service.spec.ts
@@ -1,81 +0,0 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/auth/index.ts
src/app/components/auth/login.html
@@ -1,16 +0,0 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/auth/session_spec.ts
@@ -1,49 +0,0 @@ | @@ -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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/index.ts
@@ -1 +0,0 @@ | @@ -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,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,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,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,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,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,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,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,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,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,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,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,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,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 +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,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 +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,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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-articles/article/article_view.ts
@@ -1,57 +0,0 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-articles/blog/blog.component.ts
@@ -1,47 +0,0 @@ | @@ -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,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 +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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/block.component.ts
@@ -1,20 +0,0 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/link-list/link-list.component.ts
@@ -1,20 +0,0 @@ | @@ -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,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 +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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/main-block/main-block.component.ts
@@ -1,10 +0,0 @@ | @@ -1,10 +0,0 @@ | ||
1 | -import {Component, Input} from 'ng-forward'; | ||
2 | -import {Block} from '../block.component'; | ||
3 | - | ||
4 | -@Component({ | ||
5 | - selector: 'noosfero-main-block', | ||
6 | - templateUrl: 'app/components/noosfero-blocks/main-block/main-block.html' | ||
7 | -}) | ||
8 | -export class MainBlock { | ||
9 | - | ||
10 | -} |
src/app/components/noosfero-blocks/main-block/main-block.html
@@ -1 +0,0 @@ | @@ -1 +0,0 @@ | ||
1 | -<div ui-view="mainBlockContent" autoscroll></div> |
src/app/components/noosfero-blocks/members-block/index.ts
@@ -1 +0,0 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/members-block/members-block.component.ts
@@ -1,25 +0,0 @@ | @@ -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,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,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,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,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,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,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 +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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/recent-documents/recent-documents.component.ts
@@ -1,41 +0,0 @@ | @@ -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,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,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,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,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,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 +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,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,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 +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,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,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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/components/noosfero/profile-image/profile-image.component.ts
@@ -1,47 +0,0 @@ | @@ -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,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,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,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,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,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,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,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,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,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 +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 | /** @ngInject */ | 4 | /** @ngInject */ |
5 | export function noosferoAngularRunBlock( | 5 | export function noosferoAngularRunBlock( |
6 | $log: ng.ILogService, | 6 | $log: ng.ILogService, |
7 | Restangular: restangular.IService, | 7 | Restangular: restangular.IService, |
8 | - Session: Session, | ||
9 | - Notification: Notification | 8 | + SessionService: SessionService, |
9 | + NotificationService: NotificationService | ||
10 | ) { | 10 | ) { |
11 | 11 | ||
12 | Restangular.addFullRequestInterceptor((element: any, operation: string, route: string, url: string, headers: string) => { | 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 | return <any>{ headers: <any>headers }; | 16 | return <any>{ headers: <any>headers }; |
17 | }); | 17 | }); |
18 | Restangular.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>) => { | 18 | Restangular.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>) => { |
19 | // return false to break the promise chain and don't call catch | 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,10 +5,10 @@ import {noosferoAngularRunBlock} from "./index.run"; | ||
5 | import {Main} from "./main/main.component"; | 5 | import {Main} from "./main/main.component"; |
6 | import {bootstrap, bundle} from "ng-forward"; | 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 | declare var moment: any; | 13 | declare var moment: any; |
14 | 14 |
@@ -0,0 +1,91 @@ | @@ -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 | \ No newline at end of file | 92 | \ No newline at end of file |
@@ -0,0 +1,20 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 66 | \ No newline at end of file |
@@ -0,0 +1,20 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 41 | \ No newline at end of file |
src/app/layout/blocks/main-block/main-block.component.ts
0 → 100644
@@ -0,0 +1,10 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 54 | \ No newline at end of file |
src/app/layout/blocks/members-block/members-block.component.ts
0 → 100644
@@ -0,0 +1,25 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 81 | \ No newline at end of file |
src/app/layout/blocks/recent-documents/recent-documents.component.ts
0 → 100644
@@ -0,0 +1,41 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 12 | \ No newline at end of file |
@@ -0,0 +1,33 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 70 | \ No newline at end of file |
@@ -0,0 +1,16 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 50 | \ No newline at end of file |
@@ -0,0 +1,27 @@ | @@ -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 | \ No newline at end of file | 28 | \ No newline at end of file |
src/app/main/main.component.ts
1 | import {bundle, Component, StateConfig} from "ng-forward"; | 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,7 +39,7 @@ import {MainBlock} from "../components/noosfero-blocks/main-block/main-block.com | ||
38 | @Component({ | 39 | @Component({ |
39 | selector: 'main-content', | 40 | selector: 'main-content', |
40 | templateUrl: "app/main/main.html", | 41 | templateUrl: "app/main/main.html", |
41 | - providers: [AuthService, Session] | 42 | + providers: [AuthService, SessionService] |
42 | }) | 43 | }) |
43 | export class MainContent { | 44 | export class MainContent { |
44 | 45 | ||
@@ -62,11 +63,11 @@ export class MainContent { | @@ -62,11 +63,11 @@ export class MainContent { | ||
62 | selector: 'main', | 63 | selector: 'main', |
63 | template: '<div ng-view></div>', | 64 | template: '<div ng-view></div>', |
64 | directives: [ | 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 | @StateConfig([ | 72 | @StateConfig([ |
72 | { | 73 | { |
@@ -77,12 +78,12 @@ export class MainContent { | @@ -77,12 +78,12 @@ export class MainContent { | ||
77 | { | 78 | { |
78 | url: "^/:profile", | 79 | url: "^/:profile", |
79 | abstract: true, | 80 | abstract: true, |
80 | - component: Profile, | 81 | + component: ProfileComponent, |
81 | name: 'main.profile', | 82 | name: 'main.profile', |
82 | views: { | 83 | views: { |
83 | "content": { | 84 | "content": { |
84 | templateUrl: "app/profile/profile.html", | 85 | templateUrl: "app/profile/profile.html", |
85 | - controller: Profile, | 86 | + controller: ProfileComponent, |
86 | controllerAs: "vm" | 87 | controllerAs: "vm" |
87 | } | 88 | } |
88 | } | 89 | } |
src/app/models/index.ts
src/app/models/interfaces.ts
@@ -1,12 +0,0 @@ | @@ -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,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,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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | \ No newline at end of file | 52 | \ No newline at end of file |
@@ -0,0 +1,47 @@ | @@ -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
1 | /* Module Index Entry - generated using the script npm run generate-index */ | 1 | /* Module Index Entry - generated using the script npm run generate-index */ |
2 | +export * from "./myprofile.component"; | ||
2 | export * from "./profile-home.component"; | 3 | export * from "./profile-home.component"; |
3 | export * from "./profile.component"; | 4 | export * from "./profile.component"; |
@@ -0,0 +1,40 @@ | @@ -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 @@ | @@ -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 | import {quickCreateComponent} from "../../spec/helpers"; | 1 | import {quickCreateComponent} from "../../spec/helpers"; |
2 | -import {Profile} from "./profile.component"; | 2 | +import {ProfileComponent} from "./profile.component"; |
3 | 3 | ||
4 | describe("Components", () => { | 4 | describe("Components", () => { |
5 | describe("Profile Component", () => { | 5 | describe("Profile Component", () => { |
@@ -30,7 +30,7 @@ describe("Components", () => { | @@ -30,7 +30,7 @@ describe("Components", () => { | ||
30 | }); | 30 | }); |
31 | 31 | ||
32 | it("get the profile and store in profile service", done => { | 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 | $rootScope.$apply(); | 34 | $rootScope.$apply(); |
35 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); | 35 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); |
36 | expect(component.profile).toEqual({ id: 1 }); | 36 | expect(component.profile).toEqual({ id: 1 }); |
@@ -38,7 +38,7 @@ describe("Components", () => { | @@ -38,7 +38,7 @@ describe("Components", () => { | ||
38 | }); | 38 | }); |
39 | 39 | ||
40 | it("get the profile boxes", done => { | 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 | $rootScope.$apply(); | 42 | $rootScope.$apply(); |
43 | expect(profileServiceMock.getBoxes).toHaveBeenCalled(); | 43 | expect(profileServiceMock.getBoxes).toHaveBeenCalled(); |
44 | expect(component.boxes).toEqual([{ id: 2 }]); | 44 | expect(component.boxes).toEqual([{ id: 2 }]); |
@@ -50,7 +50,7 @@ describe("Components", () => { | @@ -50,7 +50,7 @@ describe("Components", () => { | ||
50 | profileResponse.reject(); | 50 | profileResponse.reject(); |
51 | profileServiceMock.setCurrentProfileByIdentifier = jasmine.createSpy("setCurrentProfileByIdentifier").and.returnValue(profileResponse.promise); | 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 | $rootScope.$apply(); | 54 | $rootScope.$apply(); |
55 | 55 | ||
56 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); | 56 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); |
src/app/profile/profile.component.ts
1 | import {StateConfig, Component, Inject, provide} from 'ng-forward'; | 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 | import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | 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 | import {MyProfile} from "./myprofile.component"; | 10 | import {MyProfile} from "./myprofile.component"; |
11 | 11 | ||
12 | 12 | ||
@@ -20,21 +20,21 @@ import {MyProfile} from "./myprofile.component"; | @@ -20,21 +20,21 @@ import {MyProfile} from "./myprofile.component"; | ||
20 | @Component({ | 20 | @Component({ |
21 | selector: 'profile', | 21 | selector: 'profile', |
22 | templateUrl: "app/profile/profile.html", | 22 | templateUrl: "app/profile/profile.html", |
23 | - directives: [NoosferoActivities], | 23 | + directives: [ActivitiesComponent], |
24 | providers: [ | 24 | providers: [ |
25 | provide('profileService', { useClass: ProfileService }), | 25 | provide('profileService', { useClass: ProfileService }), |
26 | - provide('notification', { useClass: Notification }) | 26 | + provide('notificationService', { useClass: NotificationService }) |
27 | ] | 27 | ] |
28 | }) | 28 | }) |
29 | @StateConfig([ | 29 | @StateConfig([ |
30 | { | 30 | { |
31 | name: 'main.profile.info', | 31 | name: 'main.profile.info', |
32 | url: "^/profile/:profile", | 32 | url: "^/profile/:profile", |
33 | - component: ProfileInfo, | 33 | + component: ProfileInfoComponent, |
34 | views: { | 34 | views: { |
35 | "mainBlockContent": { | 35 | "mainBlockContent": { |
36 | templateUrl: "app/profile-info/profile-info.html", | 36 | templateUrl: "app/profile-info/profile-info.html", |
37 | - controller: ProfileInfo, | 37 | + controller: ProfileInfoComponent, |
38 | controllerAs: "vm" | 38 | controllerAs: "vm" |
39 | } | 39 | } |
40 | } | 40 | } |
@@ -47,11 +47,11 @@ import {MyProfile} from "./myprofile.component"; | @@ -47,11 +47,11 @@ import {MyProfile} from "./myprofile.component"; | ||
47 | { | 47 | { |
48 | name: 'main.profile.cms', | 48 | name: 'main.profile.cms', |
49 | url: "^/myprofile/:profile/cms", | 49 | url: "^/myprofile/:profile/cms", |
50 | - component: Cms, | 50 | + component: BasicEditorComponent, |
51 | views: { | 51 | views: { |
52 | "mainBlockContent": { | 52 | "mainBlockContent": { |
53 | - templateUrl: "app/cms/cms.html", | ||
54 | - controller: Cms, | 53 | + templateUrl: "app/article/basic-editor.html", |
54 | + controller: BasicEditorComponent, | ||
55 | controllerAs: "vm" | 55 | controllerAs: "vm" |
56 | } | 56 | } |
57 | } | 57 | } |
@@ -70,11 +70,11 @@ import {MyProfile} from "./myprofile.component"; | @@ -70,11 +70,11 @@ import {MyProfile} from "./myprofile.component"; | ||
70 | { | 70 | { |
71 | name: 'main.profile.page', | 71 | name: 'main.profile.page', |
72 | url: "/{page:any}", | 72 | url: "/{page:any}", |
73 | - component: ContentViewer, | 73 | + component: ContentViewerComponent, |
74 | views: { | 74 | views: { |
75 | "mainBlockContent": { | 75 | "mainBlockContent": { |
76 | templateUrl: "app/content-viewer/page.html", | 76 | templateUrl: "app/content-viewer/page.html", |
77 | - controller: ContentViewer, | 77 | + controller: ContentViewerComponent, |
78 | controllerAs: "vm" | 78 | controllerAs: "vm" |
79 | }, | 79 | }, |
80 | "actions@main": { | 80 | "actions@main": { |
@@ -86,19 +86,19 @@ import {MyProfile} from "./myprofile.component"; | @@ -86,19 +86,19 @@ import {MyProfile} from "./myprofile.component"; | ||
86 | } | 86 | } |
87 | ]) | 87 | ]) |
88 | @Inject(ProfileService, "$stateParams") | 88 | @Inject(ProfileService, "$stateParams") |
89 | -export class Profile { | 89 | +export class ProfileComponent { |
90 | 90 | ||
91 | boxes: noosfero.Box[]; | 91 | boxes: noosfero.Box[]; |
92 | profile: noosfero.Profile; | 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 | profileService.setCurrentProfileByIdentifier($stateParams["profile"]).then((profile: noosfero.Profile) => { | 95 | profileService.setCurrentProfileByIdentifier($stateParams["profile"]).then((profile: noosfero.Profile) => { |
96 | this.profile = profile; | 96 | this.profile = profile; |
97 | return profileService.getBoxes(<number>this.profile.id); | 97 | return profileService.getBoxes(<number>this.profile.id); |
98 | }).then((response: restangular.IResponse) => { | 98 | }).then((response: restangular.IResponse) => { |
99 | this.boxes = response.data.boxes; | 99 | this.boxes = response.data.boxes; |
100 | }).catch(() => { | 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 @@ | @@ -0,0 +1 @@ | ||
1 | +/* Module Index Entry - generated using the script npm run generate-index */ |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +/* Module Index Entry - generated using the script npm run generate-index */ |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +/* Module Index Entry - generated using the script npm run generate-index */ |
@@ -0,0 +1,12 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | +} |