Merge pull request #2396 from aughr/andrew/consolidate_tests

Convert API tests to normal JS
This commit is contained in:
Tyler Renelle 2014-01-16 15:19:21 -08:00
commit e6dc715de6
25 changed files with 518 additions and 808 deletions

View file

@ -11,10 +11,22 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
'public/bower_components/jquery/jquery.js',
'public/bower_components/angular/angular.js',
'public/bower_components/angular-loading-bar/build/loading-bar.min.js',
'public/bower_components/angular-resource/angular-resource.min.js',
'public/bower_components/angular-sanitize/angular-sanitize.js',
'public/bower_components/bootstrap/docs/assets/js/bootstrap.js',
'public/bower_components/angular-bootstrap/ui-bootstrap.js',
'public/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
'public/bower_components/angular-ui-router/release/angular-ui-router.js',
'public/bower_components/angular-ui/build/angular-ui.js',
'public/bower_components/angular-ui-utils/ui-utils.min.js',
'public/bower_components/Angular-At-Directive/src/at.js',
'public/bower_components/Angular-At-Directive/src/caret.js',
'public/bower_components/angular-mocks/angular-mocks.js',
'public/bower_components/angular-bindonce/bindonce.js',
'public/bower_components/ngInfiniteScroll/ng-infinite-scroll.js',
'public/bower_components/marked/lib/marked.js',
'public/bower_components/js-emoji/emoji.js',
'public/bower_components/habitrpg-shared/dist/habitrpg-shared.js',

View file

@ -17,7 +17,7 @@ window.env.t = function(string){
window.habitrpg = angular.module('habitrpg',
['ngResource', 'ngSanitize', 'userServices', 'groupServices', 'memberServices', 'challengeServices',
'authServices', 'notificationServices', 'guideServices',
'authServices', 'notificationServices', 'guideServices', 'authCtrl',
'ui.bootstrap', 'ui.keypress', 'ui.router', 'chieffancypants.loadingBar', 'At', 'pasvaz.bindonce', 'infinite-scroll'])
// @see https://github.com/angular-ui/ui-router/issues/110 and https://github.com/HabitRPG/habitrpg/issues/1705
@ -231,4 +231,4 @@ window.habitrpg = angular.module('habitrpg',
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}])
}])

View file

@ -4,93 +4,94 @@
The authentication controller (login & facebook)
*/
habitrpg.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','API_URL',
function($scope, $rootScope, User, $http, $location, $window, API_URL) {
var runAuth;
var showedFacebookMessage;
angular.module('authCtrl', [])
.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','API_URL',
function($scope, $rootScope, User, $http, $location, $window, API_URL) {
var runAuth;
var showedFacebookMessage;
$scope.useUUID = false;
$scope.toggleUUID = function() {
if (showedFacebookMessage === false) {
alert("Until we add Facebook, use your UUID and API Token to log in (found at https://habitrpg.com > Options > Settings).");
showedFacebookMessage = true;
}
$scope.useUUID = !$scope.useUUID;
};
$scope.logout = function() {
localStorage.clear();
window.location.href = '/logout';
};
runAuth = function(id, token) {
User.authenticate(id, token, function(err) {
$window.location.href = '/';
//$rootScope.modals.login = false;
});
};
$scope.register = function() {
/*TODO highlight invalid inputs
we have this as a workaround for https://github.com/HabitRPG/habitrpg-mobile/issues/64
*/
if ($scope.registrationForm.$invalid) {
return;
}
$http.post(API_URL + "/api/v2/register", $scope.registerVals).success(function(data, status, headers, config) {
runAuth(data.id, data.apiToken);
}).error(function(data, status, headers, config) {
if (status === 0) {
$window.alert("Server not currently reachable, try again later");
} else if (!!data && !!data.err) {
$window.alert(data.err);
} else {
$window.alert("ERROR: " + status);
}
});
};
function errorAlert(data, status, headers, config) {
if (status === 0) {
$window.alert("Server not currently reachable, try again later");
} else if (!!data && !!data.err) {
$window.alert(data.err);
} else {
$window.alert("ERROR: " + status);
}
}
$scope.auth = function() {
var data = {
username: $scope.loginUsername,
password: $scope.loginPassword
$scope.useUUID = false;
$scope.toggleUUID = function() {
if (showedFacebookMessage === false) {
alert("Until we add Facebook, use your UUID and API Token to log in (found at https://habitrpg.com > Options > Settings).");
showedFacebookMessage = true;
}
$scope.useUUID = !$scope.useUUID;
};
if ($scope.useUUID) {
runAuth($scope.loginUsername, $scope.loginPassword);
} else {
$http.post(API_URL + "/api/v2/user/auth/local", data)
.success(function(data, status, headers, config) {
runAuth(data.id, data.token);
}).error(errorAlert);
}
};
$scope.playButtonClick = function(){
if (User.authenticated()) {
window.location.href = '/#/tasks';
} else {
$('#login-modal').modal('show');
}
}
$scope.logout = function() {
localStorage.clear();
window.location.href = '/logout';
};
$scope.passwordReset = function(email){
$http.post(API_URL + '/api/v2/user/reset-password', {email:email})
.success(function(){
alert('New password sent.');
})
.error(function(data){
alert(data.err);
runAuth = function(id, token) {
User.authenticate(id, token, function(err) {
$window.location.href = '/';
//$rootScope.modals.login = false;
});
};
$scope.register = function() {
/*TODO highlight invalid inputs
we have this as a workaround for https://github.com/HabitRPG/habitrpg-mobile/issues/64
*/
if ($scope.registrationForm.$invalid) {
return;
}
$http.post(API_URL + "/api/v2/register", $scope.registerVals).success(function(data, status, headers, config) {
runAuth(data.id, data.apiToken);
}).error(function(data, status, headers, config) {
if (status === 0) {
$window.alert("Server not currently reachable, try again later");
} else if (!!data && !!data.err) {
$window.alert(data.err);
} else {
$window.alert("ERROR: " + status);
}
});
};
function errorAlert(data, status, headers, config) {
if (status === 0) {
$window.alert("Server not currently reachable, try again later");
} else if (!!data && !!data.err) {
$window.alert(data.err);
} else {
$window.alert("ERROR: " + status);
}
}
$scope.auth = function() {
var data = {
username: $scope.loginUsername,
password: $scope.loginPassword
};
if ($scope.useUUID) {
runAuth($scope.loginUsername, $scope.loginPassword);
} else {
$http.post(API_URL + "/api/v2/user/auth/local", data)
.success(function(data, status, headers, config) {
runAuth(data.id, data.token);
}).error(errorAlert);
}
};
$scope.playButtonClick = function(){
if (User.authenticated()) {
window.location.href = '/#/tasks';
} else {
$('#login-modal').modal('show');
}
}
$scope.passwordReset = function(email){
$http.post(API_URL + '/api/v2/user/reset-password', {email:email})
.success(function(){
alert('New password sent.');
})
.error(function(data){
alert(data.err);
});
}
}
}
]);

View file

@ -1,7 +1,7 @@
"use strict";
habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'User', 'Challenges', 'Notification', '$compile', 'Groups', '$state',
function($rootScope, $scope, User, Challenges, Notification, $compile, Groups, $state) {
habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', 'Challenges', 'Notification', '$compile', 'Groups', '$state',
function($rootScope, $scope, Shared, User, Challenges, Notification, $compile, Groups, $state) {
// FIXME $scope.challenges needs to be resolved first (see app.js)
$scope.groups = Groups.Group.query({type:'party,guilds,tavern'});
@ -133,7 +133,7 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'User', 'Challenge
//------------------------------------------------------------
$scope.addTask = function(addTo, listDef) {
var task = $rootScope.Shared.taskDefaults({text: listDef.newTask, type: listDef.type});
var task = Shared.taskDefaults({text: listDef.newTask, type: listDef.type});
addTo.unshift(task);
//User.log({op: "addTask", data: task}); //TODO persist
delete listDef.newTask;
@ -229,4 +229,4 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'User', 'Challenge
$scope.search.group = _.transform($scope.groups, function(m,g){m[g._id] = false});
}
}]);
}]);

