Commit d14121298f31300970fed97c1216414bbcdb5037
Exists in
master
and in
1 other branch
Merge branch 'ngforward' of softwarepublico.gov.br:noosfero-themes/angular-theme into ngforward
Showing
261 changed files
with
3984 additions
and
3345 deletions
Show diff stats
.vscode/settings.json
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +let replace = require("replace"); | |
| 2 | + | |
| 3 | +import * as path from "path"; | |
| 4 | + | |
| 5 | +let wrong_jqlite_d_ts_file = path.join(__dirname, "../node_modules/ng-forward/cjs/util/jqlite-extensions.d.ts"); | |
| 6 | + | |
| 7 | +replace({ | |
| 8 | + regex: /import JQuery from \".\/\";/, | |
| 9 | + replacement: "", | |
| 10 | + paths: [wrong_jqlite_d_ts_file], | |
| 11 | + sillent: false | |
| 12 | +}); | |
| 13 | + | ... | ... |
package.json
| ... | ... | @@ -4,21 +4,23 @@ |
| 4 | 4 | "dependencies": { |
| 5 | 5 | "angular": "^1.5.0", |
| 6 | 6 | "angular-mock": "^1.0.0", |
| 7 | - "moment": "^2.11.2" | |
| 7 | + "moment": "^2.11.2", | |
| 8 | + "ng-forward": "0.0.1-alpha.12" | |
| 8 | 9 | }, |
| 9 | 10 | "scripts": { |
| 10 | 11 | "build": "webpack; gulp build", |
| 11 | 12 | "webpack": "webpack", |
| 13 | + "karma": "concurrently \"webpack -w\" \"karma start\"", | |
| 12 | 14 | "docs": "gulp ngdocs; static-server docs", |
| 13 | - "karma": "karma", | |
| 14 | 15 | "coverage": "karma start --single-run; npm run remap-coverage", |
| 15 | 16 | "remap-coverage": "ts-node --project ./dev-scripts ./dev-scripts/remapCoverage.ts", |
| 16 | 17 | "test-single": "karma start --single-run", |
| 17 | 18 | "test-and-coverage": "karma start & npm run remap-coverage", |
| 18 | 19 | "test": "webpack -w --test", |
| 19 | - "postinstall": "npm install -g bower && bower install && typings install && cd dev-scripts && typings install", | |
| 20 | + "postinstall": "npm install -g bower; bower install; typings install; npm run fix-jqlite; cd dev-scripts; typings install", | |
| 20 | 21 | "start": "concurrently \"webpack -w\" \"gulp serve\"", |
| 21 | - "generate-indexes": "ts-node --project ./dev-scripts ./dev-scripts/generate-index-modules.ts" | |
| 22 | + "generate-indexes": "ts-node --project ./dev-scripts ./dev-scripts/generate-index-modules.ts", | |
| 23 | + "fix-jqlite": "ts-node --project ./dev-scripts dev-scripts/fix-jqlite.ts" | |
| 22 | 24 | }, |
| 23 | 25 | "devDependencies": { |
| 24 | 26 | "browser-sync": "~2.9.11", |
| ... | ... | @@ -71,7 +73,6 @@ |
| 71 | 73 | "karma-webpack": "^1.7.0", |
| 72 | 74 | "lodash": "~3.10.1", |
| 73 | 75 | "main-bower-files": "~2.9.0", |
| 74 | - "ng-forward": "0.0.1-alpha.12", | |
| 75 | 76 | "on-build-webpack": "^0.1.0", |
| 76 | 77 | "phantomjs": "~1.9.18", |
| 77 | 78 | "phantomjs-polyfill": "0.0.2", | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
| ... | ... | @@ -0,0 +1,108 @@ |
| 1 | + | |
| 2 | +import {Input, provide, Component} from 'ng-forward'; | |
| 3 | +import {ArticleViewComponent, ArticleDefaultViewComponent} from './article-default-view.component'; | |
| 4 | + | |
| 5 | +import {createComponentFromClass, quickCreateComponent} from "../../spec/helpers"; | |
| 6 | + | |
| 7 | +// this htmlTemplate will be re-used between the container components in this spec file | |
| 8 | +const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-article>'; | |
| 9 | + | |
| 10 | + | |
| 11 | +describe("Components", () => { | |
| 12 | + | |
| 13 | + describe("ArticleView Component", () => { | |
| 14 | + | |
| 15 | + // the karma preprocessor html2js transform the templates html into js files which put | |
| 16 | + // the templates to the templateCache into the module templates | |
| 17 | + // we need to load the module templates here as the template for the | |
| 18 | + // component Noosfero ArtileView will be load on our tests | |
| 19 | + beforeEach(angular.mock.module("templates")); | |
| 20 | + | |
| 21 | + it("renders the default component when no specific component is found", (done: Function) => { | |
| 22 | + // Creating a container component (ArticleContainerComponent) to include | |
| 23 | + // the component under test (ArticleView) | |
| 24 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent] }) | |
| 25 | + class ArticleContainerComponent { | |
| 26 | + article = { type: 'anyArticleType' }; | |
| 27 | + profile = { name: 'profile-name' }; | |
| 28 | + constructor() { | |
| 29 | + } | |
| 30 | + } | |
| 31 | + | |
| 32 | + createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
| 33 | + // and here we can inspect and run the test assertions | |
| 34 | + | |
| 35 | + // gets the children component of ArticleContainerComponent | |
| 36 | + let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 37 | + | |
| 38 | + // and checks if the article View rendered was the Default Article View | |
| 39 | + expect(articleView.constructor.prototype).toEqual(ArticleDefaultViewComponent.prototype); | |
| 40 | + | |
| 41 | + // done needs to be called (it isn't really needed, as we can read in | |
| 42 | + // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
| 43 | + // because createAsync in ng-forward is not really async, but as the intention | |
| 44 | + // here is write tests in angular 2 ways, this is recommended | |
| 45 | + done(); | |
| 46 | + }); | |
| 47 | + | |
| 48 | + }); | |
| 49 | + | |
| 50 | + it("receives the article and profile as inputs", (done: Function) => { | |
| 51 | + | |
| 52 | + // Creating a container component (ArticleContainerComponent) to include | |
| 53 | + // the component under test (ArticleView) | |
| 54 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent] }) | |
| 55 | + class ArticleContainerComponent { | |
| 56 | + article = { type: 'anyArticleType' }; | |
| 57 | + profile = { name: 'profile-name' }; | |
| 58 | + constructor() { | |
| 59 | + } | |
| 60 | + } | |
| 61 | + | |
| 62 | + // uses the TestComponentBuilder instance to initialize the component | |
| 63 | + createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
| 64 | + // and here we can inspect and run the test assertions | |
| 65 | + let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 66 | + | |
| 67 | + // assure the article object inside the ArticleView matches | |
| 68 | + // the provided through the parent component | |
| 69 | + expect(articleView.article.type).toEqual("anyArticleType"); | |
| 70 | + expect(articleView.profile.name).toEqual("profile-name"); | |
| 71 | + | |
| 72 | + // done needs to be called (it isn't really needed, as we can read in | |
| 73 | + // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
| 74 | + // because createAsync in ng-forward is not really async, but as the intention | |
| 75 | + // here is write tests in angular 2 ways, this is recommended | |
| 76 | + done(); | |
| 77 | + }); | |
| 78 | + }); | |
| 79 | + | |
| 80 | + | |
| 81 | + it("renders a article view which matches to the article type", done => { | |
| 82 | + // NoosferoTinyMceArticle component created to check if it will be used | |
| 83 | + // when a article with type 'TinyMceArticle' is provided to the noosfero-article (ArticleView) | |
| 84 | + // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
| 85 | + @Component({ selector: 'noosfero-tiny-mce-article', template: "<h1>TinyMceArticle</h1>" }) | |
| 86 | + class TinyMceArticleView { | |
| 87 | + @Input() article: any; | |
| 88 | + @Input() profile: any; | |
| 89 | + } | |
| 90 | + | |
| 91 | + // Creating a container component (ArticleContainerComponent) to include our NoosferoTinyMceArticle | |
| 92 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent, TinyMceArticleView] }) | |
| 93 | + class CustomArticleType { | |
| 94 | + article = { type: 'TinyMceArticle' }; | |
| 95 | + profile = { name: 'profile-name' }; | |
| 96 | + constructor() { | |
| 97 | + } | |
| 98 | + } | |
| 99 | + createComponentFromClass(CustomArticleType).then(fixture => { | |
| 100 | + let myComponent: CustomArticleType = fixture.componentInstance; | |
| 101 | + expect(myComponent.article.type).toEqual("TinyMceArticle"); | |
| 102 | + expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("TinyMceArticle"); | |
| 103 | + done(); | |
| 104 | + }); | |
| 105 | + }); | |
| 106 | + | |
| 107 | + }); | |
| 108 | +}); | |
| 0 | 109 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,57 @@ |
| 1 | +import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; | |
| 2 | +import {ArticleBlogComponent} from "./types/blog/blog.component"; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * @ngdoc controller | |
| 6 | + * @name ArticleDefaultView | |
| 7 | + * @description | |
| 8 | + * A default view for Noosfero Articles. If the specific article view is | |
| 9 | + * not implemented, then this view is used. | |
| 10 | + */ | |
| 11 | +@Component({ | |
| 12 | + selector: 'noosfero-default-article', | |
| 13 | + templateUrl: 'app/article/article.html' | |
| 14 | +}) | |
| 15 | +export class ArticleDefaultViewComponent { | |
| 16 | + | |
| 17 | + @Input() article: noosfero.Article; | |
| 18 | + @Input() profile: noosfero.Profile; | |
| 19 | + | |
| 20 | +} | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * @ngdoc controller | |
| 24 | + * @name ArticleView | |
| 25 | + * @description | |
| 26 | + * A dynamic view for articles. It uses the article type to replace | |
| 27 | + * the default template with the custom article directive. | |
| 28 | + */ | |
| 29 | +@Component({ | |
| 30 | + selector: 'noosfero-article', | |
| 31 | + template: 'not-used', | |
| 32 | + directives: [ArticleDefaultViewComponent, ArticleBlogComponent] | |
| 33 | +}) | |
| 34 | +@Inject("$element", "$scope", "$injector", "$compile") | |
| 35 | +export class ArticleViewComponent { | |
| 36 | + | |
| 37 | + @Input() article: noosfero.Article; | |
| 38 | + @Input() profile: noosfero.Profile; | |
| 39 | + directiveName: string; | |
| 40 | + | |
| 41 | + ngOnInit() { | |
| 42 | + let specificDirective = 'noosfero' + this.article.type; | |
| 43 | + this.directiveName = "noosfero-default-article"; | |
| 44 | + if (this.$injector.has(specificDirective + 'Directive')) { | |
| 45 | + this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
| 46 | + } | |
| 47 | + this.$element.replaceWith(this.$compile('<' + this.directiveName + ' [article]="ctrl.article" [profile]="ctrl.profile"></' + this.directiveName + '>')(this.$scope)); | |
| 48 | + } | |
| 49 | + | |
| 50 | + constructor( | |
| 51 | + private $element: any, | |
| 52 | + private $scope: ng.IScope, | |
| 53 | + private $injector: ng.auto.IInjectorService, | |
| 54 | + private $compile: ng.ICompileService) { | |
| 55 | + | |
| 56 | + } | |
| 57 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | +<div class="article"> | |
| 2 | + <div class="page-header"> | |
| 3 | + <h3 ng-bind="ctrl.article.title"></h3> | |
| 4 | + </div> | |
| 5 | + | |
| 6 | + <div class="sub-header clearfix"> | |
| 7 | + <div class="page-info pull-right small text-muted"> | |
| 8 | + <span class="time"> | |
| 9 | + <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> | |
| 10 | + </span> | |
| 11 | + <span class="author" ng-if="ctrl.article.author"> | |
| 12 | + <i class="fa fa-user"></i> | |
| 13 | + <a ui-sref="main.profile.home({profile: ctrl.article.author.identifier})"> | |
| 14 | + <span class="author-name" ng-bind="ctrl.article.author.name"></span> | |
| 15 | + </a> | |
| 16 | + </span> | |
| 17 | + </div> | |
| 18 | + </div> | |
| 19 | + | |
| 20 | + <div class="page-body"> | |
| 21 | + <div ng-bind-html="ctrl.article.body"></div> | |
| 22 | + </div> | |
| 23 | +</div> | ... | ... |
| ... | ... | @@ -0,0 +1,55 @@ |
| 1 | +import {quickCreateComponent} from "../../spec/helpers"; | |
| 2 | +import {BasicEditorComponent} from "./basic-editor.component"; | |
| 3 | + | |
| 4 | + | |
| 5 | +describe("Article BasicEditor", () => { | |
| 6 | + | |
| 7 | + let $rootScope: ng.IRootScopeService; | |
| 8 | + let $q: ng.IQService; | |
| 9 | + let articleServiceMock: any; | |
| 10 | + let profileServiceMock: any; | |
| 11 | + let $state: any; | |
| 12 | + let profile = { id: 1 }; | |
| 13 | + let notification: any; | |
| 14 | + | |
| 15 | + | |
| 16 | + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
| 17 | + $rootScope = _$rootScope_; | |
| 18 | + $q = _$q_; | |
| 19 | + })); | |
| 20 | + | |
| 21 | + beforeEach(() => { | |
| 22 | + $state = jasmine.createSpyObj("$state", ["transitionTo"]); | |
| 23 | + notification = jasmine.createSpyObj("notification", ["success"]); | |
| 24 | + profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); | |
| 25 | + articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInProfile"]); | |
| 26 | + | |
| 27 | + let getCurrentProfileResponse = $q.defer(); | |
| 28 | + getCurrentProfileResponse.resolve(profile); | |
| 29 | + | |
| 30 | + let articleCreate = $q.defer(); | |
| 31 | + articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } }); | |
| 32 | + | |
| 33 | + profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
| 34 | + articleServiceMock.createInProfile = jasmine.createSpy("createInProfile").and.returnValue(articleCreate.promise); | |
| 35 | + }); | |
| 36 | + | |
| 37 | + it("create an article in the current profile when save", done => { | |
| 38 | + let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | |
| 39 | + component.save(); | |
| 40 | + $rootScope.$apply(); | |
| 41 | + expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
| 42 | + expect(articleServiceMock.createInProfile).toHaveBeenCalledWith(profile, component.article); | |
| 43 | + done(); | |
| 44 | + }); | |
| 45 | + | |
| 46 | + it("got to the new article page and display an alert when saving sucessfully", done => { | |
| 47 | + let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | |
| 48 | + component.save(); | |
| 49 | + $rootScope.$apply(); | |
| 50 | + expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | |
| 51 | + expect(notification.success).toHaveBeenCalled(); | |
| 52 | + done(); | |
| 53 | + }); | |
| 54 | + | |
| 55 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,35 @@ |
| 1 | +import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
| 2 | +import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
| 3 | +import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
| 4 | +import {NotificationService} from "../shared/services/notification.service.ts"; | |
| 5 | + | |
| 6 | +@Component({ | |
| 7 | + selector: 'article-basic-editor', | |
| 8 | + templateUrl: "app/article/basic-editor.html", | |
| 9 | + providers: [ | |
| 10 | + provide('articleService', { useClass: ArticleService }), | |
| 11 | + provide('profileService', { useClass: ProfileService }), | |
| 12 | + provide('notification', { useClass: NotificationService }) | |
| 13 | + ] | |
| 14 | +}) | |
| 15 | +@Inject(ArticleService, ProfileService, "$state", NotificationService) | |
| 16 | +export class BasicEditorComponent { | |
| 17 | + | |
| 18 | + article: noosfero.Article = <noosfero.Article>{}; | |
| 19 | + | |
| 20 | + constructor(private articleService: ArticleService, | |
| 21 | + private profileService: ProfileService, | |
| 22 | + private $state: ng.ui.IStateService, | |
| 23 | + private notification: NotificationService) { } | |
| 24 | + | |
| 25 | + save() { | |
| 26 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 27 | + return this.articleService.createInProfile(profile, this.article); | |
| 28 | + }).then((response: noosfero.RestResult<noosfero.Article>) => { | |
| 29 | + let article = (<noosfero.Article>response.data); | |
| 30 | + this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); | |
| 31 | + this.notification.success("Good job!", "Article saved!"); | |
| 32 | + }); | |
| 33 | + } | |
| 34 | + | |
| 35 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | +<form> | |
| 2 | + <div class="form-group"> | |
| 3 | + <label for="titleInput">Title</label> | |
| 4 | + <input type="text" class="form-control" id="titleInput" placeholder="title" ng-model="vm.article.name"> | |
| 5 | + </div> | |
| 6 | + <div class="form-group"> | |
| 7 | + <label for="bodyInput">Text</label> | |
| 8 | + <textarea class="form-control" id="bodyInput" rows="10" ng-model="vm.article.body"></textarea> | |
| 9 | + </div> | |
| 10 | + <button type="submit" class="btn btn-default" ng-click="vm.save()">Save</button> | |
| 11 | +</form> | ... | ... |
src/app/article/content-viewer/content-viewer-actions.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,67 @@ |
| 1 | +import {providers} from 'ng-forward/cjs/testing/providers'; | |
| 2 | + | |
| 3 | +import {Input, Component, provide} from 'ng-forward'; | |
| 4 | + | |
| 5 | +import * as helpers from "../../../spec/helpers"; | |
| 6 | + | |
| 7 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 8 | +import {ContentViewerActions} from './content-viewer-actions.component'; | |
| 9 | + | |
| 10 | +// this htmlTemplate will be re-used between the container components in this spec file | |
| 11 | +const htmlTemplate: string = '<content-viewer-actions [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer-actions>'; | |
| 12 | + | |
| 13 | +describe('Content Viewer Actions Component', () => { | |
| 14 | + | |
| 15 | + beforeEach(() => { | |
| 16 | + | |
| 17 | + angular.mock.module("templates"); | |
| 18 | + | |
| 19 | + providers((provide: any) => { | |
| 20 | + return <any>[ | |
| 21 | + provide('ProfileService', { | |
| 22 | + useValue: helpers.mocks.profileService | |
| 23 | + }) | |
| 24 | + ]; | |
| 25 | + }); | |
| 26 | + }); | |
| 27 | + | |
| 28 | + let buildComponent = (): Promise<ComponentFixture> => { | |
| 29 | + return helpers.quickCreateComponent({ | |
| 30 | + providers: [ | |
| 31 | + helpers.provideEmptyObjects('Restangular'), | |
| 32 | + helpers.provideFilters('translateFilter') | |
| 33 | + ], | |
| 34 | + directives: [ContentViewerActions], | |
| 35 | + template: htmlTemplate | |
| 36 | + }); | |
| 37 | + }; | |
| 38 | + | |
| 39 | + it('renders content viewer actions directive', (done: Function) => { | |
| 40 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 41 | + expect(fixture.debugElement.query('content-viewer-actions').length).toEqual(1); | |
| 42 | + | |
| 43 | + done(); | |
| 44 | + }); | |
| 45 | + }); | |
| 46 | + | |
| 47 | + it('check if profile was loaded', (done: Function) => { | |
| 48 | + let profile: any = { | |
| 49 | + id: 1, | |
| 50 | + identifier: 'the-profile-test', | |
| 51 | + type: 'Person' | |
| 52 | + }; | |
| 53 | + | |
| 54 | + helpers.mocks.profileService.getCurrentProfile = () => { | |
| 55 | + return helpers.mocks.promiseResultTemplate(profile); | |
| 56 | + }; | |
| 57 | + | |
| 58 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 59 | + let contentViewerComp: ContentViewerActions = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 60 | + | |
| 61 | + expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile)); | |
| 62 | + | |
| 63 | + done(); | |
| 64 | + }); | |
| 65 | + }); | |
| 66 | + | |
| 67 | +}); | ... | ... |
src/app/article/content-viewer/content-viewer-actions.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +import {Component, Inject, provide} from "ng-forward"; | |
| 2 | +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: "content-viewer-actions", | |
| 6 | + templateUrl: "app/article/content-viewer/navbar-actions.html", | |
| 7 | + providers: [provide('profileService', { useClass: ProfileService })] | |
| 8 | +}) | |
| 9 | +@Inject(ProfileService) | |
| 10 | +export class ContentViewerActions { | |
| 11 | + | |
| 12 | + article: noosfero.Article; | |
| 13 | + profile: noosfero.Profile; | |
| 14 | + | |
| 15 | + constructor(profileService: ProfileService) { | |
| 16 | + profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 17 | + this.profile = profile; | |
| 18 | + }); | |
| 19 | + } | |
| 20 | +} | ... | ... |
src/app/article/content-viewer/content-viewer.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,88 @@ |
| 1 | +import {providers} from 'ng-forward/cjs/testing/providers'; | |
| 2 | + | |
| 3 | +import {Input, Component, provide} from 'ng-forward'; | |
| 4 | + | |
| 5 | +import * as helpers from "../../../spec/helpers"; | |
| 6 | + | |
| 7 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 8 | +import {ContentViewerComponent} from './content-viewer.component'; | |
| 9 | + | |
| 10 | +// this htmlTemplate will be re-used between the container components in this spec file | |
| 11 | +const htmlTemplate: string = '<content-viewer [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer>'; | |
| 12 | + | |
| 13 | +describe('Content Viewer Component', () => { | |
| 14 | + | |
| 15 | + let stateParamsService: any; | |
| 16 | + | |
| 17 | + // loading the templates | |
| 18 | + beforeEach(() => { | |
| 19 | + angular.mock.module("templates"); | |
| 20 | + | |
| 21 | + stateParamsService = { page: 1 }; | |
| 22 | + | |
| 23 | + providers((provide: any) => { | |
| 24 | + return <any>[ | |
| 25 | + provide('ArticleService', { | |
| 26 | + useValue: helpers.mocks.articleService | |
| 27 | + }), | |
| 28 | + provide('ProfileService', { | |
| 29 | + useValue: helpers.mocks.profileService | |
| 30 | + }), | |
| 31 | + // TODO: Como criar um mock do atributo "page" de stateParams | |
| 32 | + provide('$stateParams', { | |
| 33 | + useValue: stateParamsService | |
| 34 | + }) | |
| 35 | + ]; | |
| 36 | + }); | |
| 37 | + }); | |
| 38 | + | |
| 39 | + let buildComponent = (): Promise<ComponentFixture> => { | |
| 40 | + return helpers.quickCreateComponent({ | |
| 41 | + providers: [ | |
| 42 | + helpers.provideEmptyObjects('Restangular') | |
| 43 | + ], | |
| 44 | + directives: [ContentViewerComponent], | |
| 45 | + template: htmlTemplate | |
| 46 | + }); | |
| 47 | + }; | |
| 48 | + | |
| 49 | + it('renders content viewer directive', (done: Function) => { | |
| 50 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 51 | + expect(fixture.debugElement.query('content-viewer').length).toEqual(1); | |
| 52 | + | |
| 53 | + done(); | |
| 54 | + }); | |
| 55 | + }); | |
| 56 | + | |
| 57 | + it('check if article was loaded', (done: Function) => { | |
| 58 | + let article: any = { | |
| 59 | + id: 1, | |
| 60 | + title: 'The article test' | |
| 61 | + }; | |
| 62 | + let profile: any = { | |
| 63 | + id: 1, | |
| 64 | + identifier: 'the-profile-test', | |
| 65 | + type: 'Person' | |
| 66 | + }; | |
| 67 | + | |
| 68 | + helpers.mocks.profileService.getCurrentProfile = () => { | |
| 69 | + return helpers.mocks.promiseResultTemplate(profile); | |
| 70 | + }; | |
| 71 | + | |
| 72 | + helpers.mocks.articleService.getArticleByProfileAndPath = (profile: noosfero.Profile, path: string) => { | |
| 73 | + return helpers.mocks.promiseResultTemplate({ | |
| 74 | + data: article | |
| 75 | + }); | |
| 76 | + }; | |
| 77 | + | |
| 78 | + | |
| 79 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 80 | + let contentViewerComp: ContentViewerComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 81 | + | |
| 82 | + expect(contentViewerComp.profile).toEqual(profile); | |
| 83 | + expect(contentViewerComp.article).toEqual(article); | |
| 84 | + | |
| 85 | + done(); | |
| 86 | + }); | |
| 87 | + }); | |
| 88 | +}); | ... | ... |
src/app/article/content-viewer/content-viewer.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | +import {ArticleViewComponent} from "./../article-default-view.component"; | |
| 2 | +import {Input, Component, StateConfig, Inject, provide} from "ng-forward"; | |
| 3 | + | |
| 4 | +import {ArticleBlogComponent} from "./../types/blog/blog.component"; | |
| 5 | +import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service"; | |
| 6 | +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
| 7 | + | |
| 8 | +@Component({ | |
| 9 | + selector: "content-viewer", | |
| 10 | + templateUrl: "app/article/content-viewer/page.html", | |
| 11 | + directives: [ArticleBlogComponent, ArticleViewComponent], | |
| 12 | + providers: [ | |
| 13 | + provide('articleService', { useClass: ArticleService }), | |
| 14 | + provide('profileService', { useClass: ProfileService }) | |
| 15 | + ] | |
| 16 | +}) | |
| 17 | +@Inject(ArticleService, ProfileService, "$log", "$stateParams") | |
| 18 | +export class ContentViewerComponent { | |
| 19 | + | |
| 20 | + @Input() | |
| 21 | + article: noosfero.Article = null; | |
| 22 | + | |
| 23 | + @Input() | |
| 24 | + profile: noosfero.Profile = null; | |
| 25 | + | |
| 26 | + constructor(private articleService: ArticleService, private profileService: ProfileService, private $log: ng.ILogService, private $stateParams: angular.ui.IStateParamsService) { | |
| 27 | + this.activate(); | |
| 28 | + } | |
| 29 | + | |
| 30 | + activate() { | |
| 31 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 32 | + this.profile = profile; | |
| 33 | + return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]); | |
| 34 | + }).then((result: noosfero.RestResult<any>) => { | |
| 35 | + this.article = <noosfero.Article>result.data; | |
| 36 | + }); | |
| 37 | + } | |
| 38 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | +<ul class="nav navbar-nav"> | |
| 2 | + <li ng-show="vm.profile"> | |
| 3 | + <a href="#" role="button" ui-sref="main.profile.cms({profile: vm.profile.identifier})"> | |
| 4 | + <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | |
| 5 | + </a> | |
| 6 | + </li> | |
| 7 | +</ul> | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +<noosfero-article ng-if="vm.article" [article]="vm.article" [profile]="vm.profile"></noosfero-article> | ... | ... |
| ... | ... | @@ -0,0 +1,129 @@ |
| 1 | +import { | |
| 2 | +providers | |
| 3 | +} from 'ng-forward/cjs/testing/providers'; | |
| 4 | + | |
| 5 | +import { | |
| 6 | +Input, | |
| 7 | +Component | |
| 8 | +} from 'ng-forward'; | |
| 9 | +import { | |
| 10 | +ArticleBlogComponent | |
| 11 | +} from './blog.component'; | |
| 12 | + | |
| 13 | +import { | |
| 14 | +createComponentFromClass, | |
| 15 | +quickCreateComponent, | |
| 16 | +provideEmptyObjects, | |
| 17 | +createProviderToValue, | |
| 18 | +provideFilters | |
| 19 | +} from "../../../../spec/helpers.ts"; | |
| 20 | + | |
| 21 | +// this htmlTemplate will be re-used between the container components in this spec file | |
| 22 | +const htmlTemplate: string = '<noosfero-blog [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-blog>'; | |
| 23 | + | |
| 24 | +describe("Blog Component", () => { | |
| 25 | + | |
| 26 | + function promiseResultTemplate(response?: {}) { | |
| 27 | + let thenFuncEmpty = (func: Function) => { | |
| 28 | + // does nothing | |
| 29 | + }; | |
| 30 | + if (response) { | |
| 31 | + return { | |
| 32 | + then: (func: (response: any) => void) => { | |
| 33 | + func(response); | |
| 34 | + } | |
| 35 | + }; | |
| 36 | + } else { | |
| 37 | + return { | |
| 38 | + then: (func: (response: any) => void) => { | |
| 39 | + // does nothing | |
| 40 | + } | |
| 41 | + }; | |
| 42 | + } | |
| 43 | + } | |
| 44 | + | |
| 45 | + let articleService = { | |
| 46 | + getChildren: (article_id: number, filters: {}) => { | |
| 47 | + return promiseResultTemplate(null); | |
| 48 | + } | |
| 49 | + }; | |
| 50 | + | |
| 51 | + @Component({ | |
| 52 | + selector: 'test-container-component', | |
| 53 | + template: htmlTemplate, | |
| 54 | + directives: [ArticleBlogComponent], | |
| 55 | + providers: [ | |
| 56 | + provideEmptyObjects('Restangular'), | |
| 57 | + createProviderToValue('ArticleService', articleService), | |
| 58 | + provideFilters('truncateFilter') | |
| 59 | + ] | |
| 60 | + }) | |
| 61 | + class BlogContainerComponent { | |
| 62 | + article = { | |
| 63 | + type: 'anyArticleType' | |
| 64 | + }; | |
| 65 | + profile = { | |
| 66 | + name: 'profile-name' | |
| 67 | + }; | |
| 68 | + } | |
| 69 | + | |
| 70 | + beforeEach(() => { | |
| 71 | + | |
| 72 | + // the karma preprocessor html2js transform the templates html into js files which put | |
| 73 | + // the templates to the templateCache into the module templates | |
| 74 | + // we need to load the module templates here as the template for the | |
| 75 | + // component Noosfero ArtileView will be load on our tests | |
| 76 | + angular.mock.module("templates"); | |
| 77 | + | |
| 78 | + providers((provide: any) => { | |
| 79 | + return <any>[ | |
| 80 | + provide('ArticleService', { | |
| 81 | + useValue: articleService | |
| 82 | + }) | |
| 83 | + ]; | |
| 84 | + }); | |
| 85 | + }); | |
| 86 | + | |
| 87 | + it("renders the blog content", (done: Function) => { | |
| 88 | + | |
| 89 | + createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
| 90 | + | |
| 91 | + expect(fixture.debugElement.query('div.blog').length).toEqual(1); | |
| 92 | + | |
| 93 | + done(); | |
| 94 | + }); | |
| 95 | + }); | |
| 96 | + | |
| 97 | + it("verify the blog data", (done: Function) => { | |
| 98 | + | |
| 99 | + let articles = [{ | |
| 100 | + id: 1, | |
| 101 | + title: 'The article test' | |
| 102 | + }]; | |
| 103 | + | |
| 104 | + let result = { data: articles, headers: (name: string) => { return 1; } }; | |
| 105 | + | |
| 106 | + // defining a mock result to articleService.getChildren method | |
| 107 | + articleService.getChildren = (article_id: number, filters: {}) => { | |
| 108 | + return promiseResultTemplate(result); | |
| 109 | + }; | |
| 110 | + | |
| 111 | + createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
| 112 | + | |
| 113 | + // gets the children component of BlogContainerComponent | |
| 114 | + let articleBlog: BlogContainerComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 115 | + | |
| 116 | + // check if the component property are the provided by the mocked articleService | |
| 117 | + let post = { | |
| 118 | + id: 1, | |
| 119 | + title: 'The article test' | |
| 120 | + }; | |
| 121 | + expect((<any>articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post)); | |
| 122 | + expect((<any>articleBlog)["totalPosts"]).toEqual(1); | |
| 123 | + | |
| 124 | + done(); | |
| 125 | + }); | |
| 126 | + | |
| 127 | + }); | |
| 128 | + | |
| 129 | +}); | |
| 0 | 130 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | +import {Component, Input, Inject} from "ng-forward"; | |
| 2 | + | |
| 3 | +import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * @ngdoc controller | |
| 7 | + * @name ArticleBlog | |
| 8 | + * @description | |
| 9 | + * An specific {@link ArticleView} for Blog articles. | |
| 10 | + */ | |
| 11 | +@Component({ | |
| 12 | + selector: "noosfero-blog", | |
| 13 | + templateUrl: "app/article/types/blog/blog.html" | |
| 14 | +}) | |
| 15 | +@Inject(ArticleService) | |
| 16 | +export class ArticleBlogComponent { | |
| 17 | + | |
| 18 | + @Input() article: noosfero.Article; | |
| 19 | + @Input() profile: noosfero.Profile; | |
| 20 | + | |
| 21 | + private posts: noosfero.Article[]; | |
| 22 | + private perPage: number = 3; | |
| 23 | + private currentPage: number; | |
| 24 | + private totalPosts: number = 0; | |
| 25 | + | |
| 26 | + constructor(private articleService: ArticleService) { } | |
| 27 | + | |
| 28 | + ngOnInit() { | |
| 29 | + this.loadPage(); | |
| 30 | + } | |
| 31 | + | |
| 32 | + loadPage() { | |
| 33 | + let filters = { | |
| 34 | + content_type: "TinyMceArticle", | |
| 35 | + per_page: this.perPage, | |
| 36 | + page: this.currentPage | |
| 37 | + }; | |
| 38 | + | |
| 39 | + this.articleService | |
| 40 | + .getChildren(this.article, filters) | |
| 41 | + .then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 42 | + this.totalPosts = <number>result.headers("total"); | |
| 43 | + this.posts = result.data; | |
| 44 | + }); | |
| 45 | + } | |
| 46 | + | |
| 47 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | +<div class="blog"> | |
| 2 | + <div class="blog-cover" ng-show="ctrl.article.image"> | |
| 3 | + <img ng-src="{{ctrl.article.image.url}}" class="img-responsive"> | |
| 4 | + <h3 ng-bind="ctrl.article.title"></h3> | |
| 5 | + </div> | |
| 6 | + | |
| 7 | + <div class="page-header" ng-show="!ctrl.article.image"> | |
| 8 | + <h3 ng-bind="ctrl.article.title"></h3> | |
| 9 | + </div> | |
| 10 | + | |
| 11 | + <div> | |
| 12 | + <div ng-repeat="child in ctrl.posts | orderBy: 'created_at':true"> | |
| 13 | + <div class="page-header"> | |
| 14 | + <a class="title" ui-sref="main.profile.page({profile: ctrl.profile.identifier, page: child.path})"><h4 ng-bind="child.title"></h4></a> | |
| 15 | + <div class="post-lead" ng-bind-html="child.body | truncate: 500: '...': true"></div> | |
| 16 | + </div> | |
| 17 | + </div> | |
| 18 | + </div> | |
| 19 | + | |
| 20 | + <pagination ng-model="ctrl.currentPage" total-items="ctrl.totalPosts" class="pagination-sm center-block" | |
| 21 | + boundary-links="true" items-per-page="ctrl.perPage" ng-change="ctrl.loadPage()" | |
| 22 | + first-text="«" last-text="»" previous-text="‹" next-text="›"> | |
| 23 | + </pagination> | |
| 24 | +</div> | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
src/app/cms/cms.component.spec.ts
| ... | ... | @@ -1,54 +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 notification: any; | |
| 13 | - | |
| 14 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
| 15 | - $rootScope = _$rootScope_; | |
| 16 | - $q = _$q_; | |
| 17 | - })); | |
| 18 | - | |
| 19 | - beforeEach(() => { | |
| 20 | - $state = jasmine.createSpyObj("$state", ["transitionTo"]); | |
| 21 | - notification = jasmine.createSpyObj("notification", ["success"]); | |
| 22 | - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); | |
| 23 | - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["create"]); | |
| 24 | - | |
| 25 | - let getCurrentProfileResponse = $q.defer(); | |
| 26 | - getCurrentProfileResponse.resolve({ id: 1 }); | |
| 27 | - | |
| 28 | - let articleCreate = $q.defer(); | |
| 29 | - articleCreate.resolve({ data: { article: { path: "path", profile: { identifier: "profile" } } } }); | |
| 30 | - | |
| 31 | - profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
| 32 | - articleServiceMock.create = jasmine.createSpy("create").and.returnValue(articleCreate.promise); | |
| 33 | - }); | |
| 34 | - | |
| 35 | - it("create an article in the current profile when save", done => { | |
| 36 | - let component: Cms = new Cms(articleServiceMock, profileServiceMock, $state, notification); | |
| 37 | - component.save(); | |
| 38 | - $rootScope.$apply(); | |
| 39 | - expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
| 40 | - expect(articleServiceMock.create).toHaveBeenCalledWith(1, component.article); | |
| 41 | - done(); | |
| 42 | - }); | |
| 43 | - | |
| 44 | - it("got to the new article page and display an alert when saving sucessfully", done => { | |
| 45 | - let component: Cms = new Cms(articleServiceMock, profileServiceMock, $state, notification); | |
| 46 | - component.save(); | |
| 47 | - $rootScope.$apply(); | |
| 48 | - expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | |
| 49 | - expect(notification.success).toHaveBeenCalled(); | |
| 50 | - done(); | |
| 51 | - }); | |
| 52 | - | |
| 53 | - }); | |
| 54 | -}); |
src/app/cms/cms.component.ts
| ... | ... | @@ -1,35 +0,0 @@ |
| 1 | -import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
| 2 | -import {Profile} from "./../models/interfaces"; | |
| 3 | -import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
| 4 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
| 5 | -import {Notification} from "../components/notification/notification.component"; | |
| 6 | - | |
| 7 | -@Component({ | |
| 8 | - selector: 'cms', | |
| 9 | - templateUrl: "app/cms/cms.html", | |
| 10 | - providers: [ | |
| 11 | - provide('articleService', { useClass: ArticleService }), | |
| 12 | - provide('profileService', { useClass: ProfileService }), | |
| 13 | - provide('notification', { useClass: Notification }) | |
| 14 | - ] | |
| 15 | -}) | |
| 16 | -@Inject(ArticleService, ProfileService, "$state", Notification) | |
| 17 | -export class Cms { | |
| 18 | - | |
| 19 | - article: any = {}; | |
| 20 | - | |
| 21 | - constructor(private articleService: ArticleService, | |
| 22 | - private profileService: ProfileService, | |
| 23 | - private $state: ng.ui.IStateService, | |
| 24 | - private notification: Notification) { } | |
| 25 | - | |
| 26 | - save() { | |
| 27 | - this.profileService.getCurrentProfile().then((profile: Profile) => { | |
| 28 | - return this.articleService.create(profile.id, this.article); | |
| 29 | - }).then((response: restangular.IResponse) => { | |
| 30 | - this.$state.transitionTo('main.profile.page', { page: response.data.article.path, profile: response.data.article.profile.identifier }); | |
| 31 | - this.notification.success("Good job!", "Article saved!"); | |
| 32 | - }); | |
| 33 | - } | |
| 34 | - | |
| 35 | -} |
src/app/cms/cms.html
| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | -<form> | |
| 2 | - <div class="form-group"> | |
| 3 | - <label for="titleInput">Title</label> | |
| 4 | - <input type="text" class="form-control" id="titleInput" placeholder="title" ng-model="vm.article.name"> | |
| 5 | - </div> | |
| 6 | - <div class="form-group"> | |
| 7 | - <label for="bodyInput">Text</label> | |
| 8 | - <textarea class="form-control" id="bodyInput" rows="10" ng-model="vm.article.body"></textarea> | |
| 9 | - </div> | |
| 10 | - <button type="submit" class="btn btn-default" ng-click="vm.save()">Save</button> | |
| 11 | -</form> |
src/app/cms/index.ts
src/app/components/auth/auth_controller.spec.ts
| ... | ... | @@ -1,33 +0,0 @@ |
| 1 | -import {AuthController} from "./auth_controller"; | |
| 2 | -import {AuthService} from "./auth_service"; | |
| 3 | - | |
| 4 | -describe("Controllers", () => { | |
| 5 | - | |
| 6 | - | |
| 7 | - describe("AuthController", () => { | |
| 8 | - | |
| 9 | - it("calls authenticate on AuthService when login called", () => { | |
| 10 | - | |
| 11 | - // creating a Mock AuthService | |
| 12 | - let AuthServiceMock: AuthService = jasmine.createSpyObj("AuthService", ["login"]); | |
| 13 | - | |
| 14 | - // pass AuthServiceMock into the constructor | |
| 15 | - let authController = new AuthController(null, null, AuthServiceMock); | |
| 16 | - | |
| 17 | - // setup of authController -> set the credentials instance property | |
| 18 | - let credentials = { username: "username", password: "password" }; | |
| 19 | - | |
| 20 | - authController.credentials = credentials; | |
| 21 | - | |
| 22 | - // calls the authController login method | |
| 23 | - authController.login(); | |
| 24 | - | |
| 25 | - // checks if the method login of the injected AuthService has been called | |
| 26 | - expect(AuthServiceMock.login).toHaveBeenCalledWith(credentials); | |
| 27 | - | |
| 28 | - }); | |
| 29 | - | |
| 30 | - | |
| 31 | - | |
| 32 | - }); | |
| 33 | -}); |
src/app/components/auth/auth_controller.ts
| ... | ... | @@ -1,21 +0,0 @@ |
| 1 | -import {Credentials} from "./../../models/interfaces"; | |
| 2 | -import {AuthService} from "./auth_service"; | |
| 3 | - | |
| 4 | -export class AuthController { | |
| 5 | - | |
| 6 | - static $inject = ["$log", "$stateParams", "AuthService"]; | |
| 7 | - | |
| 8 | - constructor( | |
| 9 | - private $log: ng.ILogService, | |
| 10 | - private $stateParams: any, | |
| 11 | - private AuthService: AuthService | |
| 12 | - ) { | |
| 13 | - | |
| 14 | - } | |
| 15 | - | |
| 16 | - credentials: Credentials; | |
| 17 | - | |
| 18 | - login() { | |
| 19 | - this.AuthService.login(this.credentials); | |
| 20 | - } | |
| 21 | -} |
src/app/components/auth/auth_events.ts
| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | -export interface IAuthEvents { | |
| 2 | - loginSuccess: string; | |
| 3 | - loginFailed: string; | |
| 4 | - logoutSuccess: string; | |
| 5 | -} | |
| 6 | - | |
| 7 | -export const AUTH_EVENTS: IAuthEvents = { | |
| 8 | - loginSuccess: "auth-login-success", | |
| 9 | - loginFailed: "auth-login-failed", | |
| 10 | - logoutSuccess: "auth-logout-success" | |
| 11 | -}; | |
| 12 | 0 | \ No newline at end of file |
src/app/components/auth/auth_service.spec.ts
| ... | ... | @@ -1,82 +0,0 @@ |
| 1 | - | |
| 2 | - | |
| 3 | -import {AuthService, AUTH_EVENTS} from "./"; | |
| 4 | -import {User, Credentials} from "./../../models/interfaces"; | |
| 5 | - | |
| 6 | -describe("Services", () => { | |
| 7 | - | |
| 8 | - | |
| 9 | - describe("Auth Service", () => { | |
| 10 | - | |
| 11 | - let $httpBackend: ng.IHttpBackendService; | |
| 12 | - let authService: AuthService; | |
| 13 | - let credentials: Credentials; | |
| 14 | - let $rootScope: ng.IRootScopeService; | |
| 15 | - let user: User; | |
| 16 | - | |
| 17 | - beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => { | |
| 18 | - $translateProvider.translations('en', {}); | |
| 19 | - })); | |
| 20 | - | |
| 21 | - beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService, _AuthService_: AuthService) => { | |
| 22 | - $httpBackend = _$httpBackend_; | |
| 23 | - authService = _AuthService_; | |
| 24 | - $rootScope = _$rootScope_; | |
| 25 | - | |
| 26 | - user = <User>{ | |
| 27 | - id: 1, | |
| 28 | - login: "user" | |
| 29 | - }; | |
| 30 | - })); | |
| 31 | - | |
| 32 | - | |
| 33 | - describe("Succesffull login", () => { | |
| 34 | - | |
| 35 | - beforeEach(() => { | |
| 36 | - credentials = { username: "user", password: "password" }; | |
| 37 | - | |
| 38 | - $httpBackend.expectPOST("/api/v1/login", "login=user&password=password").respond(200, { user: user }); | |
| 39 | - }); | |
| 40 | - | |
| 41 | - it("should return loggedUser", (done) => { | |
| 42 | - authService.login(credentials).then((loggedUser) => { | |
| 43 | - expect(loggedUser).toBeDefined(); | |
| 44 | - done(); | |
| 45 | - }); | |
| 46 | - $httpBackend.flush(); | |
| 47 | - expect($httpBackend.verifyNoOutstandingRequest()); | |
| 48 | - }); | |
| 49 | - | |
| 50 | - | |
| 51 | - it("should emit event loggin successful with user logged data", () => { | |
| 52 | - | |
| 53 | - authService.login(credentials); | |
| 54 | - | |
| 55 | - let eventEmmited: boolean = false; | |
| 56 | - $rootScope.$on(AUTH_EVENTS.loginSuccess, (event: ng.IAngularEvent, userThroughEvent: User) => { | |
| 57 | - eventEmmited = true; | |
| 58 | - expect(userThroughEvent).toEqual(user) | |
| 59 | - }); | |
| 60 | - | |
| 61 | - $httpBackend.flush(); | |
| 62 | - | |
| 63 | - expect(eventEmmited).toBeTruthy(AUTH_EVENTS.loginSuccess + " was not emmited!"); | |
| 64 | - }); | |
| 65 | - | |
| 66 | - it("should return the current logged in user", () => { | |
| 67 | - authService.login(credentials); | |
| 68 | - $httpBackend.flush(); | |
| 69 | - let actual: User = authService.currentUser(); | |
| 70 | - expect(actual).toEqual(user, "The returned user must be present"); | |
| 71 | - }); | |
| 72 | - | |
| 73 | - it("should not return the current user after logout", () => { | |
| 74 | - authService.logout(); | |
| 75 | - let actual: any = authService.currentUser(); | |
| 76 | - expect(actual).toEqual(undefined, "The returned user must not be defined"); | |
| 77 | - }); | |
| 78 | - }); | |
| 79 | - | |
| 80 | - | |
| 81 | - }); | |
| 82 | -}); |
src/app/components/auth/auth_service.ts
| ... | ... | @@ -1,69 +0,0 @@ |
| 1 | -import {Injectable, Inject} from "ng-forward"; | |
| 2 | - | |
| 3 | -import {Credentials, NoosferoRootScope, User, 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: 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: Credentials): ng.IPromise<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(): User { | |
| 60 | - return this.session.currentUser(); | |
| 61 | - } | |
| 62 | - | |
| 63 | - public isAuthorized(authorizedRoles: string | string[]) { | |
| 64 | - if (!angular.isArray(authorizedRoles)) { | |
| 65 | - authorizedRoles = [<string>authorizedRoles]; | |
| 66 | - } | |
| 67 | - return (this.isAuthenticated() && authorizedRoles.indexOf(this.session.currentUser().userRole) !== -1); | |
| 68 | - } | |
| 69 | -} | |
| 70 | 0 | \ No newline at end of file |
src/app/components/auth/index.ts
src/app/components/auth/login.html
| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | -<div class="modal-header"> | |
| 2 | - <h3 class="modal-title">{{"auth.title" | translate}}</h3> | |
| 3 | -</div> | |
| 4 | -<div class="modal-body"> | |
| 5 | - <form> | |
| 6 | - <div class="form-group"> | |
| 7 | - <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label> | |
| 8 | - <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="vm.credentials.username"> | |
| 9 | - </div> | |
| 10 | - <div class="form-group"> | |
| 11 | - <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label> | |
| 12 | - <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="vm.credentials.password"> | |
| 13 | - </div> | |
| 14 | - <button type="submit" class="btn btn-default" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> | |
| 15 | - </form> | |
| 16 | -</div> |
src/app/components/auth/session.ts
| ... | ... | @@ -1,27 +0,0 @@ |
| 1 | -import {Injectable, Inject} from "ng-forward"; | |
| 2 | -import {UserResponse, User, 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): 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(): User { | |
| 24 | - return this.$localStorage.currentUser; | |
| 25 | - }; | |
| 26 | - | |
| 27 | -} | |
| 28 | 0 | \ No newline at end of file |
src/app/components/auth/session_spec.ts
| ... | ... | @@ -1,49 +0,0 @@ |
| 1 | -import {Component} from "ng-forward"; | |
| 2 | -import {Session} from "./"; | |
| 3 | -import {fixtures, createComponentFromClass, createProviderToValue} from "./../../../spec/helpers"; | |
| 4 | -import {UserResponse, User, INoosferoLocalStorage} from "./../../models/interfaces"; | |
| 5 | - | |
| 6 | - | |
| 7 | -describe("Services", () => { | |
| 8 | - | |
| 9 | - | |
| 10 | - describe("Session Service", () => { | |
| 11 | - | |
| 12 | - let $localStorage: INoosferoLocalStorage = null; | |
| 13 | - let $log: any; | |
| 14 | - | |
| 15 | - beforeEach(() => { | |
| 16 | - $localStorage = <INoosferoLocalStorage>{ currentUser: null }; | |
| 17 | - $log = jasmine.createSpyObj('$log', ['debug']); | |
| 18 | - }); | |
| 19 | - | |
| 20 | - it("method 'create()' saves the current user on $localstorage service", () => { | |
| 21 | - let session = new Session($localStorage, $log); | |
| 22 | - let userResponse = <UserResponse>{ | |
| 23 | - user: fixtures.user | |
| 24 | - }; | |
| 25 | - session.create(userResponse); | |
| 26 | - expect($localStorage.currentUser).toEqual(userResponse.user); | |
| 27 | - }); | |
| 28 | - | |
| 29 | - it("method 'destroy()' clean the currentUser on $localstorage", () => { | |
| 30 | - let session = new Session($localStorage, $log); | |
| 31 | - let userResponse = <UserResponse>{ | |
| 32 | - user: fixtures.user | |
| 33 | - }; | |
| 34 | - $localStorage.currentUser = fixtures.user; | |
| 35 | - session.destroy(); | |
| 36 | - expect($localStorage.currentUser).toBeUndefined(); | |
| 37 | - }); | |
| 38 | - | |
| 39 | - it("method 'currentUser()' returns the user recorded on $localstorage service", () => { | |
| 40 | - let session = new Session($localStorage, $log); | |
| 41 | - let userResponse = <UserResponse>{ | |
| 42 | - user: fixtures.user | |
| 43 | - }; | |
| 44 | - $localStorage.currentUser = fixtures.user; | |
| 45 | - expect(session.currentUser()).toEqual($localStorage.currentUser); | |
| 46 | - }); | |
| 47 | - }); | |
| 48 | - | |
| 49 | -}); | |
| 50 | 0 | \ No newline at end of file |
src/app/components/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/language-selector/language-selector.component.spec.ts
| ... | ... | @@ -1,46 +0,0 @@ |
| 1 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {provide} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import {LanguageSelector} 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: [LanguageSelector], | |
| 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 LanguageSelector(<any>translatorService); | |
| 40 | - languageSelector.changeLanguage("en"); | |
| 41 | - expect(translatorService.changeLanguage).toHaveBeenCalledWith("en"); | |
| 42 | - done(); | |
| 43 | - }); | |
| 44 | - | |
| 45 | - }); | |
| 46 | -}); |
src/app/components/language-selector/language-selector.component.ts
| ... | ... | @@ -1,24 +0,0 @@ |
| 1 | -import {Component, Inject} from "ng-forward"; | |
| 2 | -import {TranslatorService} from "../translator/translator.service"; | |
| 3 | - | |
| 4 | -@Component({ | |
| 5 | - selector: "language-selector", | |
| 6 | - templateUrl: "app/components/language-selector/language-selector.html" | |
| 7 | -}) | |
| 8 | -@Inject(TranslatorService) | |
| 9 | -export class LanguageSelector { | |
| 10 | - | |
| 11 | - constructor(private translatorService: TranslatorService) { } | |
| 12 | - | |
| 13 | - currentLanguage() { | |
| 14 | - return this.translatorService.currentLanguage(); | |
| 15 | - } | |
| 16 | - | |
| 17 | - changeLanguage(language: string) { | |
| 18 | - this.translatorService.changeLanguage(language); | |
| 19 | - } | |
| 20 | - | |
| 21 | - availableLanguages() { | |
| 22 | - return this.translatorService.availableLanguages; | |
| 23 | - } | |
| 24 | -} |
src/app/components/language-selector/language-selector.html
| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | -<li class="dropdown profile-menu" dropdown> | |
| 2 | - <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
| 3 | - <span>{{"language.selector" | translate}}</span> <b class="caret"></b> | |
| 4 | - </a> | |
| 5 | - <ul class="dropdown-menu" dropdown-menu> | |
| 6 | - <li ng-repeat="(language, description) in ctrl.availableLanguages()" | |
| 7 | - class="language language-{{language}}" ng-class="{'active': language==ctrl.currentLanguage()}"> | |
| 8 | - <a href="#" ng-click="ctrl.changeLanguage(language)"> | |
| 9 | - {{description}} | |
| 10 | - </a> | |
| 11 | - </li> | |
| 12 | - </ul> | |
| 13 | -</li> |
src/app/components/navbar/index.ts
src/app/components/navbar/navbar.directive.spec.js
| ... | ... | @@ -1,45 +0,0 @@ |
| 1 | -(function() { | |
| 2 | - 'use strict'; | |
| 3 | - | |
| 4 | - describe('directive navbar', function() { | |
| 5 | - var vm; | |
| 6 | - var el; | |
| 7 | - var AUTH_EVENTS; | |
| 8 | - var $state; | |
| 9 | - | |
| 10 | - beforeEach(module('angular')); | |
| 11 | - beforeEach(inject(function($compile, $rootScope, $httpBackend, _AUTH_EVENTS_, _$state_) { | |
| 12 | - $state = _$state_; | |
| 13 | - AUTH_EVENTS = _AUTH_EVENTS_; | |
| 14 | - $httpBackend.when('POST','/api/v1/login_from_cookie').respond({}); | |
| 15 | - | |
| 16 | - el = angular.element('<acme-navbar></acme-navbar>'); | |
| 17 | - | |
| 18 | - $compile(el)($rootScope.$new()); | |
| 19 | - $rootScope.$digest(); | |
| 20 | - vm = el.isolateScope().vm; | |
| 21 | - })); | |
| 22 | - | |
| 23 | - it('should be compiled', function() { | |
| 24 | - expect(el.html()).not.toEqual(null); | |
| 25 | - }); | |
| 26 | - | |
| 27 | - it('should have isolate scope object with instanciate members', function() { | |
| 28 | - expect(vm).toEqual(jasmine.any(Object)); | |
| 29 | - expect(vm.currentUser).toEqual(undefined); | |
| 30 | - }); | |
| 31 | - | |
| 32 | - it('should reload current state after login', function() { | |
| 33 | - spyOn($state, 'go'); | |
| 34 | - el.isolateScope().$broadcast(AUTH_EVENTS.loginSuccess, {}); | |
| 35 | - expect($state.go).toHaveBeenCalled(); | |
| 36 | - }); | |
| 37 | - | |
| 38 | - it('should open login when not logged in', function() { | |
| 39 | - spyOn(vm, 'openLogin'); | |
| 40 | - vm.activate(); | |
| 41 | - expect(vm.openLogin).toHaveBeenCalled(); | |
| 42 | - }); | |
| 43 | - | |
| 44 | - }); | |
| 45 | -})(); |
src/app/components/navbar/navbar.html
| ... | ... | @@ -1,49 +0,0 @@ |
| 1 | -<nav class="navbar navbar-static-top navbar-inverse"> | |
| 2 | - <div class="container-fluid"> | |
| 3 | - <div class="navbar-header"> | |
| 4 | - <button type="button" class="navbar-toggle collapsed" ng-click="isCollapsed = !isCollapsed"> | |
| 5 | - <span class="sr-only">{{"navbar.toggle_menu" | translate}}</span> | |
| 6 | - <span class="icon-bar"></span> | |
| 7 | - <span class="icon-bar"></span> | |
| 8 | - <span class="icon-bar"></span> | |
| 9 | - </button> | |
| 10 | - <a class="navbar-brand" ui-sref="main"> | |
| 11 | - <span class="noosfero-logo"><img src="assets/images/logo-noosfero.png"></span> {{"noosfero.name" | translate}} | |
| 12 | - </a> | |
| 13 | - </div> | |
| 14 | - | |
| 15 | - <div class="collapse navbar-collapse" id="navbar-collapse" collapse="isCollapsed"> | |
| 16 | - <ul class="nav navbar-nav"> | |
| 17 | - </ul> | |
| 18 | - | |
| 19 | - <ul class="nav navbar-nav navbar-right"> | |
| 20 | - <li ng-show="!ctrl.currentUser"> | |
| 21 | - <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a> | |
| 22 | - </li> | |
| 23 | - | |
| 24 | - <li class="dropdown profile-menu" ng-show="ctrl.currentUser" dropdown> | |
| 25 | - <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
| 26 | - <noosfero-profile-image [profile]="ctrl.currentUser.person" class="profile-image"></noosfero-profile-image> | |
| 27 | - <span ng-bind="ctrl.currentUser.person.name"></span> <b class="caret"></b> | |
| 28 | - </a> | |
| 29 | - <ul class="dropdown-menu" dropdown-menu> | |
| 30 | - <li> | |
| 31 | - <a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-user"></i> {{"navbar.profile" | translate}}</a> | |
| 32 | - </li> | |
| 33 | - <li> | |
| 34 | - <a target="_self" ui-sref="main.profile.settings({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-gear"></i> {{"navbar.settings" | translate}}</a> | |
| 35 | - </li> | |
| 36 | - <li class="divider"></li> | |
| 37 | - <li> | |
| 38 | - <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a> | |
| 39 | - </li> | |
| 40 | - </ul> | |
| 41 | - </li> | |
| 42 | - </ul> | |
| 43 | - <ul class="nav navbar-nav navbar-right"> | |
| 44 | - <language-selector class="nav navbar-nav navbar-right"></language-selector> | |
| 45 | - </ul> | |
| 46 | - <div ui-view="actions"></div> | |
| 47 | - </div> | |
| 48 | - </div> | |
| 49 | -</nav> |
src/app/components/navbar/navbar.scss
| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | -.navbar { | |
| 2 | - | |
| 3 | - .container-fluid { | |
| 4 | - padding-right: 12%; | |
| 5 | - padding-left: 12%; | |
| 6 | - @media (max-width: 978px) { | |
| 7 | - padding-right: 2%; | |
| 8 | - padding-left: 2%; | |
| 9 | - } | |
| 10 | - | |
| 11 | - .navbar-brand { | |
| 12 | - .noosfero-logo img { | |
| 13 | - height: 35px; | |
| 14 | - } | |
| 15 | - } | |
| 16 | - | |
| 17 | - .navbar-nav { | |
| 18 | - .profile-menu .profile-image { | |
| 19 | - img { | |
| 20 | - height: 30px; | |
| 21 | - width: 30px; | |
| 22 | - display: inline-block; | |
| 23 | - @extend .img-circle; | |
| 24 | - } | |
| 25 | - i { | |
| 26 | - font-size: 1.7em; | |
| 27 | - } | |
| 28 | - } | |
| 29 | - } | |
| 30 | - } | |
| 31 | -} |
src/app/components/navbar/navbar.spec.ts
| ... | ... | @@ -1,188 +0,0 @@ |
| 1 | -import * as helpers from "./../../../spec/helpers"; | |
| 2 | -import {Navbar} from "./navbar"; | |
| 3 | -import {AUTH_EVENTS} from "./../auth"; | |
| 4 | -import {User} from "./../../models/interfaces"; | |
| 5 | -import {Injectable, Provider, provide} from "ng-forward"; | |
| 6 | - | |
| 7 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 8 | - | |
| 9 | -import {Session, AuthService, AuthController, IAuthEvents} from "./../auth"; | |
| 10 | - | |
| 11 | - | |
| 12 | -describe("Components", () => { | |
| 13 | - | |
| 14 | - describe("Navbar Component", () => { | |
| 15 | - | |
| 16 | - let user: User = null; | |
| 17 | - let scope: any; | |
| 18 | - let $rootScope: ng.IRootScopeService; | |
| 19 | - | |
| 20 | - let modalInstance: any; | |
| 21 | - let $modal: any; | |
| 22 | - let authService: any; | |
| 23 | - let stateService: any; | |
| 24 | - let sessionService: Session; | |
| 25 | - | |
| 26 | - let provideFunc = provide; | |
| 27 | - | |
| 28 | - // before Each -> loading mocks on locals variables | |
| 29 | - beforeEach(() => { | |
| 30 | - user = <User>{ | |
| 31 | - id: 1, | |
| 32 | - login: "user" | |
| 33 | - }; | |
| 34 | - scope = helpers.mocks.scopeWithEvents; | |
| 35 | - modalInstance = helpers.mocks.modalInstance; | |
| 36 | - $modal = helpers.mocks.$modal; | |
| 37 | - authService = helpers.mocks.authService; | |
| 38 | - stateService = jasmine.createSpyObj("$state", ["go"]); | |
| 39 | - sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); | |
| 40 | - }); | |
| 41 | - | |
| 42 | - | |
| 43 | - // loading the templates | |
| 44 | - beforeEach(angular.mock.module("templates")); | |
| 45 | - | |
| 46 | - | |
| 47 | - // this function allow build the fixture of the container component | |
| 48 | - // and is reused in each test | |
| 49 | - // The main idea behing not prebuild it on a general beforeEach block is | |
| 50 | - // to allow tests configure the mock services accordilly their own needs | |
| 51 | - let buildComponent = (): Promise<ComponentFixture> => { | |
| 52 | - return helpers.quickCreateComponent({ | |
| 53 | - providers: [ | |
| 54 | - provide('$modal', { | |
| 55 | - useValue: $modal | |
| 56 | - }), | |
| 57 | - provide('AuthService', { | |
| 58 | - useValue: authService | |
| 59 | - }), | |
| 60 | - helpers.provideEmptyObjects('moment'), | |
| 61 | - provide('$state', { | |
| 62 | - useValue: stateService | |
| 63 | - }), | |
| 64 | - provide("$scope", { | |
| 65 | - useValue: scope | |
| 66 | - }), | |
| 67 | - provide('Session', { | |
| 68 | - useValue: sessionService | |
| 69 | - }), | |
| 70 | - provide('AUTH_EVENTS', { | |
| 71 | - useValue: { | |
| 72 | - AUTH_EVENTS | |
| 73 | - } | |
| 74 | - }), | |
| 75 | - provide('TranslatorService', { | |
| 76 | - useValue: helpers.mocks.translatorService | |
| 77 | - }) | |
| 78 | - ].concat(helpers.provideFilters("translateFilter")), | |
| 79 | - directives: [Navbar], | |
| 80 | - template: '<acme-navbar></acme-navbar>' | |
| 81 | - }); | |
| 82 | - } | |
| 83 | - | |
| 84 | - | |
| 85 | - it('should get the loggedIn user', (done: Function) => { | |
| 86 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 87 | - let navbarInstance: Navbar = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 88 | - expect(navbarInstance).toBeDefined(); | |
| 89 | - expect(navbarInstance["currentUser"]).toEqual(user); | |
| 90 | - done(); | |
| 91 | - }); | |
| 92 | - }); | |
| 93 | - | |
| 94 | - it('should open on click', (done: Function) => { | |
| 95 | - spyOn($modal, "open"); | |
| 96 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 97 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 98 | - navbarComp.openLogin(); | |
| 99 | - expect($modal.open).toHaveBeenCalled(); | |
| 100 | - expect($modal.open).toHaveBeenCalledWith({ | |
| 101 | - templateUrl: 'app/components/auth/login.html', | |
| 102 | - controller: AuthController, | |
| 103 | - controllerAs: 'vm', | |
| 104 | - bindToController: true | |
| 105 | - }); | |
| 106 | - done(); | |
| 107 | - }); | |
| 108 | - }); | |
| 109 | - | |
| 110 | - it('should logout', (done: Function) => { | |
| 111 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 112 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 113 | - spyOn(authService, "logout"); | |
| 114 | - try { | |
| 115 | - navbarComp.logout(); | |
| 116 | - expect(authService.logout).toHaveBeenCalled(); | |
| 117 | - done(); | |
| 118 | - } catch (e) { | |
| 119 | - console.error(e); | |
| 120 | - fail(e.message); | |
| 121 | - done(); | |
| 122 | - } | |
| 123 | - }); | |
| 124 | - }); | |
| 125 | - | |
| 126 | - | |
| 127 | - it('should not activate user when logged in', (done: Function) => { | |
| 128 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 129 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 130 | - spyOn(navbarComp, "openLogin"); | |
| 131 | - navbarComp.activate(); | |
| 132 | - expect((<any>navbarComp.openLogin).calls.count()).toBe(0); | |
| 133 | - done(); | |
| 134 | - }); | |
| 135 | - }); | |
| 136 | - | |
| 137 | - it('should activate when user not logged in', (done: Function) => { | |
| 138 | - spyOn(sessionService, 'currentUser').and.returnValue(null); | |
| 139 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 140 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 141 | - spyOn(navbarComp, "openLogin"); | |
| 142 | - navbarComp.activate(); | |
| 143 | - expect(navbarComp.openLogin).toHaveBeenCalled(); | |
| 144 | - done(); | |
| 145 | - }); | |
| 146 | - }); | |
| 147 | - | |
| 148 | - | |
| 149 | - it('closes the modal after login', (done: Function) => { | |
| 150 | - modalInstance = jasmine.createSpyObj("modalInstance", ["close"]); | |
| 151 | - modalInstance.close = jasmine.createSpy("close"); | |
| 152 | - | |
| 153 | - $modal.open = () => { | |
| 154 | - return modalInstance; | |
| 155 | - }; | |
| 156 | - | |
| 157 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 158 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 159 | - let localScope: ng.IScope = navbarComp["$scope"]; | |
| 160 | - | |
| 161 | - navbarComp.openLogin(); | |
| 162 | - localScope.$emit(AUTH_EVENTS.loginSuccess); | |
| 163 | - expect(modalInstance.close).toHaveBeenCalled(); | |
| 164 | - done(); | |
| 165 | - }); | |
| 166 | - }); | |
| 167 | - | |
| 168 | - it('updates current user on logout', (done: Function) => { | |
| 169 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 170 | - let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 171 | - let localScope: ng.IScope = navbarComp["$scope"]; | |
| 172 | - | |
| 173 | - // init navbar currentUser with some user | |
| 174 | - navbarComp["currentUser"] = user; | |
| 175 | - | |
| 176 | - // changes the current User to return null, | |
| 177 | - // and emmit the 'logoutSuccess' event | |
| 178 | - // just what happens when user logsout | |
| 179 | - sessionService.currentUser = () => { return null; }; | |
| 180 | - localScope.$emit(AUTH_EVENTS.logoutSuccess); | |
| 181 | - expect(navbarComp["currentUser"]).toBeNull(); | |
| 182 | - done(); | |
| 183 | - }); | |
| 184 | - }); | |
| 185 | - | |
| 186 | - | |
| 187 | - }); | |
| 188 | -}); |
src/app/components/navbar/navbar.ts
| ... | ... | @@ -1,67 +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 | -import {User} from "./../../models/interfaces"; | |
| 7 | - | |
| 8 | -@Component({ | |
| 9 | - selector: "acme-navbar", | |
| 10 | - templateUrl: "app/components/navbar/navbar.html", | |
| 11 | - directives: [LanguageSelector], | |
| 12 | - providers: [AuthService, Session] | |
| 13 | -}) | |
| 14 | -@Inject("$modal", AuthService, "Session", "$scope", "$state") | |
| 15 | -export class Navbar { | |
| 16 | - | |
| 17 | - private currentUser: User; | |
| 18 | - private modalInstance: any = null; | |
| 19 | - /** | |
| 20 | - * | |
| 21 | - */ | |
| 22 | - constructor( | |
| 23 | - private $modal: any, | |
| 24 | - private authService: AuthService, | |
| 25 | - private session: Session, | |
| 26 | - private $scope: ng.IScope, | |
| 27 | - private $state: ng.ui.IStateService | |
| 28 | - ) { | |
| 29 | - this.currentUser = this.session.currentUser(); | |
| 30 | - | |
| 31 | - this.$scope.$on(AUTH_EVENTS.loginSuccess, () => { | |
| 32 | - if (this.modalInstance) { | |
| 33 | - this.modalInstance.close(); | |
| 34 | - this.modalInstance = null; | |
| 35 | - } | |
| 36 | - | |
| 37 | - this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
| 38 | - }); | |
| 39 | - | |
| 40 | - this.$scope.$on(AUTH_EVENTS.logoutSuccess, () => { | |
| 41 | - this.currentUser = this.session.currentUser(); | |
| 42 | - }); | |
| 43 | - } | |
| 44 | - | |
| 45 | - openLogin() { | |
| 46 | - this.modalInstance = this.$modal.open({ | |
| 47 | - templateUrl: 'app/components/auth/login.html', | |
| 48 | - controller: AuthController, | |
| 49 | - controllerAs: 'vm', | |
| 50 | - bindToController: true | |
| 51 | - }); | |
| 52 | - }; | |
| 53 | - | |
| 54 | - logout() { | |
| 55 | - this.authService.logout(); | |
| 56 | - this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
| 57 | - }; | |
| 58 | - | |
| 59 | - | |
| 60 | - | |
| 61 | - activate() { | |
| 62 | - if (!this.currentUser) { | |
| 63 | - this.openLogin(); | |
| 64 | - } | |
| 65 | - } | |
| 66 | - | |
| 67 | -} |
src/app/components/noosfero-activities/activities.component.spec.ts
| ... | ... | @@ -1,36 +0,0 @@ |
| 1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | -import {provideFilters} from '../../../spec/helpers'; | |
| 4 | - | |
| 5 | -import {NoosferoActivities} from './activities.component'; | |
| 6 | - | |
| 7 | -const tcb = new TestComponentBuilder(); | |
| 8 | - | |
| 9 | -const htmlTemplate: string = '<noosfero-activities [activities]="ctrl.activities"></noosfero-activities>'; | |
| 10 | - | |
| 11 | - | |
| 12 | -describe("Components", () => { | |
| 13 | - | |
| 14 | - describe("Noosfero Activities", () => { | |
| 15 | - | |
| 16 | - beforeEach(angular.mock.module("templates")); | |
| 17 | - | |
| 18 | - @Component({ | |
| 19 | - selector: 'test-container-component', | |
| 20 | - template: htmlTemplate, | |
| 21 | - directives: [NoosferoActivities], | |
| 22 | - providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
| 23 | - }) | |
| 24 | - class BlockContainerComponent { | |
| 25 | - activities = [{ name: "activity1", verb: "create_article" }, { name: "activity2", verb: "create_article" }]; | |
| 26 | - } | |
| 27 | - | |
| 28 | - it("render a noosfero activity tag for each activity", done => { | |
| 29 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 30 | - expect(fixture.debugElement.queryAll("noosfero-activity").length).toEqual(2); | |
| 31 | - done(); | |
| 32 | - }); | |
| 33 | - }); | |
| 34 | - }); | |
| 35 | - | |
| 36 | -}); |
src/app/components/noosfero-activities/activities.component.ts
| ... | ... | @@ -1,27 +0,0 @@ |
| 1 | -import {Component, Input} from "ng-forward"; | |
| 2 | -import {NoosferoActivity} from "./activity/activity.component"; | |
| 3 | -import {Activity} from "../../models/interfaces"; | |
| 4 | - | |
| 5 | -/** | |
| 6 | - * @ngdoc controller | |
| 7 | - * @name NoosferoActivities | |
| 8 | - * @description | |
| 9 | - * The controller responsible to retreive profile activities. | |
| 10 | - */ | |
| 11 | - | |
| 12 | -@Component({ | |
| 13 | - selector: "noosfero-activities", | |
| 14 | - templateUrl: 'app/components/noosfero-activities/activities.html', | |
| 15 | - directives: [NoosferoActivity] | |
| 16 | -}) | |
| 17 | -export class NoosferoActivities { | |
| 18 | - | |
| 19 | - /** | |
| 20 | - * @ngdoc property | |
| 21 | - * @propertyOf NoosferoActivities | |
| 22 | - * @name activities | |
| 23 | - * @returns {Activity[]} An array of {@link Activity}. | |
| 24 | - */ | |
| 25 | - @Input() activities: Activity[]; | |
| 26 | - | |
| 27 | -} |
src/app/components/noosfero-activities/activities.html
src/app/components/noosfero-activities/activities.scss
src/app/components/noosfero-activities/activity/activity.component.spec.ts
| ... | ... | @@ -1,38 +0,0 @@ |
| 1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | -import {provideFilters} from '../../../../spec/helpers'; | |
| 4 | - | |
| 5 | -import {NoosferoActivity} from './activity.component'; | |
| 6 | - | |
| 7 | -const tcb = new TestComponentBuilder(); | |
| 8 | - | |
| 9 | -const htmlTemplate: string = '<noosfero-activity [activity]="ctrl.activity"></noosfero-activity>'; | |
| 10 | - | |
| 11 | - | |
| 12 | -describe("Components", () => { | |
| 13 | - | |
| 14 | - describe("Noosfero Activity", () => { | |
| 15 | - | |
| 16 | - beforeEach(angular.mock.module("templates")); | |
| 17 | - | |
| 18 | - @Component({ | |
| 19 | - selector: 'test-container-component', | |
| 20 | - template: htmlTemplate, | |
| 21 | - directives: [NoosferoActivity], | |
| 22 | - providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
| 23 | - }) | |
| 24 | - class BlockContainerComponent { | |
| 25 | - activity = { name: "activity1", verb: "create_article" }; | |
| 26 | - } | |
| 27 | - | |
| 28 | - it("render the specific template for an activity verb", done => { | |
| 29 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 30 | - let component: NoosferoActivity = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 31 | - expect(component.getActivityTemplate()).toEqual('app/components/noosfero-activities/activity/create_article.html'); | |
| 32 | - expect(fixture.debugElement.queryAll(".activity.create_article").length).toEqual(1); | |
| 33 | - done(); | |
| 34 | - }); | |
| 35 | - }); | |
| 36 | - }); | |
| 37 | - | |
| 38 | -}); |
src/app/components/noosfero-activities/activity/activity.component.ts
| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | -import {Component, Input} from "ng-forward"; | |
| 2 | -import {Activity} from "../../../models/interfaces"; | |
| 3 | - | |
| 4 | -@Component({ | |
| 5 | - selector: "noosfero-activity", | |
| 6 | - templateUrl: 'app/components/noosfero-activities/activity/activity.html' | |
| 7 | -}) | |
| 8 | -export class NoosferoActivity { | |
| 9 | - | |
| 10 | - @Input() activity: Activity; | |
| 11 | - | |
| 12 | - getActivityTemplate() { | |
| 13 | - return 'app/components/noosfero-activities/activity/' + this.activity.verb + '.html'; | |
| 14 | - } | |
| 15 | - | |
| 16 | -} |
src/app/components/noosfero-activities/activity/activity.html
src/app/components/noosfero-activities/activity/add_member_in_community.html
| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | -<timeline-badge class="info"> | |
| 2 | - <i class="fa fa-user-plus"></i> | |
| 3 | -</timeline-badge> | |
| 4 | -<timeline-panel> | |
| 5 | - <timeline-heading> | |
| 6 | - <h4 class="timeline-title"> | |
| 7 | - <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
| 8 | - <span> {{"activities.add_member_in_community.description" | translate}}</span> | |
| 9 | - </h4> | |
| 10 | - <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
| 11 | - </timeline-heading> | |
| 12 | - <div class="timeline-body"></div> | |
| 13 | -</timeline-panel> |
src/app/components/noosfero-activities/activity/create_article.html
| ... | ... | @@ -1,26 +0,0 @@ |
| 1 | -<timeline-badge class="success"> | |
| 2 | - <i class="fa fa-file-text"></i> | |
| 3 | -</timeline-badge> | |
| 4 | -<timeline-panel> | |
| 5 | - <timeline-heading> | |
| 6 | - <h4 class="timeline-title"> | |
| 7 | - <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
| 8 | - <span> {{"activities.create_article.description" | translate}} </span> | |
| 9 | - <a ui-sref="main.profile.info({profile: ctrl.activity.target.article.profile.identifier})"> | |
| 10 | - <strong ng-bind="ctrl.activity.target.article.profile.name"></strong></span> | |
| 11 | - </a> | |
| 12 | - </h4> | |
| 13 | - <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
| 14 | - </timeline-heading> | |
| 15 | - <div class="timeline-body"> | |
| 16 | - <div class="article"> | |
| 17 | - <div class="title"> | |
| 18 | - <a ui-sref="main.profile.page({profile: ctrl.activity.target.article.profile.identifier, page: ctrl.activity.target.article.path})" | |
| 19 | - ng-bind="ctrl.activity.target.article.title"></a> | |
| 20 | - </div> | |
| 21 | - <div class="lead small"> | |
| 22 | - <div ng-bind-html="ctrl.activity.target.article.body | stripTags | truncate: 100 : '...': true"></div> | |
| 23 | - </div> | |
| 24 | - </div> | |
| 25 | - </div> | |
| 26 | -</timeline-panel> |
src/app/components/noosfero-activities/activity/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-activities/activity/new_friendship.html
| ... | ... | @@ -1,18 +0,0 @@ |
| 1 | -<timeline-badge class="info"> | |
| 2 | - <i class="fa fa-user-plus"></i> | |
| 3 | -</timeline-badge> | |
| 4 | -<timeline-panel> | |
| 5 | - <timeline-heading> | |
| 6 | - <h4 class="timeline-title"> | |
| 7 | - <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
| 8 | - <span> {{"activities.new_friendship.description" | translate:{friends: ctrl.activity.params.friend_name.length}:"messageformat" }} </span> | |
| 9 | - <span class="comma-separated"> | |
| 10 | - <a class="separated-item" ui-sref="main.profile.info({profile: ctrl.activity.params.friend_url[$index].profile})" ng-repeat="friend in ctrl.activity.params.friend_name"> | |
| 11 | - <strong ng-bind="friend"></strong> | |
| 12 | - </a> | |
| 13 | - </span> | |
| 14 | - </h4> | |
| 15 | - <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
| 16 | - </timeline-heading> | |
| 17 | - <div class="timeline-body"></div> | |
| 18 | -</timeline-panel> |
src/app/components/noosfero-activities/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-articles/article/article.html
| ... | ... | @@ -1,23 +0,0 @@ |
| 1 | -<div class="article"> | |
| 2 | - <div class="page-header"> | |
| 3 | - <h3 ng-bind="ctrl.article.title"></h3> | |
| 4 | - </div> | |
| 5 | - | |
| 6 | - <div class="sub-header clearfix"> | |
| 7 | - <div class="page-info pull-right small text-muted"> | |
| 8 | - <span class="time"> | |
| 9 | - <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> | |
| 10 | - </span> | |
| 11 | - <span class="author" ng-if="ctrl.article.author"> | |
| 12 | - <i class="fa fa-user"></i> | |
| 13 | - <a ui-sref="main.profile.home({profile: ctrl.article.author.identifier})"> | |
| 14 | - <span class="author-name" ng-bind="ctrl.article.author.name"></span> | |
| 15 | - </a> | |
| 16 | - </span> | |
| 17 | - </div> | |
| 18 | - </div> | |
| 19 | - | |
| 20 | - <div class="page-body"> | |
| 21 | - <div ng-bind-html="ctrl.article.body"></div> | |
| 22 | - </div> | |
| 23 | -</div> |
src/app/components/noosfero-articles/article/article.scss
src/app/components/noosfero-articles/article/article_view.spec.ts
| ... | ... | @@ -1,108 +0,0 @@ |
| 1 | - | |
| 2 | -import {Input, provide, Component} from 'ng-forward'; | |
| 3 | -import {ArticleView, ArticleDefaultView} from './article_view'; | |
| 4 | - | |
| 5 | -import {createComponentFromClass, quickCreateComponent} from "../../../../spec/helpers"; | |
| 6 | - | |
| 7 | -// this htmlTemplate will be re-used between the container components in this spec file | |
| 8 | -const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-article>'; | |
| 9 | - | |
| 10 | - | |
| 11 | -describe("Components", () => { | |
| 12 | - | |
| 13 | - describe("ArticleView Component", () => { | |
| 14 | - | |
| 15 | - // the karma preprocessor html2js transform the templates html into js files which put | |
| 16 | - // the templates to the templateCache into the module templates | |
| 17 | - // we need to load the module templates here as the template for the | |
| 18 | - // component Noosfero ArtileView will be load on our tests | |
| 19 | - beforeEach(angular.mock.module("templates")); | |
| 20 | - | |
| 21 | - it("renders the default component when no specific component is found", (done: Function) => { | |
| 22 | - // Creating a container component (ArticleContainerComponent) to include | |
| 23 | - // the component under test (ArticleView) | |
| 24 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleView] }) | |
| 25 | - class ArticleContainerComponent { | |
| 26 | - article = { type: 'anyArticleType' }; | |
| 27 | - profile = { name: 'profile-name' }; | |
| 28 | - constructor() { | |
| 29 | - } | |
| 30 | - } | |
| 31 | - | |
| 32 | - createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
| 33 | - // and here we can inspect and run the test assertions | |
| 34 | - | |
| 35 | - // gets the children component of ArticleContainerComponent | |
| 36 | - let articleView: ArticleView = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 37 | - | |
| 38 | - // and checks if the article View rendered was the Default Article View | |
| 39 | - expect(articleView.constructor.prototype).toEqual(ArticleDefaultView.prototype); | |
| 40 | - | |
| 41 | - // done needs to be called (it isn't really needed, as we can read in | |
| 42 | - // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
| 43 | - // because createAsync in ng-forward is not really async, but as the intention | |
| 44 | - // here is write tests in angular 2 ways, this is recommended | |
| 45 | - done(); | |
| 46 | - }); | |
| 47 | - | |
| 48 | - }); | |
| 49 | - | |
| 50 | - it("receives the article and profile as inputs", (done: Function) => { | |
| 51 | - | |
| 52 | - // Creating a container component (ArticleContainerComponent) to include | |
| 53 | - // the component under test (ArticleView) | |
| 54 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleView] }) | |
| 55 | - class ArticleContainerComponent { | |
| 56 | - article = { type: 'anyArticleType' }; | |
| 57 | - profile = { name: 'profile-name' }; | |
| 58 | - constructor() { | |
| 59 | - } | |
| 60 | - } | |
| 61 | - | |
| 62 | - // uses the TestComponentBuilder instance to initialize the component | |
| 63 | - createComponentFromClass(ArticleContainerComponent).then((fixture) => { | |
| 64 | - // and here we can inspect and run the test assertions | |
| 65 | - let articleView: ArticleView = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 66 | - | |
| 67 | - // assure the article object inside the ArticleView matches | |
| 68 | - // the provided through the parent component | |
| 69 | - expect(articleView.article.type).toEqual("anyArticleType"); | |
| 70 | - expect(articleView.profile.name).toEqual("profile-name"); | |
| 71 | - | |
| 72 | - // done needs to be called (it isn't really needed, as we can read in | |
| 73 | - // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | |
| 74 | - // because createAsync in ng-forward is not really async, but as the intention | |
| 75 | - // here is write tests in angular 2 ways, this is recommended | |
| 76 | - done(); | |
| 77 | - }); | |
| 78 | - }); | |
| 79 | - | |
| 80 | - | |
| 81 | - it("renders a article view which matches to the article type", done => { | |
| 82 | - // NoosferoTinyMceArticle component created to check if it will be used | |
| 83 | - // when a article with type 'TinyMceArticle' is provided to the noosfero-article (ArticleView) | |
| 84 | - // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
| 85 | - @Component({ selector: 'noosfero-tiny-mce-article', template: "<h1>TinyMceArticle</h1>" }) | |
| 86 | - class TinyMceArticleView { | |
| 87 | - @Input() article: any; | |
| 88 | - @Input() profile: any; | |
| 89 | - } | |
| 90 | - | |
| 91 | - // Creating a container component (ArticleContainerComponent) to include our NoosferoTinyMceArticle | |
| 92 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleView, TinyMceArticleView] }) | |
| 93 | - class CustomArticleType { | |
| 94 | - article = { type: 'TinyMceArticle' }; | |
| 95 | - profile = { name: 'profile-name' }; | |
| 96 | - constructor() { | |
| 97 | - } | |
| 98 | - } | |
| 99 | - createComponentFromClass(CustomArticleType).then(fixture => { | |
| 100 | - let myComponent: CustomArticleType = fixture.componentInstance; | |
| 101 | - expect(myComponent.article.type).toEqual("TinyMceArticle"); | |
| 102 | - expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("TinyMceArticle"); | |
| 103 | - done(); | |
| 104 | - }); | |
| 105 | - }); | |
| 106 | - | |
| 107 | - }); | |
| 108 | -}); | |
| 109 | 0 | \ No newline at end of file |
src/app/components/noosfero-articles/article/article_view.ts
| ... | ... | @@ -1,57 +0,0 @@ |
| 1 | -import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; | |
| 2 | -import {ArticleBlog} from "../blog/blog.component"; | |
| 3 | - | |
| 4 | -/** | |
| 5 | - * @ngdoc controller | |
| 6 | - * @name ArticleDefaultView | |
| 7 | - * @description | |
| 8 | - * A default view for Noosfero Articles. If the specific article view is | |
| 9 | - * not implemented, then this view is used. | |
| 10 | - */ | |
| 11 | -@Component({ | |
| 12 | - selector: 'noosfero-default-article', | |
| 13 | - templateUrl: 'app/components/noosfero-articles/article/article.html' | |
| 14 | -}) | |
| 15 | -export class ArticleDefaultView { | |
| 16 | - | |
| 17 | - @Input() article: any; | |
| 18 | - @Input() profile: any; | |
| 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: any; | |
| 38 | - @Input() profile: any; | |
| 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,132 +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 | - // defining a mock result to articleService.getChildren method | |
| 100 | - articleService.getChildren = (article_id: number, filters: {}) => { | |
| 101 | - return promiseResultTemplate({ | |
| 102 | - headers: (headerName: string) => { | |
| 103 | - return 1; | |
| 104 | - }, | |
| 105 | - data: < any > { | |
| 106 | - articles: [{ | |
| 107 | - id: 1, | |
| 108 | - title: 'The article test' | |
| 109 | - }] | |
| 110 | - } | |
| 111 | - }); | |
| 112 | - }; | |
| 113 | - | |
| 114 | - createComponentFromClass(BlogContainerComponent).then((fixture) => { | |
| 115 | - | |
| 116 | - // gets the children component of BlogContainerComponent | |
| 117 | - let articleBlog: BlogContainerComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 118 | - | |
| 119 | - // check if the component property are the provided by the mocked articleService | |
| 120 | - let post = { | |
| 121 | - id: 1, | |
| 122 | - title: 'The article test' | |
| 123 | - }; | |
| 124 | - expect(( < any > articleBlog)["posts"][0]).toEqual(jasmine.objectContaining(post)); | |
| 125 | - expect(( < any > articleBlog)["totalPosts"]).toEqual(1); | |
| 126 | - | |
| 127 | - done(); | |
| 128 | - }); | |
| 129 | - | |
| 130 | - }); | |
| 131 | - | |
| 132 | -}); | |
| 133 | 0 | \ No newline at end of file |
src/app/components/noosfero-articles/blog/blog.component.ts
| ... | ... | @@ -1,48 +0,0 @@ |
| 1 | -import {Component, Input, Inject} from "ng-forward"; | |
| 2 | - | |
| 3 | -import {Article, Profile} from "./../../../models/interfaces"; | |
| 4 | -import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
| 5 | - | |
| 6 | -/** | |
| 7 | - * @ngdoc controller | |
| 8 | - * @name ArticleBlog | |
| 9 | - * @description | |
| 10 | - * An specific {@link ArticleView} for Blog articles. | |
| 11 | - */ | |
| 12 | -@Component({ | |
| 13 | - selector: "noosfero-blog", | |
| 14 | - templateUrl: "app/components/noosfero-articles/blog/blog.html" | |
| 15 | -}) | |
| 16 | -@Inject(ArticleService) | |
| 17 | -export class ArticleBlog { | |
| 18 | - | |
| 19 | - @Input() article: Article; | |
| 20 | - @Input() profile: Profile; | |
| 21 | - | |
| 22 | - private posts: any[]; | |
| 23 | - private perPage: number = 3; | |
| 24 | - private currentPage: number; | |
| 25 | - private totalPosts: number = 0; | |
| 26 | - | |
| 27 | - constructor(private articleService: ArticleService) { } | |
| 28 | - | |
| 29 | - ngOnInit() { | |
| 30 | - this.loadPage(); | |
| 31 | - } | |
| 32 | - | |
| 33 | - loadPage() { | |
| 34 | - let filters = { | |
| 35 | - content_type: "TinyMceArticle", | |
| 36 | - per_page: this.perPage, | |
| 37 | - page: this.currentPage | |
| 38 | - }; | |
| 39 | - | |
| 40 | - this.articleService | |
| 41 | - .getChildren(this.article.id, filters) | |
| 42 | - .then((response: restangular.IResponse) => { | |
| 43 | - this.totalPosts = <number>(<any>response.headers("total")); | |
| 44 | - this.posts = response.data.articles; | |
| 45 | - }); | |
| 46 | - } | |
| 47 | - | |
| 48 | -} |
src/app/components/noosfero-articles/blog/blog.html
| ... | ... | @@ -1,24 +0,0 @@ |
| 1 | -<div class="blog"> | |
| 2 | - <div class="blog-cover" ng-show="ctrl.article.image"> | |
| 3 | - <img ng-src="{{ctrl.article.image.url}}" class="img-responsive"> | |
| 4 | - <h3 ng-bind="ctrl.article.title"></h3> | |
| 5 | - </div> | |
| 6 | - | |
| 7 | - <div class="page-header" ng-show="!ctrl.article.image"> | |
| 8 | - <h3 ng-bind="ctrl.article.title"></h3> | |
| 9 | - </div> | |
| 10 | - | |
| 11 | - <div> | |
| 12 | - <div ng-repeat="child in ctrl.posts | orderBy: 'created_at':true"> | |
| 13 | - <div class="page-header"> | |
| 14 | - <a class="title" ui-sref="main.profile.page({profile: ctrl.profile.identifier, page: child.path})"><h4 ng-bind="child.title"></h4></a> | |
| 15 | - <div class="post-lead" ng-bind-html="child.body | truncate: 500: '...': true"></div> | |
| 16 | - </div> | |
| 17 | - </div> | |
| 18 | - </div> | |
| 19 | - | |
| 20 | - <pagination ng-model="ctrl.currentPage" total-items="ctrl.totalPosts" class="pagination-sm center-block" | |
| 21 | - boundary-links="true" items-per-page="ctrl.perPage" ng-change="ctrl.loadPage()" | |
| 22 | - first-text="«" last-text="»" previous-text="‹" next-text="›"> | |
| 23 | - </pagination> | |
| 24 | -</div> |
src/app/components/noosfero-articles/blog/blog.scss
src/app/components/noosfero-articles/blog/index.ts
src/app/components/noosfero-articles/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/block.component.spec.ts
| ... | ... | @@ -1,91 +0,0 @@ |
| 1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Input, provide, Component} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import {Block} from './block.component'; | |
| 5 | - | |
| 6 | -const tcb = new TestComponentBuilder(); | |
| 7 | - | |
| 8 | -const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block>'; | |
| 9 | - | |
| 10 | -describe("Components", () => { | |
| 11 | - describe("Block Component", () => { | |
| 12 | - | |
| 13 | - // the karma preprocessor html2js transform the templates html into js files which put | |
| 14 | - // the templates to the templateCache into the module templates | |
| 15 | - // we need to load the module templates here as the template for the | |
| 16 | - // component Block will be load on our tests | |
| 17 | - beforeEach(angular.mock.module("templates")); | |
| 18 | - | |
| 19 | - it("receives the block and the owner as inputs", done => { | |
| 20 | - | |
| 21 | - // Creating a container component (BlockContainerComponent) to include | |
| 22 | - // the component under test (Block) | |
| 23 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [Block] }) | |
| 24 | - class BlockContainerComponent { | |
| 25 | - block = { type: 'Block' }; | |
| 26 | - owner = { name: 'profile-name' }; | |
| 27 | - constructor() { | |
| 28 | - } | |
| 29 | - } | |
| 30 | - | |
| 31 | - // uses the TestComponentBuilder instance to initialize the component | |
| 32 | - tcb | |
| 33 | - .createAsync(BlockContainerComponent).then(fixture => { | |
| 34 | - // and here we can inspect and run the test assertions | |
| 35 | - let myComponent: Block = fixture.componentInstance; | |
| 36 | - | |
| 37 | - // assure the block object inside the Block matches | |
| 38 | - // the provided through the parent component | |
| 39 | - expect(myComponent.block.type).toEqual("Block"); | |
| 40 | - expect(myComponent.owner.name).toEqual("profile-name"); | |
| 41 | - done(); | |
| 42 | - }); | |
| 43 | - }); | |
| 44 | - | |
| 45 | - | |
| 46 | - it("renders a component which matches to the block type", done => { | |
| 47 | - // CustomBlock component created to check if it will be used | |
| 48 | - // when a block with type 'CustomBlock' is provided to the noosfero-block (Block) | |
| 49 | - // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
| 50 | - @Component({ selector: 'noosfero-custom-block', template: "<h1>My Custom Block</h1>" }) | |
| 51 | - class CustomBlock { | |
| 52 | - @Input() block: any; | |
| 53 | - @Input() owner: any; | |
| 54 | - } | |
| 55 | - | |
| 56 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [Block, CustomBlock] }) | |
| 57 | - class CustomBlockType { | |
| 58 | - block = { type: 'CustomBlock' }; | |
| 59 | - owner = { name: 'profile-name' }; | |
| 60 | - constructor() { | |
| 61 | - } | |
| 62 | - } | |
| 63 | - tcb | |
| 64 | - .createAsync(CustomBlockType).then(fixture => { | |
| 65 | - let myComponent: CustomBlockType = fixture.componentInstance; | |
| 66 | - expect(myComponent.block.type).toEqual("CustomBlock"); | |
| 67 | - expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("My Custom Block"); | |
| 68 | - done(); | |
| 69 | - }); | |
| 70 | - }); | |
| 71 | - | |
| 72 | - | |
| 73 | - it("renders the default block when hasn't defined a block type", done => { | |
| 74 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [Block] }) | |
| 75 | - class CustomBlockType { | |
| 76 | - block: any = { type: null }; | |
| 77 | - owner: any = { name: 'profile-name' }; | |
| 78 | - constructor() { | |
| 79 | - } | |
| 80 | - } | |
| 81 | - tcb | |
| 82 | - .createAsync(CustomBlockType).then(fixture => { | |
| 83 | - let myComponent: CustomBlockType = fixture.componentInstance; | |
| 84 | - expect(myComponent.block.type).toBeNull(); | |
| 85 | - expect(!!fixture.debugElement.nativeElement.querySelector("noosfero-default-block")).toBeTruthy(); | |
| 86 | - done(); | |
| 87 | - }); | |
| 88 | - }); | |
| 89 | - | |
| 90 | - }); | |
| 91 | -}); | |
| 92 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/block.component.ts
| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | -import { Input, Inject, Component } from 'ng-forward'; | |
| 2 | - | |
| 3 | -@Component({ | |
| 4 | - selector: 'noosfero-block', | |
| 5 | - template: '<div></div>' | |
| 6 | -}) | |
| 7 | -@Inject("$element", "$scope", "$injector", "$compile") | |
| 8 | -export class Block { | |
| 9 | - | |
| 10 | - @Input() block: any; | |
| 11 | - @Input() owner: any; | |
| 12 | - | |
| 13 | - ngOnInit() { | |
| 14 | - let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
| 15 | - this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); | |
| 16 | - } | |
| 17 | - | |
| 18 | - constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) { | |
| 19 | - } | |
| 20 | -} |
src/app/components/noosfero-blocks/block.scss
src/app/components/noosfero-blocks/index.ts
src/app/components/noosfero-blocks/link-list/index.ts
src/app/components/noosfero-blocks/link-list/link-list.component.spec.ts
| ... | ... | @@ -1,65 +0,0 @@ |
| 1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | -import {provideFilters} from '../../../../spec/helpers'; | |
| 4 | - | |
| 5 | -import {LinkListBlock} from './link-list.component'; | |
| 6 | - | |
| 7 | -const tcb = new TestComponentBuilder(); | |
| 8 | - | |
| 9 | -const htmlTemplate: string = '<noosfero-link-list-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-link-list-block>'; | |
| 10 | - | |
| 11 | - | |
| 12 | -describe("Components", () => { | |
| 13 | - | |
| 14 | - describe("Link List Block Component", () => { | |
| 15 | - | |
| 16 | - beforeEach(angular.mock.module("templates")); | |
| 17 | - | |
| 18 | - it("receives the block and the owner as inputs", done => { | |
| 19 | - | |
| 20 | - // Creating a container component (BlockContainerComponent) to include | |
| 21 | - // the component under test (Block) | |
| 22 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [LinkListBlock] }) | |
| 23 | - class BlockContainerComponent { | |
| 24 | - block = { type: 'Block' }; | |
| 25 | - owner = { name: 'profile-name' }; | |
| 26 | - constructor() { | |
| 27 | - } | |
| 28 | - } | |
| 29 | - | |
| 30 | - // uses the TestComponentBuilder instance to initialize the component | |
| 31 | - //.overrideView(LinkListBlock, { template: 'asdasdasd', pipes: [NoosferoTemplate] }) | |
| 32 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 33 | - // and here we can inspect and run the test assertions | |
| 34 | - let myComponent: LinkListBlock = fixture.componentInstance; | |
| 35 | - | |
| 36 | - // assure the block object inside the Block matches | |
| 37 | - // the provided through the parent component | |
| 38 | - expect(myComponent.block.type).toEqual("Block"); | |
| 39 | - expect(myComponent.owner.name).toEqual("profile-name"); | |
| 40 | - done(); | |
| 41 | - }); | |
| 42 | - }); | |
| 43 | - | |
| 44 | - | |
| 45 | - it("display links stored in block settings", done => { | |
| 46 | - | |
| 47 | - @Component({ | |
| 48 | - selector: 'test-container-component', | |
| 49 | - template: htmlTemplate, | |
| 50 | - directives: [LinkListBlock], | |
| 51 | - providers: provideFilters("noosferoTemplateFilter") | |
| 52 | - }) | |
| 53 | - class CustomBlockType { | |
| 54 | - block: any = { settings: { links: [{ name: 'link1', address: 'address1' }, { name: 'link2', address: 'address2' }] } }; | |
| 55 | - owner: any = { name: 'profile-name' }; | |
| 56 | - } | |
| 57 | - tcb.createAsync(CustomBlockType).then(fixture => { | |
| 58 | - expect(fixture.debugElement.queryAll(".link-list-block a").length).toEqual(2); | |
| 59 | - done(); | |
| 60 | - }); | |
| 61 | - }); | |
| 62 | - | |
| 63 | - }); | |
| 64 | - | |
| 65 | -}); | |
| 66 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/link-list/link-list.component.ts
| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | -import {Component, Input} from "ng-forward"; | |
| 2 | - | |
| 3 | -@Component({ | |
| 4 | - selector: "noosfero-link-list-block", | |
| 5 | - templateUrl: "app/components/noosfero-blocks/link-list/link-list.html" | |
| 6 | -}) | |
| 7 | -export class LinkListBlock { | |
| 8 | - | |
| 9 | - @Input() block: any; | |
| 10 | - @Input() owner: any; | |
| 11 | - | |
| 12 | - links: any; | |
| 13 | - | |
| 14 | - ngOnInit() { | |
| 15 | - if (this.block && this.block.settings) { | |
| 16 | - this.links = this.block.settings.links; | |
| 17 | - } | |
| 18 | - } | |
| 19 | - | |
| 20 | -} |
src/app/components/noosfero-blocks/link-list/link-list.html
src/app/components/noosfero-blocks/link-list/link-list.scss
| ... | ... | @@ -1,34 +0,0 @@ |
| 1 | -.icon-event { | |
| 2 | - @extend .fa-calendar; | |
| 3 | -} | |
| 4 | -.icon-photos { | |
| 5 | - @extend .fa-photo; | |
| 6 | -} | |
| 7 | -.icon-edit { | |
| 8 | - @extend .fa-edit; | |
| 9 | -} | |
| 10 | -.icon-ok { | |
| 11 | - @extend .fa-check; | |
| 12 | -} | |
| 13 | -.icon-send { | |
| 14 | - @extend .fa-send-o; | |
| 15 | -} | |
| 16 | -.icon-menu-people { | |
| 17 | - @extend .fa-user; | |
| 18 | -} | |
| 19 | -.icon-forum { | |
| 20 | - @extend .fa-users; | |
| 21 | -} | |
| 22 | -.icon-new { | |
| 23 | - @extend .fa-file-o; | |
| 24 | -} | |
| 25 | -.icon-save { | |
| 26 | - @extend .fa-save; | |
| 27 | -} | |
| 28 | - | |
| 29 | -.link-list-block { | |
| 30 | - a i { | |
| 31 | - line-height: 25px; | |
| 32 | - color: #949494; | |
| 33 | - } | |
| 34 | -} |
src/app/components/noosfero-blocks/main-block/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/main-block/main-block.component.spec.ts
| ... | ... | @@ -1,41 +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 | -}); | |
| 42 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/main-block/main-block.component.ts
src/app/components/noosfero-blocks/main-block/main-block.html
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -<div ui-view="mainBlockContent" autoscroll></div> |
src/app/components/noosfero-blocks/members-block/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/members-block/members-block.component.spec.ts
| ... | ... | @@ -1,53 +0,0 @@ |
| 1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Provider, Input, provide, Component} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import {MembersBlock} from './members-block.component'; | |
| 5 | - | |
| 6 | -const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>'; | |
| 7 | - | |
| 8 | -const tcb = new TestComponentBuilder(); | |
| 9 | - | |
| 10 | -describe("Components", () => { | |
| 11 | - describe("Members Block Component", () => { | |
| 12 | - | |
| 13 | - beforeEach(angular.mock.module("templates")); | |
| 14 | - | |
| 15 | - let state = jasmine.createSpyObj("state", ["go"]); | |
| 16 | - let providers = [ | |
| 17 | - new Provider('truncateFilter', { useValue: () => { } }), | |
| 18 | - new Provider('stripTagsFilter', { useValue: () => { } }), | |
| 19 | - new Provider('$state', { useValue: state }), | |
| 20 | - new Provider('ProfileService', { | |
| 21 | - useValue: { | |
| 22 | - getProfileMembers: (profileId: number, filters: any): any => { | |
| 23 | - return Promise.resolve({ data: { people: [{ identifier: "person1" }] } }); | |
| 24 | - } | |
| 25 | - } | |
| 26 | - }), | |
| 27 | - ]; | |
| 28 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MembersBlock], providers: providers }) | |
| 29 | - class BlockContainerComponent { | |
| 30 | - block = { type: 'Block', settings: {} }; | |
| 31 | - owner = { name: 'profile-name' }; | |
| 32 | - constructor() { | |
| 33 | - } | |
| 34 | - } | |
| 35 | - | |
| 36 | - it("get members of the block owner", done => { | |
| 37 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 38 | - let block: MembersBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 39 | - expect(block.members).toEqual([{ identifier: "person1" }]); | |
| 40 | - done(); | |
| 41 | - }); | |
| 42 | - }); | |
| 43 | - | |
| 44 | - it("render the profile image for each member", done => { | |
| 45 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 46 | - fixture.debugElement.getLocal("$rootScope").$apply(); | |
| 47 | - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1); | |
| 48 | - done(); | |
| 49 | - }); | |
| 50 | - }); | |
| 51 | - | |
| 52 | - }); | |
| 53 | -}); | |
| 54 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/members-block/members-block.component.ts
| ... | ... | @@ -1,25 +0,0 @@ |
| 1 | -import {Input, Inject, Component} from "ng-forward"; | |
| 2 | -import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | |
| 3 | - | |
| 4 | -@Component({ | |
| 5 | - selector: "noosfero-members-block", | |
| 6 | - templateUrl: 'app/components/noosfero-blocks/members-block/members-block.html', | |
| 7 | -}) | |
| 8 | -@Inject(ProfileService) | |
| 9 | -export class MembersBlock { | |
| 10 | - | |
| 11 | - @Input() block: any; | |
| 12 | - @Input() owner: any; | |
| 13 | - | |
| 14 | - members: any = []; | |
| 15 | - | |
| 16 | - constructor(private profileService: ProfileService) { | |
| 17 | - | |
| 18 | - } | |
| 19 | - | |
| 20 | - ngOnInit() { | |
| 21 | - this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => { | |
| 22 | - this.members = response.data.people; | |
| 23 | - }); | |
| 24 | - } | |
| 25 | -} |
src/app/components/noosfero-blocks/members-block/members-block.html
src/app/components/noosfero-blocks/members-block/members-block.scss
| ... | ... | @@ -1,17 +0,0 @@ |
| 1 | -.members-block { | |
| 2 | - .member { | |
| 3 | - img, i.profile-image { | |
| 4 | - width: 60px; | |
| 5 | - } | |
| 6 | - img { | |
| 7 | - display: inline-block; | |
| 8 | - vertical-align: top; | |
| 9 | - } | |
| 10 | - i.profile-image { | |
| 11 | - text-align: center; | |
| 12 | - background-color: #889DB1; | |
| 13 | - color: #F1F1F1; | |
| 14 | - font-size: 4.5em; | |
| 15 | - } | |
| 16 | - } | |
| 17 | -} |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.component.spec.ts
| ... | ... | @@ -1,46 +0,0 @@ |
| 1 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import {ProfileImageBlock} from './profile-image-block.component'; | |
| 5 | - | |
| 6 | -import * as helpers from "./../../../../spec/helpers"; | |
| 7 | - | |
| 8 | -const tcb = new TestComponentBuilder(); | |
| 9 | - | |
| 10 | -const htmlTemplate: string = '<noosfero-profile-image-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-image-block>'; | |
| 11 | - | |
| 12 | -describe("Components", () => { | |
| 13 | - | |
| 14 | - describe("Profile Image Block Component", () => { | |
| 15 | - | |
| 16 | - beforeEach(angular.mock.module("templates")); | |
| 17 | - | |
| 18 | - @Component({ | |
| 19 | - selector: 'test-container-component', | |
| 20 | - template: htmlTemplate, | |
| 21 | - directives: [ProfileImageBlock], | |
| 22 | - providers: helpers.provideFilters("translateFilter") | |
| 23 | - }) | |
| 24 | - class BlockContainerComponent { | |
| 25 | - block = { type: 'Block' }; | |
| 26 | - owner = { name: 'profile-name' }; | |
| 27 | - constructor() { | |
| 28 | - } | |
| 29 | - } | |
| 30 | - | |
| 31 | - it("show image if present", () => { | |
| 32 | - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 33 | - var 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 | - var elProfile = fixture.debugElement.componentViewChildren[0]; | |
| 41 | - expect(elProfile.query('a.settings-link').length).toEqual(1); | |
| 42 | - }); | |
| 43 | - }); | |
| 44 | - | |
| 45 | - }); | |
| 46 | -}); |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.component.ts
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -import {Inject, Input, Component} from "ng-forward"; | |
| 2 | -import {ProfileImage} from "./../../../components/noosfero/profile-image/profile-image.component"; | |
| 3 | - | |
| 4 | -@Component({ | |
| 5 | - selector: "noosfero-profile-image-block", | |
| 6 | - templateUrl: 'app/components/noosfero-blocks/profile-image-block/profile-image-block.html', | |
| 7 | - directives: [ProfileImage] | |
| 8 | -}) | |
| 9 | -export class ProfileImageBlock { | |
| 10 | - | |
| 11 | - @Input() block: any; | |
| 12 | - @Input() owner: any; | |
| 13 | - | |
| 14 | -} |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.html
| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | -<div class="center-block text-center profile-image-block"> | |
| 2 | - <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})"> | |
| 3 | - <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> | |
| 4 | - </a> | |
| 5 | - <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> | |
| 6 | -</div> |
src/app/components/noosfero-blocks/profile-image-block/profile-image-block.scss
src/app/components/noosfero-blocks/raw-html/raw-html.component.spec.ts
| ... | ... | @@ -1,36 +0,0 @@ |
| 1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Component} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import {RawHTMLBlock} from './raw-html.component'; | |
| 5 | - | |
| 6 | -const tcb = new TestComponentBuilder(); | |
| 7 | - | |
| 8 | -const htmlTemplate: string = '<noosfero-raw-htmlblock [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-raw-htmlblock>'; | |
| 9 | - | |
| 10 | -describe("Components", () => { | |
| 11 | - | |
| 12 | - describe("Raw Html Block Component", () => { | |
| 13 | - | |
| 14 | - beforeEach(angular.mock.module("templates")); | |
| 15 | - beforeEach(angular.mock.module("ngSanitize")); | |
| 16 | - | |
| 17 | - it("display html stored in block settings", done => { | |
| 18 | - | |
| 19 | - @Component({ | |
| 20 | - selector: 'test-container-component', | |
| 21 | - template: htmlTemplate, | |
| 22 | - directives: [RawHTMLBlock], | |
| 23 | - }) | |
| 24 | - class CustomBlockType { | |
| 25 | - block: any = { settings: { html: '<em>block content</em>' } }; | |
| 26 | - owner: any = { name: 'profile-name' }; | |
| 27 | - } | |
| 28 | - tcb.createAsync(CustomBlockType).then(fixture => { | |
| 29 | - expect(fixture.debugElement.query(".raw-html-block em").text().trim()).toEqual('block content'); | |
| 30 | - done(); | |
| 31 | - }); | |
| 32 | - }); | |
| 33 | - | |
| 34 | - }); | |
| 35 | - | |
| 36 | -}); |
src/app/components/noosfero-blocks/raw-html/raw-html.component.ts
| ... | ... | @@ -1,18 +0,0 @@ |
| 1 | -import {Component, Input} from "ng-forward"; | |
| 2 | - | |
| 3 | -@Component({ | |
| 4 | - selector: "noosfero-raw-htmlblock", | |
| 5 | - templateUrl: 'app/components/noosfero-blocks/raw-html/raw-html.html' | |
| 6 | -}) | |
| 7 | - | |
| 8 | -export class RawHTMLBlock { | |
| 9 | - | |
| 10 | - @Input() block: any; | |
| 11 | - @Input() owner: any; | |
| 12 | - | |
| 13 | - html: string; | |
| 14 | - | |
| 15 | - ngOnInit() { | |
| 16 | - this.html = this.block.settings.html; | |
| 17 | - } | |
| 18 | -} |
src/app/components/noosfero-blocks/raw-html/raw-html.html
src/app/components/noosfero-blocks/recent-documents/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero-blocks/recent-documents/recent-documents.component.spec.ts
| ... | ... | @@ -1,53 +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 | - beforeEach(angular.mock.module("templates")); | |
| 14 | - | |
| 15 | - let state = jasmine.createSpyObj("state", ["go"]); | |
| 16 | - let providers = [ | |
| 17 | - new Provider('$state', { useValue: state }), | |
| 18 | - new Provider('ArticleService', { | |
| 19 | - useValue: { | |
| 20 | - getByProfile: (profileId: number, filters: any): any => { | |
| 21 | - return Promise.resolve({ data: { articles: [{ name: "article1" }] } }); | |
| 22 | - } | |
| 23 | - } | |
| 24 | - }), | |
| 25 | - ].concat(provideFilters("truncateFilter", "stripTagsFilter")); | |
| 26 | - | |
| 27 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [RecentDocumentsBlock], providers: providers }) | |
| 28 | - class BlockContainerComponent { | |
| 29 | - block = { type: 'Block', settings: {} }; | |
| 30 | - owner = { name: 'profile-name' }; | |
| 31 | - constructor() { | |
| 32 | - } | |
| 33 | - } | |
| 34 | - | |
| 35 | - it("get recent documents from the article service", done => { | |
| 36 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 37 | - let recentDocumentsBlock: RecentDocumentsBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 38 | - expect(recentDocumentsBlock.documents).toEqual([{ name: "article1" }]); | |
| 39 | - done(); | |
| 40 | - }); | |
| 41 | - }); | |
| 42 | - | |
| 43 | - it("go to article page when open a document", done => { | |
| 44 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 45 | - let recentDocumentsBlock: RecentDocumentsBlock = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 46 | - recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
| 47 | - expect(state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "identifier" }); | |
| 48 | - done(); | |
| 49 | - }); | |
| 50 | - }); | |
| 51 | - | |
| 52 | - }); | |
| 53 | -}); | |
| 54 | 0 | \ No newline at end of file |
src/app/components/noosfero-blocks/recent-documents/recent-documents.component.ts
| ... | ... | @@ -1,38 +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 | - var limit = (this.block && this.block.settings) ? this.block.settings.limit : null || 5; | |
| 27 | - //FIXME get all text articles | |
| 28 | - this.articleService.getByProfile(this.profile.id, { content_type: 'TinyMceArticle', per_page: limit }).then((response: any) => { | |
| 29 | - this.documents = response.data.articles; | |
| 30 | - this.documentsLoaded = true; | |
| 31 | - }); | |
| 32 | - } | |
| 33 | - | |
| 34 | - openDocument(article: any) { | |
| 35 | - this.$state.go("main.profile.page", { page: article.path, profile: article.profile.identifier }); | |
| 36 | - } | |
| 37 | - | |
| 38 | -} |
src/app/components/noosfero-blocks/recent-documents/recent-documents.html
| ... | ... | @@ -1,18 +0,0 @@ |
| 1 | -<div deckgrid source="ctrl.documents" class="deckgrid"> | |
| 2 | - <div class="a-card panel media" ng-click="mother.ctrl.openDocument(card);"> | |
| 3 | - <div class="author media-left" ng-show="card.author.image"> | |
| 4 | - <img ng-src="{{card.author.image.url}}" class="img-circle"> | |
| 5 | - </div> | |
| 6 | - <div class="header media-body"> | |
| 7 | - <h5 class="title media-heading" ng-bind="card.title"></h5> | |
| 8 | - | |
| 9 | - <div class="subheader"> | |
| 10 | - <span class="time"> | |
| 11 | - <i class="fa fa-clock-o"></i> <span am-time-ago="card.created_at | dateFormat"></span> | |
| 12 | - </span> | |
| 13 | - </div> | |
| 14 | - </div> | |
| 15 | - <img ng-show="card.image" ng-src="{{card.image.url}}" class="img-responsive article-image"> | |
| 16 | - <div class="post-lead" ng-bind-html="card.body | stripTags | truncate: 100: '...': true"></div> | |
| 17 | - </div> | |
| 18 | -</div> |
src/app/components/noosfero-blocks/recent-documents/recent-documents.scss
| ... | ... | @@ -1,65 +0,0 @@ |
| 1 | -.block.recentdocumentsblock { | |
| 2 | - .deckgrid[deckgrid]::before { | |
| 3 | - font-size: 0; /* See https://github.com/akoenig/angular-deckgrid/issues/14#issuecomment-35728861 */ | |
| 4 | - visibility: hidden; | |
| 5 | - } | |
| 6 | - .author { | |
| 7 | - img { | |
| 8 | - width: 30px; | |
| 9 | - height: 30px; | |
| 10 | - } | |
| 11 | - } | |
| 12 | - .header { | |
| 13 | - .subheader { | |
| 14 | - color: #C1C1C1; | |
| 15 | - font-size: 10px; | |
| 16 | - } | |
| 17 | - } | |
| 18 | - .post-lead { | |
| 19 | - color: #8E8E8E; | |
| 20 | - font-size: 14px; | |
| 21 | - } | |
| 22 | - .article-image { | |
| 23 | - margin: 10px 0; | |
| 24 | - } | |
| 25 | -} | |
| 26 | - | |
| 27 | -.col-md-2-5 { | |
| 28 | - .deckgrid[deckgrid]::before { | |
| 29 | - content: '1 .deck-column'; | |
| 30 | - } | |
| 31 | -} | |
| 32 | - | |
| 33 | -.col-md-7 { | |
| 34 | - .block.recentdocumentsblock { | |
| 35 | - background-color: transparent; | |
| 36 | - border: 0; | |
| 37 | - | |
| 38 | - .deckgrid[deckgrid]::before { | |
| 39 | - content: '3 .deck-column'; | |
| 40 | - } | |
| 41 | - | |
| 42 | - .panel-heading { | |
| 43 | - display: none; | |
| 44 | - } | |
| 45 | - .panel-body { | |
| 46 | - padding: 0; | |
| 47 | - } | |
| 48 | - | |
| 49 | - .deckgrid { | |
| 50 | - .column { | |
| 51 | - float: left; | |
| 52 | - } | |
| 53 | - | |
| 54 | - .deck-column { | |
| 55 | - @extend .col-md-4; | |
| 56 | - padding: 0; | |
| 57 | - | |
| 58 | - .a-card { | |
| 59 | - padding: 10px; | |
| 60 | - margin: 3px; | |
| 61 | - } | |
| 62 | - } | |
| 63 | - } | |
| 64 | - } | |
| 65 | -} |
src/app/components/noosfero-boxes/box.html
| ... | ... | @@ -1,10 +0,0 @@ |
| 1 | -<div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> | |
| 2 | - <div ng-repeat="block in box.blocks | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > | |
| 3 | - <div class="panel-heading" ng-show="block.title"> | |
| 4 | - <h3 class="panel-title">{{block.title}}</h3> | |
| 5 | - </div> | |
| 6 | - <div class="panel-body"> | |
| 7 | - <noosfero-block [block]="block" [owner]="ctrl.owner"></noosfero-block> | |
| 8 | - </div> | |
| 9 | - </div> | |
| 10 | -</div> |
src/app/components/noosfero-boxes/boxes.component.spec.ts
| ... | ... | @@ -1,67 +0,0 @@ |
| 1 | -import {Component} from 'ng-forward'; | |
| 2 | - | |
| 3 | -import {Box, Profile} from "../../models/interfaces"; | |
| 4 | -import {Boxes} from './boxes.component'; | |
| 5 | - | |
| 6 | -import { | |
| 7 | - createComponentFromClass, | |
| 8 | - quickCreateComponent, | |
| 9 | - provideEmptyObjects, | |
| 10 | - createProviderToValue, | |
| 11 | - getAngularServiceFactory, | |
| 12 | - provideFilters | |
| 13 | -} from "../../../spec/helpers"; | |
| 14 | - | |
| 15 | -// this htmlTemplate will be re-used between the container components in this spec file | |
| 16 | -const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl.profile"></noosfero-blog>'; | |
| 17 | - | |
| 18 | - | |
| 19 | -describe("Boxes Component", () => { | |
| 20 | - | |
| 21 | - beforeEach(() => { | |
| 22 | - angular.mock.module("templates") | |
| 23 | - }) | |
| 24 | - | |
| 25 | - @Component({ | |
| 26 | - selector: 'test-container-component', | |
| 27 | - template: htmlTemplate, | |
| 28 | - directives: [Boxes], | |
| 29 | - providers: [] | |
| 30 | - }) | |
| 31 | - class BoxesContainerComponent { | |
| 32 | - boxes: Box[] = [ | |
| 33 | - { id: 1, position: 1 }, | |
| 34 | - { id: 2, position: 2 } | |
| 35 | - ]; | |
| 36 | - | |
| 37 | - owner: Profile = { | |
| 38 | - id: 1, | |
| 39 | - identifier: 'profile-name', | |
| 40 | - type: 'Person' | |
| 41 | - }; | |
| 42 | - } | |
| 43 | - | |
| 44 | - it("renders boxes into a container", (done: Function) => { | |
| 45 | - createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
| 46 | - | |
| 47 | - var boxesHtml = fixture.debugElement; | |
| 48 | - expect(boxesHtml.query('div.col-md-7').length).toEqual(1); | |
| 49 | - expect(boxesHtml.query('div.col-md-2-5').length).toEqual(1); | |
| 50 | - | |
| 51 | - done(); | |
| 52 | - }); | |
| 53 | - }); | |
| 54 | - | |
| 55 | - it("check the boxes order", (done: Function) => { | |
| 56 | - createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
| 57 | - | |
| 58 | - let boxesComponent: Boxes = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 59 | - let boxesContainer: BoxesContainerComponent = fixture.componentInstance; | |
| 60 | - | |
| 61 | - expect(boxesComponent.boxesOrder(boxesContainer.boxes[0])).toEqual(1); | |
| 62 | - expect(boxesComponent.boxesOrder(boxesContainer.boxes[1])).toEqual(0); | |
| 63 | - | |
| 64 | - done(); | |
| 65 | - }); | |
| 66 | - }); | |
| 67 | -}); |
src/app/components/noosfero-boxes/boxes.component.ts
| ... | ... | @@ -1,17 +0,0 @@ |
| 1 | -import {Input, Inject, Component} from 'ng-forward'; | |
| 2 | -import {Box, Profile} from "./../../models/interfaces"; | |
| 3 | - | |
| 4 | -@Component({ | |
| 5 | - selector: "noosfero-boxes", | |
| 6 | - templateUrl: "app/components/noosfero-boxes/boxes.html" | |
| 7 | -}) | |
| 8 | -export class Boxes { | |
| 9 | - | |
| 10 | - @Input() boxes: Box[]; | |
| 11 | - @Input() owner: Profile; | |
| 12 | - | |
| 13 | - boxesOrder(box: Box) { | |
| 14 | - if (box.position === 2) return 0; | |
| 15 | - return box.position; | |
| 16 | - } | |
| 17 | -} |
src/app/components/noosfero-boxes/boxes.html
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -<ng-include ng-repeat="box in ctrl.boxes | orderBy: ctrl.boxesOrder" src="'app/components/noosfero-boxes/box.html'"></ng-include> |
src/app/components/noosfero-boxes/boxes.scss
src/app/components/noosfero-boxes/index.ts
src/app/components/noosfero/date-format/date-format.filter.spec.ts
| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | -import {quickCreateComponent} from "../../../../spec/helpers"; | |
| 2 | -import {DateFormat} from './date-format.filter'; | |
| 3 | - | |
| 4 | -describe("Filters", () => { | |
| 5 | - describe("Date Format Filter", () => { | |
| 6 | - | |
| 7 | - beforeEach(angular.mock.module("templates")); | |
| 8 | - beforeEach(angular.mock.module("angularMoment")); | |
| 9 | - | |
| 10 | - it("convert date from the format returned by noosfero api to an ISO format", done => { | |
| 11 | - let date = "2016/03/10 10:46:47"; | |
| 12 | - let htmlTemplate = `{{ '${date}' | dateFormat }}`; | |
| 13 | - quickCreateComponent({ providers: [DateFormat], template: htmlTemplate }).then(fixture => { | |
| 14 | - expect(fixture.debugElement.text()).toEqual('2016-03-10T13:46:47.000Z'); | |
| 15 | - done(); | |
| 16 | - }); | |
| 17 | - }); | |
| 18 | - | |
| 19 | - }); | |
| 20 | -}); |
src/app/components/noosfero/date-format/date-format.filter.ts
| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | -import {Pipe, Inject} from "ng-forward"; | |
| 2 | - | |
| 3 | -@Pipe("dateFormat") | |
| 4 | -@Inject("amParseFilter") | |
| 5 | -export class DateFormat { | |
| 6 | - | |
| 7 | - constructor(private amParseFilter: any) { } | |
| 8 | - | |
| 9 | - transform(date: string, options: any) { | |
| 10 | - return this.amParseFilter(date, "YYYY/MM/DD HH:mm:ss").toISOString(); | |
| 11 | - } | |
| 12 | - | |
| 13 | -} |
src/app/components/noosfero/index.ts
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -/* Module Index Entry - generated using the script npm run generate-index */ |
src/app/components/noosfero/noosfero-template.filter.spec.ts
| ... | ... | @@ -1,19 +0,0 @@ |
| 1 | -import {quickCreateComponent} from "../../../spec/helpers"; | |
| 2 | -import {NoosferoTemplate} from './noosfero-template.filter'; | |
| 3 | - | |
| 4 | -describe("Filters", () => { | |
| 5 | - describe("Noosfero Template Filter", () => { | |
| 6 | - | |
| 7 | - beforeEach(angular.mock.module("templates")); | |
| 8 | - | |
| 9 | - it("replace the options in text with the values passed on options", done => { | |
| 10 | - let text = 'profile: {profile}, other: {other}'; | |
| 11 | - let htmlTemplate = `{{ '${text}' | noosferoTemplate: {profile: 'profile1', other: 'other value'} }}`; | |
| 12 | - quickCreateComponent({ providers: [NoosferoTemplate], template: htmlTemplate }).then(fixture => { | |
| 13 | - expect(fixture.debugElement.text()).toEqual("profile: profile1, other: other value"); | |
| 14 | - done(); | |
| 15 | - }); | |
| 16 | - }); | |
| 17 | - | |
| 18 | - }); | |
| 19 | -}); |
src/app/components/noosfero/noosfero-template.filter.ts
| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | -import {Pipe} from "ng-forward"; | |
| 2 | - | |
| 3 | -@Pipe("noosferoTemplate") | |
| 4 | -export class NoosferoTemplate { | |
| 5 | - | |
| 6 | - transform(text: string, options: any) { | |
| 7 | - for (var option in options) { | |
| 8 | - text = text.replace('{' + option + '}', options[option]); | |
| 9 | - } | |
| 10 | - return text; | |
| 11 | - } | |
| 12 | - | |
| 13 | -} |
src/app/components/noosfero/profile-image/index.ts
src/app/components/noosfero/profile-image/profile-image.component.spec.ts
| ... | ... | @@ -1,51 +0,0 @@ |
| 1 | -/** | |
| 2 | - * @ngdoc overview | |
| 3 | - * @name components.noosfero.profile-image.ProfileImageSpec | |
| 4 | - * @description | |
| 5 | - * This file contains the tests for the {@link components.noosfero.profile-image.ProfileImage} component. | |
| 6 | - */ | |
| 7 | - | |
| 8 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 9 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 10 | - | |
| 11 | -import * as helpers from "./../../../../spec/helpers"; | |
| 12 | - | |
| 13 | -import {ProfileImage} from "./profile-image.component" | |
| 14 | - | |
| 15 | -const tcb = new TestComponentBuilder(); | |
| 16 | - | |
| 17 | -describe("Components", () => { | |
| 18 | - | |
| 19 | - describe("Profile Image Component", () => { | |
| 20 | - | |
| 21 | - beforeEach(angular.mock.module("templates")); | |
| 22 | - | |
| 23 | - it("show community users image if profile is not Person", done => { | |
| 24 | - helpers.tcb.createAsync(ProfileImage).then(fixture => { | |
| 25 | - let profileImageComponent : ProfileImage = fixture.componentInstance | |
| 26 | - let profile = { 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 = { id: 1, identifier: "myprofile", type: "Person" } | |
| 42 | - profileImageComponent.profile = profile | |
| 43 | - profileImageComponent.ngOnInit(); | |
| 44 | - // Check the attribute | |
| 45 | - expect(profileImageComponent.defaultIcon).toEqual("fa-user", "The default icon should be person user"); | |
| 46 | - done(); | |
| 47 | - }); | |
| 48 | - }); | |
| 49 | - | |
| 50 | - }); | |
| 51 | -}); | |
| 52 | 0 | \ No newline at end of file |
src/app/components/noosfero/profile-image/profile-image.component.ts
| ... | ... | @@ -1,50 +0,0 @@ |
| 1 | -import {Inject, Input, Component} from "ng-forward"; | |
| 2 | -import {Profile} from "./../../../models/interfaces"; | |
| 3 | - | |
| 4 | - | |
| 5 | -/** | |
| 6 | - * @ngdoc controller | |
| 7 | - * @name components.noosfero.profile-image.ProfileImage | |
| 8 | - * @description The component responsible for rendering the profile image | |
| 9 | - * @exports ProfileImage | |
| 10 | - */ | |
| 11 | -@Component({ | |
| 12 | - selector: "noosfero-profile-image", | |
| 13 | - templateUrl: 'app/components/noosfero/profile-image/profile-image.html', | |
| 14 | -}) | |
| 15 | -export class ProfileImage { | |
| 16 | - | |
| 17 | - | |
| 18 | - /** | |
| 19 | - * @ngdoc property | |
| 20 | - * @name profile | |
| 21 | - * @propertyOf components.noosfero.profile-image.ProfileImage | |
| 22 | - * @description | |
| 23 | - * The Noosfero {@link models.Profile} holding the image. | |
| 24 | - */ | |
| 25 | - @Input() profile: Profile; | |
| 26 | - /** | |
| 27 | - * @ngdoc property | |
| 28 | - * @name defaultIcon | |
| 29 | - * @propertyOf components.noosfero.profile-image.ProfileImage | |
| 30 | - * @descritpion | |
| 31 | - * The default icon used by this profile | |
| 32 | - */ | |
| 33 | - defaultIcon: string; | |
| 34 | - | |
| 35 | - | |
| 36 | - /** | |
| 37 | - * @ngdoc method | |
| 38 | - * @name ngOnInit | |
| 39 | - * @methodOf components.noosfero.profile-image.ProfileImage | |
| 40 | - * @description | |
| 41 | - * Initializes the icon names to their corresponding values depending on the profile type passed to the controller | |
| 42 | - */ | |
| 43 | - ngOnInit() { | |
| 44 | - this.defaultIcon = 'fa-users'; | |
| 45 | - if (this.profile && this.profile.type === 'Person') { | |
| 46 | - this.defaultIcon = 'fa-user'; | |
| 47 | - } | |
| 48 | - } | |
| 49 | -} | |
| 50 | - |
src/app/components/noosfero/profile-image/profile-image.html
src/app/components/noosfero/profile-image/profile-image.scss
src/app/components/notification/notification.component.spec.ts
| ... | ... | @@ -1,80 +0,0 @@ |
| 1 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import * as helpers from "../../../spec/helpers"; | |
| 5 | - | |
| 6 | -import {Notification} from "./notification.component"; | |
| 7 | - | |
| 8 | -const tcb = new TestComponentBuilder(); | |
| 9 | - | |
| 10 | -describe("Components", () => { | |
| 11 | - | |
| 12 | - describe("Profile Image Component", () => { | |
| 13 | - | |
| 14 | - beforeEach(angular.mock.module("templates")); | |
| 15 | - | |
| 16 | - it("display an error message when notify an error", done => { | |
| 17 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 18 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
| 19 | - | |
| 20 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 21 | - component.error("message", "title"); | |
| 22 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 23 | - text: "message", | |
| 24 | - title: "title", | |
| 25 | - type: "error" | |
| 26 | - })); | |
| 27 | - done(); | |
| 28 | - }); | |
| 29 | - | |
| 30 | - it("use the default message when call notification component without a message", done => { | |
| 31 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 32 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
| 33 | - | |
| 34 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 35 | - component.error(); | |
| 36 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 37 | - text: Notification.DEFAULT_ERROR_MESSAGE, | |
| 38 | - type: "error" | |
| 39 | - })); | |
| 40 | - done(); | |
| 41 | - }); | |
| 42 | - | |
| 43 | - it("display a success message when call notification success", done => { | |
| 44 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 45 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
| 46 | - | |
| 47 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 48 | - component.success("title", "message", 1000); | |
| 49 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 50 | - type: "success" | |
| 51 | - })); | |
| 52 | - done(); | |
| 53 | - }); | |
| 54 | - | |
| 55 | - it("display a message relative to the http error code", done => { | |
| 56 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 57 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
| 58 | - | |
| 59 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 60 | - component.httpError(500, {}); | |
| 61 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 62 | - text: "notification.http_error.500.message" | |
| 63 | - })); | |
| 64 | - done(); | |
| 65 | - }); | |
| 66 | - | |
| 67 | - it("set the default timer in success messages", done => { | |
| 68 | - let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 69 | - sweetAlert.swal = jasmine.createSpy("swal"); | |
| 70 | - | |
| 71 | - let component: Notification = new Notification(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 72 | - component.success("title", "message"); | |
| 73 | - expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 74 | - type: "success", | |
| 75 | - timer: Notification.DEFAULT_SUCCESS_TIMER | |
| 76 | - })); | |
| 77 | - done(); | |
| 78 | - }); | |
| 79 | - }); | |
| 80 | -}); |
src/app/components/notification/notification.component.ts
| ... | ... | @@ -1,41 +0,0 @@ |
| 1 | -import {Injectable, Inject} from "ng-forward"; | |
| 2 | -import {TranslatorService} from "../translator/translator.service"; | |
| 3 | - | |
| 4 | -@Injectable() | |
| 5 | -@Inject("$log", "SweetAlert", TranslatorService) | |
| 6 | -export class Notification { | |
| 7 | - | |
| 8 | - constructor( | |
| 9 | - private $log: ng.ILogService, | |
| 10 | - private SweetAlert: any, | |
| 11 | - private translatorService: TranslatorService | |
| 12 | - ) { } | |
| 13 | - | |
| 14 | - public static DEFAULT_ERROR_TITLE = "notification.error.default.title"; | |
| 15 | - public static DEFAULT_ERROR_MESSAGE = "notification.error.default.message"; | |
| 16 | - public static DEFAULT_SUCCESS_TIMER = 1000; | |
| 17 | - | |
| 18 | - error(message: string = Notification.DEFAULT_ERROR_MESSAGE, title: string = Notification.DEFAULT_ERROR_TITLE) { | |
| 19 | - this.$log.debug("Notification error:", title, message, this.translatorService.currentLanguage()); | |
| 20 | - this.SweetAlert.swal({ | |
| 21 | - title: this.translatorService.translate(title), | |
| 22 | - text: this.translatorService.translate(message), | |
| 23 | - type: "error" | |
| 24 | - }); | |
| 25 | - } | |
| 26 | - | |
| 27 | - httpError(status: number, data: any): boolean { | |
| 28 | - this.error(`notification.http_error.${status}.message`); | |
| 29 | - return true; // return true to indicate that the error was already handled | |
| 30 | - } | |
| 31 | - | |
| 32 | - success(title: string, text: string, timer: number = Notification.DEFAULT_SUCCESS_TIMER) { | |
| 33 | - this.SweetAlert.swal({ | |
| 34 | - title: title, | |
| 35 | - text: text, | |
| 36 | - type: "success", | |
| 37 | - timer: timer | |
| 38 | - }); | |
| 39 | - } | |
| 40 | - | |
| 41 | -} |
src/app/components/translator/translator.service.spec.ts
| ... | ... | @@ -1,116 +0,0 @@ |
| 1 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | -import {provide} from 'ng-forward'; | |
| 3 | - | |
| 4 | -import {TranslatorService} from './translator.service'; | |
| 5 | - | |
| 6 | -import * as helpers from "../../../spec/helpers"; | |
| 7 | - | |
| 8 | -describe("Services", () => { | |
| 9 | - | |
| 10 | - describe("Translator Service", () => { | |
| 11 | - | |
| 12 | - let $rootScope: ng.IScope; | |
| 13 | - let $q: ng.IQService; | |
| 14 | - | |
| 15 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
| 16 | - $rootScope = _$rootScope_; | |
| 17 | - $q = _$q_; | |
| 18 | - })); | |
| 19 | - | |
| 20 | - function createComponent() { | |
| 21 | - return new TranslatorService( | |
| 22 | - <any>helpers.mocks.$translate, | |
| 23 | - <any>helpers.mocks.tmhDynamicLocale, | |
| 24 | - <any>helpers.mocks.amMoment, | |
| 25 | - <any>helpers.mocks.angularLoad, | |
| 26 | - $rootScope | |
| 27 | - ); | |
| 28 | - } | |
| 29 | - | |
| 30 | - it("set available languages when change language", (done) => { | |
| 31 | - let component: TranslatorService = createComponent(); | |
| 32 | - component.availableLanguages = null; | |
| 33 | - expect(component.availableLanguages).toBeNull(); | |
| 34 | - $rootScope.$emit("$translateChangeSuccess"); | |
| 35 | - expect(component.availableLanguages).not.toBeNull(); | |
| 36 | - done(); | |
| 37 | - }); | |
| 38 | - | |
| 39 | - it("change the language", (done) => { | |
| 40 | - let component: TranslatorService = createComponent(); | |
| 41 | - let loadScripPromise = $q.defer(); | |
| 42 | - loadScripPromise.resolve(); | |
| 43 | - component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue(loadScripPromise.promise); | |
| 44 | - component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
| 45 | - component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue("en"); | |
| 46 | - component["$translate"].use = jasmine.createSpy("use"); | |
| 47 | - | |
| 48 | - component.changeLanguage('pt'); | |
| 49 | - $rootScope.$digest(); | |
| 50 | - | |
| 51 | - expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
| 52 | - expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/messageformat/locale/pt.js"); | |
| 53 | - expect(component["tmhDynamicLocale"].set).toHaveBeenCalledWith("pt"); | |
| 54 | - expect(component["$translate"].use).toHaveBeenCalledWith("pt"); | |
| 55 | - done(); | |
| 56 | - }); | |
| 57 | - | |
| 58 | - it("do not load moment locale when change the language to english", (done) => { | |
| 59 | - let component: TranslatorService = createComponent(); | |
| 60 | - component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue($q.defer().promise); | |
| 61 | - component.changeLanguage('en'); | |
| 62 | - expect(component["angularLoad"].loadScript).not.toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
| 63 | - done(); | |
| 64 | - }); | |
| 65 | - | |
| 66 | - it("do nothing when call change language with null", (done) => { | |
| 67 | - let component: TranslatorService = createComponent(); | |
| 68 | - component["angularLoad"].loadScript = jasmine.createSpy("loadScript"); | |
| 69 | - component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
| 70 | - component["$translate"].use = jasmine.createSpy("use"); | |
| 71 | - | |
| 72 | - component.changeLanguage(null); | |
| 73 | - | |
| 74 | - expect(component["angularLoad"].loadScript).not.toHaveBeenCalled(); | |
| 75 | - expect(component["tmhDynamicLocale"].set).not.toHaveBeenCalled(); | |
| 76 | - expect(component["$translate"].use).not.toHaveBeenCalled(); | |
| 77 | - done(); | |
| 78 | - }); | |
| 79 | - | |
| 80 | - it("return the current language used by the translator", (done) => { | |
| 81 | - let component: TranslatorService = createComponent(); | |
| 82 | - component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
| 83 | - expect(component.currentLanguage()).toEqual("en"); | |
| 84 | - expect(component["$translate"].use).toHaveBeenCalled(); | |
| 85 | - done(); | |
| 86 | - }); | |
| 87 | - | |
| 88 | - it("call translate service when translate a text", (done) => { | |
| 89 | - let component: TranslatorService = createComponent(); | |
| 90 | - component["$translate"].instant = jasmine.createSpy("instant"); | |
| 91 | - component.translate("text"); | |
| 92 | - expect(component["$translate"].instant).toHaveBeenCalledWith("text"); | |
| 93 | - done(); | |
| 94 | - }); | |
| 95 | - | |
| 96 | - it("change the language when receive an event", (done) => { | |
| 97 | - let component: TranslatorService = createComponent(); | |
| 98 | - component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
| 99 | - $rootScope.$emit("$localeChangeSuccess"); | |
| 100 | - expect(component.changeLanguage).toHaveBeenCalled(); | |
| 101 | - done(); | |
| 102 | - }); | |
| 103 | - | |
| 104 | - it("use the translate language when receive a change language event and there is no language previously selected", (done) => { | |
| 105 | - let component: TranslatorService = createComponent(); | |
| 106 | - component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
| 107 | - component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue(null); | |
| 108 | - component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
| 109 | - | |
| 110 | - $rootScope.$emit("$localeChangeSuccess"); | |
| 111 | - expect(component["$translate"].use).toHaveBeenCalled(); | |
| 112 | - expect(component.changeLanguage).toHaveBeenCalledWith("en"); | |
| 113 | - done(); | |
| 114 | - }); | |
| 115 | - }); | |
| 116 | -}); |
src/app/components/translator/translator.service.ts
| ... | ... | @@ -1,59 +0,0 @@ |
| 1 | -import {Injectable, Inject} from "ng-forward"; | |
| 2 | - | |
| 3 | -@Injectable() | |
| 4 | -@Inject("$translate", "tmhDynamicLocale", "amMoment", "angularLoad", "$rootScope") | |
| 5 | -export class TranslatorService { | |
| 6 | - | |
| 7 | - availableLanguages: any; | |
| 8 | - | |
| 9 | - constructor(private $translate: angular.translate.ITranslateService, | |
| 10 | - private tmhDynamicLocale: angular.dynamicLocale.tmhDynamicLocaleService, | |
| 11 | - private amMoment: any, | |
| 12 | - private angularLoad: any, | |
| 13 | - private $rootScope: any) { | |
| 14 | - | |
| 15 | - this.$rootScope.$on("$localeChangeSuccess", () => { | |
| 16 | - this.changeLanguage(tmhDynamicLocale.get() || $translate.use()); | |
| 17 | - }); | |
| 18 | - this.$rootScope.$on("$translateChangeSuccess", () => { | |
| 19 | - this.configAvailableLanguages(); | |
| 20 | - }); | |
| 21 | - } | |
| 22 | - | |
| 23 | - currentLanguage() { | |
| 24 | - return this.$translate.use(); | |
| 25 | - } | |
| 26 | - | |
| 27 | - changeLanguage(language: string) { | |
| 28 | - if (!language) { | |
| 29 | - console.log("WARN: language undefined"); | |
| 30 | - return; | |
| 31 | - } | |
| 32 | - this.changeMomentLocale(language); | |
| 33 | - this.tmhDynamicLocale.set(language); | |
| 34 | - this.angularLoad.loadScript(`/bower_components/messageformat/locale/${language}.js`).then(() => { | |
| 35 | - return this.$translate.use(language); | |
| 36 | - }); | |
| 37 | - } | |
| 38 | - | |
| 39 | - translate(text: string) { | |
| 40 | - return this.$translate.instant(text); | |
| 41 | - } | |
| 42 | - | |
| 43 | - private configAvailableLanguages() { | |
| 44 | - this.availableLanguages = { | |
| 45 | - "en": this.$translate.instant("language.en"), | |
| 46 | - "pt": this.$translate.instant("language.pt") | |
| 47 | - }; | |
| 48 | - } | |
| 49 | - | |
| 50 | - private changeMomentLocale(language: string) { | |
| 51 | - let localePromise = Promise.resolve(); | |
| 52 | - if (language != "en") { | |
| 53 | - localePromise = this.angularLoad.loadScript(`/bower_components/moment/locale/${language}.js`); | |
| 54 | - } | |
| 55 | - localePromise.then(() => { | |
| 56 | - this.amMoment.changeLocale(language); | |
| 57 | - }); | |
| 58 | - } | |
| 59 | -} |
src/app/content-viewer/content-viewer-actions.component.spec.ts
| ... | ... | @@ -1,67 +0,0 @@ |
| 1 | -import {providers} from 'ng-forward/cjs/testing/providers'; | |
| 2 | - | |
| 3 | -import {Input, Component, provide} from 'ng-forward'; | |
| 4 | - | |
| 5 | -import * as helpers from "../../spec/helpers"; | |
| 6 | - | |
| 7 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 8 | -import {ContentViewerActions} from './content-viewer-actions.component'; | |
| 9 | - | |
| 10 | -// this htmlTemplate will be re-used between the container components in this spec file | |
| 11 | -const htmlTemplate: string = '<content-viewer-actions [article]="ctrl.article" [profile]="ctrl.profile"></content-viewer-actions>'; | |
| 12 | - | |
| 13 | -describe('Content Viewer Actions Component', () => { | |
| 14 | - | |
| 15 | - beforeEach(() => { | |
| 16 | - | |
| 17 | - angular.mock.module("templates"); | |
| 18 | - | |
| 19 | - providers((provide: any) => { | |
| 20 | - return <any>[ | |
| 21 | - provide('ProfileService', { | |
| 22 | - useValue: helpers.mocks.profileService | |
| 23 | - }) | |
| 24 | - ]; | |
| 25 | - }); | |
| 26 | - }); | |
| 27 | - | |
| 28 | - let buildComponent = (): Promise<ComponentFixture> => { | |
| 29 | - return helpers.quickCreateComponent({ | |
| 30 | - providers: [ | |
| 31 | - helpers.provideEmptyObjects('Restangular'), | |
| 32 | - helpers.provideFilters('translateFilter') | |
| 33 | - ], | |
| 34 | - directives: [ContentViewerActions], | |
| 35 | - template: htmlTemplate | |
| 36 | - }); | |
| 37 | - }; | |
| 38 | - | |
| 39 | - it('renders content viewer actions directive', (done: Function) => { | |
| 40 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 41 | - expect(fixture.debugElement.query('content-viewer-actions').length).toEqual(1); | |
| 42 | - | |
| 43 | - done(); | |
| 44 | - }); | |
| 45 | - }); | |
| 46 | - | |
| 47 | - it('check if profile was loaded', (done: Function) => { | |
| 48 | - let profile: any = { | |
| 49 | - id: 1, | |
| 50 | - identifier: 'the-profile-test', | |
| 51 | - type: 'Person' | |
| 52 | - }; | |
| 53 | - | |
| 54 | - helpers.mocks.profileService.getCurrentProfile = () => { | |
| 55 | - return helpers.mocks.promiseResultTemplate(profile); | |
| 56 | - }; | |
| 57 | - | |
| 58 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 59 | - let contentViewerComp: ContentViewerActions = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 60 | - | |
| 61 | - expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile)); | |
| 62 | - | |
| 63 | - done(); | |
| 64 | - }); | |
| 65 | - }); | |
| 66 | - | |
| 67 | -}); |
src/app/content-viewer/content-viewer-actions.component.ts
| ... | ... | @@ -1,22 +0,0 @@ |
| 1 | -import {Component, Inject, provide} from "ng-forward"; | |
| 2 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
| 3 | - | |
| 4 | -import {Profile} from "./../models/interfaces"; | |
| 5 | - | |
| 6 | -@Component({ | |
| 7 | - selector: "content-viewer-actions", | |
| 8 | - templateUrl: "app/content-viewer/navbar-actions.html", | |
| 9 | - providers: [provide('profileService', { useClass: ProfileService })] | |
| 10 | -}) | |
| 11 | -@Inject(ProfileService) | |
| 12 | -export class ContentViewerActions { | |
| 13 | - | |
| 14 | - article: any; | |
| 15 | - profile: any; | |
| 16 | - | |
| 17 | - constructor(profileService: ProfileService) { | |
| 18 | - profileService.getCurrentProfile().then((profile: Profile) => { | |
| 19 | - this.profile = profile; | |
| 20 | - }); | |
| 21 | - } | |
| 22 | -} |
src/app/content-viewer/content-viewer.component.spec.ts
| ... | ... | @@ -1,90 +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 | - var article: any = { | |
| 59 | - id: 1, | |
| 60 | - title: 'The article test' | |
| 61 | - }; | |
| 62 | - var 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.getByProfile = (id: number, params: any) => { | |
| 73 | - return helpers.mocks.promiseResultTemplate({ | |
| 74 | - data: { | |
| 75 | - article: article | |
| 76 | - } | |
| 77 | - }); | |
| 78 | - }; | |
| 79 | - | |
| 80 | - | |
| 81 | - buildComponent().then((fixture: ComponentFixture) => { | |
| 82 | - let contentViewerComp: ContentViewer = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 83 | - | |
| 84 | - expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile)); | |
| 85 | - expect(contentViewerComp.article).toEqual(jasmine.objectContaining(article)); | |
| 86 | - | |
| 87 | - done(); | |
| 88 | - }); | |
| 89 | - }); | |
| 90 | -}); |
src/app/content-viewer/content-viewer.component.ts
| ... | ... | @@ -1,37 +0,0 @@ |
| 1 | -import * as noosfero from "../models/interfaces"; | |
| 2 | - | |
| 3 | - | |
| 4 | -import {ArticleView} from "../components/noosfero-articles/article/article_view"; | |
| 5 | -import {Input, Component, StateConfig, Inject, provide} from "ng-forward"; | |
| 6 | - | |
| 7 | -import {ArticleBlog} from "./../components/noosfero-articles/blog/blog.component"; | |
| 8 | -import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
| 9 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
| 10 | - | |
| 11 | -@Component({ | |
| 12 | - selector: "content-viewer", | |
| 13 | - templateUrl: "app/content-viewer/page.html", | |
| 14 | - directives: [ArticleBlog, ArticleView], | |
| 15 | - providers: [ | |
| 16 | - provide('articleService', { useClass: ArticleService }), | |
| 17 | - provide('profileService', { useClass: ProfileService }) | |
| 18 | - ] | |
| 19 | -}) | |
| 20 | -@Inject(ArticleService, ProfileService, "$log", "$stateParams") | |
| 21 | -export class ContentViewer { | |
| 22 | - | |
| 23 | - @Input() | |
| 24 | - article: noosfero.Article = null; | |
| 25 | - | |
| 26 | - @Input() | |
| 27 | - profile: noosfero.Profile = null; | |
| 28 | - | |
| 29 | - constructor(private articleService: ArticleService, private profileService: ProfileService, private $log: ng.ILogService, private $stateParams: angular.ui.IStateParamsService) { | |
| 30 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 31 | - this.profile = profile; | |
| 32 | - return this.articleService.getByProfile(this.profile.id, { path: this.$stateParams["page"] }); | |
| 33 | - }).then((response: restangular.IResponse) => { | |
| 34 | - this.article = response.data.article; | |
| 35 | - }); | |
| 36 | - } | |
| 37 | -} |
src/app/content-viewer/index.ts
src/app/content-viewer/navbar-actions.html
| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | -<ul class="nav navbar-nav"> | |
| 2 | - <li ng-show="vm.profile"> | |
| 3 | - <a href="#" role="button" ui-sref="main.profile.cms({profile: vm.profile.identifier})"> | |
| 4 | - <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | |
| 5 | - </a> | |
| 6 | - </li> | |
| 7 | -</ul> |
src/app/content-viewer/page.html
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -<noosfero-article ng-if="vm.article" [article]="vm.article" [profile]="vm.profile"></noosfero-article> |
src/app/index.module.ts
| ... | ... | @@ -15,13 +15,13 @@ export class NoosferoApp { |
| 15 | 15 | * @returns {string} the name of this application ('noosferoApp') |
| 16 | 16 | */ |
| 17 | 17 | static appName: string = "noosferoApp"; |
| 18 | - | |
| 18 | + | |
| 19 | 19 | /** |
| 20 | 20 | * @ngdoc property |
| 21 | 21 | * @name angularModule |
| 22 | 22 | * @propertyOf NoosferoApp |
| 23 | 23 | * @returns {any} all the modules installed for this application |
| 24 | - */ | |
| 24 | + */ | |
| 25 | 25 | static angularModule: any; |
| 26 | 26 | |
| 27 | 27 | /** | ... | ... |
src/app/index.run.ts
| 1 | -import {Session} from "./components/auth/session"; | |
| 2 | -import {Notification} from "./components/notification/notification.component"; | |
| 1 | +import {SessionService} from "./login"; | |
| 2 | +import {NotificationService} from "./shared/services/notification.service"; | |
| 3 | 3 | |
| 4 | 4 | /** @ngInject */ |
| 5 | 5 | export function noosferoAngularRunBlock( |
| 6 | 6 | $log: ng.ILogService, |
| 7 | 7 | Restangular: restangular.IService, |
| 8 | - Session: Session, | |
| 9 | - Notification: Notification | |
| 8 | + SessionService: SessionService, | |
| 9 | + NotificationService: NotificationService | |
| 10 | 10 | ) { |
| 11 | 11 | |
| 12 | 12 | Restangular.addFullRequestInterceptor((element: any, operation: string, route: string, url: string, headers: string) => { |
| 13 | - if (Session.currentUser()) { | |
| 14 | - (<any>headers)["Private-Token"] = Session.currentUser().private_token; | |
| 13 | + if (SessionService.currentUser()) { | |
| 14 | + (<any>headers)["Private-Token"] = SessionService.currentUser().private_token; | |
| 15 | 15 | } |
| 16 | 16 | return <any>{ headers: <any>headers }; |
| 17 | 17 | }); |
| 18 | 18 | Restangular.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>) => { |
| 19 | 19 | // return false to break the promise chain and don't call catch |
| 20 | - return !Notification.httpError(response.status, response.data); | |
| 20 | + return !NotificationService.httpError(response.status, response.data); | |
| 21 | 21 | }); |
| 22 | 22 | } | ... | ... |
src/app/index.ts
| ... | ... | @@ -5,10 +5,10 @@ import {noosferoAngularRunBlock} from "./index.run"; |
| 5 | 5 | import {Main} from "./main/main.component"; |
| 6 | 6 | import {bootstrap, bundle} from "ng-forward"; |
| 7 | 7 | |
| 8 | -import {AUTH_EVENTS} from "./components/auth/auth_events"; | |
| 9 | -import {AuthController} from "./components/auth/auth_controller"; | |
| 8 | +import {AUTH_EVENTS} from "./login/auth-events"; | |
| 9 | +import {AuthController} from "./login/auth.controller"; | |
| 10 | 10 | |
| 11 | -import {Navbar} from "./components/navbar/navbar"; | |
| 11 | +import {Navbar} from "./layout/navbar/navbar"; | |
| 12 | 12 | |
| 13 | 13 | declare var moment: any; |
| 14 | 14 | ... | ... |
| ... | ... | @@ -0,0 +1,91 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Input, provide, Component} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {BlockComponent} from './block.component'; | |
| 5 | + | |
| 6 | +const tcb = new TestComponentBuilder(); | |
| 7 | + | |
| 8 | +const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block>'; | |
| 9 | + | |
| 10 | +describe("Components", () => { | |
| 11 | + describe("Block Component", () => { | |
| 12 | + | |
| 13 | + // the karma preprocessor html2js transform the templates html into js files which put | |
| 14 | + // the templates to the templateCache into the module templates | |
| 15 | + // we need to load the module templates here as the template for the | |
| 16 | + // component Block will be load on our tests | |
| 17 | + beforeEach(angular.mock.module("templates")); | |
| 18 | + | |
| 19 | + it("receives the block and the owner as inputs", done => { | |
| 20 | + | |
| 21 | + // Creating a container component (BlockContainerComponent) to include | |
| 22 | + // the component under test (Block) | |
| 23 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent] }) | |
| 24 | + class BlockContainerComponent { | |
| 25 | + block = { type: 'Block' }; | |
| 26 | + owner = { name: 'profile-name' }; | |
| 27 | + constructor() { | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | + // uses the TestComponentBuilder instance to initialize the component | |
| 32 | + tcb | |
| 33 | + .createAsync(BlockContainerComponent).then(fixture => { | |
| 34 | + // and here we can inspect and run the test assertions | |
| 35 | + let myComponent: BlockComponent = fixture.componentInstance; | |
| 36 | + | |
| 37 | + // assure the block object inside the Block matches | |
| 38 | + // the provided through the parent component | |
| 39 | + expect(myComponent.block.type).toEqual("Block"); | |
| 40 | + expect(myComponent.owner.name).toEqual("profile-name"); | |
| 41 | + done(); | |
| 42 | + }); | |
| 43 | + }); | |
| 44 | + | |
| 45 | + | |
| 46 | + it("renders a component which matches to the block type", done => { | |
| 47 | + // CustomBlock component created to check if it will be used | |
| 48 | + // when a block with type 'CustomBlock' is provided to the noosfero-block (Block) | |
| 49 | + // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | |
| 50 | + @Component({ selector: 'noosfero-custom-block', template: "<h1>My Custom Block</h1>" }) | |
| 51 | + class CustomBlock { | |
| 52 | + @Input() block: any; | |
| 53 | + @Input() owner: any; | |
| 54 | + } | |
| 55 | + | |
| 56 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent, CustomBlock] }) | |
| 57 | + class CustomBlockType { | |
| 58 | + block = { type: 'CustomBlock' }; | |
| 59 | + owner = { name: 'profile-name' }; | |
| 60 | + constructor() { | |
| 61 | + } | |
| 62 | + } | |
| 63 | + tcb | |
| 64 | + .createAsync(CustomBlockType).then(fixture => { | |
| 65 | + let myComponent: CustomBlockType = fixture.componentInstance; | |
| 66 | + expect(myComponent.block.type).toEqual("CustomBlock"); | |
| 67 | + expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("My Custom Block"); | |
| 68 | + done(); | |
| 69 | + }); | |
| 70 | + }); | |
| 71 | + | |
| 72 | + | |
| 73 | + it("renders the default block when hasn't defined a block type", done => { | |
| 74 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent] }) | |
| 75 | + class CustomBlockType { | |
| 76 | + block: any = { type: null }; | |
| 77 | + owner: any = { name: 'profile-name' }; | |
| 78 | + constructor() { | |
| 79 | + } | |
| 80 | + } | |
| 81 | + tcb | |
| 82 | + .createAsync(CustomBlockType).then(fixture => { | |
| 83 | + let myComponent: CustomBlockType = fixture.componentInstance; | |
| 84 | + expect(myComponent.block.type).toBeNull(); | |
| 85 | + expect(!!fixture.debugElement.nativeElement.querySelector("noosfero-default-block")).toBeTruthy(); | |
| 86 | + done(); | |
| 87 | + }); | |
| 88 | + }); | |
| 89 | + | |
| 90 | + }); | |
| 91 | +}); | |
| 0 | 92 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +import { Input, Inject, Component } from 'ng-forward'; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: 'noosfero-block', | |
| 5 | + template: '<div></div>' | |
| 6 | +}) | |
| 7 | +@Inject("$element", "$scope", "$injector", "$compile") | |
| 8 | +export class BlockComponent { | |
| 9 | + | |
| 10 | + @Input() block: any; | |
| 11 | + @Input() owner: any; | |
| 12 | + | |
| 13 | + ngOnInit() { | |
| 14 | + let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
| 15 | + this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); | |
| 16 | + } | |
| 17 | + | |
| 18 | + constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) { | |
| 19 | + } | |
| 20 | +} | ... | ... |
src/app/layout/blocks/link-list/link-list.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,65 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | +import {provideFilters} from '../../../../spec/helpers'; | |
| 4 | + | |
| 5 | +import {LinkListBlockComponent} from './link-list.component'; | |
| 6 | + | |
| 7 | +const tcb = new TestComponentBuilder(); | |
| 8 | + | |
| 9 | +const htmlTemplate: string = '<noosfero-link-list-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-link-list-block>'; | |
| 10 | + | |
| 11 | + | |
| 12 | +describe("Components", () => { | |
| 13 | + | |
| 14 | + describe("Link List Block Component", () => { | |
| 15 | + | |
| 16 | + beforeEach(angular.mock.module("templates")); | |
| 17 | + | |
| 18 | + it("receives the block and the owner as inputs", done => { | |
| 19 | + | |
| 20 | + // Creating a container component (BlockContainerComponent) to include | |
| 21 | + // the component under test (Block) | |
| 22 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [LinkListBlockComponent] }) | |
| 23 | + class BlockContainerComponent { | |
| 24 | + block = { type: 'Block' }; | |
| 25 | + owner = { name: 'profile-name' }; | |
| 26 | + constructor() { | |
| 27 | + } | |
| 28 | + } | |
| 29 | + | |
| 30 | + // uses the TestComponentBuilder instance to initialize the component | |
| 31 | + // .overrideView(LinkListBlock, { template: 'asdasdasd', pipes: [NoosferoTemplate] }) | |
| 32 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 33 | + // and here we can inspect and run the test assertions | |
| 34 | + let myComponent: LinkListBlockComponent = fixture.componentInstance; | |
| 35 | + | |
| 36 | + // assure the block object inside the Block matches | |
| 37 | + // the provided through the parent component | |
| 38 | + expect(myComponent.block.type).toEqual("Block"); | |
| 39 | + expect(myComponent.owner.name).toEqual("profile-name"); | |
| 40 | + done(); | |
| 41 | + }); | |
| 42 | + }); | |
| 43 | + | |
| 44 | + | |
| 45 | + it("display links stored in block settings", done => { | |
| 46 | + | |
| 47 | + @Component({ | |
| 48 | + selector: 'test-container-component', | |
| 49 | + template: htmlTemplate, | |
| 50 | + directives: [LinkListBlockComponent], | |
| 51 | + providers: provideFilters("noosferoTemplateFilter") | |
| 52 | + }) | |
| 53 | + class CustomBlockType { | |
| 54 | + block: any = { settings: { links: [{ name: 'link1', address: 'address1' }, { name: 'link2', address: 'address2' }] } }; | |
| 55 | + owner: any = { name: 'profile-name' }; | |
| 56 | + } | |
| 57 | + tcb.createAsync(CustomBlockType).then(fixture => { | |
| 58 | + expect(fixture.debugElement.queryAll(".link-list-block a").length).toEqual(2); | |
| 59 | + done(); | |
| 60 | + }); | |
| 61 | + }); | |
| 62 | + | |
| 63 | + }); | |
| 64 | + | |
| 65 | +}); | |
| 0 | 66 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +import {Component, Input} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: "noosfero-link-list-block", | |
| 5 | + templateUrl: "app/layout/blocks/link-list/link-list.html" | |
| 6 | +}) | |
| 7 | +export class LinkListBlockComponent { | |
| 8 | + | |
| 9 | + @Input() block: any; | |
| 10 | + @Input() owner: any; | |
| 11 | + | |
| 12 | + links: any; | |
| 13 | + | |
| 14 | + ngOnInit() { | |
| 15 | + if (this.block && this.block.settings) { | |
| 16 | + this.links = this.block.settings.links; | |
| 17 | + } | |
| 18 | + } | |
| 19 | + | |
| 20 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | +.icon-event { | |
| 2 | + @extend .fa-calendar; | |
| 3 | +} | |
| 4 | +.icon-photos { | |
| 5 | + @extend .fa-photo; | |
| 6 | +} | |
| 7 | +.icon-edit { | |
| 8 | + @extend .fa-edit; | |
| 9 | +} | |
| 10 | +.icon-ok { | |
| 11 | + @extend .fa-check; | |
| 12 | +} | |
| 13 | +.icon-send { | |
| 14 | + @extend .fa-send-o; | |
| 15 | +} | |
| 16 | +.icon-menu-people { | |
| 17 | + @extend .fa-user; | |
| 18 | +} | |
| 19 | +.icon-forum { | |
| 20 | + @extend .fa-users; | |
| 21 | +} | |
| 22 | +.icon-new { | |
| 23 | + @extend .fa-file-o; | |
| 24 | +} | |
| 25 | +.icon-save { | |
| 26 | + @extend .fa-save; | |
| 27 | +} | |
| 28 | + | |
| 29 | +.link-list-block { | |
| 30 | + a i { | |
| 31 | + line-height: 25px; | |
| 32 | + color: #949494; | |
| 33 | + } | |
| 34 | +} | ... | ... |
src/app/layout/blocks/main-block/main-block.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Input, provide, Component, StateConfig} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {MainBlockComponent} from './main-block.component'; | |
| 5 | +import {NoosferoApp} from '../../../index.module'; | |
| 6 | + | |
| 7 | +const tcb = new TestComponentBuilder(); | |
| 8 | + | |
| 9 | +const htmlTemplate: string = '<noosfero-main-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-main-block>'; | |
| 10 | + | |
| 11 | +describe("Components", () => { | |
| 12 | + describe("Main Block Component", () => { | |
| 13 | + | |
| 14 | + // the karma preprocessor html2js transform the templates html into js files which put | |
| 15 | + // the templates to the templateCache into the module templates | |
| 16 | + // we need to load the module templates here as the template for the | |
| 17 | + // component Block will be load on our tests | |
| 18 | + beforeEach(angular.mock.module("templates")); | |
| 19 | + | |
| 20 | + it("check if the main block has a tag with ui-view attribute", done => { | |
| 21 | + | |
| 22 | + // Creating a container component (BlockContainerComponent) to include | |
| 23 | + // the component under test (Block) | |
| 24 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MainBlockComponent] }) | |
| 25 | + class BlockContainerComponent { | |
| 26 | + } | |
| 27 | + | |
| 28 | + // uses the TestComponentBuilder instance to initialize the component | |
| 29 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 30 | + // and here we can inspect and run the test assertions | |
| 31 | + // let myComponent: MainBlockComponent = fixture.componentInstance; | |
| 32 | + | |
| 33 | + // assure the block object inside the Block matches | |
| 34 | + // the provided through the parent component | |
| 35 | + expect(fixture.debugElement.queryAll('[ui-view="mainBlockContent"]').length).toEqual(1); | |
| 36 | + done(); | |
| 37 | + }); | |
| 38 | + }); | |
| 39 | + }); | |
| 40 | +}); | |
| 0 | 41 | \ No newline at end of file | ... | ... |
src/app/layout/blocks/main-block/main-block.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | +import {Component, Input} from 'ng-forward'; | |
| 2 | +import {BlockComponent} from '../block.component'; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: 'noosfero-main-block', | |
| 6 | + templateUrl: 'app/layout/blocks/main-block/main-block.html' | |
| 7 | +}) | |
| 8 | +export class MainBlockComponent { | |
| 9 | + | |
| 10 | +} | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +<div ui-view="mainBlockContent" autoscroll></div> | ... | ... |
src/app/layout/blocks/members-block/members-block.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,53 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Provider, Input, provide, Component} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {MembersBlockComponent} from './members-block.component'; | |
| 5 | + | |
| 6 | +const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>'; | |
| 7 | + | |
| 8 | +const tcb = new TestComponentBuilder(); | |
| 9 | + | |
| 10 | +describe("Components", () => { | |
| 11 | + describe("Members Block Component", () => { | |
| 12 | + | |
| 13 | + beforeEach(angular.mock.module("templates")); | |
| 14 | + | |
| 15 | + let state = jasmine.createSpyObj("state", ["go"]); | |
| 16 | + let providers = [ | |
| 17 | + new Provider('truncateFilter', { useValue: () => { } }), | |
| 18 | + new Provider('stripTagsFilter', { useValue: () => { } }), | |
| 19 | + new Provider('$state', { useValue: state }), | |
| 20 | + new Provider('ProfileService', { | |
| 21 | + useValue: { | |
| 22 | + getProfileMembers: (profileId: number, filters: any): any => { | |
| 23 | + return Promise.resolve({ data: { people: [{ identifier: "person1" }] } }); | |
| 24 | + } | |
| 25 | + } | |
| 26 | + }), | |
| 27 | + ]; | |
| 28 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MembersBlockComponent], providers: providers }) | |
| 29 | + class BlockContainerComponent { | |
| 30 | + block = { type: 'Block', settings: {} }; | |
| 31 | + owner = { name: 'profile-name' }; | |
| 32 | + constructor() { | |
| 33 | + } | |
| 34 | + } | |
| 35 | + | |
| 36 | + it("get members of the block owner", done => { | |
| 37 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 38 | + let block: MembersBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 39 | + expect(block.members).toEqual([{ identifier: "person1" }]); | |
| 40 | + done(); | |
| 41 | + }); | |
| 42 | + }); | |
| 43 | + | |
| 44 | + it("render the profile image for each member", done => { | |
| 45 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 46 | + fixture.debugElement.getLocal("$rootScope").$apply(); | |
| 47 | + expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1); | |
| 48 | + done(); | |
| 49 | + }); | |
| 50 | + }); | |
| 51 | + | |
| 52 | + }); | |
| 53 | +}); | |
| 0 | 54 | \ No newline at end of file | ... | ... |
src/app/layout/blocks/members-block/members-block.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +import {Input, Inject, Component} from "ng-forward"; | |
| 2 | +import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: "noosfero-members-block", | |
| 6 | + templateUrl: 'app/layout/blocks/members-block/members-block.html', | |
| 7 | +}) | |
| 8 | +@Inject(ProfileService) | |
| 9 | +export class MembersBlockComponent { | |
| 10 | + | |
| 11 | + @Input() block: noosfero.Block; | |
| 12 | + @Input() owner: noosfero.Profile; | |
| 13 | + | |
| 14 | + members: any = []; | |
| 15 | + | |
| 16 | + constructor(private profileService: ProfileService) { | |
| 17 | + | |
| 18 | + } | |
| 19 | + | |
| 20 | + ngOnInit() { | |
| 21 | + this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => { | |
| 22 | + this.members = response.data.people; | |
| 23 | + }); | |
| 24 | + } | |
| 25 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +.members-block { | |
| 2 | + .member { | |
| 3 | + img, i.profile-image { | |
| 4 | + width: 60px; | |
| 5 | + } | |
| 6 | + img { | |
| 7 | + display: inline-block; | |
| 8 | + vertical-align: top; | |
| 9 | + } | |
| 10 | + i.profile-image { | |
| 11 | + text-align: center; | |
| 12 | + background-color: #889DB1; | |
| 13 | + color: #F1F1F1; | |
| 14 | + font-size: 4.5em; | |
| 15 | + } | |
| 16 | + } | |
| 17 | +} | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | +import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {ProfileImageBlockComponent} from './profile-image-block.component'; | |
| 5 | + | |
| 6 | +import * as helpers from "./../../../../spec/helpers"; | |
| 7 | + | |
| 8 | +const tcb = new TestComponentBuilder(); | |
| 9 | + | |
| 10 | +const htmlTemplate: string = '<noosfero-profile-image-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-image-block>'; | |
| 11 | + | |
| 12 | +describe("Components", () => { | |
| 13 | + | |
| 14 | + describe("Profile Image Block Component", () => { | |
| 15 | + | |
| 16 | + beforeEach(angular.mock.module("templates")); | |
| 17 | + | |
| 18 | + @Component({ | |
| 19 | + selector: 'test-container-component', | |
| 20 | + template: htmlTemplate, | |
| 21 | + directives: [ProfileImageBlockComponent], | |
| 22 | + providers: helpers.provideFilters("translateFilter") | |
| 23 | + }) | |
| 24 | + class BlockContainerComponent { | |
| 25 | + block = { type: 'Block' }; | |
| 26 | + owner = { name: 'profile-name' }; | |
| 27 | + constructor() { | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | + it("show image if present", () => { | |
| 32 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 33 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | |
| 34 | + expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
| 35 | + }); | |
| 36 | + }); | |
| 37 | + | |
| 38 | + it("has link to the profile", () => { | |
| 39 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 40 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | |
| 41 | + expect(elProfile.query('a.settings-link').length).toEqual(1); | |
| 42 | + }); | |
| 43 | + }); | |
| 44 | + | |
| 45 | + }); | |
| 46 | +}); | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +import {Inject, Input, Component} from "ng-forward"; | |
| 2 | +import {ProfileImageComponent} from "./../../../profile/image/image.component"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: "noosfero-profile-image-block", | |
| 6 | + templateUrl: 'app/layout/blocks/profile-image-block/profile-image-block.html', | |
| 7 | + directives: [ProfileImageComponent] | |
| 8 | +}) | |
| 9 | +export class ProfileImageBlockComponent { | |
| 10 | + | |
| 11 | + @Input() block: noosfero.Block; | |
| 12 | + @Input() owner: noosfero.Profile; | |
| 13 | + | |
| 14 | +} | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.html
0 → 100644
| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | +<div class="center-block text-center profile-image-block"> | |
| 2 | + <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})"> | |
| 3 | + <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> | |
| 4 | + </a> | |
| 5 | + <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> | |
| 6 | +</div> | ... | ... |
src/app/layout/blocks/profile-image-block/profile-image-block.scss
0 → 100644
src/app/layout/blocks/raw-html/raw-html.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Component} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {RawHTMLBlockComponent} from './raw-html.component'; | |
| 5 | + | |
| 6 | +const tcb = new TestComponentBuilder(); | |
| 7 | + | |
| 8 | +const htmlTemplate: string = '<noosfero-raw-htmlblock [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-raw-htmlblock>'; | |
| 9 | + | |
| 10 | +describe("Components", () => { | |
| 11 | + | |
| 12 | + describe("Raw Html Block Component", () => { | |
| 13 | + | |
| 14 | + beforeEach(angular.mock.module("templates")); | |
| 15 | + beforeEach(angular.mock.module("ngSanitize")); | |
| 16 | + | |
| 17 | + it("display html stored in block settings", done => { | |
| 18 | + | |
| 19 | + @Component({ | |
| 20 | + selector: 'test-container-component', | |
| 21 | + template: htmlTemplate, | |
| 22 | + directives: [RawHTMLBlockComponent], | |
| 23 | + }) | |
| 24 | + class CustomBlockType { | |
| 25 | + block: any = { settings: { html: '<em>block content</em>' } }; | |
| 26 | + owner: any = { name: 'profile-name' }; | |
| 27 | + } | |
| 28 | + tcb.createAsync(CustomBlockType).then(fixture => { | |
| 29 | + expect(fixture.debugElement.query(".raw-html-block em").text().trim()).toEqual('block content'); | |
| 30 | + done(); | |
| 31 | + }); | |
| 32 | + }); | |
| 33 | + | |
| 34 | + }); | |
| 35 | + | |
| 36 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +import {Component, Input} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: "noosfero-raw-htmlblock", | |
| 5 | + templateUrl: 'app/layout/blocks/raw-html/raw-html.html' | |
| 6 | +}) | |
| 7 | + | |
| 8 | +export class RawHTMLBlockComponent { | |
| 9 | + | |
| 10 | + @Input() block: any; | |
| 11 | + @Input() owner: any; | |
| 12 | + | |
| 13 | + html: string; | |
| 14 | + | |
| 15 | + ngOnInit() { | |
| 16 | + this.html = this.block.settings.html; | |
| 17 | + } | |
| 18 | +} | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,80 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Provider, Input, provide, Component} from 'ng-forward'; | |
| 3 | +import {provideFilters} from '../../../../spec/helpers'; | |
| 4 | +import {RecentDocumentsBlockComponent} from './recent-documents.component'; | |
| 5 | + | |
| 6 | +const htmlTemplate: string = '<noosfero-recent-documents-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-recent-documents-block>'; | |
| 7 | + | |
| 8 | +const tcb = new TestComponentBuilder(); | |
| 9 | + | |
| 10 | +describe("Components", () => { | |
| 11 | + describe("Recent Documents Block Component", () => { | |
| 12 | + | |
| 13 | + let settingsObj = {}; | |
| 14 | + let mockedArticleService = { | |
| 15 | + getByProfile: (profile: noosfero.Profile, filters: any): any => { | |
| 16 | + return Promise.resolve({ data: [{ name: "article1" }], headers: (name: string) => { return name; } }); | |
| 17 | + } | |
| 18 | + }; | |
| 19 | + let profile = { name: 'profile-name' }; | |
| 20 | + beforeEach(angular.mock.module("templates")); | |
| 21 | + | |
| 22 | + let state = jasmine.createSpyObj("state", ["go"]); | |
| 23 | + | |
| 24 | + | |
| 25 | + function getProviders() { | |
| 26 | + return [ | |
| 27 | + new Provider('$state', { useValue: state }), | |
| 28 | + new Provider('ArticleService', { | |
| 29 | + useValue: mockedArticleService | |
| 30 | + }), | |
| 31 | + ].concat(provideFilters("truncateFilter", "stripTagsFilter")); | |
| 32 | + } | |
| 33 | + let componentClass: any = null; | |
| 34 | + | |
| 35 | + function getComponent() { | |
| 36 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [RecentDocumentsBlockComponent], providers: getProviders() }) | |
| 37 | + class BlockContainerComponent { | |
| 38 | + block = { type: 'Block', settings: settingsObj }; | |
| 39 | + owner = profile; | |
| 40 | + constructor() { | |
| 41 | + } | |
| 42 | + } | |
| 43 | + return BlockContainerComponent; | |
| 44 | + } | |
| 45 | + | |
| 46 | + | |
| 47 | + it("get recent documents from the article service", done => { | |
| 48 | + tcb.createAsync(getComponent()).then(fixture => { | |
| 49 | + let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 50 | + expect(recentDocumentsBlock.documents).toEqual([{ name: "article1" }]); | |
| 51 | + done(); | |
| 52 | + }); | |
| 53 | + }); | |
| 54 | + | |
| 55 | + it("go to article page when open a document", done => { | |
| 56 | + tcb.createAsync(getComponent()).then(fixture => { | |
| 57 | + let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 58 | + recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
| 59 | + expect(state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "identifier" }); | |
| 60 | + done(); | |
| 61 | + }); | |
| 62 | + }); | |
| 63 | + | |
| 64 | + it("it uses default limit 5 if not defined on block", done => { | |
| 65 | + settingsObj = null; | |
| 66 | + mockedArticleService = jasmine.createSpyObj("mockedArticleService", ["getByProfile"]); | |
| 67 | + (<any>mockedArticleService).mocked = true; | |
| 68 | + let thenMocked = jasmine.createSpy("then"); | |
| 69 | + mockedArticleService.getByProfile = jasmine.createSpy("getByProfile").and.returnValue({then: thenMocked}); | |
| 70 | + let getByProfileFunct = mockedArticleService.getByProfile; | |
| 71 | + tcb.createAsync(getComponent()).then(fixture => { | |
| 72 | + let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 73 | + recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } }); | |
| 74 | + expect(getByProfileFunct).toHaveBeenCalledWith(profile, { content_type: 'TinyMceArticle', per_page: 5 }); | |
| 75 | + done(); | |
| 76 | + }); | |
| 77 | + }); | |
| 78 | + | |
| 79 | + }); | |
| 80 | +}); | |
| 0 | 81 | \ No newline at end of file | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | +import {Component, Inject, Input} from "ng-forward"; | |
| 2 | +import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: "noosfero-recent-documents-block", | |
| 6 | + templateUrl: 'app/layout/blocks/recent-documents/recent-documents.html' | |
| 7 | +}) | |
| 8 | +@Inject(ArticleService, "$state") | |
| 9 | +export class RecentDocumentsBlockComponent { | |
| 10 | + | |
| 11 | + @Input() block: any; | |
| 12 | + @Input() owner: any; | |
| 13 | + | |
| 14 | + profile: any; | |
| 15 | + documents: any; | |
| 16 | + | |
| 17 | + documentsLoaded: boolean = false; | |
| 18 | + | |
| 19 | + constructor(private articleService: ArticleService, private $state: any) { | |
| 20 | + } | |
| 21 | + | |
| 22 | + ngOnInit() { | |
| 23 | + this.profile = this.owner; | |
| 24 | + this.documents = []; | |
| 25 | + | |
| 26 | + let limit = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5; | |
| 27 | + // FIXME get all text articles | |
| 28 | + // FIXME make the getByProfile a generic method where we tell the type passing a class TinyMceArticle | |
| 29 | + // and the promise should be of type TinyMceArticle[], per example | |
| 30 | + this.articleService.getByProfile(this.profile, { content_type: 'TinyMceArticle', per_page: limit }) | |
| 31 | + .then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 32 | + this.documents = <noosfero.Article[]>result.data; | |
| 33 | + this.documentsLoaded = true; | |
| 34 | + }); | |
| 35 | + } | |
| 36 | + | |
| 37 | + openDocument(article: any) { | |
| 38 | + this.$state.go("main.profile.page", { page: article.path, profile: article.profile.identifier }); | |
| 39 | + } | |
| 40 | + | |
| 41 | +} | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.html
0 → 100644
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +<div deckgrid source="ctrl.documents" class="deckgrid"> | |
| 2 | + <div class="a-card panel media" ng-click="mother.ctrl.openDocument(card);"> | |
| 3 | + <div class="author media-left" ng-show="card.author.image"> | |
| 4 | + <img ng-src="{{card.author.image.url}}" class="img-circle"> | |
| 5 | + </div> | |
| 6 | + <div class="header media-body"> | |
| 7 | + <h5 class="title media-heading" ng-bind="card.title"></h5> | |
| 8 | + | |
| 9 | + <div class="subheader"> | |
| 10 | + <span class="time"> | |
| 11 | + <i class="fa fa-clock-o"></i> <span am-time-ago="card.created_at | dateFormat"></span> | |
| 12 | + </span> | |
| 13 | + </div> | |
| 14 | + </div> | |
| 15 | + <img ng-show="card.image" ng-src="{{card.image.url}}" class="img-responsive article-image"> | |
| 16 | + <div class="post-lead" ng-bind-html="card.body | stripTags | truncate: 100: '...': true"></div> | |
| 17 | + </div> | |
| 18 | +</div> | ... | ... |
src/app/layout/blocks/recent-documents/recent-documents.scss
0 → 100644
| ... | ... | @@ -0,0 +1,65 @@ |
| 1 | +.block.recentdocumentsblock { | |
| 2 | + .deckgrid[deckgrid]::before { | |
| 3 | + font-size: 0; /* See https://github.com/akoenig/angular-deckgrid/issues/14#issuecomment-35728861 */ | |
| 4 | + visibility: hidden; | |
| 5 | + } | |
| 6 | + .author { | |
| 7 | + img { | |
| 8 | + width: 30px; | |
| 9 | + height: 30px; | |
| 10 | + } | |
| 11 | + } | |
| 12 | + .header { | |
| 13 | + .subheader { | |
| 14 | + color: #C1C1C1; | |
| 15 | + font-size: 10px; | |
| 16 | + } | |
| 17 | + } | |
| 18 | + .post-lead { | |
| 19 | + color: #8E8E8E; | |
| 20 | + font-size: 14px; | |
| 21 | + } | |
| 22 | + .article-image { | |
| 23 | + margin: 10px 0; | |
| 24 | + } | |
| 25 | +} | |
| 26 | + | |
| 27 | +.col-md-2-5 { | |
| 28 | + .deckgrid[deckgrid]::before { | |
| 29 | + content: '1 .deck-column'; | |
| 30 | + } | |
| 31 | +} | |
| 32 | + | |
| 33 | +.col-md-7 { | |
| 34 | + .block.recentdocumentsblock { | |
| 35 | + background-color: transparent; | |
| 36 | + border: 0; | |
| 37 | + | |
| 38 | + .deckgrid[deckgrid]::before { | |
| 39 | + content: '3 .deck-column'; | |
| 40 | + } | |
| 41 | + | |
| 42 | + .panel-heading { | |
| 43 | + display: none; | |
| 44 | + } | |
| 45 | + .panel-body { | |
| 46 | + padding: 0; | |
| 47 | + } | |
| 48 | + | |
| 49 | + .deckgrid { | |
| 50 | + .column { | |
| 51 | + float: left; | |
| 52 | + } | |
| 53 | + | |
| 54 | + .deck-column { | |
| 55 | + @extend .col-md-4; | |
| 56 | + padding: 0; | |
| 57 | + | |
| 58 | + .a-card { | |
| 59 | + padding: 10px; | |
| 60 | + margin: 3px; | |
| 61 | + } | |
| 62 | + } | |
| 63 | + } | |
| 64 | + } | |
| 65 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | +<div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> | |
| 2 | + <div ng-repeat="block in box.blocks | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > | |
| 3 | + <div class="panel-heading" ng-show="block.title"> | |
| 4 | + <h3 class="panel-title">{{block.title}}</h3> | |
| 5 | + </div> | |
| 6 | + <div class="panel-body"> | |
| 7 | + <noosfero-block [block]="block" [owner]="ctrl.owner"></noosfero-block> | |
| 8 | + </div> | |
| 9 | + </div> | |
| 10 | +</div> | ... | ... |
| ... | ... | @@ -0,0 +1,65 @@ |
| 1 | +import {Component} from 'ng-forward'; | |
| 2 | + | |
| 3 | +import {BoxesComponent} from './boxes.component'; | |
| 4 | + | |
| 5 | +import { | |
| 6 | + createComponentFromClass, | |
| 7 | + quickCreateComponent, | |
| 8 | + provideEmptyObjects, | |
| 9 | + createProviderToValue, | |
| 10 | + getAngularServiceFactory, | |
| 11 | + provideFilters | |
| 12 | +} from "../../../spec/helpers"; | |
| 13 | + | |
| 14 | +// this htmlTemplate will be re-used between the container components in this spec file | |
| 15 | +const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl.profile"></noosfero-blog>'; | |
| 16 | + | |
| 17 | + | |
| 18 | +describe("Boxes Component", () => { | |
| 19 | + | |
| 20 | + beforeEach(() => { | |
| 21 | + angular.mock.module("templates"); | |
| 22 | + }); | |
| 23 | + | |
| 24 | + @Component({ | |
| 25 | + selector: 'test-container-component', | |
| 26 | + template: htmlTemplate, | |
| 27 | + directives: [BoxesComponent], | |
| 28 | + providers: [] | |
| 29 | + }) | |
| 30 | + class BoxesContainerComponent { | |
| 31 | + boxes: noosfero.Box[] = [ | |
| 32 | + { id: 1, position: 1 }, | |
| 33 | + { id: 2, position: 2 } | |
| 34 | + ]; | |
| 35 | + | |
| 36 | + owner: noosfero.Profile = <noosfero.Profile> { | |
| 37 | + id: 1, | |
| 38 | + identifier: 'profile-name', | |
| 39 | + type: 'Person' | |
| 40 | + }; | |
| 41 | + } | |
| 42 | + | |
| 43 | + it("renders boxes into a container", (done: Function) => { | |
| 44 | + createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
| 45 | + let boxesHtml = fixture.debugElement; | |
| 46 | + expect(boxesHtml.query('div.col-md-7').length).toEqual(1); | |
| 47 | + expect(boxesHtml.query('div.col-md-2-5').length).toEqual(1); | |
| 48 | + | |
| 49 | + done(); | |
| 50 | + }); | |
| 51 | + }); | |
| 52 | + | |
| 53 | + it("check the boxes order", (done: Function) => { | |
| 54 | + createComponentFromClass(BoxesContainerComponent).then((fixture) => { | |
| 55 | + | |
| 56 | + let boxesComponent: BoxesComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 57 | + let boxesContainer: BoxesContainerComponent = fixture.componentInstance; | |
| 58 | + | |
| 59 | + expect(boxesComponent.boxesOrder(boxesContainer.boxes[0])).toEqual(1); | |
| 60 | + expect(boxesComponent.boxesOrder(boxesContainer.boxes[1])).toEqual(0); | |
| 61 | + | |
| 62 | + done(); | |
| 63 | + }); | |
| 64 | + }); | |
| 65 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +import {Input, Inject, Component} from 'ng-forward'; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: "noosfero-boxes", | |
| 5 | + templateUrl: "app/layout/boxes/boxes.html" | |
| 6 | +}) | |
| 7 | +export class BoxesComponent { | |
| 8 | + | |
| 9 | + @Input() boxes: noosfero.Box[]; | |
| 10 | + @Input() owner: noosfero.Profile; | |
| 11 | + | |
| 12 | + boxesOrder(box: noosfero.Box) { | |
| 13 | + if (box.position === 2) return 0; | |
| 14 | + return box.position; | |
| 15 | + } | |
| 16 | +} | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +<ng-include ng-repeat="box in ctrl.boxes | orderBy: ctrl.boxesOrder" src="'app/layout/boxes/box.html'"></ng-include> | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
src/app/layout/language-selector/language-selector.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {provide} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {LanguageSelectorService} from './language-selector.component'; | |
| 5 | + | |
| 6 | +import * as helpers from "../../../spec/helpers"; | |
| 7 | + | |
| 8 | +describe("Components", () => { | |
| 9 | + | |
| 10 | + describe("Language Selector Component", () => { | |
| 11 | + | |
| 12 | + beforeEach(angular.mock.module("templates")); | |
| 13 | + | |
| 14 | + let translatorService: any; | |
| 15 | + | |
| 16 | + let buildComponent = (): Promise<ComponentFixture> => { | |
| 17 | + translatorService = jasmine.createSpyObj("translatorService", ["availableLanguages", "currentLanguage"]); | |
| 18 | + return helpers.quickCreateComponent({ | |
| 19 | + template: "<language-selector></language-selector>", | |
| 20 | + directives: [LanguageSelectorService], | |
| 21 | + providers: [ | |
| 22 | + provide('TranslatorService', { | |
| 23 | + useValue: translatorService | |
| 24 | + }) | |
| 25 | + ].concat(helpers.provideFilters("translateFilter")) | |
| 26 | + }); | |
| 27 | + }; | |
| 28 | + | |
| 29 | + it("display language options", (done) => { | |
| 30 | + buildComponent().then(fixture => { | |
| 31 | + fixture.debugElement.getLocal("$rootScope").$apply(); | |
| 32 | + expect(fixture.debugElement.queryAll('li.language').length).toEqual(2); | |
| 33 | + done(); | |
| 34 | + }); | |
| 35 | + }); | |
| 36 | + | |
| 37 | + it("call the translator service when change the language", (done) => { | |
| 38 | + let translatorService = jasmine.createSpyObj("translatorService", ["changeLanguage"]); | |
| 39 | + let languageSelector = new LanguageSelectorService(<any>translatorService); | |
| 40 | + languageSelector.changeLanguage("en"); | |
| 41 | + expect(translatorService.changeLanguage).toHaveBeenCalledWith("en"); | |
| 42 | + done(); | |
| 43 | + }); | |
| 44 | + | |
| 45 | + }); | |
| 46 | +}); | ... | ... |
src/app/layout/language-selector/language-selector.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | +import {Component, Inject} from "ng-forward"; | |
| 2 | +import {TranslatorService} from "../../shared/services/translator.service"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: "language-selector", | |
| 6 | + templateUrl: "app/layout/language-selector/language-selector.html" | |
| 7 | +}) | |
| 8 | +@Inject(TranslatorService) | |
| 9 | +export class LanguageSelectorService { | |
| 10 | + | |
| 11 | + constructor(private translatorService: TranslatorService) { } | |
| 12 | + | |
| 13 | + currentLanguage() { | |
| 14 | + return this.translatorService.currentLanguage(); | |
| 15 | + } | |
| 16 | + | |
| 17 | + changeLanguage(language: string) { | |
| 18 | + this.translatorService.changeLanguage(language); | |
| 19 | + } | |
| 20 | + | |
| 21 | + availableLanguages() { | |
| 22 | + return this.translatorService.availableLanguages; | |
| 23 | + } | |
| 24 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +<li class="dropdown profile-menu" dropdown> | |
| 2 | + <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
| 3 | + <span>{{"language.selector" | translate}}</span> <b class="caret"></b> | |
| 4 | + </a> | |
| 5 | + <ul class="dropdown-menu" dropdown-menu> | |
| 6 | + <li ng-repeat="(language, description) in ctrl.availableLanguages()" | |
| 7 | + class="language language-{{language}}" ng-class="{'active': language==ctrl.currentLanguage()}"> | |
| 8 | + <a href="#" ng-click="ctrl.changeLanguage(language)"> | |
| 9 | + {{description}} | |
| 10 | + </a> | |
| 11 | + </li> | |
| 12 | + </ul> | |
| 13 | +</li> | ... | ... |
| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | +(function() { | |
| 2 | + 'use strict'; | |
| 3 | + | |
| 4 | + describe('directive navbar', function() { | |
| 5 | + var vm; | |
| 6 | + var el; | |
| 7 | + var AUTH_EVENTS; | |
| 8 | + var $state; | |
| 9 | + | |
| 10 | + beforeEach(module('angular')); | |
| 11 | + beforeEach(inject(function($compile, $rootScope, $httpBackend, _AUTH_EVENTS_, _$state_) { | |
| 12 | + $state = _$state_; | |
| 13 | + AUTH_EVENTS = _AUTH_EVENTS_; | |
| 14 | + $httpBackend.when('POST','/api/v1/login_from_cookie').respond({}); | |
| 15 | + | |
| 16 | + el = angular.element('<acme-navbar></acme-navbar>'); | |
| 17 | + | |
| 18 | + $compile(el)($rootScope.$new()); | |
| 19 | + $rootScope.$digest(); | |
| 20 | + vm = el.isolateScope().vm; | |
| 21 | + })); | |
| 22 | + | |
| 23 | + it('should be compiled', function() { | |
| 24 | + expect(el.html()).not.toEqual(null); | |
| 25 | + }); | |
| 26 | + | |
| 27 | + it('should have isolate scope object with instanciate members', function() { | |
| 28 | + expect(vm).toEqual(jasmine.any(Object)); | |
| 29 | + expect(vm.currentUser).toEqual(undefined); | |
| 30 | + }); | |
| 31 | + | |
| 32 | + it('should reload current state after login', function() { | |
| 33 | + spyOn($state, 'go'); | |
| 34 | + el.isolateScope().$broadcast(AUTH_EVENTS.loginSuccess, {}); | |
| 35 | + expect($state.go).toHaveBeenCalled(); | |
| 36 | + }); | |
| 37 | + | |
| 38 | + it('should open login when not logged in', function() { | |
| 39 | + spyOn(vm, 'openLogin'); | |
| 40 | + vm.activate(); | |
| 41 | + expect(vm.openLogin).toHaveBeenCalled(); | |
| 42 | + }); | |
| 43 | + | |
| 44 | + }); | |
| 45 | +})(); | ... | ... |
| ... | ... | @@ -0,0 +1,49 @@ |
| 1 | +<nav class="navbar navbar-static-top navbar-inverse"> | |
| 2 | + <div class="container-fluid"> | |
| 3 | + <div class="navbar-header"> | |
| 4 | + <button type="button" class="navbar-toggle collapsed" ng-click="isCollapsed = !isCollapsed"> | |
| 5 | + <span class="sr-only">{{"navbar.toggle_menu" | translate}}</span> | |
| 6 | + <span class="icon-bar"></span> | |
| 7 | + <span class="icon-bar"></span> | |
| 8 | + <span class="icon-bar"></span> | |
| 9 | + </button> | |
| 10 | + <a class="navbar-brand" ui-sref="main"> | |
| 11 | + <span class="noosfero-logo"><img src="assets/images/logo-noosfero.png"></span> {{"noosfero.name" | translate}} | |
| 12 | + </a> | |
| 13 | + </div> | |
| 14 | + | |
| 15 | + <div class="collapse navbar-collapse" id="navbar-collapse" collapse="isCollapsed"> | |
| 16 | + <ul class="nav navbar-nav"> | |
| 17 | + </ul> | |
| 18 | + | |
| 19 | + <ul class="nav navbar-nav navbar-right"> | |
| 20 | + <li ng-show="!ctrl.currentUser"> | |
| 21 | + <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a> | |
| 22 | + </li> | |
| 23 | + | |
| 24 | + <li class="dropdown profile-menu" ng-show="ctrl.currentUser" dropdown> | |
| 25 | + <a href="#" class="dropdown-toggle" aria-expanded="false" dropdown-toggle> | |
| 26 | + <noosfero-profile-image [profile]="ctrl.currentUser.person" class="profile-image"></noosfero-profile-image> | |
| 27 | + <span ng-bind="ctrl.currentUser.person.name"></span> <b class="caret"></b> | |
| 28 | + </a> | |
| 29 | + <ul class="dropdown-menu" dropdown-menu> | |
| 30 | + <li> | |
| 31 | + <a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-user"></i> {{"navbar.profile" | translate}}</a> | |
| 32 | + </li> | |
| 33 | + <li> | |
| 34 | + <a target="_self" ui-sref="main.profile.settings({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-gear"></i> {{"navbar.settings" | translate}}</a> | |
| 35 | + </li> | |
| 36 | + <li class="divider"></li> | |
| 37 | + <li> | |
| 38 | + <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a> | |
| 39 | + </li> | |
| 40 | + </ul> | |
| 41 | + </li> | |
| 42 | + </ul> | |
| 43 | + <ul class="nav navbar-nav navbar-right"> | |
| 44 | + <language-selector class="nav navbar-nav navbar-right"></language-selector> | |
| 45 | + </ul> | |
| 46 | + <div ui-view="actions"></div> | |
| 47 | + </div> | |
| 48 | + </div> | |
| 49 | +</nav> | ... | ... |
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +.navbar { | |
| 2 | + | |
| 3 | + .container-fluid { | |
| 4 | + padding-right: 12%; | |
| 5 | + padding-left: 12%; | |
| 6 | + @media (max-width: 978px) { | |
| 7 | + padding-right: 2%; | |
| 8 | + padding-left: 2%; | |
| 9 | + } | |
| 10 | + | |
| 11 | + .navbar-brand { | |
| 12 | + .noosfero-logo img { | |
| 13 | + height: 35px; | |
| 14 | + } | |
| 15 | + } | |
| 16 | + | |
| 17 | + .navbar-nav { | |
| 18 | + .profile-menu .profile-image { | |
| 19 | + img { | |
| 20 | + height: 30px; | |
| 21 | + width: 30px; | |
| 22 | + display: inline-block; | |
| 23 | + @extend .img-circle; | |
| 24 | + } | |
| 25 | + i { | |
| 26 | + font-size: 1.7em; | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,187 @@ |
| 1 | +import * as helpers from "./../../../spec/helpers"; | |
| 2 | +import {Navbar} from "./navbar"; | |
| 3 | + | |
| 4 | +import {Injectable, Provider, provide} from "ng-forward"; | |
| 5 | + | |
| 6 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 7 | + | |
| 8 | +import {SessionService, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../../login"; | |
| 9 | + | |
| 10 | + | |
| 11 | +describe("Components", () => { | |
| 12 | + | |
| 13 | + describe("Navbar Component", () => { | |
| 14 | + | |
| 15 | + let user: noosfero.User = null; | |
| 16 | + let scope: any; | |
| 17 | + let $rootScope: ng.IRootScopeService; | |
| 18 | + | |
| 19 | + let modalInstance: any; | |
| 20 | + let $modal: any; | |
| 21 | + let authService: any; | |
| 22 | + let stateService: any; | |
| 23 | + let sessionService: SessionService; | |
| 24 | + | |
| 25 | + let provideFunc = provide; | |
| 26 | + | |
| 27 | + // before Each -> loading mocks on locals variables | |
| 28 | + beforeEach(() => { | |
| 29 | + user = <noosfero.User>{ | |
| 30 | + id: 1, | |
| 31 | + login: "user" | |
| 32 | + }; | |
| 33 | + scope = helpers.mocks.scopeWithEvents; | |
| 34 | + modalInstance = helpers.mocks.modalInstance; | |
| 35 | + $modal = helpers.mocks.$modal; | |
| 36 | + authService = helpers.mocks.authService; | |
| 37 | + stateService = jasmine.createSpyObj("$state", ["go"]); | |
| 38 | + sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); | |
| 39 | + }); | |
| 40 | + | |
| 41 | + | |
| 42 | + // loading the templates | |
| 43 | + beforeEach(angular.mock.module("templates")); | |
| 44 | + | |
| 45 | + | |
| 46 | + // this function allow build the fixture of the container component | |
| 47 | + // and is reused in each test | |
| 48 | + // The main idea behing not prebuild it on a general beforeEach block is | |
| 49 | + // to allow tests configure the mock services accordilly their own needs | |
| 50 | + let buildComponent = (): Promise<ComponentFixture> => { | |
| 51 | + return helpers.quickCreateComponent({ | |
| 52 | + providers: [ | |
| 53 | + provide('$modal', { | |
| 54 | + useValue: $modal | |
| 55 | + }), | |
| 56 | + provide('AuthService', { | |
| 57 | + useValue: authService | |
| 58 | + }), | |
| 59 | + helpers.provideEmptyObjects('moment'), | |
| 60 | + provide('$state', { | |
| 61 | + useValue: stateService | |
| 62 | + }), | |
| 63 | + provide("$scope", { | |
| 64 | + useValue: scope | |
| 65 | + }), | |
| 66 | + provide('SessionService', { | |
| 67 | + useValue: sessionService | |
| 68 | + }), | |
| 69 | + provide('AUTH_EVENTS', { | |
| 70 | + useValue: { | |
| 71 | + AUTH_EVENTS | |
| 72 | + } | |
| 73 | + }), | |
| 74 | + provide('TranslatorService', { | |
| 75 | + useValue: helpers.mocks.translatorService | |
| 76 | + }) | |
| 77 | + ].concat(helpers.provideFilters("translateFilter")), | |
| 78 | + directives: [Navbar], | |
| 79 | + template: '<acme-navbar></acme-navbar>' | |
| 80 | + }); | |
| 81 | + }; | |
| 82 | + | |
| 83 | + | |
| 84 | + it('should get the loggedIn user', (done: Function) => { | |
| 85 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 86 | + let navbarInstance: Navbar = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 87 | + expect(navbarInstance).toBeDefined(); | |
| 88 | + expect(navbarInstance["currentUser"]).toEqual(user); | |
| 89 | + done(); | |
| 90 | + }); | |
| 91 | + }); | |
| 92 | + | |
| 93 | + it('should open on click', (done: Function) => { | |
| 94 | + spyOn($modal, "open"); | |
| 95 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 96 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 97 | + navbarComp.openLogin(); | |
| 98 | + expect($modal.open).toHaveBeenCalled(); | |
| 99 | + expect($modal.open).toHaveBeenCalledWith({ | |
| 100 | + templateUrl: 'app/components/auth/login.html', | |
| 101 | + controller: AuthController, | |
| 102 | + controllerAs: 'vm', | |
| 103 | + bindToController: true | |
| 104 | + }); | |
| 105 | + done(); | |
| 106 | + }); | |
| 107 | + }); | |
| 108 | + | |
| 109 | + it('should logout', (done: Function) => { | |
| 110 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 111 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 112 | + spyOn(authService, "logout"); | |
| 113 | + try { | |
| 114 | + navbarComp.logout(); | |
| 115 | + expect(authService.logout).toHaveBeenCalled(); | |
| 116 | + done(); | |
| 117 | + } catch (e) { | |
| 118 | + console.error(e); | |
| 119 | + fail(e.message); | |
| 120 | + done(); | |
| 121 | + } | |
| 122 | + }); | |
| 123 | + }); | |
| 124 | + | |
| 125 | + | |
| 126 | + it('should not activate user when logged in', (done: Function) => { | |
| 127 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 128 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 129 | + spyOn(navbarComp, "openLogin"); | |
| 130 | + navbarComp.activate(); | |
| 131 | + expect((<any>navbarComp.openLogin).calls.count()).toBe(0); | |
| 132 | + done(); | |
| 133 | + }); | |
| 134 | + }); | |
| 135 | + | |
| 136 | + it('should activate when user not logged in', (done: Function) => { | |
| 137 | + spyOn(sessionService, 'currentUser').and.returnValue(null); | |
| 138 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 139 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 140 | + spyOn(navbarComp, "openLogin"); | |
| 141 | + navbarComp.activate(); | |
| 142 | + expect(navbarComp.openLogin).toHaveBeenCalled(); | |
| 143 | + done(); | |
| 144 | + }); | |
| 145 | + }); | |
| 146 | + | |
| 147 | + | |
| 148 | + it('closes the modal after login', (done: Function) => { | |
| 149 | + modalInstance = jasmine.createSpyObj("modalInstance", ["close"]); | |
| 150 | + modalInstance.close = jasmine.createSpy("close"); | |
| 151 | + | |
| 152 | + $modal.open = () => { | |
| 153 | + return modalInstance; | |
| 154 | + }; | |
| 155 | + | |
| 156 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 157 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 158 | + let localScope: ng.IScope = navbarComp["$scope"]; | |
| 159 | + | |
| 160 | + navbarComp.openLogin(); | |
| 161 | + localScope.$emit(AUTH_EVENTS.loginSuccess); | |
| 162 | + expect(modalInstance.close).toHaveBeenCalled(); | |
| 163 | + done(); | |
| 164 | + }); | |
| 165 | + }); | |
| 166 | + | |
| 167 | + it('updates current user on logout', (done: Function) => { | |
| 168 | + buildComponent().then((fixture: ComponentFixture) => { | |
| 169 | + let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 170 | + let localScope: ng.IScope = navbarComp["$scope"]; | |
| 171 | + | |
| 172 | + // init navbar currentUser with some user | |
| 173 | + navbarComp["currentUser"] = user; | |
| 174 | + | |
| 175 | + // changes the current User to return null, | |
| 176 | + // and emmit the 'logoutSuccess' event | |
| 177 | + // just what happens when user logsout | |
| 178 | + sessionService.currentUser = () => { return null; }; | |
| 179 | + localScope.$emit(AUTH_EVENTS.logoutSuccess); | |
| 180 | + expect(navbarComp["currentUser"]).toBeNull(); | |
| 181 | + done(); | |
| 182 | + }); | |
| 183 | + }); | |
| 184 | + | |
| 185 | + | |
| 186 | + }); | |
| 187 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | +import {Component, Inject} from "ng-forward"; | |
| 2 | +import {LanguageSelectorService} from "../language-selector/language-selector.component"; | |
| 3 | + | |
| 4 | + | |
| 5 | +import {SessionService, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../../login"; | |
| 6 | + | |
| 7 | +@Component({ | |
| 8 | + selector: "acme-navbar", | |
| 9 | + templateUrl: "app/layout/navbar/navbar.html", | |
| 10 | + directives: [LanguageSelectorService], | |
| 11 | + providers: [AuthService, SessionService] | |
| 12 | +}) | |
| 13 | +@Inject("$modal", AuthService, "SessionService", "$scope", "$state") | |
| 14 | +export class Navbar { | |
| 15 | + | |
| 16 | + private currentUser: noosfero.User; | |
| 17 | + private modalInstance: any = null; | |
| 18 | + /** | |
| 19 | + * | |
| 20 | + */ | |
| 21 | + constructor( | |
| 22 | + private $modal: any, | |
| 23 | + private authService: AuthService, | |
| 24 | + private session: SessionService, | |
| 25 | + private $scope: ng.IScope, | |
| 26 | + private $state: ng.ui.IStateService | |
| 27 | + ) { | |
| 28 | + this.currentUser = this.session.currentUser(); | |
| 29 | + | |
| 30 | + this.$scope.$on(AUTH_EVENTS.loginSuccess, () => { | |
| 31 | + if (this.modalInstance) { | |
| 32 | + this.modalInstance.close(); | |
| 33 | + this.modalInstance = null; | |
| 34 | + } | |
| 35 | + | |
| 36 | + this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
| 37 | + }); | |
| 38 | + | |
| 39 | + this.$scope.$on(AUTH_EVENTS.logoutSuccess, () => { | |
| 40 | + this.currentUser = this.session.currentUser(); | |
| 41 | + }); | |
| 42 | + } | |
| 43 | + | |
| 44 | + openLogin() { | |
| 45 | + this.modalInstance = this.$modal.open({ | |
| 46 | + templateUrl: 'app/components/auth/login.html', | |
| 47 | + controller: AuthController, | |
| 48 | + controllerAs: 'vm', | |
| 49 | + bindToController: true | |
| 50 | + }); | |
| 51 | + }; | |
| 52 | + | |
| 53 | + logout() { | |
| 54 | + this.authService.logout(); | |
| 55 | + this.$state.go(this.$state.current, {}, { reload: true }); // TODO move to auth | |
| 56 | + }; | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + activate() { | |
| 61 | + if (!this.currentUser) { | |
| 62 | + this.openLogin(); | |
| 63 | + } | |
| 64 | + } | |
| 65 | + | |
| 66 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | +export interface IAuthEvents { | |
| 2 | + loginSuccess: string; | |
| 3 | + loginFailed: string; | |
| 4 | + logoutSuccess: string; | |
| 5 | +} | |
| 6 | + | |
| 7 | +export const AUTH_EVENTS: IAuthEvents = { | |
| 8 | + loginSuccess: "auth-login-success", | |
| 9 | + loginFailed: "auth-login-failed", | |
| 10 | + logoutSuccess: "auth-logout-success" | |
| 11 | +}; | |
| 0 | 12 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,33 @@ |
| 1 | +import {AuthController} from "./auth.controller"; | |
| 2 | +import {AuthService} from "./auth.service"; | |
| 3 | + | |
| 4 | +describe("Controllers", () => { | |
| 5 | + | |
| 6 | + | |
| 7 | + describe("AuthController", () => { | |
| 8 | + | |
| 9 | + it("calls authenticate on AuthService when login called", () => { | |
| 10 | + | |
| 11 | + // creating a Mock AuthService | |
| 12 | + let AuthServiceMock: AuthService = jasmine.createSpyObj("AuthService", ["login"]); | |
| 13 | + | |
| 14 | + // pass AuthServiceMock into the constructor | |
| 15 | + let authController = new AuthController(null, null, AuthServiceMock); | |
| 16 | + | |
| 17 | + // setup of authController -> set the credentials instance property | |
| 18 | + let credentials = { username: "username", password: "password" }; | |
| 19 | + | |
| 20 | + authController.credentials = credentials; | |
| 21 | + | |
| 22 | + // calls the authController login method | |
| 23 | + authController.login(); | |
| 24 | + | |
| 25 | + // checks if the method login of the injected AuthService has been called | |
| 26 | + expect(AuthServiceMock.login).toHaveBeenCalledWith(credentials); | |
| 27 | + | |
| 28 | + }); | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + }); | |
| 33 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +import {AuthService} from "./auth.service"; | |
| 2 | + | |
| 3 | +export class AuthController { | |
| 4 | + | |
| 5 | + static $inject = ["$log", "$stateParams", "AuthService"]; | |
| 6 | + | |
| 7 | + constructor( | |
| 8 | + private $log: ng.ILogService, | |
| 9 | + private $stateParams: any, | |
| 10 | + private AuthService: AuthService | |
| 11 | + ) { | |
| 12 | + | |
| 13 | + } | |
| 14 | + | |
| 15 | + credentials: noosfero.Credentials; | |
| 16 | + | |
| 17 | + login() { | |
| 18 | + this.AuthService.login(this.credentials); | |
| 19 | + } | |
| 20 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,81 @@ |
| 1 | + | |
| 2 | + | |
| 3 | +import {AuthService, AUTH_EVENTS} from "./"; | |
| 4 | + | |
| 5 | +describe("Services", () => { | |
| 6 | + | |
| 7 | + | |
| 8 | + describe("Auth Service", () => { | |
| 9 | + | |
| 10 | + let $httpBackend: ng.IHttpBackendService; | |
| 11 | + let authService: AuthService; | |
| 12 | + let credentials: noosfero.Credentials; | |
| 13 | + let $rootScope: ng.IRootScopeService; | |
| 14 | + let user: noosfero.User; | |
| 15 | + | |
| 16 | + beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => { | |
| 17 | + $translateProvider.translations('en', {}); | |
| 18 | + })); | |
| 19 | + | |
| 20 | + beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService, _AuthService_: AuthService) => { | |
| 21 | + $httpBackend = _$httpBackend_; | |
| 22 | + authService = _AuthService_; | |
| 23 | + $rootScope = _$rootScope_; | |
| 24 | + | |
| 25 | + user = <noosfero.User>{ | |
| 26 | + id: 1, | |
| 27 | + login: "user" | |
| 28 | + }; | |
| 29 | + })); | |
| 30 | + | |
| 31 | + | |
| 32 | + describe("Succesffull login", () => { | |
| 33 | + | |
| 34 | + beforeEach(() => { | |
| 35 | + credentials = { username: "user", password: "password" }; | |
| 36 | + | |
| 37 | + $httpBackend.expectPOST("/api/v1/login", "login=user&password=password").respond(200, { user: user }); | |
| 38 | + }); | |
| 39 | + | |
| 40 | + it("should return loggedUser", (done) => { | |
| 41 | + authService.login(credentials).then((loggedUser) => { | |
| 42 | + expect(loggedUser).toBeDefined(); | |
| 43 | + done(); | |
| 44 | + }); | |
| 45 | + $httpBackend.flush(); | |
| 46 | + expect($httpBackend.verifyNoOutstandingRequest()); | |
| 47 | + }); | |
| 48 | + | |
| 49 | + | |
| 50 | + it("should emit event loggin successful with user logged data", () => { | |
| 51 | + | |
| 52 | + authService.login(credentials); | |
| 53 | + | |
| 54 | + let eventEmmited: boolean = false; | |
| 55 | + $rootScope.$on(AUTH_EVENTS.loginSuccess, (event: ng.IAngularEvent, userThroughEvent: noosfero.User) => { | |
| 56 | + eventEmmited = true; | |
| 57 | + expect(userThroughEvent).toEqual(user); | |
| 58 | + }); | |
| 59 | + | |
| 60 | + $httpBackend.flush(); | |
| 61 | + | |
| 62 | + expect(eventEmmited).toBeTruthy(AUTH_EVENTS.loginSuccess + " was not emmited!"); | |
| 63 | + }); | |
| 64 | + | |
| 65 | + it("should return the current logged in user", () => { | |
| 66 | + authService.login(credentials); | |
| 67 | + $httpBackend.flush(); | |
| 68 | + let actual: noosfero.User = authService.currentUser(); | |
| 69 | + expect(actual).toEqual(user, "The returned user must be present"); | |
| 70 | + }); | |
| 71 | + | |
| 72 | + it("should not return the current user after logout", () => { | |
| 73 | + authService.logout(); | |
| 74 | + let actual: any = authService.currentUser(); | |
| 75 | + expect(actual).toEqual(undefined, "The returned user must not be defined"); | |
| 76 | + }); | |
| 77 | + }); | |
| 78 | + | |
| 79 | + | |
| 80 | + }); | |
| 81 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,69 @@ |
| 1 | +import {Injectable, Inject} from "ng-forward"; | |
| 2 | + | |
| 3 | +import {NoosferoRootScope, UserResponse} from "./../shared/models/interfaces"; | |
| 4 | +import {SessionService} from "./session.service"; | |
| 5 | + | |
| 6 | +import {AUTH_EVENTS, IAuthEvents} from "./auth-events"; | |
| 7 | + | |
| 8 | +@Injectable() | |
| 9 | +@Inject("$q", "$http", "$rootScope", "SessionService", "$log", "AUTH_EVENTS") | |
| 10 | +export class AuthService { | |
| 11 | + | |
| 12 | + constructor(private $q: ng.IQService, | |
| 13 | + private $http: ng.IHttpService, | |
| 14 | + private $rootScope: NoosferoRootScope, | |
| 15 | + private sessionService: SessionService, | |
| 16 | + private $log: ng.ILogService, | |
| 17 | + private auth_events: IAuthEvents) { | |
| 18 | + | |
| 19 | + } | |
| 20 | + | |
| 21 | + loginFromCookie() { | |
| 22 | + let url: string = '/api/v1/login_from_cookie'; | |
| 23 | + return this.$http.post(url, null).then(this.loginSuccessCallback.bind(this), this.loginFailedCallback.bind(this)); | |
| 24 | + } | |
| 25 | + | |
| 26 | + | |
| 27 | + private loginSuccessCallback(response: ng.IHttpPromiseCallbackArg<UserResponse>) { | |
| 28 | + this.$log.debug('AuthService.login [SUCCESS] response', response); | |
| 29 | + let currentUser: noosfero.User = this.sessionService.create(response.data); | |
| 30 | + this.$rootScope.currentUser = currentUser; | |
| 31 | + this.$rootScope.$broadcast(this.auth_events.loginSuccess, currentUser); | |
| 32 | + return currentUser; | |
| 33 | + } | |
| 34 | + | |
| 35 | + login(credentials: noosfero.Credentials): ng.IPromise<noosfero.User> { | |
| 36 | + let url = '/api/v1/login'; | |
| 37 | + let encodedData = 'login=' + credentials.username + '&password=' + credentials.password; | |
| 38 | + return this.$http.post(url, encodedData).then(this.loginSuccessCallback.bind(this), this.loginFailedCallback.bind(this)); | |
| 39 | + } | |
| 40 | + | |
| 41 | + private loginFailedCallback(response: ng.IHttpPromiseCallbackArg<any>): any { | |
| 42 | + this.$log.debug('AuthService.login [FAIL] response', response); | |
| 43 | + this.$rootScope.$broadcast(this.auth_events.loginFailed); | |
| 44 | + // return $q.reject(response); | |
| 45 | + return null; | |
| 46 | + } | |
| 47 | + | |
| 48 | + public logout() { | |
| 49 | + this.sessionService.destroy(); | |
| 50 | + this.$rootScope.currentUser = undefined; | |
| 51 | + this.$rootScope.$broadcast(this.auth_events.logoutSuccess); | |
| 52 | + this.$http.jsonp('/account/logout'); // FIXME logout from noosfero to sync login state | |
| 53 | + } | |
| 54 | + | |
| 55 | + public isAuthenticated() { | |
| 56 | + return !!this.sessionService.currentUser(); | |
| 57 | + } | |
| 58 | + | |
| 59 | + public currentUser(): noosfero.User { | |
| 60 | + return this.sessionService.currentUser(); | |
| 61 | + } | |
| 62 | + | |
| 63 | + public isAuthorized(authorizedRoles: string | string[]) { | |
| 64 | + if (!angular.isArray(authorizedRoles)) { | |
| 65 | + authorizedRoles = [<string>authorizedRoles]; | |
| 66 | + } | |
| 67 | + return (this.isAuthenticated() && authorizedRoles.indexOf(this.sessionService.currentUser().userRole) !== -1); | |
| 68 | + } | |
| 69 | +} | |
| 0 | 70 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +<div class="modal-header"> | |
| 2 | + <h3 class="modal-title">{{"auth.title" | translate}}</h3> | |
| 3 | +</div> | |
| 4 | +<div class="modal-body"> | |
| 5 | + <form> | |
| 6 | + <div class="form-group"> | |
| 7 | + <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label> | |
| 8 | + <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="vm.credentials.username"> | |
| 9 | + </div> | |
| 10 | + <div class="form-group"> | |
| 11 | + <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label> | |
| 12 | + <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="vm.credentials.password"> | |
| 13 | + </div> | |
| 14 | + <button type="submit" class="btn btn-default" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> | |
| 15 | + </form> | |
| 16 | +</div> | ... | ... |
| ... | ... | @@ -0,0 +1,49 @@ |
| 1 | +import {Component} from "ng-forward"; | |
| 2 | +import {SessionService} from "./session.service"; | |
| 3 | +import {fixtures, createComponentFromClass, createProviderToValue} from "./../../spec/helpers"; | |
| 4 | +import {UserResponse, INoosferoLocalStorage} from "./../shared/models/interfaces"; | |
| 5 | + | |
| 6 | + | |
| 7 | +describe("Services", () => { | |
| 8 | + | |
| 9 | + | |
| 10 | + describe("Session Service", () => { | |
| 11 | + | |
| 12 | + let $localStorage: INoosferoLocalStorage = null; | |
| 13 | + let $log: any; | |
| 14 | + | |
| 15 | + beforeEach(() => { | |
| 16 | + $localStorage = <INoosferoLocalStorage>{ currentUser: null }; | |
| 17 | + $log = jasmine.createSpyObj('$log', ['debug']); | |
| 18 | + }); | |
| 19 | + | |
| 20 | + it("method 'create()' saves the current user on $localstorage service", () => { | |
| 21 | + let session = new SessionService($localStorage, $log); | |
| 22 | + let userResponse = <UserResponse>{ | |
| 23 | + user: fixtures.user | |
| 24 | + }; | |
| 25 | + session.create(userResponse); | |
| 26 | + expect($localStorage.currentUser).toEqual(userResponse.user); | |
| 27 | + }); | |
| 28 | + | |
| 29 | + it("method 'destroy()' clean the currentUser on $localstorage", () => { | |
| 30 | + let session = new SessionService($localStorage, $log); | |
| 31 | + let userResponse = <UserResponse>{ | |
| 32 | + user: fixtures.user | |
| 33 | + }; | |
| 34 | + $localStorage.currentUser = fixtures.user; | |
| 35 | + session.destroy(); | |
| 36 | + expect($localStorage.currentUser).toBeUndefined(); | |
| 37 | + }); | |
| 38 | + | |
| 39 | + it("method 'currentUser()' returns the user recorded on $localstorage service", () => { | |
| 40 | + let session = new SessionService($localStorage, $log); | |
| 41 | + let userResponse = <UserResponse>{ | |
| 42 | + user: fixtures.user | |
| 43 | + }; | |
| 44 | + $localStorage.currentUser = fixtures.user; | |
| 45 | + expect(session.currentUser()).toEqual($localStorage.currentUser); | |
| 46 | + }); | |
| 47 | + }); | |
| 48 | + | |
| 49 | +}); | |
| 0 | 50 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +import {Injectable, Inject} from "ng-forward"; | |
| 2 | +import {UserResponse, INoosferoLocalStorage} from "./../shared/models/interfaces"; | |
| 3 | + | |
| 4 | +@Injectable() | |
| 5 | +@Inject("$localStorage", "$log") | |
| 6 | +export class SessionService { | |
| 7 | + | |
| 8 | + constructor(private $localStorage: INoosferoLocalStorage, private $log: ng.ILogService) { | |
| 9 | + | |
| 10 | + } | |
| 11 | + | |
| 12 | + create(data: UserResponse): noosfero.User { | |
| 13 | + this.$localStorage.currentUser = data.user; | |
| 14 | + this.$log.debug('User session created.', this.$localStorage.currentUser); | |
| 15 | + return this.$localStorage.currentUser; | |
| 16 | + }; | |
| 17 | + | |
| 18 | + destroy() { | |
| 19 | + delete this.$localStorage.currentUser; | |
| 20 | + this.$log.debug('User session destroyed.'); | |
| 21 | + }; | |
| 22 | + | |
| 23 | + currentUser(): noosfero.User { | |
| 24 | + return this.$localStorage.currentUser; | |
| 25 | + }; | |
| 26 | + | |
| 27 | +} | |
| 0 | 28 | \ No newline at end of file | ... | ... |
src/app/main/main.component.ts
| 1 | 1 | import {bundle, Component, StateConfig} from "ng-forward"; |
| 2 | -import {ArticleBlog} from "./../components/noosfero-articles/blog/blog.component"; | |
| 2 | +import {ArticleBlogComponent} from "./../article/types/blog/blog.component"; | |
| 3 | 3 | |
| 4 | -import {ArticleView} from "../components/noosfero-articles/article/article_view"; | |
| 4 | +import {ArticleViewComponent} from "./../article/article-default-view.component"; | |
| 5 | 5 | |
| 6 | -import {Profile} from "../profile/profile.component"; | |
| 7 | -import {Boxes} from "../components/noosfero-boxes/boxes.component"; | |
| 8 | -import {Block} from "../components/noosfero-blocks/block.component"; | |
| 9 | -import {LinkListBlock} from "../components/noosfero-blocks/link-list/link-list.component"; | |
| 10 | -import {RecentDocumentsBlock} from "../components/noosfero-blocks/recent-documents/recent-documents.component"; | |
| 11 | -import {ProfileImageBlock} from "../components/noosfero-blocks/profile-image-block/profile-image-block.component"; | |
| 12 | -import {RawHTMLBlock} from "../components/noosfero-blocks/raw-html/raw-html.component"; | |
| 6 | +import {ProfileComponent} from "../profile/profile.component"; | |
| 7 | +import {BoxesComponent} from "../layout/boxes/boxes.component"; | |
| 8 | +import {BlockComponent} from "../layout/blocks/block.component"; | |
| 9 | +import {LinkListBlockComponent} from "./../layout/blocks/link-list/link-list.component"; | |
| 10 | +import {RecentDocumentsBlockComponent} from "../layout/blocks/recent-documents/recent-documents.component"; | |
| 11 | +import {ProfileImageBlockComponent} from "../layout/blocks/profile-image-block/profile-image-block.component"; | |
| 12 | +import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html.component"; | |
| 13 | 13 | |
| 14 | -import {MembersBlock} from "../components/noosfero-blocks/members-block/members-block.component"; | |
| 15 | -import {NoosferoTemplate} from "../components/noosfero/noosfero-template.filter"; | |
| 16 | -import {DateFormat} from "../components/noosfero/date-format/date-format.filter"; | |
| 14 | +import {MembersBlockComponent} from "./../layout/blocks/members-block/members-block.component"; | |
| 15 | +import {NoosferoTemplate} from "../shared/pipes/noosfero-template.filter"; | |
| 16 | +import {DateFormat} from "../shared/pipes/date-format.filter"; | |
| 17 | 17 | |
| 18 | -import {AuthService} from "./../components/auth/auth_service"; | |
| 19 | -import {Session} from "./../components/auth/session"; | |
| 20 | -import {Notification} from "./../components/notification/notification.component"; | |
| 18 | +import {AuthService} from "../login/auth.service"; | |
| 19 | +import {SessionService} from "../login/session.service"; | |
| 21 | 20 | |
| 21 | +import {NotificationService} from "../shared/services/notification.service"; | |
| 22 | 22 | |
| 23 | -import {Navbar} from "../components/navbar/navbar"; | |
| 24 | 23 | |
| 25 | -import {MainBlock} from "../components/noosfero-blocks/main-block/main-block.component"; | |
| 24 | +import {Navbar} from "../layout/navbar/navbar"; | |
| 25 | + | |
| 26 | +import {MainBlockComponent} from "../layout/blocks/main-block/main-block.component"; | |
| 26 | 27 | |
| 27 | 28 | |
| 28 | 29 | /** |
| ... | ... | @@ -38,7 +39,7 @@ import {MainBlock} from "../components/noosfero-blocks/main-block/main-block.com |
| 38 | 39 | @Component({ |
| 39 | 40 | selector: 'main-content', |
| 40 | 41 | templateUrl: "app/main/main.html", |
| 41 | - providers: [AuthService, Session] | |
| 42 | + providers: [AuthService, SessionService] | |
| 42 | 43 | }) |
| 43 | 44 | export class MainContent { |
| 44 | 45 | |
| ... | ... | @@ -62,11 +63,11 @@ export class MainContent { |
| 62 | 63 | selector: 'main', |
| 63 | 64 | template: '<div ng-view></div>', |
| 64 | 65 | directives: [ |
| 65 | - ArticleBlog, ArticleView, Boxes, Block, LinkListBlock, | |
| 66 | - MainBlock, RecentDocumentsBlock, Navbar, ProfileImageBlock, | |
| 67 | - MembersBlock, NoosferoTemplate, DateFormat, RawHTMLBlock | |
| 66 | + ArticleBlogComponent, ArticleViewComponent, BoxesComponent, BlockComponent, LinkListBlockComponent, | |
| 67 | + MainBlockComponent, RecentDocumentsBlockComponent, Navbar, ProfileImageBlockComponent, | |
| 68 | + MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent | |
| 68 | 69 | ], |
| 69 | - providers: [AuthService, Session, Notification] | |
| 70 | + providers: [AuthService, SessionService, NotificationService] | |
| 70 | 71 | }) |
| 71 | 72 | @StateConfig([ |
| 72 | 73 | { |
| ... | ... | @@ -77,12 +78,12 @@ export class MainContent { |
| 77 | 78 | { |
| 78 | 79 | url: "^/:profile", |
| 79 | 80 | abstract: true, |
| 80 | - component: Profile, | |
| 81 | + component: ProfileComponent, | |
| 81 | 82 | name: 'main.profile', |
| 82 | 83 | views: { |
| 83 | 84 | "content": { |
| 84 | 85 | templateUrl: "app/profile/profile.html", |
| 85 | - controller: Profile, | |
| 86 | + controller: ProfileComponent, | |
| 86 | 87 | controllerAs: "vm" |
| 87 | 88 | } |
| 88 | 89 | } | ... | ... |
src/app/models/index.ts
src/app/models/interfaces.ts
| ... | ... | @@ -1,113 +0,0 @@ |
| 1 | -export interface NoosferoRootScope extends ng.IScope { | |
| 2 | - currentUser: User; | |
| 3 | -} | |
| 4 | - | |
| 5 | -export interface Event extends Article { | |
| 6 | - id: number; | |
| 7 | -} | |
| 8 | - | |
| 9 | -export interface Article { | |
| 10 | - id: number; | |
| 11 | -} | |
| 12 | - | |
| 13 | -/** | |
| 14 | - * @ngdoc interface | |
| 15 | - * @name models.Profile | |
| 16 | - * @description | |
| 17 | - * A representation of a Noosfero Profile. | |
| 18 | - */ | |
| 19 | -export interface Profile { | |
| 20 | - | |
| 21 | - /** | |
| 22 | - * @ngdoc property | |
| 23 | - * @name id | |
| 24 | - * @propertyOf models.Profile | |
| 25 | - * @returns {number} The Profile id | |
| 26 | - */ | |
| 27 | - id: number; | |
| 28 | - | |
| 29 | - /** | |
| 30 | - * @ngdoc property | |
| 31 | - * @name identifier | |
| 32 | - * @propertyOf models.Profile | |
| 33 | - * @returns {string} The unque identifier for the Profile | |
| 34 | - */ | |
| 35 | - identifier: string; | |
| 36 | - | |
| 37 | - /** | |
| 38 | - * @ngdoc property | |
| 39 | - * @name type | |
| 40 | - * @propertyOf models.Profile | |
| 41 | - * @returns {string} The Profile type (e.g.: "Person", etc.) | |
| 42 | - */ | |
| 43 | - type: string; | |
| 44 | -} | |
| 45 | - | |
| 46 | - | |
| 47 | -/** | |
| 48 | - * @ngdoc interface | |
| 49 | - * @name models.Person | |
| 50 | - * @description | |
| 51 | - * A representation of a Person in Noosfero. | |
| 52 | - */ | |
| 53 | -export interface Person extends Profile { | |
| 54 | - /** | |
| 55 | - * @ngdoc property | |
| 56 | - * @name id | |
| 57 | - * @propertyOf models.Person | |
| 58 | - * @returns {number} The Person id | |
| 59 | - */ | |
| 60 | - id: number; | |
| 61 | -} | |
| 62 | - | |
| 63 | -export interface TynyMceArticle extends Article { | |
| 64 | - id: number; | |
| 65 | -} | |
| 66 | - | |
| 67 | -export interface Blog extends Article { | |
| 68 | - id: number; | |
| 69 | -} | |
| 70 | - | |
| 71 | -export interface Credentials { | |
| 72 | - username: string; | |
| 73 | - password: string; | |
| 74 | -} | |
| 75 | - | |
| 76 | -export interface User { | |
| 77 | - id: number; | |
| 78 | - login: string; | |
| 79 | - email: string; | |
| 80 | - person: Person; | |
| 81 | - private_token: string; | |
| 82 | - userRole: string; | |
| 83 | -} | |
| 84 | - | |
| 85 | -export interface UserResponse { | |
| 86 | - user: User; | |
| 87 | -} | |
| 88 | - | |
| 89 | - | |
| 90 | -export interface Box { | |
| 91 | - id: number; | |
| 92 | - position: number; | |
| 93 | -} | |
| 94 | - | |
| 95 | -/** | |
| 96 | - * @ngdoc interface | |
| 97 | - * @name models.Activity | |
| 98 | - * @description | |
| 99 | - * A representation of a {@link models.Profile} activity in Noosfero. | |
| 100 | - */ | |
| 101 | -export interface Activity { | |
| 102 | - /** | |
| 103 | - * @ngdoc property | |
| 104 | - * @name verb | |
| 105 | - * @propertyOf models.Activity | |
| 106 | - * @returns {string} The activity verb. | |
| 107 | - */ | |
| 108 | - verb: string; | |
| 109 | -} | |
| 110 | - | |
| 111 | -export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { | |
| 112 | - currentUser: User; | |
| 113 | -} |
src/app/profile-info/index.ts
src/app/profile-info/profile-info.component.spec.ts
| ... | ... | @@ -1,40 +0,0 @@ |
| 1 | -import {quickCreateComponent} from "../../spec/helpers"; | |
| 2 | -import {ProfileInfo} from "./profile-info.component"; | |
| 3 | - | |
| 4 | -describe("Components", () => { | |
| 5 | - describe("Profile Info Component", () => { | |
| 6 | - | |
| 7 | - let $rootScope: ng.IRootScopeService; | |
| 8 | - let $q: ng.IQService; | |
| 9 | - let profileServiceMock: any; | |
| 10 | - let $stateParams: any; | |
| 11 | - | |
| 12 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
| 13 | - $rootScope = _$rootScope_; | |
| 14 | - $q = _$q_; | |
| 15 | - })); | |
| 16 | - | |
| 17 | - beforeEach(() => { | |
| 18 | - $stateParams = jasmine.createSpyObj("$stateParams", ["profile"]); | |
| 19 | - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile", "getActivities"]); | |
| 20 | - | |
| 21 | - let getCurrentProfileResponse = $q.defer(); | |
| 22 | - getCurrentProfileResponse.resolve({ id: 1 }); | |
| 23 | - | |
| 24 | - let getActivitiesResponse = $q.defer(); | |
| 25 | - getActivitiesResponse.resolve({ data: { activities: [{ id: 1 }, { id: 2 }] } }); | |
| 26 | - | |
| 27 | - profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
| 28 | - profileServiceMock.getActivities = jasmine.createSpy("getActivities").and.returnValue(getActivitiesResponse.promise); | |
| 29 | - }); | |
| 30 | - | |
| 31 | - it("get the profile activities", done => { | |
| 32 | - let component: ProfileInfo = new ProfileInfo(profileServiceMock); | |
| 33 | - $rootScope.$apply(); | |
| 34 | - expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
| 35 | - expect(profileServiceMock.getActivities).toHaveBeenCalled(); | |
| 36 | - expect(component.activities).toEqual([{ id: 1 }, { id: 2 }]); | |
| 37 | - done(); | |
| 38 | - }); | |
| 39 | - }); | |
| 40 | -}); |
src/app/profile-info/profile-info.component.ts
| ... | ... | @@ -1,29 +0,0 @@ |
| 1 | -import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
| 2 | - | |
| 3 | -import {Profile} from "./../models/interfaces"; | |
| 4 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
| 5 | - | |
| 6 | -@Component({ | |
| 7 | - selector: 'profile', | |
| 8 | - templateUrl: "app/profile-info/profile-info.html", | |
| 9 | - providers: [provide('profileService', { useClass: ProfileService })] | |
| 10 | -}) | |
| 11 | -@Inject(ProfileService) | |
| 12 | -export class ProfileInfo { | |
| 13 | - | |
| 14 | - activities: any | |
| 15 | - profile: any | |
| 16 | - | |
| 17 | - constructor(private profileService: ProfileService) { | |
| 18 | - this.activate(); | |
| 19 | - } | |
| 20 | - | |
| 21 | - activate() { | |
| 22 | - this.profileService.getCurrentProfile().then((profile: Profile) => { | |
| 23 | - this.profile = profile; | |
| 24 | - return this.profileService.getActivities(this.profile.id); | |
| 25 | - }).then((response: restangular.IResponse) => { | |
| 26 | - this.activities = response.data.activities; | |
| 27 | - }); | |
| 28 | - } | |
| 29 | -} |
src/app/profile-info/profile-info.html
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | +import {provideFilters} from '../../../spec/helpers'; | |
| 4 | + | |
| 5 | +import {ActivitiesComponent} from './activities.component'; | |
| 6 | + | |
| 7 | +const tcb = new TestComponentBuilder(); | |
| 8 | + | |
| 9 | +const htmlTemplate: string = '<noosfero-activities [activities]="ctrl.activities"></noosfero-activities>'; | |
| 10 | + | |
| 11 | + | |
| 12 | +describe("Components", () => { | |
| 13 | + | |
| 14 | + describe("Noosfero Activities", () => { | |
| 15 | + | |
| 16 | + beforeEach(angular.mock.module("templates")); | |
| 17 | + | |
| 18 | + @Component({ | |
| 19 | + selector: 'test-container-component', | |
| 20 | + template: htmlTemplate, | |
| 21 | + directives: [ActivitiesComponent], | |
| 22 | + providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
| 23 | + }) | |
| 24 | + class BlockContainerComponent { | |
| 25 | + activities = [{ name: "activity1", verb: "create_article" }, { name: "activity2", verb: "create_article" }]; | |
| 26 | + } | |
| 27 | + | |
| 28 | + it("render a noosfero activity tag for each activity", done => { | |
| 29 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 30 | + expect(fixture.debugElement.queryAll("noosfero-activity").length).toEqual(2); | |
| 31 | + done(); | |
| 32 | + }); | |
| 33 | + }); | |
| 34 | + }); | |
| 35 | + | |
| 36 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +import {Component, Input} from "ng-forward"; | |
| 2 | +import {ActivityComponent} from "./activity/activity.component"; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * @ngdoc controller | |
| 6 | + * @name NoosferoActivities | |
| 7 | + * @description | |
| 8 | + * The controller responsible to retreive profile activities. | |
| 9 | + */ | |
| 10 | + | |
| 11 | +@Component({ | |
| 12 | + selector: "noosfero-activities", | |
| 13 | + templateUrl: 'app/profile/activities/activities.html', | |
| 14 | + directives: [ActivityComponent] | |
| 15 | +}) | |
| 16 | +export class ActivitiesComponent { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * @ngdoc property | |
| 20 | + * @propertyOf NoosferoActivities | |
| 21 | + * @name activities | |
| 22 | + * @returns {Activity[]} An array of {@link Activity}. | |
| 23 | + */ | |
| 24 | + @Input() activities: noosfero.Activity[]; | |
| 25 | + | |
| 26 | + | |
| 27 | +} | ... | ... |
src/app/profile/activities/activity/activity.component.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | +import {provideFilters} from '../../../../spec/helpers'; | |
| 4 | + | |
| 5 | +import {ActivityComponent} from './activity.component'; | |
| 6 | + | |
| 7 | +const tcb = new TestComponentBuilder(); | |
| 8 | + | |
| 9 | +const htmlTemplate: string = '<noosfero-activity [activity]="ctrl.activity"></noosfero-activity>'; | |
| 10 | + | |
| 11 | + | |
| 12 | +describe("Components", () => { | |
| 13 | + | |
| 14 | + describe("Noosfero Activity", () => { | |
| 15 | + | |
| 16 | + beforeEach(angular.mock.module("templates")); | |
| 17 | + | |
| 18 | + @Component({ | |
| 19 | + selector: 'test-container-component', | |
| 20 | + template: htmlTemplate, | |
| 21 | + directives: [ActivityComponent], | |
| 22 | + providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter") | |
| 23 | + }) | |
| 24 | + class BlockContainerComponent { | |
| 25 | + activity = { name: "activity1", verb: "create_article" }; | |
| 26 | + } | |
| 27 | + | |
| 28 | + it("render the specific template for an activity verb", done => { | |
| 29 | + tcb.createAsync(BlockContainerComponent).then(fixture => { | |
| 30 | + let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
| 31 | + expect(component.getActivityTemplate()).toEqual('app/profile/activities/activity/create_article.html'); | |
| 32 | + expect(fixture.debugElement.queryAll(".activity.create_article").length).toEqual(1); | |
| 33 | + done(); | |
| 34 | + }); | |
| 35 | + }); | |
| 36 | + }); | |
| 37 | + | |
| 38 | +}); | ... | ... |
src/app/profile/activities/activity/activity.component.ts
0 → 100644
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +import {Component, Input} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Component({ | |
| 4 | + selector: "noosfero-activity", | |
| 5 | + templateUrl: 'app/profile/activities/activity/activity.html' | |
| 6 | +}) | |
| 7 | +export class ActivityComponent { | |
| 8 | + | |
| 9 | + @Input() activity: noosfero.Activity; | |
| 10 | + | |
| 11 | + getActivityTemplate() { | |
| 12 | + return 'app/profile/activities/activity/' + this.activity.verb + '.html'; | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | ... | ... |
src/app/profile/activities/activity/add_member_in_community.html
0 → 100644
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +<timeline-badge class="info"> | |
| 2 | + <i class="fa fa-user-plus"></i> | |
| 3 | +</timeline-badge> | |
| 4 | +<timeline-panel> | |
| 5 | + <timeline-heading> | |
| 6 | + <h4 class="timeline-title"> | |
| 7 | + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
| 8 | + <span> {{"activities.add_member_in_community.description" | translate}}</span> | |
| 9 | + </h4> | |
| 10 | + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
| 11 | + </timeline-heading> | |
| 12 | + <div class="timeline-body"></div> | |
| 13 | +</timeline-panel> | ... | ... |
| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | +<timeline-badge class="success"> | |
| 2 | + <i class="fa fa-file-text"></i> | |
| 3 | +</timeline-badge> | |
| 4 | +<timeline-panel> | |
| 5 | + <timeline-heading> | |
| 6 | + <h4 class="timeline-title"> | |
| 7 | + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
| 8 | + <span> {{"activities.create_article.description" | translate}} </span> | |
| 9 | + <a ui-sref="main.profile.info({profile: ctrl.activity.target.article.profile.identifier})"> | |
| 10 | + <strong ng-bind="ctrl.activity.target.article.profile.name"></strong></span> | |
| 11 | + </a> | |
| 12 | + </h4> | |
| 13 | + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
| 14 | + </timeline-heading> | |
| 15 | + <div class="timeline-body"> | |
| 16 | + <div class="article"> | |
| 17 | + <div class="title"> | |
| 18 | + <a ui-sref="main.profile.page({profile: ctrl.activity.target.article.profile.identifier, page: ctrl.activity.target.article.path})" | |
| 19 | + ng-bind="ctrl.activity.target.article.title"></a> | |
| 20 | + </div> | |
| 21 | + <div class="lead small"> | |
| 22 | + <div ng-bind-html="ctrl.activity.target.article.body | stripTags | truncate: 100 : '...': true"></div> | |
| 23 | + </div> | |
| 24 | + </div> | |
| 25 | + </div> | |
| 26 | +</timeline-panel> | ... | ... |
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +<timeline-badge class="info"> | |
| 2 | + <i class="fa fa-user-plus"></i> | |
| 3 | +</timeline-badge> | |
| 4 | +<timeline-panel> | |
| 5 | + <timeline-heading> | |
| 6 | + <h4 class="timeline-title"> | |
| 7 | + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a> | |
| 8 | + <span> {{"activities.new_friendship.description" | translate:{friends: ctrl.activity.params.friend_name.length}:"messageformat" }} </span> | |
| 9 | + <span class="comma-separated"> | |
| 10 | + <a class="separated-item" ui-sref="main.profile.info({profile: ctrl.activity.params.friend_url[$index].profile})" ng-repeat="friend in ctrl.activity.params.friend_name"> | |
| 11 | + <strong ng-bind="friend"></strong> | |
| 12 | + </a> | |
| 13 | + </span> | |
| 14 | + </h4> | |
| 15 | + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p> | |
| 16 | + </timeline-heading> | |
| 17 | + <div class="timeline-body"></div> | |
| 18 | +</timeline-panel> | ... | ... |
| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | +/** | |
| 2 | + * @ngdoc overview | |
| 3 | + * @name components.noosfero.profile-image.ProfileImageSpec | |
| 4 | + * @description | |
| 5 | + * This file contains the tests for the {@link components.noosfero.profile-image.ProfileImage} component. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 9 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 10 | + | |
| 11 | +import * as helpers from "../../../spec/helpers"; | |
| 12 | + | |
| 13 | +import {ProfileImageComponent} from "./image.component"; | |
| 14 | + | |
| 15 | +const tcb = new TestComponentBuilder(); | |
| 16 | + | |
| 17 | +describe("Components", () => { | |
| 18 | + | |
| 19 | + describe("Profile Image Component", () => { | |
| 20 | + | |
| 21 | + beforeEach(angular.mock.module("templates")); | |
| 22 | + | |
| 23 | + it("show community users image if profile is not Person", done => { | |
| 24 | + helpers.tcb.createAsync(ProfileImageComponent).then(fixture => { | |
| 25 | + let profileImageComponent: ProfileImageComponent = fixture.componentInstance; | |
| 26 | + let profile = <noosfero.Profile>{ id: 1, identifier: "myprofile", type: "Community" }; | |
| 27 | + profileImageComponent.profile = profile; | |
| 28 | + profileImageComponent.ngOnInit(); | |
| 29 | + | |
| 30 | + // Check the attribute | |
| 31 | + expect(profileImageComponent.defaultIcon).toBe("fa-users", "The default icon should be community users"); | |
| 32 | + // var elProfile = fixture.debugElement.componentViewChildren[0]; | |
| 33 | + // expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
| 34 | + done(); | |
| 35 | + }); | |
| 36 | + }); | |
| 37 | + | |
| 38 | + it("show Person image if profile is Person", done => { | |
| 39 | + tcb.createAsync(ProfileImageComponent).then(fixture => { | |
| 40 | + let profileImageComponent: ProfileImageComponent = fixture.componentInstance; | |
| 41 | + let profile = <noosfero.Profile>{ id: 1, identifier: "myprofile", type: "Person" }; | |
| 42 | + profileImageComponent.profile = profile; | |
| 43 | + profileImageComponent.ngOnInit(); | |
| 44 | + // Check the attribute | |
| 45 | + expect(profileImageComponent.defaultIcon).toEqual("fa-user", "The default icon should be person user"); | |
| 46 | + done(); | |
| 47 | + }); | |
| 48 | + }); | |
| 49 | + | |
| 50 | + }); | |
| 51 | +}); | |
| 0 | 52 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | +import {Inject, Input, Component} from "ng-forward"; | |
| 2 | + | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * @ngdoc controller | |
| 6 | + * @name components.noosfero.profile-image.ProfileImage | |
| 7 | + * @description The component responsible for rendering the profile image | |
| 8 | + * @exports ProfileImage | |
| 9 | + */ | |
| 10 | +@Component({ | |
| 11 | + selector: "noosfero-profile-image", | |
| 12 | + templateUrl: 'app/profile/image/image.html', | |
| 13 | +}) | |
| 14 | +export class ProfileImageComponent { | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * @ngdoc property | |
| 18 | + * @name profile | |
| 19 | + * @propertyOf components.noosfero.profile-image.ProfileImage | |
| 20 | + * @description | |
| 21 | + * The Noosfero {@link models.Profile} holding the image. | |
| 22 | + */ | |
| 23 | + @Input() profile: noosfero.Profile; | |
| 24 | + /** | |
| 25 | + * @ngdoc property | |
| 26 | + * @name defaultIcon | |
| 27 | + * @propertyOf components.noosfero.profile-image.ProfileImage | |
| 28 | + * @descritpion | |
| 29 | + * The default icon used by this profile | |
| 30 | + */ | |
| 31 | + defaultIcon: string; | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * @ngdoc method | |
| 35 | + * @name ngOnInit | |
| 36 | + * @methodOf components.noosfero.profile-image.ProfileImage | |
| 37 | + * @description | |
| 38 | + * Initializes the icon names to their corresponding values depending on the profile type passed to the controller | |
| 39 | + */ | |
| 40 | + ngOnInit() { | |
| 41 | + this.defaultIcon = 'fa-users'; | |
| 42 | + if (this.profile && this.profile.type === 'Person') { | |
| 43 | + this.defaultIcon = 'fa-user'; | |
| 44 | + } | |
| 45 | + } | |
| 46 | +} | |
| 47 | + | ... | ... |
src/app/profile/index.ts
| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | +import {quickCreateComponent} from "../../../spec/helpers"; | |
| 2 | +import {ProfileInfoComponent} from "./profile-info.component"; | |
| 3 | + | |
| 4 | +describe("Components", () => { | |
| 5 | + describe("Profile Info Component", () => { | |
| 6 | + | |
| 7 | + let $rootScope: ng.IRootScopeService; | |
| 8 | + let $q: ng.IQService; | |
| 9 | + let profileServiceMock: any; | |
| 10 | + let $stateParams: any; | |
| 11 | + | |
| 12 | + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
| 13 | + $rootScope = _$rootScope_; | |
| 14 | + $q = _$q_; | |
| 15 | + })); | |
| 16 | + | |
| 17 | + beforeEach(() => { | |
| 18 | + $stateParams = jasmine.createSpyObj("$stateParams", ["profile"]); | |
| 19 | + profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile", "getActivities"]); | |
| 20 | + | |
| 21 | + let getCurrentProfileResponse = $q.defer(); | |
| 22 | + getCurrentProfileResponse.resolve({ id: 1 }); | |
| 23 | + | |
| 24 | + let getActivitiesResponse = $q.defer(); | |
| 25 | + getActivitiesResponse.resolve({ data: { activities: [{ id: 1 }, { id: 2 }] } }); | |
| 26 | + | |
| 27 | + profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
| 28 | + profileServiceMock.getActivities = jasmine.createSpy("getActivities").and.returnValue(getActivitiesResponse.promise); | |
| 29 | + }); | |
| 30 | + | |
| 31 | + it("get the profile activities", done => { | |
| 32 | + let component: ProfileInfoComponent = new ProfileInfoComponent(profileServiceMock); | |
| 33 | + $rootScope.$apply(); | |
| 34 | + expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
| 35 | + expect(profileServiceMock.getActivities).toHaveBeenCalled(); | |
| 36 | + expect(component.activities).toEqual([{ id: 1 }, { id: 2 }]); | |
| 37 | + done(); | |
| 38 | + }); | |
| 39 | + }); | |
| 40 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
| 2 | +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
| 3 | + | |
| 4 | +@Component({ | |
| 5 | + selector: 'profile', | |
| 6 | + templateUrl: "app/profile/info/profile-info.html", | |
| 7 | + providers: [provide('profileService', { useClass: ProfileService })] | |
| 8 | +}) | |
| 9 | +@Inject(ProfileService) | |
| 10 | +export class ProfileInfoComponent { | |
| 11 | + | |
| 12 | + activities: any; | |
| 13 | + profile: noosfero.Profile; | |
| 14 | + | |
| 15 | + constructor(private profileService: ProfileService) { | |
| 16 | + this.activate(); | |
| 17 | + } | |
| 18 | + | |
| 19 | + activate() { | |
| 20 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 21 | + this.profile = profile; | |
| 22 | + return this.profileService.getActivities(<number>this.profile.id); | |
| 23 | + }).then((response: restangular.IResponse) => { | |
| 24 | + this.activities = response.data.activities; | |
| 25 | + }); | |
| 26 | + } | |
| 27 | +} | ... | ... |
src/app/profile/profile-home.component.ts
| 1 | 1 | import {StateConfig, Component, Inject, provide} from 'ng-forward'; |
| 2 | 2 | |
| 3 | -import {Profile} from "./../models/interfaces"; | |
| 4 | 3 | import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; |
| 5 | 4 | |
| 6 | 5 | @Component({ |
| ... | ... | @@ -11,12 +10,12 @@ import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; |
| 11 | 10 | @Inject(ProfileService, "$state") |
| 12 | 11 | export class ProfileHome { |
| 13 | 12 | |
| 14 | - profile: Profile; | |
| 13 | + profile: noosfero.Profile; | |
| 15 | 14 | |
| 16 | 15 | constructor(profileService: ProfileService, $state: ng.ui.IStateService) { |
| 17 | - profileService.getCurrentProfile().then((profile: Profile) => { | |
| 16 | + profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 18 | 17 | this.profile = profile; |
| 19 | - return profileService.getHomePage(this.profile.id, { fields: 'path' }); | |
| 18 | + return profileService.getHomePage(<number>this.profile.id, { fields: 'path' }); | |
| 20 | 19 | }).then((response: restangular.IResponse) => { |
| 21 | 20 | if (response.data.article) { |
| 22 | 21 | $state.transitionTo('main.profile.page', { page: response.data.article.path, profile: this.profile.identifier }, { location: false }); | ... | ... |
src/app/profile/profile.component.spec.ts
| 1 | 1 | import {quickCreateComponent} from "../../spec/helpers"; |
| 2 | -import {Profile} from "./profile.component"; | |
| 2 | +import {ProfileComponent} from "./profile.component"; | |
| 3 | 3 | |
| 4 | 4 | describe("Components", () => { |
| 5 | 5 | describe("Profile Component", () => { |
| ... | ... | @@ -30,7 +30,7 @@ describe("Components", () => { |
| 30 | 30 | }); |
| 31 | 31 | |
| 32 | 32 | it("get the profile and store in profile service", done => { |
| 33 | - let component: Profile = new Profile(profileServiceMock, $stateParams, notificationMock); | |
| 33 | + let component: ProfileComponent = new ProfileComponent(profileServiceMock, $stateParams, notificationMock); | |
| 34 | 34 | $rootScope.$apply(); |
| 35 | 35 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); |
| 36 | 36 | expect(component.profile).toEqual({ id: 1 }); |
| ... | ... | @@ -38,7 +38,7 @@ describe("Components", () => { |
| 38 | 38 | }); |
| 39 | 39 | |
| 40 | 40 | it("get the profile boxes", done => { |
| 41 | - let component: Profile = new Profile(profileServiceMock, $stateParams, notificationMock); | |
| 41 | + let component: ProfileComponent = new ProfileComponent(profileServiceMock, $stateParams, notificationMock); | |
| 42 | 42 | $rootScope.$apply(); |
| 43 | 43 | expect(profileServiceMock.getBoxes).toHaveBeenCalled(); |
| 44 | 44 | expect(component.boxes).toEqual([{ id: 2 }]); |
| ... | ... | @@ -50,7 +50,7 @@ describe("Components", () => { |
| 50 | 50 | profileResponse.reject(); |
| 51 | 51 | profileServiceMock.setCurrentProfileByIdentifier = jasmine.createSpy("setCurrentProfileByIdentifier").and.returnValue(profileResponse.promise); |
| 52 | 52 | |
| 53 | - let component: Profile = new Profile(profileServiceMock, $stateParams, notificationMock); | |
| 53 | + let component: ProfileComponent = new ProfileComponent(profileServiceMock, $stateParams, notificationMock); | |
| 54 | 54 | $rootScope.$apply(); |
| 55 | 55 | |
| 56 | 56 | expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); | ... | ... |
src/app/profile/profile.component.ts
| 1 | 1 | import {StateConfig, Component, Inject, provide} from 'ng-forward'; |
| 2 | -import {ProfileInfo} from '../profile-info/profile-info.component'; | |
| 3 | -import {ProfileHome} from '../profile/profile-home.component'; | |
| 4 | -import {Cms} from '../cms/cms.component'; | |
| 5 | -import {ContentViewer} from "../content-viewer/content-viewer.component"; | |
| 6 | -import {ContentViewerActions} from "../content-viewer/content-viewer-actions.component"; | |
| 7 | -import {NoosferoActivities} from "../components/noosfero-activities/activities.component"; | |
| 2 | +import {ProfileInfoComponent} from './info/profile-info.component'; | |
| 3 | +import {ProfileHome} from './profile-home.component'; | |
| 4 | +import {BasicEditorComponent} from '../article/basic-editor.component'; | |
| 5 | +import {ContentViewerComponent} from "../article/content-viewer/content-viewer.component"; | |
| 6 | +import {ContentViewerActions} from "../article/content-viewer/content-viewer-actions.component"; | |
| 7 | +import {ActivitiesComponent} from "./activities/activities.component"; | |
| 8 | 8 | import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; |
| 9 | -import {Notification} from "../components/notification/notification.component"; | |
| 9 | +import {NotificationService} from "../shared/services/notification.service"; | |
| 10 | 10 | import {MyProfile} from "./myprofile.component"; |
| 11 | 11 | |
| 12 | -import * as noosferoModels from "./../models/interfaces"; | |
| 13 | 12 | |
| 14 | 13 | /** |
| 15 | 14 | * @ngdoc controller |
| ... | ... | @@ -17,24 +16,25 @@ import * as noosferoModels from "./../models/interfaces"; |
| 17 | 16 | * @description |
| 18 | 17 | * This is the profile controller. It provide routes to supported Noosfero Profiles. |
| 19 | 18 | */ |
| 19 | + | |
| 20 | 20 | @Component({ |
| 21 | 21 | selector: 'profile', |
| 22 | 22 | templateUrl: "app/profile/profile.html", |
| 23 | - directives: [NoosferoActivities], | |
| 23 | + directives: [ActivitiesComponent], | |
| 24 | 24 | providers: [ |
| 25 | 25 | provide('profileService', { useClass: ProfileService }), |
| 26 | - provide('notification', { useClass: Notification }) | |
| 26 | + provide('notificationService', { useClass: NotificationService }) | |
| 27 | 27 | ] |
| 28 | 28 | }) |
| 29 | 29 | @StateConfig([ |
| 30 | 30 | { |
| 31 | 31 | name: 'main.profile.info', |
| 32 | 32 | url: "^/profile/:profile", |
| 33 | - component: ProfileInfo, | |
| 33 | + component: ProfileInfoComponent, | |
| 34 | 34 | views: { |
| 35 | 35 | "mainBlockContent": { |
| 36 | - templateUrl: "app/profile-info/profile-info.html", | |
| 37 | - controller: ProfileInfo, | |
| 36 | + templateUrl: "app/profile/info/profile-info.html", | |
| 37 | + controller: ProfileInfoComponent, | |
| 38 | 38 | controllerAs: "vm" |
| 39 | 39 | } |
| 40 | 40 | } |
| ... | ... | @@ -47,11 +47,11 @@ import * as noosferoModels from "./../models/interfaces"; |
| 47 | 47 | { |
| 48 | 48 | name: 'main.profile.cms', |
| 49 | 49 | url: "^/myprofile/:profile/cms", |
| 50 | - component: Cms, | |
| 50 | + component: BasicEditorComponent, | |
| 51 | 51 | views: { |
| 52 | 52 | "mainBlockContent": { |
| 53 | - templateUrl: "app/cms/cms.html", | |
| 54 | - controller: Cms, | |
| 53 | + templateUrl: "app/article/basic-editor.html", | |
| 54 | + controller: BasicEditorComponent, | |
| 55 | 55 | controllerAs: "vm" |
| 56 | 56 | } |
| 57 | 57 | } |
| ... | ... | @@ -70,15 +70,15 @@ import * as noosferoModels from "./../models/interfaces"; |
| 70 | 70 | { |
| 71 | 71 | name: 'main.profile.page', |
| 72 | 72 | url: "/{page:any}", |
| 73 | - component: ContentViewer, | |
| 73 | + component: ContentViewerComponent, | |
| 74 | 74 | views: { |
| 75 | 75 | "mainBlockContent": { |
| 76 | - templateUrl: "app/content-viewer/page.html", | |
| 77 | - controller: ContentViewer, | |
| 76 | + templateUrl: "app/article/content-viewer/page.html", | |
| 77 | + controller: ContentViewerComponent, | |
| 78 | 78 | controllerAs: "vm" |
| 79 | 79 | }, |
| 80 | 80 | "actions@main": { |
| 81 | - templateUrl: "app/content-viewer/navbar-actions.html", | |
| 81 | + templateUrl: "app/article/content-viewer/navbar-actions.html", | |
| 82 | 82 | controller: ContentViewerActions, |
| 83 | 83 | controllerAs: "vm" |
| 84 | 84 | } |
| ... | ... | @@ -86,19 +86,19 @@ import * as noosferoModels from "./../models/interfaces"; |
| 86 | 86 | } |
| 87 | 87 | ]) |
| 88 | 88 | @Inject(ProfileService, "$stateParams") |
| 89 | -export class Profile { | |
| 89 | +export class ProfileComponent { | |
| 90 | 90 | |
| 91 | - boxes: noosferoModels.Box[]; | |
| 92 | - profile: noosferoModels.Profile; | |
| 91 | + boxes: noosfero.Box[]; | |
| 92 | + profile: noosfero.Profile; | |
| 93 | 93 | |
| 94 | - constructor(profileService: ProfileService, $stateParams: ng.ui.IStateParamsService, notification: Notification) { | |
| 95 | - profileService.setCurrentProfileByIdentifier($stateParams["profile"]).then((profile: noosferoModels.Profile) => { | |
| 94 | + constructor(profileService: ProfileService, $stateParams: ng.ui.IStateParamsService, notificationService: NotificationService) { | |
| 95 | + profileService.setCurrentProfileByIdentifier($stateParams["profile"]).then((profile: noosfero.Profile) => { | |
| 96 | 96 | this.profile = profile; |
| 97 | - return profileService.getBoxes(this.profile.id); | |
| 97 | + return profileService.getBoxes(<number>this.profile.id); | |
| 98 | 98 | }).then((response: restangular.IResponse) => { |
| 99 | 99 | this.boxes = response.data.boxes; |
| 100 | 100 | }).catch(() => { |
| 101 | - notification.error("notification.profile.not_found"); | |
| 101 | + notificationService.error("notification.profile.not_found"); | |
| 102 | 102 | }); |
| 103 | 103 | } |
| 104 | 104 | } | ... | ... |
src/app/profile/profile.scss
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +/* Module Index Entry - generated using the script npm run generate-index */ | ... | ... |
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +export interface NoosferoRootScope extends ng.IScope { | |
| 2 | + currentUser: noosfero.User; | |
| 3 | +} | |
| 4 | + | |
| 5 | +export interface UserResponse { | |
| 6 | + user: noosfero.User; | |
| 7 | +} | |
| 8 | + | |
| 9 | + | |
| 10 | +export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { | |
| 11 | + currentUser: noosfero.User; | |
| 12 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +import {quickCreateComponent} from "../../../spec/helpers"; | |
| 2 | +import {DateFormat} from './date-format.filter'; | |
| 3 | + | |
| 4 | +describe("Filters", () => { | |
| 5 | + describe("Date Format Filter", () => { | |
| 6 | + | |
| 7 | + beforeEach(angular.mock.module("templates")); | |
| 8 | + beforeEach(angular.mock.module("angularMoment")); | |
| 9 | + | |
| 10 | + it("convert date from the format returned by noosfero api to an ISO format", done => { | |
| 11 | + let date = "2016/03/10 10:46:47"; | |
| 12 | + let htmlTemplate = `{{ '${date}' | dateFormat }}`; | |
| 13 | + quickCreateComponent({ providers: [DateFormat], template: htmlTemplate }).then(fixture => { | |
| 14 | + expect(fixture.debugElement.text()).toEqual('2016-03-10T13:46:47.000Z'); | |
| 15 | + done(); | |
| 16 | + }); | |
| 17 | + }); | |
| 18 | + | |
| 19 | + }); | |
| 20 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +import {Pipe, Inject} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Pipe("dateFormat") | |
| 4 | +@Inject("amParseFilter") | |
| 5 | +export class DateFormat { | |
| 6 | + | |
| 7 | + constructor(private amParseFilter: any) { } | |
| 8 | + | |
| 9 | + transform(date: string, options: any) { | |
| 10 | + return this.amParseFilter(date, "YYYY/MM/DD HH:mm:ss").toISOString(); | |
| 11 | + } | |
| 12 | + | |
| 13 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | +import {quickCreateComponent} from "../../../spec/helpers"; | |
| 2 | +import {NoosferoTemplate} from './noosfero-template.filter'; | |
| 3 | + | |
| 4 | +describe("Filters", () => { | |
| 5 | + describe("Noosfero Template Filter", () => { | |
| 6 | + | |
| 7 | + beforeEach(angular.mock.module("templates")); | |
| 8 | + | |
| 9 | + it("replace the options in text with the values passed on options", done => { | |
| 10 | + let text = 'profile: {profile}, other: {other}'; | |
| 11 | + let htmlTemplate = `{{ '${text}' | noosferoTemplate: {profile: 'profile1', other: 'other value'} }}`; | |
| 12 | + quickCreateComponent({ providers: [NoosferoTemplate], template: htmlTemplate }).then(fixture => { | |
| 13 | + expect(fixture.debugElement.text()).toEqual("profile: profile1, other: other value"); | |
| 14 | + done(); | |
| 15 | + }); | |
| 16 | + }); | |
| 17 | + | |
| 18 | + }); | |
| 19 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +import {Pipe} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Pipe("noosferoTemplate") | |
| 4 | +export class NoosferoTemplate { | |
| 5 | + | |
| 6 | + transform(text: string, options: any) { | |
| 7 | + for (let option in options) { | |
| 8 | + text = text.replace('{' + option + '}', options[option]); | |
| 9 | + } | |
| 10 | + return text; | |
| 11 | + } | |
| 12 | + | |
| 13 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,80 @@ |
| 1 | +import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {Pipe, Input, provide, Component} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import * as helpers from "../../../spec/helpers"; | |
| 5 | + | |
| 6 | +import {NotificationService} from "./notification.service"; | |
| 7 | + | |
| 8 | +const tcb = new TestComponentBuilder(); | |
| 9 | + | |
| 10 | +describe("Components", () => { | |
| 11 | + | |
| 12 | + describe("Profile Image Component", () => { | |
| 13 | + | |
| 14 | + beforeEach(angular.mock.module("templates")); | |
| 15 | + | |
| 16 | + it("display an error message when notify an error", done => { | |
| 17 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 18 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
| 19 | + | |
| 20 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 21 | + component.error("message", "title"); | |
| 22 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 23 | + text: "message", | |
| 24 | + title: "title", | |
| 25 | + type: "error" | |
| 26 | + })); | |
| 27 | + done(); | |
| 28 | + }); | |
| 29 | + | |
| 30 | + it("use the default message when call notification component without a message", done => { | |
| 31 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 32 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
| 33 | + | |
| 34 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 35 | + component.error(); | |
| 36 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 37 | + text: NotificationService.DEFAULT_ERROR_MESSAGE, | |
| 38 | + type: "error" | |
| 39 | + })); | |
| 40 | + done(); | |
| 41 | + }); | |
| 42 | + | |
| 43 | + it("display a success message when call notification success", done => { | |
| 44 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 45 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
| 46 | + | |
| 47 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 48 | + component.success("title", "message", 1000); | |
| 49 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 50 | + type: "success" | |
| 51 | + })); | |
| 52 | + done(); | |
| 53 | + }); | |
| 54 | + | |
| 55 | + it("display a message relative to the http error code", done => { | |
| 56 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 57 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
| 58 | + | |
| 59 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 60 | + component.httpError(500, {}); | |
| 61 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 62 | + text: "notification.http_error.500.message" | |
| 63 | + })); | |
| 64 | + done(); | |
| 65 | + }); | |
| 66 | + | |
| 67 | + it("set the default timer in success messages", done => { | |
| 68 | + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]); | |
| 69 | + sweetAlert.swal = jasmine.createSpy("swal"); | |
| 70 | + | |
| 71 | + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService); | |
| 72 | + component.success("title", "message"); | |
| 73 | + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({ | |
| 74 | + type: "success", | |
| 75 | + timer: NotificationService.DEFAULT_SUCCESS_TIMER | |
| 76 | + })); | |
| 77 | + done(); | |
| 78 | + }); | |
| 79 | + }); | |
| 80 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | +import {Injectable, Inject} from "ng-forward"; | |
| 2 | +import {TranslatorService} from "./translator.service"; | |
| 3 | + | |
| 4 | +@Injectable() | |
| 5 | +@Inject("$log", "SweetAlert", TranslatorService) | |
| 6 | +export class NotificationService { | |
| 7 | + | |
| 8 | + constructor( | |
| 9 | + private $log: ng.ILogService, | |
| 10 | + private SweetAlert: any, | |
| 11 | + private translatorService: TranslatorService | |
| 12 | + ) { } | |
| 13 | + | |
| 14 | + public static DEFAULT_ERROR_TITLE = "notification.error.default.title"; | |
| 15 | + public static DEFAULT_ERROR_MESSAGE = "notification.error.default.message"; | |
| 16 | + public static DEFAULT_SUCCESS_TIMER = 1000; | |
| 17 | + | |
| 18 | + error(message: string = NotificationService.DEFAULT_ERROR_MESSAGE, title: string = NotificationService.DEFAULT_ERROR_TITLE) { | |
| 19 | + this.$log.debug("Notification error:", title, message, this.translatorService.currentLanguage()); | |
| 20 | + this.SweetAlert.swal({ | |
| 21 | + title: this.translatorService.translate(title), | |
| 22 | + text: this.translatorService.translate(message), | |
| 23 | + type: "error" | |
| 24 | + }); | |
| 25 | + } | |
| 26 | + | |
| 27 | + httpError(status: number, data: any): boolean { | |
| 28 | + this.error(`notification.http_error.${status}.message`); | |
| 29 | + return true; // return true to indicate that the error was already handled | |
| 30 | + } | |
| 31 | + | |
| 32 | + success(title: string, text: string, timer: number = NotificationService.DEFAULT_SUCCESS_TIMER) { | |
| 33 | + this.SweetAlert.swal({ | |
| 34 | + title: title, | |
| 35 | + text: text, | |
| 36 | + type: "success", | |
| 37 | + timer: timer | |
| 38 | + }); | |
| 39 | + } | |
| 40 | + | |
| 41 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,116 @@ |
| 1 | +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 2 | +import {provide} from 'ng-forward'; | |
| 3 | + | |
| 4 | +import {TranslatorService} from './translator.service'; | |
| 5 | + | |
| 6 | +import * as helpers from "../../../spec/helpers"; | |
| 7 | + | |
| 8 | +describe("Services", () => { | |
| 9 | + | |
| 10 | + describe("Translator Service", () => { | |
| 11 | + | |
| 12 | + let $rootScope: ng.IScope; | |
| 13 | + let $q: ng.IQService; | |
| 14 | + | |
| 15 | + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
| 16 | + $rootScope = _$rootScope_; | |
| 17 | + $q = _$q_; | |
| 18 | + })); | |
| 19 | + | |
| 20 | + function createComponent() { | |
| 21 | + return new TranslatorService( | |
| 22 | + <any>helpers.mocks.$translate, | |
| 23 | + <any>helpers.mocks.tmhDynamicLocale, | |
| 24 | + <any>helpers.mocks.amMoment, | |
| 25 | + <any>helpers.mocks.angularLoad, | |
| 26 | + $rootScope | |
| 27 | + ); | |
| 28 | + } | |
| 29 | + | |
| 30 | + it("set available languages when change language", (done) => { | |
| 31 | + let component: TranslatorService = createComponent(); | |
| 32 | + component.availableLanguages = null; | |
| 33 | + expect(component.availableLanguages).toBeNull(); | |
| 34 | + $rootScope.$emit("$translateChangeSuccess"); | |
| 35 | + expect(component.availableLanguages).not.toBeNull(); | |
| 36 | + done(); | |
| 37 | + }); | |
| 38 | + | |
| 39 | + it("change the language", (done) => { | |
| 40 | + let component: TranslatorService = createComponent(); | |
| 41 | + let loadScripPromise = $q.defer(); | |
| 42 | + loadScripPromise.resolve(); | |
| 43 | + component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue(loadScripPromise.promise); | |
| 44 | + component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
| 45 | + component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue("en"); | |
| 46 | + component["$translate"].use = jasmine.createSpy("use"); | |
| 47 | + | |
| 48 | + component.changeLanguage('pt'); | |
| 49 | + $rootScope.$digest(); | |
| 50 | + | |
| 51 | + expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
| 52 | + expect(component["angularLoad"].loadScript).toHaveBeenCalledWith("/bower_components/messageformat/locale/pt.js"); | |
| 53 | + expect(component["tmhDynamicLocale"].set).toHaveBeenCalledWith("pt"); | |
| 54 | + expect(component["$translate"].use).toHaveBeenCalledWith("pt"); | |
| 55 | + done(); | |
| 56 | + }); | |
| 57 | + | |
| 58 | + it("do not load moment locale when change the language to english", (done) => { | |
| 59 | + let component: TranslatorService = createComponent(); | |
| 60 | + component["angularLoad"].loadScript = jasmine.createSpy("loadScript").and.returnValue($q.defer().promise); | |
| 61 | + component.changeLanguage('en'); | |
| 62 | + expect(component["angularLoad"].loadScript).not.toHaveBeenCalledWith("/bower_components/moment/locale/pt.js"); | |
| 63 | + done(); | |
| 64 | + }); | |
| 65 | + | |
| 66 | + it("do nothing when call change language with null", (done) => { | |
| 67 | + let component: TranslatorService = createComponent(); | |
| 68 | + component["angularLoad"].loadScript = jasmine.createSpy("loadScript"); | |
| 69 | + component["tmhDynamicLocale"].set = jasmine.createSpy("set"); | |
| 70 | + component["$translate"].use = jasmine.createSpy("use"); | |
| 71 | + | |
| 72 | + component.changeLanguage(null); | |
| 73 | + | |
| 74 | + expect(component["angularLoad"].loadScript).not.toHaveBeenCalled(); | |
| 75 | + expect(component["tmhDynamicLocale"].set).not.toHaveBeenCalled(); | |
| 76 | + expect(component["$translate"].use).not.toHaveBeenCalled(); | |
| 77 | + done(); | |
| 78 | + }); | |
| 79 | + | |
| 80 | + it("return the current language used by the translator", (done) => { | |
| 81 | + let component: TranslatorService = createComponent(); | |
| 82 | + component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
| 83 | + expect(component.currentLanguage()).toEqual("en"); | |
| 84 | + expect(component["$translate"].use).toHaveBeenCalled(); | |
| 85 | + done(); | |
| 86 | + }); | |
| 87 | + | |
| 88 | + it("call translate service when translate a text", (done) => { | |
| 89 | + let component: TranslatorService = createComponent(); | |
| 90 | + component["$translate"].instant = jasmine.createSpy("instant"); | |
| 91 | + component.translate("text"); | |
| 92 | + expect(component["$translate"].instant).toHaveBeenCalledWith("text"); | |
| 93 | + done(); | |
| 94 | + }); | |
| 95 | + | |
| 96 | + it("change the language when receive an event", (done) => { | |
| 97 | + let component: TranslatorService = createComponent(); | |
| 98 | + component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
| 99 | + $rootScope.$emit("$localeChangeSuccess"); | |
| 100 | + expect(component.changeLanguage).toHaveBeenCalled(); | |
| 101 | + done(); | |
| 102 | + }); | |
| 103 | + | |
| 104 | + it("use the translate language when receive a change language event and there is no language previously selected", (done) => { | |
| 105 | + let component: TranslatorService = createComponent(); | |
| 106 | + component.changeLanguage = jasmine.createSpy("changeLanguage"); | |
| 107 | + component["tmhDynamicLocale"].get = jasmine.createSpy("get").and.returnValue(null); | |
| 108 | + component["$translate"].use = jasmine.createSpy("use").and.returnValue("en"); | |
| 109 | + | |
| 110 | + $rootScope.$emit("$localeChangeSuccess"); | |
| 111 | + expect(component["$translate"].use).toHaveBeenCalled(); | |
| 112 | + expect(component.changeLanguage).toHaveBeenCalledWith("en"); | |
| 113 | + done(); | |
| 114 | + }); | |
| 115 | + }); | |
| 116 | +}); | ... | ... |
| ... | ... | @@ -0,0 +1,59 @@ |
| 1 | +import {Injectable, Inject} from "ng-forward"; | |
| 2 | + | |
| 3 | +@Injectable() | |
| 4 | +@Inject("$translate", "tmhDynamicLocale", "amMoment", "angularLoad", "$rootScope") | |
| 5 | +export class TranslatorService { | |
| 6 | + | |
| 7 | + availableLanguages: any; | |
| 8 | + | |
| 9 | + constructor(private $translate: angular.translate.ITranslateService, | |
| 10 | + private tmhDynamicLocale: angular.dynamicLocale.tmhDynamicLocaleService, | |
| 11 | + private amMoment: any, | |
| 12 | + private angularLoad: any, | |
| 13 | + private $rootScope: any) { | |
| 14 | + | |
| 15 | + this.$rootScope.$on("$localeChangeSuccess", () => { | |
| 16 | + this.changeLanguage(tmhDynamicLocale.get() || $translate.use()); | |
| 17 | + }); | |
| 18 | + this.$rootScope.$on("$translateChangeSuccess", () => { | |
| 19 | + this.configAvailableLanguages(); | |
| 20 | + }); | |
| 21 | + } | |
| 22 | + | |
| 23 | + currentLanguage() { | |
| 24 | + return this.$translate.use(); | |
| 25 | + } | |
| 26 | + | |
| 27 | + changeLanguage(language: string) { | |
| 28 | + if (!language) { | |
| 29 | + console.log("WARN: language undefined"); | |
| 30 | + return; | |
| 31 | + } | |
| 32 | + this.changeMomentLocale(language); | |
| 33 | + this.tmhDynamicLocale.set(language); | |
| 34 | + this.angularLoad.loadScript(`/bower_components/messageformat/locale/${language}.js`).then(() => { | |
| 35 | + return this.$translate.use(language); | |
| 36 | + }); | |
| 37 | + } | |
| 38 | + | |
| 39 | + translate(text: string) { | |
| 40 | + return this.$translate.instant(text); | |
| 41 | + } | |
| 42 | + | |
| 43 | + private configAvailableLanguages() { | |
| 44 | + this.availableLanguages = { | |
| 45 | + "en": this.$translate.instant("language.en"), | |
| 46 | + "pt": this.$translate.instant("language.pt") | |
| 47 | + }; | |
| 48 | + } | |
| 49 | + | |
| 50 | + private changeMomentLocale(language: string) { | |
| 51 | + let localePromise = Promise.resolve(); | |
| 52 | + if (language !== "en") { | |
| 53 | + localePromise = this.angularLoad.loadScript(`/bower_components/moment/locale/${language}.js`); | |
| 54 | + } | |
| 55 | + localePromise.then(() => { | |
| 56 | + this.amMoment.changeLocale(language); | |
| 57 | + }); | |
| 58 | + } | |
| 59 | +} | ... | ... |
src/lib/ng-noosfero-api/http/article.service.spec.ts
| 1 | -import {Article} from "../../../app/models/interfaces"; | |
| 2 | 1 | import {ArticleService} from "./article.service"; |
| 3 | 2 | |
| 4 | 3 | |
| ... | ... | @@ -24,8 +23,8 @@ describe("Services", () => { |
| 24 | 23 | it("should return article children", (done) => { |
| 25 | 24 | let articleId = 1; |
| 26 | 25 | $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] }); |
| 27 | - articleService.getChildren(articleId).then((response: restangular.IResponse) => { | |
| 28 | - expect(response.data.articles).toEqual([{ name: "article1" }]); | |
| 26 | + articleService.getChildren(<noosfero.Article>{id: articleId}).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 27 | + expect(result.data).toEqual([{ name: "article1" }]); | |
| 29 | 28 | done(); |
| 30 | 29 | }); |
| 31 | 30 | $httpBackend.flush(); |
| ... | ... | @@ -34,8 +33,8 @@ describe("Services", () => { |
| 34 | 33 | it("should get articles by profile", (done) => { |
| 35 | 34 | let profileId = 1; |
| 36 | 35 | $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles`).respond(200, { articles: [{ name: "article1" }] }); |
| 37 | - articleService.getByProfile(profileId).then((response: restangular.IResponse) => { | |
| 38 | - expect(response.data.articles).toEqual([{ name: "article1" }]); | |
| 36 | + articleService.getByProfile(<noosfero.Profile>{id: profileId}).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 37 | + expect(result.data).toEqual([{ name: "article1" }]); | |
| 39 | 38 | done(); |
| 40 | 39 | }); |
| 41 | 40 | $httpBackend.flush(); |
| ... | ... | @@ -44,8 +43,8 @@ describe("Services", () => { |
| 44 | 43 | it("should get articles by profile with additional filters", (done) => { |
| 45 | 44 | let profileId = 1; |
| 46 | 45 | $httpBackend.expectGET(`/api/v1/profiles/${profileId}/articles?path=test`).respond(200, { articles: [{ name: "article1" }] }); |
| 47 | - articleService.getByProfile(profileId, { path: 'test' }).then((response: restangular.IResponse) => { | |
| 48 | - expect(response.data.articles).toEqual([{ name: "article1" }]); | |
| 46 | + articleService.getByProfile(<noosfero.Profile>{id: profileId}, { path: 'test' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 47 | + expect(result.data).toEqual([{ name: "article1" }]); | |
| 49 | 48 | done(); |
| 50 | 49 | }); |
| 51 | 50 | $httpBackend.flush(); |
| ... | ... | @@ -54,8 +53,8 @@ describe("Services", () => { |
| 54 | 53 | it("should get article children with additional filters", (done) => { |
| 55 | 54 | let articleId = 1; |
| 56 | 55 | $httpBackend.expectGET(`/api/v1/articles/${articleId}/children?path=test`).respond(200, { articles: [{ name: "article1" }] }); |
| 57 | - articleService.getChildren(articleId, { path: 'test' }).then((response: restangular.IResponse) => { | |
| 58 | - expect(response.data.articles).toEqual([{ name: "article1" }]); | |
| 56 | + articleService.getChildren(<noosfero.Article>{id: articleId}, { path: 'test' }).then((result: noosfero.RestResult<noosfero.Article[]>) => { | |
| 57 | + expect(result.data).toEqual([{ name: "article1" }]); | |
| 59 | 58 | done(); |
| 60 | 59 | }); |
| 61 | 60 | $httpBackend.flush(); |
| ... | ... | @@ -63,10 +62,10 @@ describe("Services", () => { |
| 63 | 62 | |
| 64 | 63 | it("should create an article in a profile", (done) => { |
| 65 | 64 | let profileId = 1; |
| 66 | - let article: Article = { id: null }; | |
| 67 | - $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, { articles: [{ id: 2 }] }); | |
| 68 | - articleService.create(profileId, article).then((response: restangular.IResponse) => { | |
| 69 | - expect(response.data.articles).toEqual([{ id: 2 }]); | |
| 65 | + let article: noosfero.Article = <any>{ id: null}; | |
| 66 | + $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/articles`, { article: article }).respond(200, {article: { id: 2 }}); | |
| 67 | + articleService.createInProfile(<noosfero.Profile>{id: profileId}, article).then((result: noosfero.RestResult<noosfero.Article>) => { | |
| 68 | + expect(result.data).toEqual({ id: 2 }); | |
| 70 | 69 | done(); |
| 71 | 70 | }); |
| 72 | 71 | $httpBackend.flush(); | ... | ... |
src/lib/ng-noosfero-api/http/article.service.ts
| 1 | 1 | import { Injectable, Inject } from "ng-forward"; |
| 2 | -import {Article} from "../../../app/models/interfaces"; | |
| 2 | +import {RestangularService} from "./restangular_service"; | |
| 3 | +import {ProfileService} from "./profile.service"; | |
| 3 | 4 | |
| 4 | 5 | @Injectable() |
| 5 | -@Inject("Restangular") | |
| 6 | -export class ArticleService { | |
| 6 | +@Inject("Restangular", "$q", "$log", ProfileService) | |
| 7 | 7 | |
| 8 | - constructor(private Restangular: any) { } | |
| 8 | +export class ArticleService extends RestangularService<noosfero.Article> { | |
| 9 | 9 | |
| 10 | - create(profileId: number, article: Article) { | |
| 11 | - return this.Restangular.one('profiles', profileId).customPOST( | |
| 12 | - { article: article }, | |
| 13 | - 'articles', | |
| 14 | - {}, | |
| 15 | - { 'Content-Type': 'application/json' } | |
| 16 | - ); | |
| 10 | + constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService, protected profileService: ProfileService) { | |
| 11 | + super(Restangular, $q, $log); | |
| 17 | 12 | } |
| 18 | 13 | |
| 19 | - getByProfile(profileId: number, params?: any) { | |
| 20 | - return this.Restangular.one('profiles', profileId).customGET('articles', params); | |
| 14 | + getResourcePath() { | |
| 15 | + return "articles"; | |
| 21 | 16 | } |
| 22 | 17 | |
| 23 | - getChildren(articleId: number, params?: any) { | |
| 24 | - return this.get(articleId).customGET('children', params); | |
| 18 | + getDataKeys() { | |
| 19 | + return { | |
| 20 | + singular: 'article', | |
| 21 | + plural: 'articles' | |
| 22 | + }; | |
| 25 | 23 | } |
| 26 | 24 | |
| 27 | - private get(articleId: number) { | |
| 28 | - return this.Restangular.one('articles', articleId); | |
| 25 | + | |
| 26 | + createInProfile(profile: noosfero.Profile, article: noosfero.Article): ng.IPromise<noosfero.RestResult<noosfero.Article>> { | |
| 27 | + let profileElement = this.profileService.get(<number>profile.id); | |
| 28 | + (<any>profileElement).id = profile.id; | |
| 29 | + let headers = { | |
| 30 | + 'Content-Type': 'application/json' | |
| 31 | + }; | |
| 32 | + return this.create(article, <noosfero.RestModel>profileElement, null, headers); | |
| 33 | + } | |
| 34 | + | |
| 35 | + | |
| 36 | + getAsCollectionChildrenOf<C>(rootElement: noosfero.Environment | noosfero.Article | noosfero.Profile, path: string, queryParams?: any, headers?: any): restangular.ICollectionPromise<C> { | |
| 37 | + return rootElement.getList<C>(path, queryParams, headers); | |
| 38 | + } | |
| 39 | + | |
| 40 | + getAsElementChildrenOf<C>(rootElement: noosfero.Environment | noosfero.Article | noosfero.Profile, path: string, id: number, queryParams?: any, headers?: any) { | |
| 41 | + return rootElement.one(path, id).get<C>(queryParams, headers); | |
| 29 | 42 | } |
| 30 | 43 | |
| 44 | + getByProfile<T>(profile: noosfero.Profile, params?: any): ng.IPromise<noosfero.RestResult<noosfero.Article[]>> { | |
| 45 | + let profileElement = this.profileService.get(<number>profile.id); | |
| 46 | + return this.list(profileElement, params); | |
| 47 | + } | |
| 48 | + | |
| 49 | + getArticleByProfileAndPath(profile: noosfero.Profile, path: string): ng.IPromise<noosfero.RestResult<noosfero.Article>> { | |
| 50 | + let deferred = this.$q.defer<noosfero.RestResult<noosfero.Article>>(); | |
| 51 | + let profileElement = this.profileService.get(<number>profile.id); | |
| 52 | + | |
| 53 | + let restRequest: ng.IPromise<any>; | |
| 54 | + | |
| 55 | + let params = { path: path }; | |
| 56 | + | |
| 57 | + restRequest = profileElement.customGET(this.getResourcePath(), params); | |
| 58 | + | |
| 59 | + | |
| 60 | + restRequest | |
| 61 | + .then(this.getHandleSuccessFunction(deferred)) | |
| 62 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 63 | + | |
| 64 | + | |
| 65 | + return deferred.promise; | |
| 66 | + } | |
| 67 | + | |
| 68 | + getOneByProfile<T>(profile: noosfero.Profile, params?: any): ng.IPromise<noosfero.RestResult<noosfero.Article>> { | |
| 69 | + let profileElement = this.profileService.get(<number>profile.id); | |
| 70 | + return this.getSub(profileElement, params); | |
| 71 | + } | |
| 72 | + | |
| 73 | + getChildren<T>(article: noosfero.Article, params?: any): ng.IPromise<noosfero.RestResult<noosfero.Article[]>> { | |
| 74 | + let articleElement = this.getElement(article.id); | |
| 75 | + articleElement.id = article.id; | |
| 76 | + return this.listSubElements(<noosfero.Article>articleElement, "children", params); | |
| 77 | + } | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 31 | 81 | } | ... | ... |
src/lib/ng-noosfero-api/http/http_client.ts
| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | -namespace NgNoosferoAPI { | |
| 2 | - export class NoosferoHttpClient { | |
| 3 | - static $inject = ['$http', '$q']; | |
| 4 | - | |
| 5 | - constructor(private $http: ng.IHttpService, private $q: ng.IQService) { | |
| 6 | - | |
| 7 | - } | |
| 8 | - } | |
| 9 | - | |
| 10 | - NgNoosferoAPI.ngModule.service(NoosferoHttpClient); | |
| 11 | -} | |
| 12 | 0 | \ No newline at end of file |
src/lib/ng-noosfero-api/http/profile.service.spec.ts
| 1 | -import {Profile} from "../../../app/models/interfaces"; | |
| 2 | 1 | import {ProfileService} from "./profile.service"; |
| 3 | 2 | |
| 4 | 3 | describe("Services", () => { |
| ... | ... | @@ -24,7 +23,7 @@ describe("Services", () => { |
| 24 | 23 | it("should return profile by its identifier", (done) => { |
| 25 | 24 | let identifier = 'profile1'; |
| 26 | 25 | $httpBackend.expectGET(`/api/v1/profiles?identifier=${identifier}`).respond(200, [{ name: "profile1" }]); |
| 27 | - profileService.getByIdentifier(identifier).then((profile: Profile) => { | |
| 26 | + profileService.getByIdentifier(identifier).then((profile: noosfero.Profile) => { | |
| 28 | 27 | expect(profile).toEqual({ name: "profile1" }); |
| 29 | 28 | done(); |
| 30 | 29 | }); |
| ... | ... | @@ -72,11 +71,11 @@ describe("Services", () => { |
| 72 | 71 | |
| 73 | 72 | it("should resolve the current profile", (done) => { |
| 74 | 73 | let profile = { id: 1, identifier: "profile1" }; |
| 75 | - profileService.getCurrentProfile().then((currentProfile: Profile) => { | |
| 74 | + profileService.getCurrentProfile().then((currentProfile: noosfero.Profile) => { | |
| 76 | 75 | expect(currentProfile).toEqual(currentProfile); |
| 77 | 76 | done(); |
| 78 | 77 | }); |
| 79 | - profileService.setCurrentProfile(<Profile>profile); | |
| 78 | + profileService.setCurrentProfile(<any>profile); | |
| 80 | 79 | $rootScope.$apply(); |
| 81 | 80 | }); |
| 82 | 81 | |
| ... | ... | @@ -93,9 +92,9 @@ describe("Services", () => { |
| 93 | 92 | it("should find the profile by identifier, set and resolve the current profile", (done) => { |
| 94 | 93 | let identifier = 'profile1'; |
| 95 | 94 | $httpBackend.expectGET(`/api/v1/profiles?identifier=${identifier}`).respond(200, [{ name: "profile1" }]); |
| 96 | - profileService.setCurrentProfileByIdentifier(identifier).then((profile: Profile) => { | |
| 95 | + profileService.setCurrentProfileByIdentifier(identifier).then((profile: noosfero.Profile) => { | |
| 97 | 96 | expect(profile).toEqual({ name: "profile1" }); |
| 98 | - profileService.getCurrentProfile().then((profile: Profile) => { | |
| 97 | + profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
| 99 | 98 | expect(profile).toEqual({ name: "profile1" }); |
| 100 | 99 | done(); |
| 101 | 100 | }); | ... | ... |
src/lib/ng-noosfero-api/http/profile.service.ts
| 1 | 1 | import { Injectable, Inject } from "ng-forward"; |
| 2 | -import {Profile} from "../../../app/models/interfaces"; | |
| 3 | 2 | |
| 4 | 3 | @Injectable() |
| 5 | 4 | @Inject("Restangular", "$q") |
| 6 | 5 | export class ProfileService { |
| 7 | 6 | |
| 8 | - private _currentProfilePromise: ng.IDeferred<Profile>; | |
| 7 | + private _currentProfilePromise: ng.IDeferred<noosfero.Profile>; | |
| 9 | 8 | |
| 10 | 9 | constructor(private restangular: restangular.IService, private $q: ng.IQService) { |
| 11 | 10 | this.resetCurrentProfile(); |
| ... | ... | @@ -15,17 +14,17 @@ export class ProfileService { |
| 15 | 14 | this._currentProfilePromise = this.$q.defer(); |
| 16 | 15 | } |
| 17 | 16 | |
| 18 | - getCurrentProfile(): ng.IPromise<Profile> { | |
| 17 | + getCurrentProfile(): ng.IPromise<noosfero.Profile> { | |
| 19 | 18 | return this._currentProfilePromise.promise; |
| 20 | 19 | } |
| 21 | 20 | |
| 22 | - setCurrentProfile(profile: Profile) { | |
| 21 | + setCurrentProfile(profile: noosfero.Profile) { | |
| 23 | 22 | this._currentProfilePromise.resolve(profile); |
| 24 | 23 | } |
| 25 | 24 | |
| 26 | 25 | setCurrentProfileByIdentifier(identifier: string) { |
| 27 | 26 | this.resetCurrentProfile(); |
| 28 | - return this.getByIdentifier(identifier).then((profile: Profile) => { | |
| 27 | + return this.getByIdentifier(identifier).then((profile: noosfero.Profile) => { | |
| 29 | 28 | this.setCurrentProfile(profile); |
| 30 | 29 | return this.getCurrentProfile(); |
| 31 | 30 | }); |
| ... | ... | @@ -35,10 +34,10 @@ export class ProfileService { |
| 35 | 34 | return this.get(profileId).customGET("home_page", params); |
| 36 | 35 | } |
| 37 | 36 | |
| 38 | - getByIdentifier(identifier: string): ng.IPromise<any> { | |
| 37 | + getByIdentifier(identifier: string): ng.IPromise<noosfero.Profile> { | |
| 39 | 38 | let p = this.restangular.one('profiles').get({ identifier: identifier }); |
| 40 | 39 | return p.then((response: restangular.IResponse) => { |
| 41 | - if (response.data.length == 0) { | |
| 40 | + if (response.data.length === 0) { | |
| 42 | 41 | return this.$q.reject(p); |
| 43 | 42 | } |
| 44 | 43 | return response.data[0]; | ... | ... |
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +namespace noosfero.http { | |
| 2 | + export function Rest(config: { | |
| 3 | + path: string | |
| 4 | + } | |
| 5 | + ): Function { | |
| 6 | + let path = config.path; | |
| 7 | + return (t: Function) => { | |
| 8 | + if (!path) { | |
| 9 | + throw new Error(`Rest decorator error in ${(<any>t).name}. Rest path should be provided`); | |
| 10 | + } | |
| 11 | + t.prototype.getPath = function() { | |
| 12 | + return path; | |
| 13 | + }; | |
| 14 | + }; | |
| 15 | + } | |
| 16 | +} | |
| 0 | 17 | \ No newline at end of file | ... | ... |
src/lib/ng-noosfero-api/http/restangular_service.spec.ts
0 → 100644
| ... | ... | @@ -0,0 +1,210 @@ |
| 1 | +import {RestangularService} from "./restangular_service"; | |
| 2 | + | |
| 3 | +interface ObjectModel extends noosfero.RestModel { | |
| 4 | +} | |
| 5 | + | |
| 6 | +interface RootObjectModel extends noosfero.RestModel { | |
| 7 | + | |
| 8 | +} | |
| 9 | + | |
| 10 | +describe("Restangular Service - base Class", () => { | |
| 11 | + | |
| 12 | + class ObjectRestService extends RestangularService<ObjectModel> { | |
| 13 | + public getDataKeys() { | |
| 14 | + return { | |
| 15 | + singular: "object", | |
| 16 | + plural: "objects" | |
| 17 | + }; | |
| 18 | + } | |
| 19 | + | |
| 20 | + public getResourcePath() { | |
| 21 | + return "objects"; | |
| 22 | + } | |
| 23 | + } | |
| 24 | + | |
| 25 | + class RootObjectRestService extends RestangularService<ObjectModel> { | |
| 26 | + public getDataKeys() { | |
| 27 | + return { | |
| 28 | + singular: "rootObject", | |
| 29 | + plural: "rootObjects" | |
| 30 | + }; | |
| 31 | + } | |
| 32 | + | |
| 33 | + public getResourcePath() { | |
| 34 | + return "rootObjects"; | |
| 35 | + } | |
| 36 | + } | |
| 37 | + | |
| 38 | + let restangularService: restangular.IService; | |
| 39 | + let $httpBackend: ng.IHttpBackendService; | |
| 40 | + let objectRestService: ObjectRestService; | |
| 41 | + let rootObjectRestService: RootObjectRestService; | |
| 42 | + | |
| 43 | + beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => { | |
| 44 | + $translateProvider.translations('en', {}); | |
| 45 | + })); | |
| 46 | + | |
| 47 | + beforeEach(inject((_Restangular_: restangular.IService, _$q_: ng.IQService, _$httpBackend_: ng.IHttpBackendService) => { | |
| 48 | + restangularService = _Restangular_; | |
| 49 | + objectRestService = new ObjectRestService(_Restangular_, _$q_, null); | |
| 50 | + rootObjectRestService = new RootObjectRestService(_Restangular_, _$q_, null); | |
| 51 | + $httpBackend = _$httpBackend_; | |
| 52 | + })); | |
| 53 | + | |
| 54 | + it("list() calls GET /objects", (done) => { | |
| 55 | + $httpBackend.expectGET("/api/v1/objects").respond(200, { objects: [{ id: 1 }, { id: 2 }] }); | |
| 56 | + | |
| 57 | + objectRestService.list().then((result: noosfero.RestResult<ObjectModel[]>) => { | |
| 58 | + expect(result.data).toBeDefined(); | |
| 59 | + expect((<ObjectModel[]>result.data).length).toEqual(2); | |
| 60 | + done(); | |
| 61 | + }); | |
| 62 | + | |
| 63 | + $httpBackend.flush(); | |
| 64 | + | |
| 65 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 66 | + }); | |
| 67 | + | |
| 68 | + it("list(rootObject) calls GET /rootObjects/1/objects", (done) => { | |
| 69 | + | |
| 70 | + $httpBackend.expectGET("/api/v1/rootObjects/1/objects").respond(200, { objects: [{ id: 1 }, { id: 2 }] }); | |
| 71 | + let rootObj: RootObjectModel = rootObjectRestService.getElement(1); | |
| 72 | + | |
| 73 | + objectRestService.list(rootObj).then((result: noosfero.RestResult<ObjectModel[]>) => { | |
| 74 | + expect(result.data).toBeDefined(); | |
| 75 | + expect((<ObjectModel[]>result.data).length).toEqual(2); | |
| 76 | + done(); | |
| 77 | + }); | |
| 78 | + | |
| 79 | + $httpBackend.flush(); | |
| 80 | + | |
| 81 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 82 | + }); | |
| 83 | + | |
| 84 | + it("get(1) calls GET /objects/1", (done) => { | |
| 85 | + $httpBackend.expectGET("/api/v1/objects/1").respond(200, { object: { id: 1 } }); | |
| 86 | + | |
| 87 | + objectRestService.get(1).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 88 | + expect(result.data).toBeDefined(); | |
| 89 | + expect((<ObjectModel>result.data).id).toEqual(1); | |
| 90 | + done(); | |
| 91 | + }); | |
| 92 | + $httpBackend.flush(); | |
| 93 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 94 | + }); | |
| 95 | + | |
| 96 | + it("objectService.get(1, rootObject) calls GET /rootObjects/1/objects/1", (done) => { | |
| 97 | + let rootObj: RootObjectModel = rootObjectRestService.getElement(1); | |
| 98 | + $httpBackend.expectGET("/api/v1/rootObjects/1/objects/1").respond(200, { object: { id: 1 } }); | |
| 99 | + | |
| 100 | + objectRestService.get(1, rootObj).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 101 | + expect(result.data).toBeDefined(); | |
| 102 | + expect((<ObjectModel>result.data).id).toEqual(1); | |
| 103 | + done(); | |
| 104 | + }); | |
| 105 | + $httpBackend.flush(); | |
| 106 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 107 | + }); | |
| 108 | + | |
| 109 | + it("remove(object) calls DELETE /objects/1", (done) => { | |
| 110 | + $httpBackend.expectGET("/api/v1/objects/1").respond(200, { object: { id: 1 } }); | |
| 111 | + objectRestService.get(1).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 112 | + let object: ObjectModel = <ObjectModel>result.data; | |
| 113 | + | |
| 114 | + $httpBackend.expectDELETE("/api/v1/objects/1").respond(204, { object: { id: 1 } }); | |
| 115 | + | |
| 116 | + objectRestService.remove(object).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 117 | + expect(result.data).toBeDefined(); | |
| 118 | + expect((<ObjectModel>result.data).id).toEqual(1); | |
| 119 | + done(); | |
| 120 | + }); | |
| 121 | + }); | |
| 122 | + | |
| 123 | + $httpBackend.flush(); | |
| 124 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 125 | + }); | |
| 126 | + | |
| 127 | + | |
| 128 | + it("remove(object, rootObject) calls DELETE /rootObjects/1/objects/1", (done) => { | |
| 129 | + let rootObj: RootObjectModel = rootObjectRestService.getElement(1); | |
| 130 | + | |
| 131 | + let obj: ObjectModel = objectRestService.getElement(1, rootObj); | |
| 132 | + | |
| 133 | + $httpBackend.expectDELETE("/api/v1/rootObjects/1/objects/1").respond(204, { object: { id: 1, rootId: 1 } }); | |
| 134 | + | |
| 135 | + objectRestService.remove(obj, rootObj).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 136 | + expect(result.data).toBeDefined(); | |
| 137 | + expect((<ObjectModel>result.data).id).toEqual(1); | |
| 138 | + expect((<any>result.data).rootId).toEqual(1); | |
| 139 | + done(); | |
| 140 | + }); | |
| 141 | + $httpBackend.flush(); | |
| 142 | + }); | |
| 143 | + | |
| 144 | + | |
| 145 | + it("update(object) calls PUT /objects/1", (done) => { | |
| 146 | + $httpBackend.expectPUT("/api/v1/objects/1").respond(200, { object: { id: 1 } }); | |
| 147 | + | |
| 148 | + let object: ObjectModel = objectRestService.getElement(1); | |
| 149 | + | |
| 150 | + objectRestService.update(object).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 151 | + expect(result.data).toBeDefined(); | |
| 152 | + expect((<ObjectModel>result.data).id).toEqual(1); | |
| 153 | + done(); | |
| 154 | + }); | |
| 155 | + | |
| 156 | + $httpBackend.flush(); | |
| 157 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 158 | + }); | |
| 159 | + | |
| 160 | + | |
| 161 | + it("update(object, rootObject) calls PUT /rootObjects/1/objects/1", (done) => { | |
| 162 | + let rootObj: RootObjectModel = rootObjectRestService.getElement(1); | |
| 163 | + | |
| 164 | + let obj: ObjectModel = objectRestService.getElement(1, rootObj); | |
| 165 | + | |
| 166 | + $httpBackend.expectPUT("/api/v1/rootObjects/1/objects/1").respond(200, { object: { id: 1, rootId: 1 } }); | |
| 167 | + | |
| 168 | + objectRestService.update(obj, rootObj).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 169 | + expect(result.data).toBeDefined(); | |
| 170 | + expect((<ObjectModel>result.data).id).toEqual(1); | |
| 171 | + expect((<any>result.data).rootId).toEqual(1); | |
| 172 | + done(); | |
| 173 | + }); | |
| 174 | + $httpBackend.flush(); | |
| 175 | + }); | |
| 176 | + | |
| 177 | + | |
| 178 | + it("save(object) calls POST /objects", (done) => { | |
| 179 | + $httpBackend.expectPOST("/api/v1/objects").respond(201, { object: { attr: 1 } }); | |
| 180 | + | |
| 181 | + let object: ObjectModel = objectRestService.getElement(1); | |
| 182 | + | |
| 183 | + objectRestService.create(object).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 184 | + expect(result.data).toBeDefined(); | |
| 185 | + expect((<any>result.data).attr).toEqual(1); | |
| 186 | + done(); | |
| 187 | + }); | |
| 188 | + | |
| 189 | + $httpBackend.flush(); | |
| 190 | + $httpBackend.verifyNoOutstandingExpectation(); | |
| 191 | + }); | |
| 192 | + | |
| 193 | + | |
| 194 | + it("save(object, rootObject) calls POST /rootObjects/1/objects", (done) => { | |
| 195 | + let rootObj: RootObjectModel = rootObjectRestService.getElement(1); | |
| 196 | + | |
| 197 | + let obj: ObjectModel = objectRestService.getElement(1, rootObj); | |
| 198 | + | |
| 199 | + $httpBackend.expectPOST("/api/v1/rootObjects/1/objects").respond(201, { object: { attr: 1, rootId: 1 } }); | |
| 200 | + | |
| 201 | + objectRestService.create(obj, rootObj).then((result: noosfero.RestResult<ObjectModel>) => { | |
| 202 | + expect(result.data).toBeDefined(); | |
| 203 | + expect((<any>result.data).attr).toEqual(1); | |
| 204 | + expect((<any>result.data).rootId).toEqual(1); | |
| 205 | + done(); | |
| 206 | + }); | |
| 207 | + $httpBackend.flush(); | |
| 208 | + }); | |
| 209 | + | |
| 210 | +}); | |
| 0 | 211 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,297 @@ |
| 1 | +/** | |
| 2 | + * @name RestangularService | |
| 3 | + * Base class to be extended by classes which will provide access | |
| 4 | + * to te Noosfero REST API | |
| 5 | + * | |
| 6 | + * @export RestangularService | |
| 7 | + * @abstract | |
| 8 | + * @class RestangularService | |
| 9 | + * @template T | |
| 10 | + */ | |
| 11 | +export abstract class RestangularService<T extends noosfero.RestModel> { | |
| 12 | + | |
| 13 | + private baseResource: restangular.IElement; | |
| 14 | + /** | |
| 15 | + * Creates an instance of RestangularService. | |
| 16 | + * | |
| 17 | + * @param {restangular.IService} Restangular (description) | |
| 18 | + * @param {ng.IQService} $q (description) | |
| 19 | + * @param {ng.ILogService} $log (description) | |
| 20 | + */ | |
| 21 | + constructor(protected restangularService: restangular.IService, protected $q: ng.IQService, protected $log: ng.ILogService) { | |
| 22 | + this.baseResource = restangularService.all(this.getResourcePath()); | |
| 23 | + // TODO | |
| 24 | + // this.restangularService.setResponseInterceptor((data, operation, what, url, response, deferred) => { | |
| 25 | + // let transformedData: any = data; | |
| 26 | + // if (operation === "getList" && url.endsWith("/" + this.getDataKeys().plural)) { | |
| 27 | + // transformedData = data[this.getDataKeys()["plural"]]; | |
| 28 | + // return transformedData; | |
| 29 | + // } else { | |
| 30 | + // return data; | |
| 31 | + // } | |
| 32 | + // }); | |
| 33 | + } | |
| 34 | + | |
| 35 | + protected extractData(response: restangular.IResponse): noosfero.RestResult<T> { | |
| 36 | + let dataKey: string; | |
| 37 | + if (response.data && this.getDataKeys()) { | |
| 38 | + if ((<Object>response.data).hasOwnProperty(this.getDataKeys().singular)) { | |
| 39 | + dataKey = this.getDataKeys().singular; | |
| 40 | + } else if ((<Object>response.data).hasOwnProperty(this.getDataKeys().plural)) { | |
| 41 | + dataKey = this.getDataKeys().plural; | |
| 42 | + } | |
| 43 | + } | |
| 44 | + return { | |
| 45 | + data: response.data[dataKey], | |
| 46 | + headers: response.headers | |
| 47 | + }; | |
| 48 | + }; | |
| 49 | + | |
| 50 | + protected buildResult(response: restangular.IResponse): noosfero.RestResult<T> { | |
| 51 | + return { | |
| 52 | + data: response.data, | |
| 53 | + headers: response.headers | |
| 54 | + }; | |
| 55 | + }; | |
| 56 | + /** | |
| 57 | + * Abstract getPath() method is used to mount the url | |
| 58 | + * on REST Operations | |
| 59 | + * @protected | |
| 60 | + * @abstract | |
| 61 | + * @returns {string} The path of the REST endpoint | |
| 62 | + */ | |
| 63 | + public abstract getResourcePath(): string; | |
| 64 | + | |
| 65 | + /** | |
| 66 | + * Abstract getDataKeys() | |
| 67 | + * | |
| 68 | + * Should be implemented into the child classes and | |
| 69 | + * returns the singular and plural names of the represented resource | |
| 70 | + * | |
| 71 | + * @protected | |
| 72 | + * @abstract | |
| 73 | + * @returns {{ singular: string, plural: string }} (description) | |
| 74 | + */ | |
| 75 | + protected abstract getDataKeys(): { singular: string, plural: string }; | |
| 76 | + | |
| 77 | + /** | |
| 78 | + * Do a HTTP GET call to the resource represented using the id provided | |
| 79 | + * | |
| 80 | + * @protected | |
| 81 | + * @param {number} id The resource id | |
| 82 | + * @returns {ng.IPromise<T>} Returns a Promise to the Generic Type | |
| 83 | + */ | |
| 84 | + public get(id: number, rootElement?: restangular.IElement, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<T>> { | |
| 85 | + let deferred = this.$q.defer<noosfero.RestResult<T>>(); | |
| 86 | + | |
| 87 | + let restRequest: ng.IPromise<noosfero.RestResult<T>>; | |
| 88 | + | |
| 89 | + if (rootElement) { | |
| 90 | + restRequest = rootElement.one(this.getResourcePath(), id).get(queryParams, headers); | |
| 91 | + } else { | |
| 92 | + restRequest = this.restangularService.one(this.getResourcePath(), id).get(queryParams, headers); | |
| 93 | + } | |
| 94 | + | |
| 95 | + restRequest.then(this.getHandleSuccessFunction(deferred)) | |
| 96 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 97 | + | |
| 98 | + | |
| 99 | + return deferred.promise; | |
| 100 | + } | |
| 101 | + | |
| 102 | + /** | |
| 103 | + * Do a HTTP GET call to the resource collection represented | |
| 104 | + * | |
| 105 | + * @protected | |
| 106 | + * @param {number} id (description) | |
| 107 | + * @returns {ng.IPromise<T>} Returns a Promise to the Generic Type | |
| 108 | + */ | |
| 109 | + public list(rootElement?: restangular.IElement, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<T[]>> { | |
| 110 | + let deferred = this.$q.defer<noosfero.RestResult<T[]>>(); | |
| 111 | + | |
| 112 | + let restRequest: ng.IPromise<any>; | |
| 113 | + | |
| 114 | + if (rootElement) { | |
| 115 | + restRequest = rootElement.customGET(this.getResourcePath(), queryParams, headers); | |
| 116 | + } else { | |
| 117 | + restRequest = this.baseResource.customGET("", queryParams, headers); | |
| 118 | + } | |
| 119 | + | |
| 120 | + | |
| 121 | + restRequest | |
| 122 | + .then(this.getHandleSuccessFunction(deferred)) | |
| 123 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 124 | + | |
| 125 | + | |
| 126 | + return deferred.promise; | |
| 127 | + } | |
| 128 | + | |
| 129 | + /** | |
| 130 | + * Do a HTTP GET call to the resource collection represented | |
| 131 | + * | |
| 132 | + * @protected | |
| 133 | + * @param {number} id (description) | |
| 134 | + * @returns {ng.IPromise<T>} Returns a Promise to the Generic Type | |
| 135 | + */ | |
| 136 | + public getSub(rootElement?: restangular.IElement, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<T>> { | |
| 137 | + let deferred = this.$q.defer<noosfero.RestResult<T>>(); | |
| 138 | + | |
| 139 | + let restRequest: ng.IPromise<any>; | |
| 140 | + | |
| 141 | + if (rootElement) { | |
| 142 | + restRequest = rootElement.customGET(this.getResourcePath(), queryParams, headers); | |
| 143 | + } else { | |
| 144 | + restRequest = this.baseResource.customGET("", queryParams, headers); | |
| 145 | + } | |
| 146 | + | |
| 147 | + | |
| 148 | + restRequest | |
| 149 | + .then(this.getHandleSuccessFunction(deferred)) | |
| 150 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 151 | + | |
| 152 | + | |
| 153 | + return deferred.promise; | |
| 154 | + } | |
| 155 | + | |
| 156 | + public listSubElements<C>(obj: T, subElement: string, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<C>> { | |
| 157 | + let deferred = this.$q.defer<noosfero.RestResult<C>>(); | |
| 158 | + let restRequest: ng.IPromise<noosfero.RestResult<T>>; | |
| 159 | + let objElement = this.getElement(obj.id); | |
| 160 | + objElement.id = obj.id; | |
| 161 | + restRequest = objElement.customGET(subElement, queryParams, headers); | |
| 162 | + restRequest.then(this.getHandleSuccessFunction(deferred)) | |
| 163 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 164 | + return deferred.promise; | |
| 165 | + } | |
| 166 | + | |
| 167 | + /** | |
| 168 | + * Removes the object provided from the resource collection, | |
| 169 | + * calls DELETE /resourcepath/:resourceId | |
| 170 | + */ | |
| 171 | + public remove(obj: T, rootElement?: noosfero.RestModel, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<T>> { | |
| 172 | + let restangularObj: restangular.IElement; | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + if (rootElement) { | |
| 177 | + restangularObj = rootElement.one(this.getResourcePath(), obj.id); | |
| 178 | + } else { | |
| 179 | + restangularObj = this.restangularService.one(this.getResourcePath(), obj.id); | |
| 180 | + } | |
| 181 | + | |
| 182 | + let deferred = this.$q.defer<noosfero.RestResult<T>>(); | |
| 183 | + | |
| 184 | + let restRequest: ng.IPromise<noosfero.RestResult<T>>; | |
| 185 | + | |
| 186 | + restRequest = restangularObj.remove(queryParams, headers); | |
| 187 | + | |
| 188 | + restRequest | |
| 189 | + .then(this.getHandleSuccessFunction(deferred)) | |
| 190 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 191 | + | |
| 192 | + return deferred.promise; | |
| 193 | + } | |
| 194 | + | |
| 195 | + /** | |
| 196 | + * Updates the object into the resource collection | |
| 197 | + * calls PUT /resourcePath/:resourceId {object} | |
| 198 | + */ | |
| 199 | + public update(obj: T, rootElement?: noosfero.RestModel, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<T>> { | |
| 200 | + let deferred = this.$q.defer<noosfero.RestResult<T>>(); | |
| 201 | + | |
| 202 | + let restRequest: ng.IPromise<noosfero.RestResult<T>>; | |
| 203 | + | |
| 204 | + let restangularObj: restangular.IElement; | |
| 205 | + | |
| 206 | + if (rootElement) { | |
| 207 | + restangularObj = rootElement.one(this.getResourcePath(), obj.id); | |
| 208 | + } else { | |
| 209 | + restangularObj = this.restangularService.one(this.getResourcePath(), obj.id); | |
| 210 | + } | |
| 211 | + | |
| 212 | + restRequest = restangularObj.put(queryParams, headers); | |
| 213 | + | |
| 214 | + restRequest.then(this.getHandleSuccessFunction(deferred)) | |
| 215 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 216 | + | |
| 217 | + return deferred.promise; | |
| 218 | + } | |
| 219 | + | |
| 220 | + /** | |
| 221 | + * Creates a new Resource into the resource collection | |
| 222 | + * calls POST /resourcePath | |
| 223 | + */ | |
| 224 | + public create(obj: T, rootElement?: noosfero.RestModel, queryParams?: any, headers?: any): ng.IPromise<noosfero.RestResult<T>> { | |
| 225 | + let deferred = this.$q.defer<noosfero.RestResult<T>>(); | |
| 226 | + | |
| 227 | + let restRequest: ng.IPromise<noosfero.RestResult<T>>; | |
| 228 | + | |
| 229 | + let data = <any>{ }; | |
| 230 | + data[this.getDataKeys().singular] = obj; | |
| 231 | + | |
| 232 | + if (rootElement) { | |
| 233 | + restRequest = rootElement.all(this.getResourcePath()).post(data, queryParams, headers); | |
| 234 | + } else { | |
| 235 | + restRequest = this.baseResource.post(data, queryParams, headers); | |
| 236 | + } | |
| 237 | + | |
| 238 | + restRequest.then(this.getHandleSuccessFunction(deferred)) | |
| 239 | + .catch(this.getHandleErrorFunction(deferred)); | |
| 240 | + | |
| 241 | + return deferred.promise; | |
| 242 | + } | |
| 243 | + | |
| 244 | + /** | |
| 245 | + * Returns a Restangular IElement representing the | |
| 246 | + */ | |
| 247 | + public getElement(id: number, rootElement?: noosfero.RestModel): noosfero.RestModel { | |
| 248 | + if (rootElement) { | |
| 249 | + return <noosfero.RestModel>rootElement.one(this.getResourcePath(), id); | |
| 250 | + } else { | |
| 251 | + return <noosfero.RestModel>this.restangularService.one(this.getResourcePath(), id); | |
| 252 | + } | |
| 253 | + } | |
| 254 | + | |
| 255 | + /** HANDLERS */ | |
| 256 | + protected getHandleSuccessFunction<C>(deferred: ng.IDeferred<noosfero.RestResult<C | T | any>>, responseKey?: string): (response: restangular.IResponse) => void { | |
| 257 | + let self = this; | |
| 258 | + | |
| 259 | + /** | |
| 260 | + * (description) | |
| 261 | + * | |
| 262 | + * @param {restangular.IResponse} response (description) | |
| 263 | + */ | |
| 264 | + let successFunction = (response: restangular.IResponse): void => { | |
| 265 | + if (self.$log) { | |
| 266 | + self.$log.debug("Request successfull executed", response.data, self, response); | |
| 267 | + } | |
| 268 | + deferred.resolve(<any>this.extractData(response)); | |
| 269 | + }; | |
| 270 | + return successFunction; | |
| 271 | + } | |
| 272 | + | |
| 273 | + /** | |
| 274 | + * (description) | |
| 275 | + * | |
| 276 | + * @template T | |
| 277 | + * @param {ng.IDeferred<T>} deferred (description) | |
| 278 | + * @returns {(response: restangular.IResponse) => void} (description) | |
| 279 | + */ | |
| 280 | + getHandleErrorFunction<T>(deferred: ng.IDeferred<T>): (response: restangular.IResponse) => void { | |
| 281 | + let self = this; | |
| 282 | + /** | |
| 283 | + * (description) | |
| 284 | + * | |
| 285 | + * @param {restangular.IResponse} response (description) | |
| 286 | + */ | |
| 287 | + let successFunction = (response: restangular.IResponse): void => { | |
| 288 | + if (self.$log) { | |
| 289 | + self.$log.error("Error executing request", self, response); | |
| 290 | + } | |
| 291 | + deferred.reject(response); | |
| 292 | + }; | |
| 293 | + return successFunction; | |
| 294 | + } | |
| 295 | + /** END HANDLERS */ | |
| 296 | + | |
| 297 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +namespace noosfero { | |
| 2 | + /** | |
| 3 | + * @ngdoc interface | |
| 4 | + * @name noofero.Activity | |
| 5 | + * @description | |
| 6 | + * A representation of a {@link noosfero.Profile} activity in Noosfero. | |
| 7 | + */ | |
| 8 | + export interface Activity { | |
| 9 | + /** | |
| 10 | + * @ngdoc property | |
| 11 | + * @name verb | |
| 12 | + * @propertyOf noofero.Activity | |
| 13 | + * @returns {string} The activity verb. | |
| 14 | + */ | |
| 15 | + verb: string; | |
| 16 | + } | |
| 17 | +} | |
| 0 | 18 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | + | |
| 2 | +namespace noosfero { | |
| 3 | + /** | |
| 4 | + * @ngdoc interface | |
| 5 | + * @name noofero.Profile | |
| 6 | + * @description | |
| 7 | + * A representation of a Noosfero Profile. | |
| 8 | + */ | |
| 9 | + export interface Profile extends RestModel { | |
| 10 | + /** | |
| 11 | + * @ngdoc property | |
| 12 | + * @name id | |
| 13 | + * @propertyOf noofero.Profile | |
| 14 | + * @returns {number} The Profile id | |
| 15 | + */ | |
| 16 | + id: number; | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * @ngdoc property | |
| 20 | + * @name identifier | |
| 21 | + * @propertyOf noofero.Profile | |
| 22 | + * @returns {string} The unque identifier for the Profile | |
| 23 | + */ | |
| 24 | + identifier: string; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * @ngdoc property | |
| 28 | + * @name type | |
| 29 | + * @propertyOf noofero.Profile | |
| 30 | + * @returns {string} The Profile type (e.g.: "Person", etc.) | |
| 31 | + */ | |
| 32 | + type: string; | |
| 33 | + | |
| 34 | + name: string; | |
| 35 | + } | |
| 36 | +} | |
| 0 | 37 | \ No newline at end of file | ... | ... |
src/spec/helpers.ts
| ... | ... | @@ -2,7 +2,6 @@ |
| 2 | 2 | import {ngClass, TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; |
| 3 | 3 | import {providers} from 'ng-forward/cjs/testing/providers'; |
| 4 | 4 | import {Injectable, Inject, Provider, Input, provide, Component} from 'ng-forward'; |
| 5 | -import {User, Person} from "./../app/models/interfaces"; | |
| 6 | 5 | |
| 7 | 6 | |
| 8 | 7 | export var ngforward = { |
| ... | ... | @@ -101,7 +100,7 @@ export var fixtures = { |
| 101 | 100 | id: 1, |
| 102 | 101 | login: 'user', |
| 103 | 102 | email: 'user@company.com', |
| 104 | - person: <Person>{ | |
| 103 | + person: <noosfero.Person>{ | |
| 105 | 104 | id: 1, |
| 106 | 105 | identifier: 'user' |
| 107 | 106 | }, | ... | ... |
src/spec/mocks.ts
| ... | ... | @@ -48,6 +48,17 @@ export var mocks = { |
| 48 | 48 | } |
| 49 | 49 | }; |
| 50 | 50 | }, |
| 51 | + getArticleByProfileAndPath: (profile: noosfero.Profile, path: string) => { | |
| 52 | + return { | |
| 53 | + then: (func?: Function) => { | |
| 54 | + if (func) func({ | |
| 55 | + data: { | |
| 56 | + article: null | |
| 57 | + } | |
| 58 | + }); | |
| 59 | + } | |
| 60 | + }; | |
| 61 | + }, | |
| 51 | 62 | getChildren: (articleId: number, params?: any) => { |
| 52 | 63 | return { |
| 53 | 64 | then: (func?: Function) => { if (func) func(); } |
| ... | ... | @@ -71,7 +82,7 @@ export var mocks = { |
| 71 | 82 | use: (lang?: string) => { |
| 72 | 83 | return lang ? Promise.resolve(lang) : "en"; |
| 73 | 84 | }, |
| 74 | - instant: (text: string) => { return text } | |
| 85 | + instant: (text: string) => { return text; } | |
| 75 | 86 | }, |
| 76 | 87 | tmhDynamicLocale: { |
| 77 | 88 | get: () => { }, |
| ... | ... | @@ -99,6 +110,6 @@ export var mocks = { |
| 99 | 110 | translatorService: { |
| 100 | 111 | currentLanguage: () => { }, |
| 101 | 112 | changeLanguage: (lang: string) => { }, |
| 102 | - translate: (text: string) => { return text } | |
| 113 | + translate: (text: string) => { return text; } | |
| 103 | 114 | } |
| 104 | 115 | }; | ... | ... |