Skip to content

Commit

Permalink
Implemented tests for articleService saving tags, added helper method…
Browse files Browse the repository at this point in the history
…s to changeawaredecorator and base abstract model
  • Loading branch information
zakhenry committed Aug 28, 2015
1 parent e64ef62 commit 3a48107
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 42 deletions.
4 changes: 2 additions & 2 deletions app/src/app/admin/articles/article/article.spec.ts
Expand Up @@ -23,7 +23,7 @@ namespace app.admin.articles.article {
newArticle:true
},
articleService = {
saveArticle:(article:common.models.Article, newArticle:boolean = false) => {
saveArticleWithRelated:(article:common.models.Article, newArticle:boolean = false) => {
return $q.when(true);
}
},
Expand Down Expand Up @@ -70,7 +70,7 @@ namespace app.admin.articles.article {

$scope.$apply();

expect(articleService.saveArticle).to.have.been.calledWith(article);
expect(articleService.saveArticleWithRelated).to.have.been.calledWith(article);

expect(notificationService.toast).to.have.been.calledOnce;

Expand Down
2 changes: 1 addition & 1 deletion app/src/app/admin/articles/article/article.ts
Expand Up @@ -92,7 +92,7 @@ namespace app.admin.articles.article {
*/
public save(){

return this.articleService.saveArticle(this.article, this.$stateParams.newArticle)
return this.articleService.saveArticleWithRelated(this.article, this.$stateParams.newArticle)
.then(() => {
this.notificationService.toast('Article saved').pop();
});
Expand Down
4 changes: 2 additions & 2 deletions app/src/app/user/profile/profile.spec.ts
Expand Up @@ -158,11 +158,11 @@ namespace app.user.profile {

it('should be able to unlink a social network login method', () => {

let userLoginDataFacebook:common.models.UserSocialLogin = {
let userLoginDataFacebook = new common.models.UserSocialLogin({
userId:ProfileController.fullUserInfo.userId,
provider:common.models.UserSocialLogin.facebookType,
token:'eyJtZXRob2QiOiJnb29nbGUiLCJzdWIiOiJkODU2ZWI2OS1jYTU4LTQ2M2MtOWNlZS05MTRlMDlkOWZlNWYiLCJfdXNlci'
};
});

ProfileController.fullUserInfo._socialLogins = (<common.models.UserSocialLogin[]>[]);

Expand Down
14 changes: 11 additions & 3 deletions app/src/common/decorators/changeAwareDecorator.ts
Expand Up @@ -4,7 +4,7 @@ namespace common.decorators {
getChangedProperties?():string[];
resetChangedProperties?():void;
getOriginal?():typeof common.models.AbstractModel;
getChanged?():{
getChanged?(omitUnderscoredKeys?:boolean):{
[key:string]: any;
};
}
Expand Down Expand Up @@ -59,8 +59,16 @@ namespace common.decorators {

Object.defineProperty(obj, 'getChanged', {
enumerable: false,
value: function(){
return _.pick(this, changedProperties);
value: function(includeUnderscoredKeys:boolean = false){
let attributes = _.pick(this, changedProperties);

if (includeUnderscoredKeys){
return attributes;
}

return _.omit(attributes, (value, key) => {
return _.startsWith(key, '_');
});
}
});

Expand Down
29 changes: 25 additions & 4 deletions app/src/common/models/abstractModel.ts
@@ -1,15 +1,17 @@
//note this file MUST be loaded before any depending classes @todo resolve model load order
namespace common.models {

export interface IModel{} //@todo add common methods/properties of a Model
export interface IModel{
getAttributes(includeUnderscoredKeys?:boolean):Object;
}

export interface IModelFactory{
(data:any):IModel;
}

export class AbstractModel implements IModel {

protected nestedEntityMap;
protected _nestedEntityMap;

constructor(data?:any) {
this.hydrate(data);
Expand All @@ -23,7 +25,7 @@ namespace common.models {
if (_.isObject(data)) {
_.assign(this, data);

if (_.size(this.nestedEntityMap) > 1) {
if (_.size(this._nestedEntityMap) > 1) {
this.hydrateNested(data);
}
}
Expand All @@ -36,7 +38,7 @@ namespace common.models {
*/
protected hydrateNested(data:any){

_.forIn(this.nestedEntityMap, (model:typeof AbstractModel, nestedKey:string) => {
_.forIn(this._nestedEntityMap, (model:typeof AbstractModel, nestedKey:string) => {

let key = '_' + nestedKey;
if (_.has(data, key) && !_.isNull(data[key])){
Expand Down Expand Up @@ -67,6 +69,25 @@ namespace common.models {

}

/**
* Get all enumerable attributes of the model, by default excluding all keys starting with an _underscore
* @param includeUnderscoredKeys
* @returns {any}
*/
public getAttributes(includeUnderscoredKeys:boolean = false):Object{

let attributes = _.clone(this);

if (includeUnderscoredKeys){
return attributes;
}

return _.omit(attributes, (value, key) => {
return _.startsWith(key, '_');
});

}

}

}
Expand Down
2 changes: 1 addition & 1 deletion app/src/common/models/article/articleModel.ts
Expand Up @@ -3,7 +3,7 @@ namespace common.models {
@common.decorators.changeAware
export class Article extends AbstractModel{

protected nestedEntityMap = {
protected _nestedEntityMap = {
tags: Tag
};

Expand Down
12 changes: 6 additions & 6 deletions app/src/common/models/user/userModel.spec.ts
Expand Up @@ -46,17 +46,17 @@ namespace common.models {

let user = new User(_.clone(userData, true));

let userLoginDataFacebook:common.models.UserSocialLogin = {
let userLoginDataFacebook = new common.models.UserSocialLogin ({
userId:user.userId,
provider:common.models.UserSocialLogin.facebookType,
token:seededChance.apple_token() // Closest thing to a token in Chance JS library
};
});

let userLoginDataGoogle:common.models.UserSocialLogin = {
let userLoginDataGoogle = new common.models.UserSocialLogin ({
userId:user.userId,
provider:common.models.UserSocialLogin.googleType,
token:seededChance.apple_token() // Closest thing to a token in Chance JS library
};
});

user._socialLogins.push(userLoginDataFacebook, userLoginDataGoogle);

Expand All @@ -68,11 +68,11 @@ namespace common.models {

let user = new User(_.clone(userData, true));

let userLoginDataGoogle:common.models.UserSocialLogin = {
let userLoginDataGoogle = new common.models.UserSocialLogin({
userId:user.userId,
provider:common.models.UserSocialLogin.googleType,
token:seededChance.apple_token() // Closest thing to a token in Chance JS library
};
});

user._socialLogins.push(userLoginDataGoogle);

Expand Down
5 changes: 2 additions & 3 deletions app/src/common/models/user/userModel.ts
Expand Up @@ -20,10 +20,9 @@ namespace common.models {
public _socialLogins:common.models.UserSocialLogin[] = undefined;
public userType:string = undefined;

constructor(data:global.IUserData) {
constructor(data:any) {
super(data);

_.assign(this, data);
this.hydrate(data);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions app/src/common/models/user/userProfileModel.ts
Expand Up @@ -4,7 +4,7 @@ module common.models {
value:string;
}

export class UserProfile implements IModel {
export class UserProfile extends AbstractModel {

public dob:string = undefined;
public mobile:string = undefined;
Expand All @@ -24,7 +24,7 @@ module common.models {
];

constructor(data:any) {
_.assign(this, data);
super(data);
}

}
Expand Down
4 changes: 2 additions & 2 deletions app/src/common/models/user/userSocialLoginModel.ts
@@ -1,6 +1,6 @@
module common.models {

export class UserSocialLogin implements IModel {
export class UserSocialLogin extends AbstractModel {

public static googleType = 'google';
public static facebookType = 'facebook';
Expand All @@ -11,7 +11,7 @@ module common.models {
public token:string = undefined;

constructor(data:any) {
_.assign(this, data);
super(data);
}

}
Expand Down
39 changes: 37 additions & 2 deletions app/src/common/services/article/articleService.spec.ts
Expand Up @@ -8,9 +8,20 @@
let title = seededChance.sentence();

return new common.models.Article({
articleId: seededChance.guid(),
title: title,
body: seededChance.paragraph(),
permalink: title.replace(' ', '-'),
_tags: [
{
tagId: seededChance.guid(),
tag: seededChance.word,
},
{
tagId: seededChance.guid(),
tag: seededChance.word,
}
]
});

},
Expand All @@ -20,22 +31,24 @@
}
};

describe('Article Service', () => {
describe.only('Article Service', () => {

let articleService:common.services.article.ArticleService;
let $httpBackend:ng.IHttpBackendService;
let ngRestAdapter:NgRestAdapter.NgRestAdapterService;
let $rootScope:ng.IRootScopeService;

beforeEach(()=> {

module('app');

inject((_$httpBackend_, _articleService_, _ngRestAdapter_) => {
inject((_$httpBackend_, _articleService_, _ngRestAdapter_, _$rootScope_) => {

if (!articleService) { //dont rebind, so each test gets the singleton
$httpBackend = _$httpBackend_;
articleService = _articleService_;
ngRestAdapter = _ngRestAdapter_;
$rootScope = _$rootScope_;
}
});

Expand Down Expand Up @@ -118,6 +131,28 @@

});

describe('Save Article', () => {


it('should save a new article and all related entities', () => {

let article = fixtures.getArticle();

$httpBackend.expectPUT('/api/articles/'+article.articleId, article.getAttributes()).respond(201);
$httpBackend.expectPUT('/api/articles/'+article.articleId+'/tags', _.clone(article._tags, true)).respond(201);

let savePromise = articleService.saveArticleWithRelated(article, true);

expect(savePromise).eventually.to.be.fulfilled;
expect(savePromise).eventually.to.deep.equal(article);

$httpBackend.flush();

});

});


});

})();

0 comments on commit 3a48107

Please sign in to comment.