Merge branch 'TheHollidayInn-markdown-for-spells' into develop

This commit is contained in:
Blade Barringer 2015-06-02 07:58:29 -05:00
commit 24c299783a
5 changed files with 400 additions and 105 deletions

View file

@ -12,6 +12,7 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
'website/public/bower_components/jquery/dist/jquery.js',
'website/public/bower_components/pnotify/jquery.pnotify.js',
'website/public/bower_components/angular/angular.js',
'website/public/bower_components/angular-loading-bar/build/loading-bar.min.js',
'website/public/bower_components/angular-resource/angular-resource.min.js',

View file

@ -1,9 +1,14 @@
'use strict';
//TODO mock bootstrapGrowl, add remaining tests
describe('notificationServices', function() {
var notification;
before(function(){
sinon.stub($, 'pnotify', function(){
return { click: function(){}}
});
});
beforeEach(function() {
module(function($provide){
$provide.value('User', {});
@ -14,12 +19,190 @@ describe('notificationServices', function() {
});
});
afterEach(function() {
$.pnotify.reset();
});
it('notifies coins amount', function() {
var SILVER_COIN = "<span class='notification-icon shop_silver'></span>";
var GOLD_COIN = "<span class='notification-icon shop_gold'></span>";
expect(notification.coins(0)).to.not.exist;
expect(notification.coins(0.01)).to.eql("1 " + SILVER_COIN);
expect(notification.coins(0.1)).to.eql("10 " + SILVER_COIN);
expect(notification.coins(1)).to.eql("1 " + GOLD_COIN);
expect(notification.coins(12.34)).to.eql("12 " + GOLD_COIN +" 33 " + SILVER_COIN);
});
it('sends crit notification', function() {
notification.crit(5);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('crit');
expect(arg.text).to.eql('Critical Hit! Bonus: 5%');
expect(arg.icon).to.eql('glyphicon glyphicon-certificate');
});
it('sends drop notification for unspecified item', function() {
notification.drop('msg');
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('drop');
expect(arg.text).to.eql('msg');
expect(arg.icon).to.eql(false);
});
it('sends drop notification for Egg', function() {
var item = { type: 'Egg', key: 'wolf' };
notification.drop('msg', item);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('drop');
expect(arg.text).to.eql('msg');
expect(arg.icon).to.eql('Pet_Egg_wolf');
});
it('sends drop notification for Hatching Potion', function() {
var item = { type: 'HatchingPotion', key: 'red' };
notification.drop('msg', item);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('drop');
expect(arg.text).to.eql('msg');
expect(arg.icon).to.eql('Pet_HatchingPotion_red');
});
it('sends drop notification for Food', function() {
var item = { type: 'Food', key: 'meat' };
notification.drop('msg', item);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('drop');
expect(arg.text).to.eql('msg');
expect(arg.icon).to.eql('Pet_Food_meat');
});
it('does not send exp notification if val < -50', function() {
notification.exp(-51);
expect($.pnotify).to.not.have.been.called;
});
it('send exp notification if val >= -50', function() {
notification.exp(50);
notification.exp(0);
notification.exp(-50);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledThrice;
expect(arg.type).to.eql('xp');
expect(arg.text).to.eql('+ 50 XP');
expect(arg.icon).to.eql('glyphicon glyphicon-star');
});
it('send exp notification with rounded value', function() {
notification.exp(50.23333);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('xp');
expect(arg.text).to.eql('+ 50.2 XP');
expect(arg.icon).to.eql('glyphicon glyphicon-star');
});
it('send error notification', function() {
notification.error('there was an error');
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('danger');
expect(arg.text).to.eql('there was an error');
expect(arg.icon).to.eql('glyphicon glyphicon-exclamation-sign');
});
it('send gp gained notification', function() {
notification.gp(50, 4);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('gp');
expect(arg.text).to.eql('+ 46 <span class=\'notification-icon shop_gold\'></span>');
expect(arg.icon).to.eql(false);
});
it('send hp notification', function() {
notification.hp(10);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('hp');
expect(arg.text).to.eql('+ 10 HP');
expect(arg.icon).to.eql('glyphicon glyphicon-heart');
});
it('send level up notification', function() {
notification.lvl(10);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('lvl');
expect(arg.text).to.eql('Level Up!');
expect(arg.icon).to.eql('glyphicon glyphicon-chevron-up');
});
it('send markdown parsed notification', function() {
notification.markdown(":smile: - task name");
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('info');
expect(arg.text).to.eql('<p><span class="emoji" style="background-image:url(common/img/emoji/unicode/1f604.png)">:smile:</span> - task name</p>\n');
expect(arg.icon).to.eql(false);
});
it('does not send markdown notification if no text is given', function() {
notification.markdown();
expect($.pnotify).to.not.have.been.called;
});
it('send mp notification', function() {
notification.mp(10);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('mp');
expect(arg.text).to.eql('+ 10 MP');
expect(arg.icon).to.eql('glyphicon glyphicon-fire');
});
it('send streak notification', function() {
notification.streak(10);
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('streak');
expect(arg.text).to.eql('Streak Achievements: 10');
expect(arg.icon).to.eql('glyphicon glyphicon-repeat');
});
it('send text notification', function() {
notification.text('task name');
var arg = $.pnotify.args[0][0]
expect($.pnotify).to.have.been.calledOnce;
expect(arg.type).to.eql('info');
expect(arg.text).to.eql('task name');
expect(arg.icon).to.eql(false);
});
it('does not send text notification if no text is given', function() {
notification.text();
expect($.pnotify).to.not.have.been.called;
});
});

View file

@ -1,38 +1,112 @@
'use strict';
// @TODO: Something here is calling a full page reload
describe('Root Controller', function() {
var scope, user, ctrl;
var scope, rootscope, user, User, notification, ctrl, $httpBackend;
beforeEach(function () {
module(function($provide) {
$provide.value('User', {});
});
inject(function($rootScope, $controller) {
inject(function($rootScope, $controller, _$httpBackend_, Notification) {
scope = $rootScope.$new();
scope.loginUsername = 'user'
scope.loginPassword = 'pass'
user = specHelper.newUser();
scope.loginUsername = 'user';
scope.loginPassword = 'pass';
ctrl = $controller('RootCtrl', {$scope: scope, User: {user: user}});
rootscope = $rootScope;
$httpBackend = _$httpBackend_;
notification = Notification;
sinon.stub(notification, 'text')
user = specHelper.newUser();
User = {user: user};
User.save = sinon.spy();
User.sync = sinon.spy();
ctrl = $controller('RootCtrl', {$scope: scope, User: User});
});
});
it('shows contributor level text', function(){
expect(scope.contribText()).to.eql(undefined);
expect(scope.contribText(null, {npc: 'NPC'})).to.eql('NPC');
expect(scope.contribText({level: 0, text: 'Blacksmith'})).to.eql(undefined);
expect(scope.contribText({level: 1, text: 'Blacksmith'})).to.eql('Friend Blacksmith');
expect(scope.contribText({level: 2, text: 'Blacksmith'})).to.eql('Friend Blacksmith');
expect(scope.contribText({level: 3, text: 'Blacksmith'})).to.eql('Elite Blacksmith');
expect(scope.contribText({level: 4, text: 'Blacksmith'})).to.eql('Elite Blacksmith');
expect(scope.contribText({level: 5, text: 'Blacksmith'})).to.eql('Champion Blacksmith');
expect(scope.contribText({level: 6, text: 'Blacksmith'})).to.eql('Champion Blacksmith');
expect(scope.contribText({level: 7, text: 'Blacksmith'})).to.eql('Legendary Blacksmith');
expect(scope.contribText({level: 8, text: 'Blacksmith'})).to.eql('Guardian Blacksmith');
expect(scope.contribText({level: 9, text: 'Blacksmith'})).to.eql('Heroic Blacksmith');
expect(scope.contribText({level: 9, text: 'Blacksmith'}, {npc: 'NPC'})).to.eql('NPC');
afterEach(function() {
notification.text.reset();
User.save.reset();
User.sync.reset();
});
describe('contribText', function(){
it('shows contributor level text', function(){
expect(scope.contribText()).to.eql(undefined);
expect(scope.contribText(null, {npc: 'NPC'})).to.eql('NPC');
expect(scope.contribText({level: 0, text: 'Blacksmith'})).to.eql(undefined);
expect(scope.contribText({level: 1, text: 'Blacksmith'})).to.eql('Friend Blacksmith');
expect(scope.contribText({level: 2, text: 'Blacksmith'})).to.eql('Friend Blacksmith');
expect(scope.contribText({level: 3, text: 'Blacksmith'})).to.eql('Elite Blacksmith');
expect(scope.contribText({level: 4, text: 'Blacksmith'})).to.eql('Elite Blacksmith');
expect(scope.contribText({level: 5, text: 'Blacksmith'})).to.eql('Champion Blacksmith');
expect(scope.contribText({level: 6, text: 'Blacksmith'})).to.eql('Champion Blacksmith');
expect(scope.contribText({level: 7, text: 'Blacksmith'})).to.eql('Legendary Blacksmith');
expect(scope.contribText({level: 8, text: 'Blacksmith'})).to.eql('Guardian Blacksmith');
expect(scope.contribText({level: 9, text: 'Blacksmith'})).to.eql('Heroic Blacksmith');
expect(scope.contribText({level: 9, text: 'Blacksmith'}, {npc: 'NPC'})).to.eql('NPC');
});
});
describe('castEnd', function(){
var task_target, type;
beforeEach(function(){
task_target = { id: 'task-id' };
type = 'task';
scope.spell = {
target: 'task',
key: 'fireball',
mana: 10,
text: env.t('spellWizardFireballText'),
cast: function(){}
};
rootscope.applyingAction = true;
});
context('fails', function(){
it('exits early if there is no applying action', function(){
rootscope.applyingAction = null;
expect(scope.castEnd(task_target, type)).to.be.eql('No applying action');
});
it('sends notification if target is invalid', function(){
scope.spell.target = 'not_the_same_target';
scope.castEnd(task_target, type)
notification.text.should.have.been.calledWith(window.env.t('invalidTarget'));
});
});
context('succeeds', function(){
it('sets scope.spell and rootScope.applyingAction to falsy values', function(){
scope.castEnd(task_target, type)
expect(rootscope.applyingAction).to.eql(false);
expect(scope.spell).to.eql(null);
});
it('calls $scope.spell.cast', function(){
// Kind of a hack, would prefer to use sinon.spy,
// but scope.spell gets turned to null in scope.castEnd
var spellWasCast = false;
scope.spell.cast = function(){ spellWasCast = true };
scope.castEnd(task_target, type)
expect(spellWasCast).to.eql(true);
});
it('calls cast endpoint');
it('sends notification that spell was cast');
});
});
});

View file

@ -259,7 +259,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
}
$scope.castEnd = function(target, type, $event){
if (!$rootScope.applyingAction) return;
if (!$rootScope.applyingAction) return 'No applying action';
$event && ($event.stopPropagation(),$event.preventDefault());
if ($scope.spell.target != type) return Notification.text(window.env.t('invalidTarget'));
$scope.spell.cast(User.user, target);
@ -271,17 +271,16 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
$rootScope.applyingAction = false;
$http.post(ApiUrl.get() + '/api/v2/user/class/cast/'+spell.key+'?targetType='+type+'&targetId='+targetId)
.success(function(){
var msg = window.env.t('youCast', {spell: spell.text()});
switch (type) {
case 'task': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.text});break;
case 'user': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.profile.name});break;
case 'party': msg = window.env.t('youCastParty', {spell: spell.text()});break;
}
Notification.text(msg);
User.sync();
});
.success(function(){
var msg = window.env.t('youCast', {spell: spell.text()});
switch (type) {
case 'task': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.text});break;
case 'user': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.profile.name});break;
case 'party': msg = window.env.t('youCastParty', {spell: spell.text()});break;
}
Notification.markdown(msg);
User.sync();
});
}
$rootScope.castCancel = function(){

View file

@ -1,24 +1,27 @@
'use strict'
/**
Set up "+1 Exp", "Level Up", etc notifications
*/
angular.module("habitrpg").factory("Notification",
[function() {
var stack_topright = {"dir1": "down", "dir2": "left", "spacing1": 15, "spacing2": 15, "firstpos1": 60};
function notify(html, type, icon) {
var notice = $.pnotify({
type: type || 'warning', //('info', 'text', 'warning', 'success', 'gp', 'xp', 'hp', 'lvl', 'death', 'mp', 'crit')
text: html,
opacity: 1,
addclass: 'alert-' + type,
delay: 7000,
hide: (type == 'error') ? false : true,
mouse_reset: false,
width: "250px",
stack: stack_topright,
icon: icon || false
}).click(function() { notice.pnotify_remove() });
['$filter', function($filter) {
var notificationService = {
coins: coins,
crit: crit,
drop: drop,
exp: exp,
error: error,
gp: gp,
hp: hp,
lvl: lvl,
markdown: markdown,
mp: mp,
streak: streak,
text: text
};
return notificationService;
/**
Show "+ 5 {gold_coin} 3 {silver_coin}"
*/
@ -34,67 +37,102 @@ angular.module("habitrpg").factory("Notification",
} else if (silver > 0) {
return "" + silver + " <span class='notification-icon shop_silver'></span>";
}
};
}
var sign = function(number){
function crit(val) {
_notify(window.env.t('critBonus') + Math.round(val) + "%", 'crit', 'glyphicon glyphicon-certificate');
}
function drop(val, item) {
var dropClass = "";
if ( item !== undefined ) {
switch ( item.type ) {
case "Egg":
dropClass = 'Pet_Egg_' + item.key;
break;
case "HatchingPotion":
dropClass = 'Pet_HatchingPotion_' + item.key;
break;
case "Food":
dropClass = 'Pet_Food_' + item.key;
break;
default:
dropClass = 'glyphicon glyphicon-gift';
}
}
_notify(val, 'drop', dropClass);
}
function exp(val) {
if (val < -50) return; // don't show when they level up (resetting their exp)
_notify(_sign(val) + " " + _round(val) + " " + window.env.t('xp'), 'xp', 'glyphicon glyphicon-star');
}
function error(error){
_notify(error, "danger", 'glyphicon glyphicon-exclamation-sign');
}
function gp(val, bonus) {
_notify(_sign(val) + " " + coins(val - bonus), 'gp');
}
function hp(val) {
// don't show notifications if user dead
_notify(_sign(val) + " " + _round(val) + " " + window.env.t('hp'), 'hp', 'glyphicon glyphicon-heart');
}
function lvl(){
_notify(window.env.t('levelUp'), 'lvl', 'glyphicon glyphicon-chevron-up');
}
function markdown(val){
if (val) {
var parsed_markdown = $filter("markdown")(val);
_notify(parsed_markdown, 'info');
}
}
function mp(val) {
_notify(_sign(val) + " " + _round(val) + " " + window.env.t('mp'), 'mp', 'glyphicon glyphicon-fire');
}
function streak(val) {
_notify(window.env.t('streakName') + ': ' + val, 'streak', 'glyphicon glyphicon-repeat');
}
function text(val){
if (val) {
_notify(val, 'info');
}
}
//--------------------------------------------------
// Private Methods
//--------------------------------------------------
function _sign(number){
return number?number<0?'-':'+':'+';
}
var round = function(number){
function _round(number){
return Math.abs(number.toFixed(1));
}
return {
coins: coins,
hp: function(val) {
// don't show notifications if user dead
notify(sign(val) + " " + round(val) + " " + window.env.t('hp'), 'hp', 'glyphicon glyphicon-heart');
},
exp: function(val) {
if (val < -50) return; // don't show when they level up (resetting their exp)
notify(sign(val) + " " + round(val) + " " + window.env.t('xp'), 'xp', 'glyphicon glyphicon-star');
},
gp: function(val, bonus) {
notify(sign(val) + " " + coins(val - bonus), 'gp');
},
text: function(val){
if (val) {
notify(val, 'info');
}
},
lvl: function(){
notify(window.env.t('levelUp'), 'lvl', 'glyphicon glyphicon-chevron-up');
},
error: function(error){
notify(error, "danger", 'glyphicon glyphicon-exclamation-sign');
},
mp: function(val) {
notify(sign(val) + " " + round(val) + " " + window.env.t('mp'), 'mp', 'glyphicon glyphicon-fire');
},
crit: function(val) {
notify(window.env.t('critBonus') + Math.round(val) + "%", 'crit', 'glyphicon glyphicon-certificate');
},
streak: function(val) {
notify(window.env.t('streakName') + ': ' + val, 'streak', 'glyphicon glyphicon-repeat');
},
drop: function(val, item) {
var dropClass = "";
if ( item !== undefined ) {
switch ( item.type ) {
case "Egg":
dropClass = 'Pet_Egg_' + item.key;
break;
case "HatchingPotion":
dropClass = 'Pet_HatchingPotion_' + item.key;
break;
case "Food":
dropClass = 'Pet_Food_' + item.key;
break;
default:
dropClass = 'glyphicon glyphicon-gift';
}
}
notify(val, 'drop', dropClass);
}
};
// Used to stack notifications, must be outside of _notify
var stack_topright = {"dir1": "down", "dir2": "left", "spacing1": 15, "spacing2": 15, "firstpos1": 60};
function _notify(html, type, icon) {
var notice = $.pnotify({
type: type || 'warning', //('info', 'text', 'warning', 'success', 'gp', 'xp', 'hp', 'lvl', 'death', 'mp', 'crit')
text: html,
opacity: 1,
addclass: 'alert-' + type,
delay: 7000,
hide: (type == 'error') ? false : true,
mouse_reset: false,
width: "250px",
stack: stack_topright,
icon: icon || false
}).click(function() { notice.pnotify_remove() });
}
}]);