View file

@ -1,7 +1,7 @@
"use strict";
habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'API_URL', '$q', 'User', 'Members', '$state',
function($scope, $rootScope, Groups, $http, API_URL, $q, User, Members, $state) {
habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '$http', 'API_URL', '$q', 'User', 'Members', '$state',
function($scope, $rootScope, Shared, Groups, $http, API_URL, $q, User, Members, $state) {
$scope.isMember = function(user, group){
return ~(group.members.indexOf(user._id));
@ -66,7 +66,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'A
// We watch Members.selectedMember because it's asynchronously set, so would be a hassle to handle updates here
$scope.$watch( function() { return Members.selectedMember; }, function (member) {
if(member)
member.petCount = $rootScope.Shared.countPets(null, member.items.pets);
member.petCount = Shared.countPets(null, member.items.pets);
$scope.profile = member;
});
}

View file

@ -1,8 +1,7 @@
habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User',
function($rootScope, $scope, $window, User) {
habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', 'Content',
function($rootScope, $scope, $window, User, Content) {
var user = User.user;
var Content = $rootScope.Content;
// convenience vars since these are accessed frequently
@ -135,4 +134,4 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User',
$scope.closeQuest();
}
}
]);
]);

View file

@ -1,7 +1,7 @@
'use strict';
habitrpg.controller('NotificationCtrl',
['$scope', '$rootScope', 'User', 'Guide', 'Notification', function ($scope, $rootScope, User, Guide, Notification) {
['$scope', '$rootScope', 'Shared', 'User', 'Guide', 'Notification', function ($scope, $rootScope, Shared, User, Guide, Notification) {
$rootScope.$watch('user.stats.hp', function(after, before) {
if (after == before) return;
@ -71,7 +71,7 @@ habitrpg.controller('NotificationCtrl',
$rootScope.$watch('user.items.pets', function(after, before){
if(_.size(after) === _.size(before) ||
$rootScope.Shared.countPets(null, after) < 90) return;
Shared.countPets(null, after) < 90) return;
User.user.achievements.beastMaster = true;
$rootScope.modals.achievements.beastMaster = true;
}, true);

View file

@ -3,8 +3,8 @@
/* Make user and settings available for everyone through root scope.
*/
habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups',
function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups) {
habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', 'Shared', 'Content',
function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups, Shared, Content) {
var user = User.user;
var initSticky = _.once(function(){
@ -20,8 +20,8 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
$rootScope.moment = window.moment;
$rootScope._ = window._;
$rootScope.settings = User.settings;
$rootScope.Shared = window.habitrpgShared;
$rootScope.Content = window.habitrpgShared.content;
$rootScope.Shared = Shared;
$rootScope.Content = Content;
// Angular UI Router
$rootScope.$state = $state;
@ -45,10 +45,10 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
// count pets, mounts collected totals, etc
$rootScope.countExists = function(items) {return _.reduce(items,function(m,v){return m+(v?1:0)},0)}
$rootScope.petCount = $rootScope.Shared.countPets(null, User.user.items.pets);
$rootScope.petCount = Shared.countPets(null, User.user.items.pets);
$rootScope.$watch('user.items.pets', function(pets){
$rootScope.petCount = $rootScope.Shared.countPets($rootScope.countExists(pets), User.user.items.pets);
$rootScope.petCount = Shared.countPets($rootScope.countExists(pets), User.user.items.pets);
}, true);
$scope.safeApply = function(fn) {

View file

@ -1,7 +1,7 @@
"use strict";
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'API_URL', '$timeout',
function($scope, $rootScope, $location, User, Notification, $http, API_URL, $timeout) {
habitrpg.controller("TasksCtrl", ['$scope', '$location', 'User','Notification', '$http', 'API_URL', '$timeout', 'Shared',
function($scope, $location, User, Notification, $http, API_URL, $timeout, Shared) {
$scope.obj = User.user; // used for task-lists
$scope.user = User.user;
@ -138,7 +138,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
*/
$scope.$watch('user.items.gear.equipped', function(){
$scope.itemStore = $rootScope.Shared.updateStore(User.user);
$scope.itemStore = Shared.updateStore(User.user);
},true);
$scope.buy = function(item) {

View file

@ -1,9 +1,9 @@
"use strict";
habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$http', '$state', 'Guide',
function($rootScope, $scope, $location, User, $http, $state, Guide) {
habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$http', '$state', 'Guide', 'Shared',
function($rootScope, $scope, $location, User, $http, $state, Guide, Shared) {
$scope.profile = User.user;
$scope.profile.petCount = $rootScope.Shared.countPets(null, $scope.profile.items.pets);
$scope.profile.petCount = Shared.countPets(null, $scope.profile.items.pets);
$scope.hideUserAvatar = function() {
$(".userAvatar").hide();
};
@ -25,7 +25,7 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$
User.user.ops.changeClass({query:{class:klass}});
$scope.selectedClass = undefined;
$rootScope.Shared.updateStore(User.user);
Shared.updateStore(User.user);
$state.go('options.profile.stats');
window.setTimeout(Guide.classesTour, 10);
}

View file

@ -4,9 +4,9 @@
* Services that persists and retrieves user from localStorage.
*/
angular.module('memberServices', ['ngResource']).
factory('Members', ['$rootScope', 'API_URL', '$resource',
function($rootScope, API_URL, $resource) {
angular.module('memberServices', ['ngResource', 'sharedServices']).
factory('Members', ['$rootScope', 'Shared', 'API_URL', '$resource',
function($rootScope, Shared, API_URL, $resource) {
var members = {};
var Member = $resource(API_URL + '/api/v2/members/:uid', {uid:'@_id'});
var memberServices = {
@ -65,12 +65,12 @@ angular.module('memberServices', ['ngResource']).
// and then for guild)
// and if not, fetch them
if (members[uid] && members[uid].items && members[uid].items.weapon) {
$rootScope.Shared.wrap(members[uid],false);
Shared.wrap(members[uid],false);
self.selectedMember = members[uid];
} else {
Member.get({uid: uid}, function(member){
self.populate(member); // lazy load for later
$rootScope.Shared.wrap(member,false);
Shared.wrap(member,false);
self.selectedMember = members[member._id];
});
}

View file

@ -0,0 +1,15 @@
'use strict';
/**
* Services that expose habitrpg-shared
*/
angular.module('sharedServices', []).
factory('Shared', [function () {
return window.habitrpgShared;
}
]).
factory('Content', ['Shared', function (Shared) {
return Shared.content;
}
]);

View file

@ -1,7 +1,7 @@
"use strict";
window.habitrpg = angular.module('habitrpg', ['notificationServices', 'userServices', 'chieffancypants.loadingBar'])
window.habitrpgStatic = angular.module('habitrpgStatic', ['notificationServices', 'userServices', 'chieffancypants.loadingBar', 'authCtrl'])
.constant("API_URL", "")
.constant("STORAGE_USER_ID", 'habitrpg-user')
.constant("STORAGE_SETTINGS_ID", 'habit-mobile-settings')
.constant("MOBILE_APP", false)
.constant("MOBILE_APP", false)

View file

@ -32,6 +32,7 @@
"bower_components/habitrpg-shared/dist/habitrpg-shared.js",
"js/app.js",
"js/services/sharedServices.js",
"js/services/authServices.js",
"js/services/notificationServices.js",
"bower_components/habitrpg-shared/script/userServices.js",
@ -87,4 +88,4 @@
"static.css"
]
}
}
}

View file

@ -1,6 +1,5 @@
We need to clean up this directory. The *real* tests are in spec/ mock/ e2e/ and api.mocha.coffee. We want to:
1. Move all old / deprecated tests from casper, test2, etc into spec, mock, e2e
1. Move api.mocha.coffee to vanilla javascript
1. Remove dependency of api.mocha.coffee on Derby, port it to Mongoose
1. Add better test-coverage
1. Add better test-coverage

View file

@ -1,656 +0,0 @@
_ = require 'lodash'
expect = require 'expect.js'
require 'coffee-script'
async = require 'async'
superagentDefaults = require 'superagent-defaults'
request = superagentDefaults()
conf = require("nconf")
conf.argv().env().file({file: __dirname + '../config.json'}).defaults
conf.set('port','1337')
# Override normal ENV values with nconf ENV values (ENV values are used the same way without nconf)
#FIXME can't get nconf file above to load...
process.env.BASE_URL = conf.get("BASE_URL")
process.env.FACEBOOK_KEY = conf.get("FACEBOOK_KEY")
process.env.FACEBOOK_SECRET = conf.get("FACEBOOK_SECRET")
process.env.NODE_DB_URI = 'mongodb://localhost/habitrpg'
User = require('../src/models/user').model
Group = require('../src/models/group').model
Challenge = require('../src/models/challenge').model
app = require '../src/server'
## monkey-patch expect.js for better diffs on mocha
## see: https://github.com/LearnBoost/expect.js/pull/34
#origBe = expect.Assertion::be
#expect.Assertion::be = expect.Assertion::equal = (obj) ->
# @_expected = obj
# origBe.call this, obj
# Custom modules
shared = require 'habitrpg-shared'
###### Helpers & Variables ######
model = null
uuid = null
taskPath = null
baseURL = 'http://localhost:3000/api/v2'
###
expect().eql expects object keys to be in the correct order, this sorts that out
###
expectUserEqual = (u1, u2) ->
[u1, u2] = _.map [u1, u2], (obj) ->
'update__ stats.toNextLevel stats.maxHealth __v'.split(' ').forEach (path) ->
helpers.dotSet path, null, obj
sorted = {}
_.each _.keys(obj).sort(), (k) -> sorted[k] = obj[k]
sorted.tasks = _.sortBy sorted.tasks, 'id'
sorted
# console.log {u1, u2}
expect(u1).to.eql(u2)
expectSameValues = (obj1, obj2, paths) ->
_.each paths, (k) ->
expect(helpers.dotGet(k,obj1)).to.eql helpers.dotGet(k,obj2)
expectCode = (res, code) ->
expect(res.body.err).to.be undefined if code is 200
expect(res.statusCode).to.be code
###### Specs ######
describe 'API', ->
user = null
_id = null
apiToken = null
username = null
password = null
registerNewUser = (cb, main=true)->
randomID = shared.uuid()
[username,password] = [randomID,randomID] if main
request.post("#{baseURL}/register")
.set('Accept', 'application/json')
.send({
username: randomID
password: randomID
confirmPassword: randomID
email: "#{randomID}@gmail.com"
})
.end (res) ->
return cb(null,res.body) unless main
{_id,apiToken} = res.body
console.log {_id,apiToken}
User.findOne {_id, apiToken}, (err, _user) ->
expect(err).to.not.be.ok
user = _user
request
.set('Accept', 'application/json')
.set('X-API-User', _id)
.set('X-API-Key', apiToken)
cb null, res.body
before (done)->
require '../src/server' #start the server
# then wait for it to do it's thing. TODO make a cb-compatible export of server
setTimeout done, 2000
describe 'Without token or user id', ->
it '/api/v2/status', (done) ->
request.get("#{baseURL}/status")
.set('Accept', 'application/json')
.end (res) ->
expect(res.statusCode).to.be 200
expect(res.body.status).to.be 'up'
done()
it '/api/v2/user', (done) ->
request.get("#{baseURL}/user")
.set('Accept', 'application/json')
.end (res) ->
expect(res.statusCode).to.be 401
expect(res.body.err).to.be 'You must include a token and uid (user id) in your request'
done()
describe 'With token and user id', ->
currentUser = null
before (done) ->
registerNewUser(done,true)
beforeEach (done) ->
User.findById _id, (err,_user) ->
currentUser = _user
done()
############
# Groups
############
describe 'Groups', ->
group = undefined
before (done) ->
request.post("#{baseURL}/groups")
.send({name:"TestGroup", type:"party"})
.end (res) ->
expectCode res, 200
group = res.body
expect(group.members.length).to.be 1
expect(group.leader).to.be user._id
done()
describe 'Challenges', ->
challenge = undefined
updateTodo = undefined
it 'Creates a challenge', (done) ->
request.post("#{baseURL}/challenges")
.send({
group:group._id
dailys: [{type:'daily',text:'Challenge Daily'}]
todos: [{type:'todo', text:'Challenge Todo', notes:'Challenge Notes'}]
rewards: []
habits: []
official: true
})
.end (res) ->
expectCode res, 200
async.parallel [
(cb) -> User.findById _id, cb
(cb) -> Challenge.findById res.body._id, cb
], (err, results) ->
[_user,challenge] = [results[0],results[1]]
expect(_user.dailys[_user.dailys.length-1].text).to.be('Challenge Daily')
updateTodo = _user.todos[_user.todos.length-1]
expect(updateTodo.text).to.be('Challenge Todo')
expect(challenge.official).to.be false
done()
it 'User updates challenge notes', (done) ->
updateTodo.notes = "User overriden notes"
request.put("#{baseURL}/user/tasks/#{updateTodo.id}")
.send(updateTodo)
.end (res) ->
done() #we'll do the check down below
it 'Change challenge daily', (done) ->
challenge.dailys[0].text = 'Updated Daily'
challenge.todos[0].notes = 'Challenge Updated Todo Notes'
request.post("#{baseURL}/challenges/#{challenge._id}")
.send(challenge)
.end (res) ->
setTimeout ->
User.findById _id, (err,_user) ->
expectCode res, 200
expect(_user.dailys[_user.dailys.length-1].text).to.be('Updated Daily')
expect(res.body.todos[0].notes).to.be('Challenge Updated Todo Notes')
expect(_user.todos[_user.todos.length-1].notes).to.be('User overriden notes')
currentUser = _user
done()
, 500 # we have to wait a while for users' tasks to be updated, called async on server
it 'Shows user notes on challenge page', (done) ->
request.get("#{baseURL}/challenges/#{challenge._id}/member/#{_id}")
.end (res) ->
expect(res.body.todos[res.body.todos.length-1].notes).to.be('User overriden notes')
done()
it 'Complete To-Dos', (done) ->
u = currentUser
request.post("#{baseURL}/user/tasks/#{u.todos[0].id}/up").end (res) ->
request.post("#{baseURL}/user/tasks/#{u.todos[1].id}/up").end (res) ->
request.post("#{baseURL}/user/tasks/").send({type:'todo'}).end (res) ->
request.post("#{baseURL}/user/tasks/clear-completed").end (res) ->
expect(_.size res.body).to.be 2
done()
it 'Admin creates a challenge', (done) ->
User.findByIdAndUpdate _id, {$set:{'contributor.admin':true}}, (err,_user) ->
expect(err).to.not.be.ok
async.parallel [
(cb)->
request.post("#{baseURL}/challenges")
.send({group:group._id, dailys: [], todos: [], rewards: [], habits: [], official: false}).end (res) ->
expect(res.body.official).to.be false
cb()
(cb)->
request.post("#{baseURL}/challenges")
.send({group:group._id, dailys: [], todos: [], rewards: [], habits: [], official: true}).end (res) ->
expect(res.body.official).to.be true
cb()
], done
describe 'Quests', ->
party = undefined
participating = []
notParticipating = []
it 'Invites some members', (done) ->
async.waterfall [
# Register new users
(cb) ->
async.parallel [
(cb2) -> registerNewUser(cb2,false)
(cb2) -> registerNewUser(cb2,false)
(cb2) -> registerNewUser(cb2,false)
], cb
# Send them invitations
(_party, cb) ->
party = _party
async.parallel [
(cb2) -> request.post("#{baseURL}/groups/#{group._id}/invite?uuid=#{party[0]._id}").end (-> cb2())
(cb2) -> request.post("#{baseURL}/groups/#{group._id}/invite?uuid=#{party[1]._id}").end (-> cb2())
(cb2) -> request.post("#{baseURL}/groups/#{group._id}/invite?uuid=#{party[2]._id}").end (-> cb2())
], cb
# Accept / Reject
(results, cb) ->
#series since they'll be modifying the same group record
async.series (_.reduce party, (m,v,i) ->
m.push (cb2) ->
request.post("#{baseURL}/groups/#{group._id}/join")
.set('X-API-User', party[i]._id)
.set('X-API-Key', party[i].apiToken)
.end (res) -> cb2()
m
, []), cb
# Make sure the invites stuck
(whatever, cb) ->
Group.findById group._id, (err, g) ->
expect(g.members.length).to.be 4
cb()
], (err, results) ->
expect(err).to.be.ok
done()
it 'Starts a quest', (done) ->
async.waterfall [
(cb)->
request.post("#{baseURL}/groups/#{group._id}/questAccept?key=evilsanta")
.end (res) ->
expectCode(res, 401)
User.findByIdAndUpdate _id, {$set:'items.quests.evilsanta':1}, cb
(_user,cb)->
request.post("#{baseURL}/groups/#{group._id}/questAccept?key=evilsanta")
.end (res) ->
expectCode(res, 200)
Group.findById group._id,cb
(_group,cb)->
group = _group #refresh local group
expect(group.quest.key).to.be 'evilsanta'
async.series (_.reduce party, (m,v,i) ->
m.push (cb2) ->
request.post("#{baseURL}/groups/#{group._id}/questAccept")
.set('X-API-User', party[i]._id)
.set('X-API-Key', party[i].apiToken)
.end (res) -> cb2()
m
, []), cb
], done
it "Doesn't include people who aren't participating"
# ############
# # Batch Update
# ############
#
# describe 'Batch Update', ->
#
# it 'POST /api/v1/batch-update', (done) ->
# userBefore = _.cloneDeep(currentUser)
#
# ops = [
# # Good scores
# op: 'score', params: {id:user.habits[0].id, direction: 'up'}
# op: 'score', params: {id:user.habits[1].id, direction: 'down'}
# op: 'score', params: {id:user.dailys[0].id, direction: 'up'}
# op: 'score', params: {id:user.todos[0].id, direction: 'up'}
# ]
#
# request.post("#{baseURL}/user/batch-update")
# .send(ops)
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# #expectUserEqual(userBefore, res.body)
# done()
#
#
# ############
# # To Be Updated (these are old v1 tests which haven't been touched in over 6 months, need to be portd to new API tests or deleted)
# ############
#
# it.skip 'POST /api/v2/batch-update (handles corrupt values)', (done) ->
# registerNewUser (_res) ->
# # corrupt the tasks, and let's see how the server handles this
# ids = _res.dailyIds
# _res.tasks[ids[0]].value = NaN
# _res.tasks[ids[1]].value = undefined
# _res.tasks[ids[2]] = {}
# _res.tasks["undefined"] = {}
#
# _res.stats.hp = _res.stats.gp = NaN
#
# _res.lastCron = +new Date('08/13/2013')
#
# ops = [
# op: 'score', task: _res.tasks[ids[0]], dir: 'up'
# ]
#
# model.set "users.#{_res.id}", _res, ->
# request.post("#{baseURL}/user/batch-update")
# .set('Accept', 'application/json')
# .set('X-API-User', _res.id)
# .set('X-API-Key', _res.apiToken)
# .send(ops)
# .end (res) ->
# expect(res.statusCode).to.be 200
# console.log {stats:res.body.stats, tasks:res.body.tasks}
# done()
#
#
# #FIXME figure out how to compare the objects
# it.skip 'GET /api/v1/user', (done) ->
# request.get("#{baseURL}/user")
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# expect(res.body.id).not.to.be.empty()
# self = _.clone(currentUser)
# delete self.apiToken
# self.stats.toNextLevel = 150
# self.stats.maxHealth = 50
#
# expectUserEqual(res.body, self)
# done()
#
# it.skip 'GET /api/v1/user/task/:id', (done) ->
# tid = _.pluck(currentUser.tasks, 'id')[0]
# request.get("#{baseURL}/user/task/#{tid}")
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# expect(res.body).to.eql currentUser.tasks[tid]
# done()
#
# it.skip 'POST /api/v1/user/task', (done) ->
# request.post("#{baseURL}/user/task")
# .send({title: 'Title', text: 'Text', type: 'habit'})
# .end (res) ->
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 201
# expect(res.body.id).not.to.be.empty()
# # Ensure that user owns the newly created object
# saved = user.get("tasks.#{res.body.id}")
# expect(saved).to.be.an('object')
# done()
#
# it.skip 'POST /api/v1/user/task (without type)', (done) ->
# request.post("#{baseURL}/user/task")
# .send({})
# .end (res) ->
# expect(res.body.err).to.be 'type must be habit, todo, daily, or reward'
# expect(res.statusCode).to.be 400
# done()
#
# it.skip 'POST /api/v1/user/task (only type)', (done) ->
# request.post("#{baseURL}/user/task")
# .send(type: 'habit')
# .end (res) ->
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 201
# expect(res.body.id).not.to.be.empty()
# # Ensure that user owns the newly created object
# expect(user.get().tasks[res.body.id]).to.be.an('object')
# # Ensure that value gets set to 0 since not otherwise specified
# expect(user.get().tasks[res.body.id].value).to.be.equal(0)
# done()
#
# it.skip 'PUT /api/v1/user/task/:id', (done) ->
# tid = _.pluck(currentUser.tasks, 'id')[0]
# request.put("#{baseURL}/user/task/#{tid}")
# .send(text: 'bye')
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# currentUser.tasks[tid].text = 'bye'
# expectSameValues res.body, currentUser.tasks[tid], ['id','type','text']
# #expect(res.body).to.eql currentUser.tasks[tid]
# done()
#
# it.skip 'PUT /api/v1/user/task/:id (shouldnt update type)', (done) ->
# tid = _.pluck(currentUser.tasks, 'id')[1]
# type = if currentUser.tasks[tid].type is 'habit' then 'daily' else 'habit'
# request.put("#{baseURL}/user/task/#{tid}")
# .send(type: type, text: 'fishman')
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# currentUser.tasks[tid].text = 'fishman'
# expect(res.body).to.eql currentUser.tasks[tid]
# done()
#
# it.skip 'PUT /api/v1/user/task/:id (update notes)', (done) ->
# tid = _.pluck(currentUser.tasks, 'id')[2]
# request.put("#{baseURL}/user/task/#{tid}")
# .send(text: 'hi',notes:'foobar matey')
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# currentUser.tasks[tid].text = 'hi'
# currentUser.tasks[tid].notes = 'foobar matey'
# expect(res.body).to.eql currentUser.tasks[tid]
# done()
#
# it.skip 'GET /api/v1/user/tasks', (done) ->
# request.get("#{baseURL}/user/tasks")
# .end (res) ->
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expect(res.body.err).to.be undefined
# expect(user.get()).to.be.ok()
# expect(res.statusCode).to.be 200
# model.ref '_user', user
# tasks = []
# for type in ['habit','todo','daily','reward']
# model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
# tasks = tasks.concat model.get("_#{type}List")
# # Ensure that user owns the tasks
# expect(res.body.length).to.equal tasks.length
# # Ensure that the two sets are equal
# expect(_.difference(_.pluck(res.body,'id'), _.pluck(tasks,'id')).length).to.equal 0
# done()
#
# it.skip 'GET /api/v1/user/tasks (todos)', (done) ->
# request.get("#{baseURL}/user/tasks")
# .query(type:'todo')
# .end (res) ->
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# model.ref '_user', user
# model.refList "_todoList", "_user.tasks", "_user.todoIds"
# tasks = model.get("_todoList")
# # Ensure that user owns the tasks
# expect(res.body.length).to.equal tasks.length
# # Ensure that the two sets are equal
# expect(_.difference(_.pluck(res.body,'id'), _.pluck(tasks,'id')).length).to.equal 0
# done()
#
# it.skip 'DELETE /api/v1/user/task/:id', (done) ->
# tid = currentUser.habitIds[2]
# request.del("#{baseURL}/user/task/#{tid}")
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 204
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expect(user.get('habitIds').indexOf(tid)).to.be -1
# expect(user.get("tasks.#{tid}")).to.be undefined
# done()
#
# it.skip 'DELETE /api/v1/user/task/:id (no task found)', (done) ->
# tid = "adsfasdfjunkshouldntbeatask"
# request.del("#{baseURL}/user/task/#{tid}")
# .end (res) ->
# expect(res.statusCode).to.be 400
# expect(res.body.err).to.be 'No task found.'
# done()
#
# it.skip 'POST /api/v1/user/task/:id/up (habit)', (done) ->
# tid = currentUser.habitIds[0]
# request.post("#{baseURL}/user/task/#{tid}/up")
# .send({})
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# expect(res.body).to.eql { gp: 1, exp: 7.5, lvl: 1, hp: 50, delta: 1 }
# done()
#
# it.skip 'POST /api/v1/user/task/:id/up (daily)', (done) ->
# tid = currentUser.dailyIds[0]
# request.post("#{baseURL}/user/task/#{tid}/up")
# .send({})
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# expect(res.body).to.eql { gp: 2, exp: 15, lvl: 1, hp: 50, delta: 1 }
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expect(user.get("tasks.#{tid}.completed")).to.be true
# done()
#
# it.skip 'POST /api/v1/user/task (array)', (done) ->
# habitId = currentUser.habitIds[0]
# dailyId = currentUser.dailyIds[0]
# arr = [{
# id: habitId
# text: 'hello'
# notes: 'note'
# },{
# text: 'new task'
# notes: 'notes!'
# },{
# id: dailyId
# del: true
# }]
#
# request.post("#{baseURL}/user/tasks")
# .send(arr)
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 201
#
# expectSameValues res.body[0], {id: habitId,text: 'hello',notes: 'note'}, ['id','text','notes']
# expect(res.body[1].id).to.be.a 'string'
# expect(res.body[1].text).to.be 'new task'
# expect(res.body[1].notes).to.be 'notes!'
# expect(res.body[2]).to.eql deleted: true
#
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# expectSameValues user.get("tasks.#{habitId}"), {id: habitId,text: 'hello',notes: 'note'}, ['id','text','notes']
# expect(user.get("tasks.#{dailyId}")).to.be undefined
# expectSameValues user.get("tasks.#{res.body[1].id}"), {id: res.body[1].id, text: 'new task', notes: 'notes!'}, ['id','text','notes']
# done()
#
# it.skip 'PUT /api/v1/user (bad path)', (done) ->
# # These updates should not save, as per the API changes
# userUpdates =
# stats: hp: 30
# flags: itemsEnabled: true
# tasks: [{
# text: 'hello2'
# notes: 'note2'
# }]
#
# request.put("#{baseURL}/user")
# .send(userUpdates)
# .end (res) ->
# expect(res.body.err).to.be.ok()
# expect(res.statusCode).to.be 500
# done()
#
# it.skip 'PUT /api/v1/user', (done) ->
# userBefore = {}
# query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
# query.fetch (err, user) ->
# userBefore = user.get()
#
# habitId = currentUser.habitIds[0]
# dailyId = currentUser.dailyIds[0]
# updates = {}
# updates['stats.hp'] = 30
# updates['flags.itemsEnabled'] = true
# updates["tasks.#{habitId}.text"] = 'hello2'
# updates["tasks.#{habitId}.notes"] = 'note2'
#
# request.put("#{baseURL}/user")
# .send(updates)
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# changesWereMade = (obj) ->
# expect(obj.stats.hp).to.be 30
# expect(obj.flags.itemsEnabled).to.be true
# expectSameValues _.find(obj.tasks,{id:habitId}), {id: habitId,text: 'hello2',notes: 'note2'}, ['id','text','notes']
# changesWereMade res.body
# query.fetch (err, user) ->
# changesWereMade user.get()
# done()
#
# it.skip 'POST /api/v1/user/auth/local', (done) ->
# userAuth = {username, password}
# request.post("#{baseURL}/user/auth/local")
# .set('Accept', 'application/json')
# .send(userAuth)
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# expect(res.body.id).to.be currentUser.id
# expect(res.body.token).to.be currentUser.apiToken
# done()
#
# it.skip 'POST /api/v1/user/auth/facebook', (done) ->
# id = shared.uuid()
# userAuth = facebook_id: 12345, name: 'Tyler Renelle', email: 'x@y.com'
# newUser = helpers.newUser(true)
# newUser.id = id
# newUser.auth = facebook:
# id: userAuth.facebook_id
# name: userAuth.name
# email: userAuth.email
# model.set "users.#{id}", newUser, ->
#
# request.post("#{baseURL}/user/auth/facebook")
# .set('Accept', 'application/json')
# .send(userAuth)
# .end (res) ->
# expect(res.body.err).to.be undefined
# expect(res.statusCode).to.be 200
# expect(res.body.id).to.be newUser.id
# #expect(res.body.token).to.be newUser.apiToken
# done()
#
#

334
test/api.mocha.js Normal file
View file

@ -0,0 +1,334 @@
/*jslint node: true */
/*global describe, before, beforeEach, it*/
'use strict';
var _ = require('lodash');
var expect = require('expect.js');
var async = require('async');
var superagentDefaults = require('superagent-defaults');
var request = superagentDefaults();
var conf = require('nconf');
conf.argv().env().file({file: __dirname + '../config.json'}).defaults();
conf.set('port','1337');
// Override normal ENV values with nconf ENV values (ENV values are used the same way without nconf)
// FIXME can't get nconf file above to load...
process.env.BASE_URL = conf.get("BASE_URL");
process.env.FACEBOOK_KEY = conf.get("FACEBOOK_KEY");
process.env.FACEBOOK_SECRET = conf.get("FACEBOOK_SECRET");
process.env.NODE_DB_URI = 'mongodb://localhost/habitrpg';
var User = require('../src/models/user').model;
var Group = require('../src/models/group').model;
var Challenge = require('../src/models/challenge').model;
var app = require('../src/server');
var shared = require('habitrpg-shared');
// ###### Helpers & Variables ######
var model, uuid, taskPath,
baseURL = 'http://localhost:3000/api/v2';
var expectCode = function (res, code) {
if (code == 200)
expect(res.body.err).to.be(undefined);
expect(res.statusCode).to.be(code);
};
describe('API', function () {
var user, _id, apiToken, username, password;
var registerNewUser = function(cb, main) {
if (main === undefined) main = true;
var randomID = shared.uuid();
if (main) {
username = password = randomID;
}
request.post(baseURL + "/register")
.set('Accept', 'application/json')
.send({
username: randomID,
password: randomID,
confirmPassword: randomID,
email: randomID + "@gmail.com"
})
.end(function (res) {
if (!main) return cb(null, res.body);
_id = res.body._id;
apiToken = res.body.apiToken;
User.findOne({_id: _id,
apiToken: apiToken},
function (err, _user) {
expect(err).to.not.be.ok();
user = _user;
request
.set('Accept', 'application/json')
.set('X-API-User', _id)
.set('X-API-Key', apiToken);
cb(null, res.body);
});
});
};
before(function (done) {
require('../src/server'); // start the server
// then wait for it to do it's thing. TODO make a cb-compatible export of server
setTimeout(done, 2000);
});
describe('Without token or user id', function () {
it('/api/v2/status', function (done) {
request.get(baseURL + "/status")
.set('Accept', 'application/json')
.end(function (res) {
expect(res.statusCode).to.be(200);
expect(res.body.status).to.be('up');
done();
});
});
it('/api/v2/user', function (done) {
request.get(baseURL + "/user")
.set('Accept', 'application/json')
.end(function (res) {
expect(res.statusCode).to.be(401);
expect(res.body.err).to.be('You must include a token and uid (user id) in your request');
done();
});
});
});
describe('With token and user id', function () {
var currentUser;
before(function (done) {
registerNewUser(done, true);
});
beforeEach(function (done) {
User.findById(_id, function (err, _user) {
currentUser = _user;
done();
});
});
/**
* GROUPS
*/
describe('Groups', function () {
var group;
before(function (done) {
request.post(baseURL + "/groups")
.send({name:"TestGroup", type:"party"})
.end(function (res) {
expectCode(res, 200);
group = res.body;
expect(group.members.length).to.be(1);
expect(group.leader).to.be(user._id);
done();
});
});
describe('Challenges', function () {
var challenge, updateTodo;
it('Creates a challenge', function (done) {
request.post(baseURL + "/challenges")
.send({
group: group._id,
dailys: [{type:'daily', text:'Challenge Daily'}],
todos: [{type:'todo', text:'Challenge Todo', notes:'Challenge Notes'}],
rewards: [],
habits: [],
official: true
})
.end(function (res) {
expectCode(res, 200);
async.parallel([
function (cb) { User.findById(_id, cb); },
function (cb) { Challenge.findById(res.body._id, cb); },
], function (err, results) {
var _user = results[0];
challenge = results[1];
expect(_user.dailys[_user.dailys.length-1].text).to.be('Challenge Daily');
updateTodo = _user.todos[_user.todos.length-1];
expect(updateTodo.text).to.be('Challenge Todo');
expect(challenge.official).to.be(false);
done();
});
});
});
it('User updates challenge notes', function (done) {
updateTodo.notes = "User overriden notes";
request.put(baseURL + "/user/tasks/" + updateTodo.id)
.send(updateTodo)
.end(function (res) {
done(); // we'll do the check down below
});
});
it('Change challenge daily', function (done) {
challenge.dailys[0].text = 'Updated Daily';
challenge.todos[0].notes = 'Challenge Updated Todo Notes';
request.post(baseURL + "/challenges/" + challenge._id)
.send(challenge)
.end(function (res) {
setTimeout(function () {
User.findById(_id, function (err,_user) {
expectCode(res, 200);
expect(_user.dailys[_user.dailys.length-1].text).to.be('Updated Daily');
expect(res.body.todos[0].notes).to.be('Challenge Updated Todo Notes');
expect(_user.todos[_user.todos.length-1].notes).to.be('User overriden notes');
currentUser = _user;
done();
});
}, 500); // we have to wait a while for users' tasks to be updated, called async on server
});
});
it('Shows user notes on challenge page', function (done) {
request.get(baseURL + "/challenges/" + challenge._id + "/member/" + _id)
.end(function (res) {
expect(res.body.todos[res.body.todos.length-1].notes).to.be('User overriden notes');
done();
});
});
it('Complete To-Dos', function (done) {
var u = currentUser;
request.post(baseURL + "/user/tasks/" + u.todos[0].id + "/up").end(function (res) {
request.post(baseURL + "/user/tasks/" + u.todos[1].id + "/up").end(function (res) {
request.post(baseURL + "/user/tasks/").send({type:'todo'}).end(function (res) {
request.post(baseURL + "/user/tasks/clear-completed").end(function (res) {
expect(_.size(res.body)).to.be(2);
done();
});
});
});
});
});
it('Admin creates a challenge', function (done) {
User.findByIdAndUpdate(_id, {$set:{'contributor.admin':true}}, function (err,_user) {
expect(err).to.not.be.ok();
async.parallel([
function (cb) {
request.post(baseURL + "/challenges")
.send({group:group._id, dailys: [], todos: [], rewards: [], habits: [], official: false}).end(function (res) {
expect(res.body.official).to.be(false);
cb();
});
},
function (cb) {
request.post(baseURL + "/challenges")
.send({group:group._id, dailys: [], todos: [], rewards: [], habits: [], official: true}).end(function (res) {
expect(res.body.official).to.be(true);
cb();
});
}], done);
});
});
});
describe('Quests', function () {
var party,
participating = [],
notParticipating = [];
it('Invites some members', function (done) {
async.waterfall([
// Register new users
function (cb) {
async.parallel([
function (cb2) { registerNewUser(cb2,false); },
function (cb2) { registerNewUser(cb2,false); },
function (cb2) { registerNewUser(cb2,false); }
], cb);
},
// Send them invitations
function (_party, cb) {
party = _party;
var inviteURL = baseURL + "/groups/" + group._id + "/invite?uuid=";
async.parallel([
function (cb2) { request.post(inviteURL + party[0]._id).end(cb2); },
function (cb2) { request.post(inviteURL + party[1]._id).end(cb2); },
function (cb2) { request.post(inviteURL + party[2]._id).end(cb2); }
], cb);
},
// Accept / Reject
function (results, cb) {
// series since they'll be modifying the same group record
async.series(_.reduce(party, function (m,v,i) {
m.push(function (cb2) {
request.post("#{baseURL}/groups/#{group._id}/join")
.set('X-API-User', party[i]._id)
.set('X-API-Key', party[i].apiToken)
.end(cb2);
});
return m;
}, []), cb);
},
// Make sure the invites stuck
function (whatever, cb) {
Group.findById(group._id, function (err, g) {
expect(g.members.length).to.be(4);
cb();
});
}
], function (err, results) {
expect(err).to.be.ok();
done();
});
});
it('Starts a quest', function (done) {
async.waterfall([
function (cb) {
request.post(baseURL + "/groups/" + group._id + "/questAccept?key=evilsanta")
.end(function (res) {
expectCode(res, 401);
User.findByIdAndUpdate(_id, {$set: {'items.quests.evilsanta':1}}, cb);
});
},
function (_user,cb) {
request.post(baseURL + "/groups/" + group._id + "/questAccept?key=evilsanta")
.end(function (res) {
expectCode(res, 200);
Group.findById(group._id, cb);
});
},
function (_group,cb) {
group = _group; //refresh local group
expect(group.quest.key).to.be('evilsanta');
async.series(_.reduce(party, function (m,v,i) {
m.push(function (cb2) {
request.post(baseURL + "/groups/" + group._id + "/questAccept")
.set('X-API-User', party[i]._id)
.set('X-API-Key', party[i].apiToken)
.end(function () { cb2(); });
});
return m;
}, []), cb);
}], done);
});
it("Doesn't include people who aren't participating");
});
});
});
});

View file

@ -1,7 +1,8 @@
--colors
--reporter spec
--timeout 4000
--check-leaks
--growl
--debug
--compilers coffee:coffee-script
--globals io
--globals io

View file

@ -16,5 +16,17 @@ NODE_DB_URI="$TEST_DB_URI" PORT=$TEST_SERVER_PORT node ./src/server.js > /dev/nu
NODE_PID=$!
trap "kill $NODE_PID && curl http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer" EXIT
sleep 3 # Wait for Selenium
# Wait for selenium
MAX_WAIT=30
WAITED=0
until nc -z localhost 4444; do
if [ $WAITED -ge $MAX_WAIT ]; then
echo "Waited $MAX_WAIT seconds, but Selenium never responded" >&2
kill $NODE_PID
exit 1
fi
sleep 1
let 'WAITED+=1'
done
mocha && grunt karma:continuous && ./node_modules/protractor/bin/protractor protractor.conf.js

View file

@ -2,7 +2,7 @@
describe('Auth Controller', function() {
beforeEach(module('habitrpg'));
beforeEach(module('habitrpgStatic'));
describe('AuthCtrl', function(){
var scope, ctrl, user, $httpBackend, $window;
@ -35,4 +35,4 @@ describe('Auth Controller', function() {
});
});
});
});

View file

@ -4,9 +4,9 @@ describe('Filters Controller', function() {
var scope, user;
beforeEach(module('habitrpg'));
beforeEach(inject(function($rootScope, $controller) {
beforeEach(inject(function($rootScope, $controller, Shared) {
user = {filters: {}};
window.habitrpgShared.wrap(user);
Shared.wrap(user);
scope = $rootScope.$new();
$controller('FiltersCtrl', {$scope: scope, User: {user: user}});
}));
@ -18,11 +18,11 @@ describe('Filters Controller', function() {
expect(user.tags[0]).to.have.property('id');
});
it('toggles tag filtering', function(){
var tag = {id: window.habitrpgShared.uuid(), name: 'myTag'};
it('toggles tag filtering', inject(function(Shared){
var tag = {id: Shared.uuid(), name: 'myTag'};
scope.toggleFilter(tag);
expect(user.filters[tag.id]).to.eql(true);
scope.toggleFilter(tag);
expect(user.filters[tag.id]).to.eql(false);
})
});
}))
});

View file

@ -4,20 +4,19 @@ describe('Inventory Controller', function() {
var scope, ctrl, user, $rootScope;
beforeEach(module('habitrpg'));
beforeEach(inject(function($rootScope, $controller){
beforeEach(inject(function($rootScope, $controller, Shared){
user = {
balance: 4,
stats: {gp: 0},
items: {eggs: {Cactus: 1}, hatchingPotions: {Base: 1}, food: {Meat: 1}, pets: {}},
};
window.habitrpgShared.wrap(user);
Shared.wrap(user);
var mockWindow = {
confirm: function(msg){
return true;
}
};
scope = $rootScope.$new();
$rootScope.Content = window.habitrpgShared.content;
ctrl = $controller('InventoryCtrl', {$scope: scope, User: {user: user}, $window: mockWindow});
}));
@ -74,9 +73,9 @@ describe('Inventory Controller', function() {
expect(user.items.currentPet).to.eql('Cactus-Base');
});
it('purchases an egg', function(){
scope.purchase('eggs', window.habitrpgShared.content.eggs['Wolf']);
it('purchases an egg', inject(function(Content){
scope.purchase('eggs', Content.eggs['Wolf']);
expect(user.balance).to.eql(3.25);
expect(user.items.eggs).to.eql({Cactus: 1, Wolf: 1})
});
}));
});

View file

@ -6,16 +6,10 @@ describe('memberServices', function() {
beforeEach(module('memberServices'));
beforeEach(module('habitrpg'));
beforeEach(function(){
inject(function(_$httpBackend_, $rootScope){
$httpBackend = _$httpBackend_;
$rootScope.Shared = window.habitrpgShared;
});
// $rootScope.Shared is set, so now we can inject Members
inject(function(Members){
members = Members;
});
});
beforeEach(inject(function (_$httpBackend_, Members) {
$httpBackend = _$httpBackend_;
members = Members;
}));
it('has no members at the beginning', function() {
expect(members.members).to.be.an('object');
@ -43,4 +37,4 @@ describe('memberServices', function() {
expect(members.members).to.have.property(uid);
});
});
});

View file

@ -8,8 +8,7 @@ describe('userServices', function() {
beforeEach(function(){
module(function($provide){
var habitrpgShared = {helpers: {newUser: sinon.spy()}};
$window = {href: '', alert: sinon.spy(), location: {search: '', pathname: ''}, habitrpgShared: habitrpgShared};
$window = {href: '', alert: sinon.spy(), location: {search: '', pathname: ''}};
$provide.value('$window', $window);
});
@ -48,4 +47,4 @@ describe('userServices', function() {
//TODO where does that null comes from?
expect(user.settings.sync.queue).to.eql([null, {}]);
});
});
});

View file

@ -7,7 +7,7 @@ block title
title=env.t('titleFront')
block content
div(ng-app='habitrpg')
div(ng-app='habitrpgStatic')
#wrap(ng-controller='AuthCtrl')
//-include ./header
.jumbotron.masthead