diff --git a/package.json b/package.json index f0b0936308..4857c0dba4 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "express-csv": "~0.6.0", "firebase": "^2.2.9", "firebase-token-generator": "^2.0.0", + "glob": "^4.3.5", "grunt": "~0.4.1", "grunt-browserify": "^3.3.0", "grunt-cli": "~0.1.9", @@ -32,7 +33,11 @@ "grunt-hashres": "~0.4.1", "grunt-karma": "~0.6.2", "gulp": "^3.9.0", + "gulp-clean": "^0.3.1", "gulp-grunt": "^0.5.2", + "gulp-imagemin": "^2.3.0", + "gulp-nodemon": "^2.0.4", + "gulp.spritesmith": "^4.1.0", "icalendar": "lefnire/node-icalendar#e06da0e55901f0ba940dfadc42c158ed0b1fead9", "image-size": "~0.3.2", "in-app-purchase": "^0.2.0", @@ -41,6 +46,7 @@ "lodash": "~2.4.1", "loggly": "~1.0.8", "marked": "^0.3.5", + "merge-stream": "^1.0.0", "method-override": "~2.2.0", "moment": "~2.8.3", "mongoose": "~3.8.23", @@ -62,6 +68,7 @@ "request": "~2.44.0", "s3-upload-stream": "^1.0.6", "stripe": "*", + "superagent": "~0.15.7", "swagger-node-express": "lefnire/swagger-node-express#habitrpg", "universal-analytics": "~0.3.2", "validator": "~3.19.0", @@ -93,11 +100,6 @@ "deep-diff": "~0.1.4", "event-stream": "^3.2.2", "expect.js": "~0.2.0", - "glob": "^4.3.5", - "gulp-clean": "^0.3.1", - "gulp-imagemin": "^2.3.0", - "gulp-nodemon": "^2.0.4", - "gulp.spritesmith": "^4.1.0", "istanbul": "^0.3.14", "karma": "~0.10.2", "karma-chai-plugins": "~0.1.0", @@ -114,7 +116,6 @@ "karma-requirejs": "~0.2.0", "karma-script-launcher": "~0.1.0", "lcov-result-merger": "^1.0.2", - "merge-stream": "^1.0.0", "mocha": "~1.12.1", "mongoskin": "~0.6.1", "protractor": "~2.0.0", @@ -122,7 +123,6 @@ "shelljs": "^0.4.0", "sinon": "1.15.4", "sinon-chai": "^2.7.0", - "superagent": "~0.15.7", "superagent-defaults": "~0.1.5", "vinyl-source-stream": "^1.0.0", "vinyl-transform": "^1.0.0" diff --git a/test/server_side/controllers/groups.test.js b/test/server_side/controllers/groups.test.js index 5146fb8f9b..ad9196e13f 100644 --- a/test/server_side/controllers/groups.test.js +++ b/test/server_side/controllers/groups.test.js @@ -151,6 +151,7 @@ describe('Groups Controller', function() { 'another-user' ], save: sinon.stub().yields(), + leave: sinon.stub().yields(), markModified: sinon.spy() }; @@ -177,54 +178,67 @@ describe('Groups Controller', function() { context('party', function() { beforeEach(function() { group.type = 'party'; + }); + + it('prevents user from leaving party if quest is active and part of the active members list', function() { group.quest = { - leader : 'another-user', active: true, members: { - 'user-id': true, - 'another-user': true - }, - key : 'vice1', - progress : { - hp : 364, - collect : {} + another_user: true, + yet_another_user: null, + 'user-id': true } }; - sinon.spy(Group, 'update'); - }); - - afterEach(function() { - Group.update.restore(); - }); - - it('prevents user from leaving party if quest is active', function() { - user.party = { - quest : { - key : 'vice1', - progress : { - up : 50, - down : 0, - collect : {} - }, - completed : null, - RSVPNeeded : false - } - } - groupsController.leave(req, res); - expect(Group.update).to.not.be.called; + expect(group.leave).to.not.be.called; expect(res.json).to.be.calledOnce; expect(res.json).to.be.calledWith(403, 'You cannot leave party during an active quest. Please leave the quest first'); }); - it('leaves party if quest is not active', function() { - user.party = { quest: { key: null } }; + it('prevents quest leader from leaving a party if they have started a quest', function() { + group.quest = { + active: false, + leader: 'user-id' + }; groupsController.leave(req, res); - expect(Group.update).to.be.calledOnce; + expect(group.leave).to.not.be.called; + expect(res.json).to.be.calledOnce; + expect(res.json).to.be.calledWith(403, 'You cannot leave your party when you have started a quest. Abort the quest first.'); + }); + + it('leaves party if quest is not active', function() { + group.quest = { + active: false, + members: { + another_user: true, + yet_another_user: null, + 'user-id': null + } + }; + + groupsController.leave(req, res); + + expect(group.leave).to.be.calledOnce; + expect(res.json).to.not.be.called; + }); + + it('leaves party if quest is active, but user is not part of quest', function() { + group.quest = { + active: true, + members: { + another_user: true, + yet_another_user: null, + 'user-id': null + } + }; + + groupsController.leave(req, res); + + expect(group.leave).to.be.calledOnce; expect(res.json).to.not.be.called; }); }); @@ -379,4 +393,105 @@ describe('Groups Controller', function() { }); }); }); + + describe('#removeMember', function() { + var req, res, group, user; + + beforeEach(function() { + user = { _id: 'user-id' }; + group = { + _id: 'group-id', + leader: 'user-id', + members: ['user-id', 'member-to-boot', 'another-user'] + } + res = { + locals: { + user: user, + group: group + }, + send: sinon.stub() + }; + req = { + query: { + uuid: 'member-to-boot' + } + }; + + sinon.stub(Group, 'update'); + sinon.stub(User, 'update'); + sinon.stub(User, 'findById'); + }); + + afterEach(function() { + Group.update.restore(); + User.update.restore(); + User.findById.restore(); + }); + + context('quest behavior', function() { + it('removes quest from party if booted member was quest leader', function() { + group.quest = { + leader: 'member-to-boot', + active: true, + members: { + 'user-id': true, + 'leader-id': true, + 'member-to-boot': true + }, + key: 'whale' + } + + groupsController.removeMember(req, res); + + expect(Group.update).to.be.calledOnce; + expect(Group.update).to.be.calledWith( + { _id: 'group-id'}, + { + '$inc': { memberCount: -1 }, + '$pull': { members: 'member-to-boot' }, + '$set': { quest: {key: null, leader: null} } + } + ); + }); + + it('returns quest scroll to booted member if booted member was leader of quest', function() { + Group.update.yields(); + var bootedMember = { + _id: 'member-to-boot', + apiToken: 'api', + preferences: { + emailNotifications: { + kickedGroup: false + } + } + }; + User.findById.yields(null, bootedMember); + User.update.returns({ + exec: sinon.stub() + }); + + group.quest = { + leader: 'member-to-boot', + active: true, + members: { + 'user-id': true, + 'leader-id': true, + 'member-to-boot': true + }, + key: 'whale' + } + + groupsController.removeMember(req, res); + + expect(User.update).to.be.calledOnce; + expect(User.update).to.be.calledWith( + { _id: 'member-to-boot', apiToken: 'api' }, + { + '$unset': { 'newMessages.group-id': ''}, + '$inc': { 'items.quests.whale': 1 } + } + ); + }); + }); + }); }); diff --git a/test/spec/controllers/inventoryCtrlSpec.js b/test/spec/controllers/inventoryCtrlSpec.js index acdcc86840..f1c5bc3b73 100644 --- a/test/spec/controllers/inventoryCtrlSpec.js +++ b/test/spec/controllers/inventoryCtrlSpec.js @@ -157,6 +157,72 @@ describe('Inventory Controller', function() { }); }); + describe('#buyQuest', function() { + var quests, questObject; + + beforeEach(inject(function(Quests) { + quests = Quests; + questObject = { key: 'whale' }; + + sandbox.stub(quests, 'buyQuest').returns({ then: function(res) { res(questObject); } }); + })); + + it('calls Quests.buyQuest', function() { + scope.buyQuest('foo'); + + expect(quests.buyQuest).to.be.calledOnce; + expect(quests.buyQuest).to.be.calledWith('foo'); + }); + + it('sets selectedQuest to resolved quest object', function() { + scope.buyQuest('whale'); + + expect(rootScope.selectedQuest).to.eql(questObject); + }); + + it('opens buyQuest modal', function() { + sandbox.spy(rootScope, 'openModal'); + + scope.buyQuest('whale'); + + expect(rootScope.openModal).to.be.calledOnce; + expect(rootScope.openModal).to.be.calledWith('buyQuest', {controller: 'InventoryCtrl'}); + }); + }); + + describe('#showQuest', function() { + var quests, questObject; + + beforeEach(inject(function(Quests) { + quests = Quests; + questObject = { key: 'whale' }; + + sandbox.stub(quests, 'showQuest').returns({ then: function(res) { res(questObject); } }); + })); + + it('calls Quests.showQuest', function() { + scope.showQuest('foo'); + + expect(quests.showQuest).to.be.calledOnce; + expect(quests.showQuest).to.be.calledWith('foo'); + }); + + it('sets selectedQuest to resolved quest object', function() { + scope.showQuest('whale'); + + expect(rootScope.selectedQuest).to.eql(questObject); + }); + + it('opens showQuest modal', function() { + sandbox.spy(rootScope, 'openModal'); + + scope.showQuest('whale'); + + expect(rootScope.openModal).to.be.calledOnce; + expect(rootScope.openModal).to.be.calledWith('showQuest', {controller: 'InventoryCtrl'}); + }); + }); + describe('#hasAllTimeTravelerItems', function() { it('returns false if there are items left in the time traveler store', function() { expect(scope.hasAllTimeTravelerItems()).to.eql(false); diff --git a/test/spec/controllers/partyCtrlSpec.js b/test/spec/controllers/partyCtrlSpec.js index 30d7925b01..092072988d 100644 --- a/test/spec/controllers/partyCtrlSpec.js +++ b/test/spec/controllers/partyCtrlSpec.js @@ -1,7 +1,7 @@ 'use strict'; describe("Party Controller", function() { - var scope, ctrl, user, User, groups, rootScope, $controller; + var scope, ctrl, user, User, questsService, groups, rootScope, $controller; beforeEach(function() { user = specHelper.newUser(), @@ -15,7 +15,7 @@ describe("Party Controller", function() { $provide.value('User', User); }); - inject(function(_$rootScope_, _$controller_, Groups){ + inject(function(_$rootScope_, _$controller_, Groups, Quests){ rootScope = _$rootScope_; @@ -24,6 +24,7 @@ describe("Party Controller", function() { $controller = _$controller_; groups = Groups; + questsService = Quests; // Load RootCtrl to ensure shared behaviors are loaded $controller('RootCtrl', {$scope: scope, User: User}); @@ -33,129 +34,180 @@ describe("Party Controller", function() { }); describe('questAccept', function() { - it('calls Groups.questAccept', function() { - var party = {}; - var groupSpy = sandbox.stub(groups, "questAccept", function(){return true;}); - scope.questAccept(party); - groupSpy.should.have.been.calledOnce; + beforeEach(function() { + scope.group = { + quest: { members: { 'user-id': true } } + }; + sandbox.stub(questsService, 'sendAction').returns({ + then: sandbox.stub().yields({members: {another: true}}) + }); + }); + + it('calls Quests.sendAction', function() { + scope.questAccept(); + + expect(questsService.sendAction).to.be.calledOnce; + expect(questsService.sendAction).to.be.calledWith('questAccept'); + }); + + + it('updates quest object with new participants list', function() { + scope.group.quest = { + members: { user: true, another: true } + }; + + scope.questAccept(); + + expect(scope.group.quest).to.eql({members: { another: true }}); }); }); describe('questReject', function() { - it('calls Groups.questReject', function() { - var party = {}; - var groupSpy = sandbox.stub(groups, "questReject", function(){return true;}); - scope.questReject(party); - groupSpy.should.have.been.calledOnce; + beforeEach(function() { + scope.group = { + quest: { members: { 'user-id': true } } + }; + sandbox.stub(questsService, 'sendAction').returns({ + then: sandbox.stub().yields({members: {another: true}}) + }); + }); + + it('calls Quests.sendAction', function() { + scope.questReject(); + + expect(questsService.sendAction).to.be.calledOnce; + expect(questsService.sendAction).to.be.calledWith('questReject'); + }); + + + it('updates quest object with new participants list', function() { + scope.group.quest = { + members: { user: true, another: true } + }; + + scope.questReject(); + + expect(scope.group.quest).to.eql({members: { another: true }}); }); }); describe('questCancel', function() { var party, cancelSpy, windowSpy; beforeEach(function() { - party = {}; - cancelSpy = sandbox.stub(groups, "questCancel", function(){return true;}); + sandbox.stub(questsService, 'sendAction').returns({ + then: sandbox.stub().yields({members: {another: true}}) + }); }); - afterEach(function() { - windowSpy.restore(); - cancelSpy.restore(); + it('calls Quests.sendAction when alert box is confirmed', function() { + sandbox.stub(window, "confirm").returns(true); + + scope.questCancel(); + + expect(window.confirm).to.be.calledOnce; + expect(window.confirm).to.be.calledWith(window.env.t('sureCancel')); + expect(questsService.sendAction).to.be.calledOnce; + expect(questsService.sendAction).to.be.calledWith('questCancel'); }); - it('calls Groups.questCancel when alert box is confirmed', function() { - windowSpy = sandbox.stub(window, "confirm", function(){return true}); + it('does not call Quests.sendAction when alert box is not confirmed', function() { + sandbox.stub(window, "confirm").returns(false); - scope.questCancel(party); - windowSpy.should.have.been.calledOnce; - windowSpy.should.have.been.calledWith(window.env.t('sureCancel')); - cancelSpy.should.have.been.calledOnce; - }); + scope.questCancel(); - it('does not call Groups.questCancel when alert box is not confirmed', function() { - windowSpy = sandbox.stub(window, "confirm", function(){return false}); - - scope.questCancel(party); - windowSpy.should.have.been.calledOnce; - cancelSpy.should.not.have.been.calledOnce; + expect(window.confirm).to.be.calledOnce; + expect(questsService.sendAction).to.not.be.called; }); }); describe('questAbort', function() { - var party, abortSpy, windowSpy; beforeEach(function() { - party = {}; - abortSpy = sandbox.stub(groups, "questAbort", function(){return true;}); + sandbox.stub(questsService, 'sendAction').returns({ + then: sandbox.stub().yields({members: {another: true}}) + }); }); - afterEach(function() { - windowSpy.restore(); - abortSpy.restore(); + it('calls Quests.sendAction when two alert boxes are confirmed', function() { + sandbox.stub(window, "confirm", function(){return true}); + + scope.questAbort(); + expect(window.confirm).to.be.calledTwice; + expect(window.confirm).to.be.calledWith(window.env.t('sureAbort')); + expect(window.confirm).to.be.calledWith(window.env.t('doubleSureAbort')); + + expect(questsService.sendAction).to.be.calledOnce; + expect(questsService.sendAction).to.be.calledWith('questAbort'); }); - it('calls Groups.questAbort when two alert boxes are confirmed', function() { - windowSpy = sandbox.stub(window, "confirm", function(){return true}); + it('does not call Quests.sendAction when first alert box is not confirmed', function() { + sandbox.stub(window, "confirm", function(){return false}); - scope.questAbort(party); - windowSpy.should.have.been.calledTwice; - windowSpy.should.have.been.calledWith(window.env.t('sureAbort')); - windowSpy.should.have.been.calledWith(window.env.t('doubleSureAbort')); - abortSpy.should.have.been.calledOnce; + scope.questAbort(); + + expect(window.confirm).to.be.calledOnce; + expect(window.confirm).to.be.calledWith(window.env.t('sureAbort')); + expect(window.confirm).to.not.be.calledWith(window.env.t('doubleSureAbort')); + + expect(questsService.sendAction).to.not.be.called; }); - it('does not call Groups.questAbort when first alert box is not confirmed', function() { - windowSpy = sandbox.stub(window, "confirm", function(){return false}); - - scope.questAbort(party); - windowSpy.should.have.been.calledOnce; - windowSpy.should.have.been.calledWith(window.env.t('sureAbort')); - windowSpy.should.not.have.been.calledWith(window.env.t('doubleSureAbort')); - abortSpy.should.not.have.been.calledOnce; - }); - - it('does not call Groups.questAbort when first alert box is confirmed but second one is not', function() { + it('does not call Quests.sendAction when first alert box is confirmed but second one is not', function() { // Hack to confirm first window, but not second + // Should not be necessary when we upgrade sinon var shouldReturn = false; - windowSpy = sandbox.stub(window, "confirm", function(){ + sandbox.stub(window, 'confirm', function(){ shouldReturn = !shouldReturn; return shouldReturn; }); - scope.questAbort(party); - windowSpy.should.have.been.calledTwice; - windowSpy.should.have.been.calledWith(window.env.t('sureAbort')); - windowSpy.should.have.been.calledWith(window.env.t('doubleSureAbort')); - abortSpy.should.not.have.been.calledOnce; + scope.questAbort(); + + expect(window.confirm).to.be.calledTwice; + expect(window.confirm).to.be.calledWith(window.env.t('sureAbort')); + expect(window.confirm).to.be.calledWith(window.env.t('doubleSureAbort')); + expect(questsService.sendAction).to.not.be.called; }); }); describe('#questLeave', function() { - var party, leaveSpy, windowSpy; - beforeEach(function() { - party = {}; scope.group = { quest: { members: { 'user-id': true } } }; - leaveSpy = sandbox.stub(groups, 'questLeave').returns({ - then: sandbox.stub().yields() + sandbox.stub(questsService, 'sendAction').returns({ + then: sandbox.stub().yields({members: {another: true}}) }); }); - it('calls Groups.questLeave when alert box is confirmed', function() { - windowSpy = sandbox.stub(window, "confirm").returns(true); + it('calls Quests.sendAction when alert box is confirmed', function() { + sandbox.stub(window, "confirm").returns(true); - scope.questLeave(party); - windowSpy.should.have.been.calledOnce; - windowSpy.should.have.been.calledWith(window.env.t('sureLeave')); - leaveSpy.should.have.been.calledOnce; + scope.questLeave(); + + expect(window.confirm).to.be.calledOnce; + expect(window.confirm).to.be.calledWith(window.env.t('sureLeave')); + expect(questsService.sendAction).to.be.calledOnce; + expect(questsService.sendAction).to.be.calledWith('questLeave'); }); - it('does not call Groups.questLeave when alert box is not confirmed', function() { - windowSpy = sandbox.stub(window, "confirm").returns(false); + it('does not call Quests.sendAction when alert box is not confirmed', function() { + sandbox.stub(window, "confirm").returns(false); - scope.questLeave(party); - windowSpy.should.have.been.calledOnce; - leaveSpy.should.not.have.been.calledOnce; + scope.questLeave(); + + expect(window.confirm).to.be.calledOnce; + questsService.sendAction.should.not.have.been.calledOnce; + }); + + it('updates quest object with new participants list', function() { + scope.group.quest = { + members: { user: true, another: true } + }; + sandbox.stub(window, "confirm").returns(true); + + scope.questLeave(); + + expect(scope.group.quest).to.eql({members: { another: true }}); }); }); @@ -241,4 +293,28 @@ describe("Party Controller", function() { }); }); }); + + describe('#canEditQuest', function() { + var party; + + beforeEach(function() { + party = specHelper.newGroup({ + type: 'party', + leader: {}, + quest: {} + }); + }); + + it('returns false if user is not the quest leader', function() { + party.quest.leader = 'another-user'; + + expect(scope.canEditQuest(party)).to.eql(false); + }); + + it('returns true if user is quest leader', function() { + party.quest.leader = 'unique-user-id'; + + expect(scope.canEditQuest(party)).to.eql(true); + }); + }); }); diff --git a/test/spec/services/groupServicesSpec.js b/test/spec/services/groupServicesSpec.js index ff3d12ba98..e9a9285265 100644 --- a/test/spec/services/groupServicesSpec.js +++ b/test/spec/services/groupServicesSpec.js @@ -39,67 +39,4 @@ describe('groupServices', function() { groups.myGuilds(); $httpBackend.flush(); }); - - context('quest function wrappers', function() { - var successPromise, failPromise; - - beforeEach(function() { - sandbox.spy(user, 'sync'); - sandbox.stub(console, 'log'); - - successPromise = sandbox.stub().returns({ - then: function(success, failure) { - return success(); - } - }); - - failPromise = sandbox.stub().returns({ - then: function(success, failure) { - return failure('fail'); - } - }); - }); - - var questFunctions = [ - 'questAccept', - 'questReject', - 'questCancel', - 'questAbort', - 'questLeave' - ]; - - for (var i in questFunctions) { - var questFunc = questFunctions[i]; - - describe('#' + questFunc, function() { - it('calls party.$' + questFunc, function() { - var party = { }; - party['$' + questFunc] = successPromise; - - groups[questFunc](party); - - expect(party['$' + questFunc]).to.be.calledOnce; - }); - - it('syncs user if $' + questFunc + ' succeeds', function() { - var successParty = { }; - successParty['$' + questFunc] = successPromise; - - groups[questFunc](successParty); - - user.sync.should.have.been.calledOnce; - }); - - it('does not sync user if $' + questFunc + ' fails', function() { - var failParty = { }; - failParty['$' + questFunc] = failPromise; - - groups[questFunc](failParty); - - user.sync.should.not.have.been.calledOnce; - console.log.should.have.been.calledWith('fail'); - }); - }); - } - }); }); diff --git a/test/spec/services/questServicesSpec.js b/test/spec/services/questServicesSpec.js index ef5f93e7ce..e7f8c638b2 100644 --- a/test/spec/services/questServicesSpec.js +++ b/test/spec/services/questServicesSpec.js @@ -1,7 +1,7 @@ 'use strict'; describe('Quests Service', function() { - var scope, rootScope, groupsService, quest, questsService, user, content; + var groupsService, quest, questsService, user, content, resolveSpy, rejectSpy; beforeEach(function() { user = specHelper.newUser(); @@ -13,108 +13,378 @@ describe('Quests Service', function() { quest = {lvl:20}; module(function($provide) { - $provide.value('User', {user: user}); + $provide.value('User', {sync: sinon.stub(), user: user}); }); - inject(function($rootScope, $controller, Quests, Groups, Content) { - scope = $rootScope.$new(); - rootScope = $rootScope; - $controller('RootCtrl', {$scope: scope, User: {user: user}}); + inject(function(Quests, Groups, Content) { questsService = Quests; groupsService = Groups; content = Content; }); sandbox.stub(groupsService, 'inviteOrStartParty'); - sandbox.stub(rootScope, 'openModal'); - sandbox.stub(window,'confirm',function(){return true}); + sandbox.stub(window,'confirm'); sandbox.stub(window,'alert'); + resolveSpy = sandbox.spy(); + rejectSpy = sandbox.spy(); }); - context('functions', function() { + describe('#lockQuest', function() { - describe('lock quest', function() { + it('locks quest when user does not meet level requirement', function() { + user.stats.lvl = 15; - it('locks quest when user does not meet level requirement', function() { - user.stats.lvl = 15; + expect(questsService.lockQuest(quest)).to.be.ok; + }); - expect(questsService.lockQuest(quest)).to.be.ok; - }); + it('does not lock quest if we ignore level requirement', function() { + user.stats.lvl = 15; - it('does not lock quest if we ignore level requirement', function() { - user.stats.lvl = 15; + expect(questsService.lockQuest(quest,true)).to.not.be.ok; + }); - expect(questsService.lockQuest(quest,true)).to.not.be.ok; - }); + it('does not lock quest if user meets level requirement', function() { + user.stats.lvl = 20; - it('does not lock quest if user meets level requirement', function() { - user.stats.lvl = 20; + expect(questsService.lockQuest(quest)).to.not.be.ok; + }); - expect(questsService.lockQuest(quest)).to.not.be.ok; - }); + it('locks quest if user has not completed previous quest in series', function() { + quest.previous = 'priorQuest'; + user.stats.lvl = 25; - it('locks quest if user has not completed previous quest in series', function() { - quest.previous = 'priorQuest'; - user.stats.lvl = 25; + expect(questsService.lockQuest(quest)).to.be.ok; + }); - expect(questsService.lockQuest(quest)).to.be.ok; - }); + it('does not lock quest if user has completed previous quest in series', function() { + quest.previous = 'priorQuest'; + user.stats.lvl = 25; + user.achievements.quests.priorQuest = 1; - it('does not lock quest if user has completed previous quest in series', function() { - quest.previous = 'priorQuest'; - user.stats.lvl = 25; - user.achievements.quests.priorQuest = 1; + expect(questsService.lockQuest(quest)).to.not.be.ok; + }); + }); - expect(questsService.lockQuest(quest)).to.not.be.ok; + describe('#buyQuest', function() { + var scope; + + beforeEach(inject(function($rootScope) { + scope = $rootScope.$new(); + })); + + it('returns a promise', function() { + var promise = questsService.buyQuest('whale'); + expect(promise).to.respondTo('then'); + }); + + context('Quest key does not exist', function() { + it('rejects with message that quest is not found', function(done) { + questsService.buyQuest('foo') + .then(resolveSpy, function(rej) { + expect(rej).to.eql('No quest with that key found'); + expect(resolveSpy).to.not.be.called; + done(); + }); + + scope.$apply(); }); }); - describe('buy quest', function() { - + context('invite friends', function() { it('prompts user to invite friends to party for invite reward quests', function() { questsService.buyQuest('basilist'); - expect(window.confirm).to.have.been.calledOnce; - expect(groupsService.inviteOrStartParty).to.have.been.calledOnce; - expect(rootScope.openModal).to.have.been.notCalled; + expect(window.confirm).to.be.calledOnce; + expect(window.confirm).to.be.calledWith(env.t('mustInviteFriend')); }); - it('does not allow user to buy quests whose previous quests are incomplete', function() { + it('rejects promise if confirm is cancelled', function(done) { + window.confirm.returns(false); + + questsService.buyQuest('basilist') + .then(resolveSpy, function(rej) { + expect(rej).to.eql('Did not want to invite friends'); + expect(window.confirm).to.be.calledOnce; + expect(groupsService.inviteOrStartParty).to.not.be.called; + done(); + }); + + scope.$apply(); + }); + + it('rejects promise if confirm is cofirmed and calls groups service', function(done) { + window.confirm.returns(true); + + questsService.buyQuest('basilist') + .then(resolveSpy, function(rej) { + expect(rej).to.eql('Invite or start party'); + expect(window.confirm).to.be.calledOnce; + expect(groupsService.inviteOrStartParty).to.be.calledOnce; + done(); + }); + + scope.$apply(); + }); + }); + + context('quests in a series', function() { + it('does not allow user to buy subsquent quests in a series if user has no quest achievements', function(done) { user.stats.lvl = 100; + user.achievements.quests = undefined; - questsService.buyQuest('goldenknight2'); + questsService.buyQuest('goldenknight2') + .then(resolveSpy, function(res) { + expect(window.alert).to.have.been.calledOnce; + expect(res).to.eql('unlockByQuesting'); + expect(resolveSpy).to.not.be.called; + done(); + }); - expect(window.alert).to.have.been.calledOnce; - expect(rootScope.openModal).to.have.been.notCalled; + scope.$apply(); }); - it('does not allow user to buy quests beyond their level', function() { + it('does not allow user to buy quests whose previous quests are incomplete', function(done) { + user.stats.lvl = 100; + user.achievements.quests = { + 'atom1': 1 + }; + + questsService.buyQuest('goldenknight2') + .then(resolveSpy, function(res) { + expect(window.alert).to.have.been.calledOnce; + expect(resolveSpy).to.not.be.called; + done(); + }); + + scope.$apply(); + }); + }); + + context('quests with level requirement', function() { + it('does not allow user to buy quests beyond their level', function(done) { user.stats.lvl = 1; - questsService.buyQuest('vice1'); + questsService.buyQuest('vice1') + .then(resolveSpy, function(res) { + expect(window.alert).to.have.been.calledOnce; + expect(res).to.eql('mustLvlQuest'); + done(); + }); - expect(window.alert).to.have.been.calledOnce; - expect(rootScope.openModal).to.have.been.notCalled; + scope.$apply(); }); - it('opens purchase modal if Gem quest prerequisites are met', function() { - user.stats.lvl = 100; - user.achievements.quests.atom1 = 2; + it('allows user to buy quest if they meet level requirement', function(done) { + user.stats.lvl = 30; - questsService.buyQuest('atom2'); + questsService.buyQuest('vice1') + .then(function(res) { + expect(res).to.eql(content.quests.vice1); + expect(window.alert).to.not.be.called; + expect(rejectSpy).to.not.be.called; + done(); + }, rejectSpy); - expect(scope.selectedQuest).to.eql(content.quests.atom2); - expect(rootScope.openModal).to.have.been.calledOnce; - expect(rootScope.openModal).to.have.been.calledWith('buyQuest'); + scope.$apply(); }); + }); - it('opens purchase modal if quest is Gold-purchasable', function() { - questsService.buyQuest('dilatoryDistress1'); + context('gold purchasable quests', function() { + it('sends quest object', function(done) { + questsService.buyQuest('dilatoryDistress1') + .then(function(res) { + expect(res).to.eql(content.quests.dilatoryDistress1); + expect(window.alert).to.not.be.called; + expect(rejectSpy).to.not.be.called; + done(); + }, rejectSpy); - expect(scope.selectedQuest).to.eql(content.quests.dilatoryDistress1); - expect(rootScope.openModal).to.have.been.calledOnce; - expect(rootScope.openModal).to.have.been.calledWith('buyQuest'); + scope.$apply(); + }); + }); + + context('all other quests', function() { + it('sends quest object', function(done) { + questsService.buyQuest('whale') + .then(function(res) { + expect(res).to.eql(content.quests.whale); + expect(window.alert).to.not.be.called; + expect(rejectSpy).to.not.be.called; + done(); + }, rejectSpy); + + scope.$apply(); }); }); }); + + describe('#showQuest', function() { + var scope; + + beforeEach(inject(function($rootScope) { + scope = $rootScope.$new(); + })); + + it('returns a promise', function() { + var promise = questsService.showQuest('whale'); + expect(promise).to.respondTo('then'); + }); + + context('Quest key does not exist', function() { + it('rejects with message that quest is not found', function(done) { + questsService.showQuest('foo') + .then(resolveSpy, function(rej) { + expect(rej).to.eql('No quest with that key found'); + expect(resolveSpy).to.not.be.called; + done(); + }); + + scope.$apply(); + }); + }); + + context('quests in a series', function() { + it('does not allow user to buy subsquent quests in a series if user has no quest achievements', function(done) { + user.stats.lvl = 100; + user.achievements.quests = undefined; + + questsService.showQuest('goldenknight2') + .then(resolveSpy, function(res) { + expect(window.alert).to.have.been.calledOnce; + expect(res).to.eql('unlockByQuesting'); + expect(resolveSpy).to.not.be.called; + done(); + }); + + scope.$apply(); + }); + + it('does not allow user to buy quests whose previous quests are incomplete', function(done) { + user.stats.lvl = 100; + user.achievements.quests = { + 'atom1': 1 + }; + + questsService.showQuest('goldenknight2') + .then(resolveSpy, function(res) { + expect(window.alert).to.have.been.calledOnce; + expect(resolveSpy).to.not.be.called; + done(); + }); + + scope.$apply(); + }); + }); + + context('quests with level requirement', function() { + it('does not allow user to buy quests beyond their level', function(done) { + user.stats.lvl = 1; + + questsService.showQuest('vice1') + .then(resolveSpy, function(res) { + expect(window.alert).to.have.been.calledOnce; + expect(res).to.eql('mustLvlQuest'); + done(); + }); + + scope.$apply(); + }); + + it('allows user to buy quest if they meet level requirement', function(done) { + user.stats.lvl = 30; + + questsService.showQuest('vice1') + .then(function(res) { + expect(res).to.eql(content.quests.vice1); + expect(window.alert).to.not.be.called; + expect(rejectSpy).to.not.be.called; + done(); + }, rejectSpy); + + scope.$apply(); + }); + }); + + context('gold purchasable quests', function() { + it('sends quest object', function(done) { + questsService.showQuest('dilatoryDistress1') + .then(function(res) { + expect(res).to.eql(content.quests.dilatoryDistress1); + expect(window.alert).to.not.be.called; + expect(rejectSpy).to.not.be.called; + done(); + }, rejectSpy); + + scope.$apply(); + }); + }); + + context('all other quests', function() { + it('sends quest object', function(done) { + questsService.showQuest('whale') + .then(function(res) { + expect(res).to.eql(content.quests.whale); + expect(window.alert).to.not.be.called; + expect(rejectSpy).to.not.be.called; + done(); + }, rejectSpy); + + scope.$apply(); + }); + }); + }); + + describe('#initQuest', function() { + + it('returns a promise', function() { + var promise = questsService.initQuest('whale'); + expect(promise).to.respondTo('then'); + }); + + it('accepts quest'); + + it('brings user to party page'); + }); + + describe('#sendAction', function() { + var fakeBackend, scope; + + beforeEach(inject(function($httpBackend, $rootScope) { + scope = $rootScope.$new(); + fakeBackend = $httpBackend; + + fakeBackend.when('GET', 'partials/main.html').respond({}); + fakeBackend.when('GET', '/api/v2/groups/party').respond({_id: 'party-id'}); + fakeBackend.when('POST', '/api/v2/groups/party-id/questReject').respond({quest: { key: 'whale' } }); + fakeBackend.flush(); + })); + + it('returns a promise', function() { + var promise = questsService.sendAction('questReject'); + expect(promise).to.respondTo('then'); + }); + + it('calls specified quest endpoint', function(done) { + fakeBackend.expectPOST('/api/v2/groups/party-id/questReject'); + + questsService.sendAction('questReject') + .then(function(res) { + expect(res.key).to.eql('whale'); + done(); + }); + + fakeBackend.flush(); + scope.$apply(); + }); + + it('syncs User', function() { + questsService.sendAction('questReject') + .then(function(res) { + expect(User.sync).to.be.calledOnce; + done(); + }); + + scope.$apply(); + }); + }); }); diff --git a/website/public/js/controllers/inventoryCtrl.js b/website/public/js/controllers/inventoryCtrl.js index e140a3798e..102303a76e 100644 --- a/website/public/js/controllers/inventoryCtrl.js +++ b/website/public/js/controllers/inventoryCtrl.js @@ -13,11 +13,33 @@ habitrpg.controller("InventoryCtrl", // Functions from Quests service $scope.lockQuest = Quests.lockQuest; - $scope.buyQuest = Quests.buyQuest; + + $scope.buyQuest = function(questScroll) { + Quests.buyQuest(questScroll) + .then(function(quest) { + $rootScope.selectedQuest = quest; + $rootScope.openModal('buyQuest', {controller:'InventoryCtrl'}); + }); + }; + $scope.questPopover = Quests.questPopover; - $scope.showQuest = Quests.showQuest; - $scope.closeQuest = Quests.closeQuest; - $scope.questInit = Quests.questInit; + + $scope.showQuest = function(questScroll) { + Quests.showQuest(questScroll) + .then(function(quest) { + $rootScope.selectedQuest = quest; + $rootScope.openModal('showQuest', {controller:'InventoryCtrl'}); + }); + }; + + $scope.questInit = function() { + var key = $rootScope.selectedQuest.key; + + Quests.initQuest(key).then(function() { + $rootScope.selectedQuest = undefined; + $scope.$close(); + }); + }; // count egg, food, hatchingPotion stack totals var countStacks = function(items) { return _.reduce(items,function(m,v){return m+v;},0);} diff --git a/website/public/js/controllers/partyCtrl.js b/website/public/js/controllers/partyCtrl.js index b154539e75..40af2b7e61 100644 --- a/website/public/js/controllers/partyCtrl.js +++ b/website/public/js/controllers/partyCtrl.js @@ -7,7 +7,6 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' $scope.group = $rootScope.party = Groups.party(); $scope.newGroup = new Groups.Group({type:'party'}); $scope.inviteOrStartParty = Groups.inviteOrStartParty; - $scope.questInit = Quests.questInit; if ($state.is('options.social.party')) { $scope.group.$syncParty(); // Sync party automatically when navigating to party page @@ -105,30 +104,52 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' User.set({'invitations.party':{}}); } - $scope.questCancel = function(party){ + $scope.questCancel = function(){ if (!confirm(window.env.t('sureCancel'))) return; - Groups.questCancel(party); + + Quests.sendAction('questCancel') + .then(function(quest) { + $scope.group.quest = quest; + }); } - $scope.questAbort = function(party){ + $scope.questAbort = function(){ if (!confirm(window.env.t('sureAbort'))) return; if (!confirm(window.env.t('doubleSureAbort'))) return; - Groups.questAbort(party); + + Quests.sendAction('questAbort') + .then(function(quest) { + $scope.group.quest = quest; + }); } - $scope.questLeave = function(party){ + $scope.questLeave = function(){ if (!confirm(window.env.t('sureLeave'))) return; - delete $scope.group.quest.members[User.user._id]; - Groups.questLeave(party); + Quests.sendAction('questLeave') + .then(function(quest) { + $scope.group.quest = quest; + }); } - $scope.questAccept = function(party){ - Groups.questAccept(party); + $scope.questAccept = function(){ + Quests.sendAction('questAccept') + .then(function(quest) { + $scope.group.quest = quest; + }); }; - $scope.questReject = function(party){ - Groups.questReject(party); - } + $scope.questReject = function(){ + Quests.sendAction('questReject') + .then(function(quest) { + $scope.group.quest = quest; + }); + }; + + $scope.canEditQuest = function(party) { + var isQuestLeader = party.quest && party.quest.leader === User.user._id; + + return isQuestLeader; + }; } ]); diff --git a/website/public/js/services/groupServices.js b/website/public/js/services/groupServices.js index ed1c821576..001a137d63 100644 --- a/website/public/js/services/groupServices.js +++ b/website/public/js/services/groupServices.js @@ -39,21 +39,9 @@ leave: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/leave'}, invite: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/invite'}, removeMember: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/removeMember'}, - questAccept: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questAccept'}, - questReject: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questReject'}, - questCancel: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questCancel'}, - questAbort: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questAbort'}, - questLeave: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questLeave'} + startQuest: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questAccept'} }); - function _syncUser() { - User.sync(); - } - - function _logError(err) { - console.log(err); - } - function party(cb) { if (!data.party) return (data.party = Group.get({gid: 'party'}, cb)); return (cb) ? cb(party) : data.party; @@ -75,36 +63,6 @@ return data.tavern; } - function questAccept(party) { - Analytics.updateUser({'partyID':party.id,'partySize':party.memberCount}); - return party.$questAccept() - .then(_syncUser, _logError); - } - - function questReject(party) { - Analytics.updateUser({'partyID':party.id,'partySize':party.memberCount}); - return party.$questReject() - .then(_syncUser, _logError); - } - - function questCancel(party) { - Analytics.updateUser({'partyID':party.id,'partySize':party.memberCount}); - return party.$questCancel() - .then(_syncUser, _logError); - } - - function questAbort(party) { - Analytics.updateUser({'partyID':party.id,'partySize':party.memberCount}); - return party.$questAbort() - .then(_syncUser, _logError); - } - - function questLeave(party) { - Analytics.updateUser({'partyID':party.id,'partySize':party.memberCount}); - return party.$questLeave() - .then(_syncUser, _logError); - } - function inviteOrStartParty(group) { Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Invite Friends'}); if (group.type === "party" || $location.$$path === "/options/groups/party") { @@ -125,11 +83,6 @@ publicGuilds: publicGuilds, myGuilds: myGuilds, tavern: tavern, - questAccept: questAccept, - questReject: questReject, - questAbort: questAbort, - questLeave: questLeave, - questCancel: questCancel, inviteOrStartParty: inviteOrStartParty, data: data, diff --git a/website/public/js/services/questServices.js b/website/public/js/services/questServices.js index 18fcc393c6..a69ae800d8 100644 --- a/website/public/js/services/questServices.js +++ b/website/public/js/services/questServices.js @@ -6,14 +6,17 @@ .factory('Quests', questsFactory); questsFactory.$inject = [ - '$rootScope', + '$http', + '$state', + '$q', + 'ApiUrl', 'Content', 'Groups', 'User', 'Analytics' ]; - function questsFactory($rootScope,Content,Groups,User,Analytics) { + function questsFactory($http, $state, $q, ApiUrl, Content, Groups, User, Analytics) { var user = User.user; var party = Groups.party(); @@ -26,21 +29,39 @@ return (quest.previous); } - function buyQuest(quest) { - var item = Content.quests[quest]; + function _preventQuestModal(quest) { + if (!quest) { + return 'No quest with that key found'; + } - if (item.unlockCondition && item.unlockCondition.condition === 'party invite') { - if (!confirm(window.env.t('mustInviteFriend'))) return; - return Groups.inviteOrStartParty(party); + if (quest.previous && (!user.achievements.quests || (user.achievements.quests && !user.achievements.quests[quest.previous]))){ + alert(window.env.t('unlockByQuesting', {title: Content.quests[quest.previous].text()})); + return 'unlockByQuesting'; } - if (item.previous && (!User.user.achievements.quests || (User.user.achievements.quests && !User.user.achievements.quests[item.previous]))){ - return alert(window.env.t('unlockByQuesting', {title: Content.quests[item.previous].text()})); + + if (quest.lvl > user.stats.lvl) { + alert(window.env.t('mustLvlQuest', {level: quest.lvl})) + return 'mustLvlQuest'; } - if (item.lvl && item.lvl > user.stats.lvl) { - return alert(window.env.t('mustLvlQuest', {level: item.lvl})); - } - $rootScope.selectedQuest = item; - $rootScope.openModal('buyQuest', {controller:'InventoryCtrl'}); + } + + function buyQuest(quest) { + return $q(function(resolve, reject) { + var item = Content.quests[quest]; + + var preventQuestModal = _preventQuestModal(item); + if (preventQuestModal) { + return reject(preventQuestModal); + } + + if (item.unlockCondition && item.unlockCondition.condition === 'party invite') { + if (!confirm(window.env.t('mustInviteFriend'))) return reject('Did not want to invite friends'); + Groups.inviteOrStartParty(party) + return reject('Invite or start party'); + } + + resolve(item); + }); } function questPopover(quest) { @@ -71,37 +92,55 @@ } function showQuest(quest) { - var item = Content.quests[quest]; - var completedPrevious = !item.previous || (User.user.achievements.quests && User.user.achievements.quests[item.previous]); - if (!completedPrevious) - return alert(window.env.t('mustComplete', {quest: $rootScope.Content.quests[item.previous].text()})); - if (item.lvl && item.lvl > user.stats.lvl) - return alert(window.env.t('mustLevel', {level: item.lvl})); - $rootScope.selectedQuest = item; - $rootScope.openModal('showQuest', {controller:'InventoryCtrl'}); - } + return $q(function(resolve, reject) { + var item = Content.quests[quest]; - function closeQuest(){ - $rootScope.selectedQuest = undefined; - } + var preventQuestModal = _preventQuestModal(item); + if (preventQuestModal) { + return reject(preventQuestModal); + } - function questInit(){ - Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'quest','owner':true,'response':'accept','questName':$rootScope.selectedQuest.key}); - Analytics.updateUser({'partyID':party._id,'partySize':party.memberCount}); - party.$questAccept({key:$rootScope.selectedQuest.key}, function(){ - party.$get(); - $rootScope.$state.go('options.social.party'); + resolve(item); + }); + } + + function initQuest(key) { + return $q(function(resolve, reject) { + Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'quest','owner':true,'response':'accept','questName': key}); + Analytics.updateUser({'partyID':party._id,'partySize':party.memberCount}); + party.$startQuest({key:key}, function(){ + party.$syncParty(); + $state.go('options.social.party'); + resolve(); + }); + }); + } + + function sendAction(action) { + return $q(function(resolve, reject) { + + $http.post(ApiUrl.get() + '/api/v2/groups/' + party._id + '/' + action) + .then(function(response) { + User.sync(); + + Analytics.updateUser({ + partyID: party._id, + partySize: party.memberCount + }); + + var quest = response.data.quest; + resolve(quest); + });; }); - closeQuest(); } return { lockQuest: lockQuest, buyQuest: buyQuest, questPopover: questPopover, + sendAction: sendAction, showQuest: showQuest, - closeQuest: closeQuest, - questInit: questInit + initQuest: initQuest } } }()); diff --git a/website/src/controllers/groups.js b/website/src/controllers/groups.js index 64b45da0e8..573142887c 100644 --- a/website/src/controllers/groups.js +++ b/website/src/controllers/groups.js @@ -505,8 +505,14 @@ api.leave = function(req, res, next) { var user = res.locals.user; var group = res.locals.group; - if (group.type === 'party' && user.party.quest && user.party.quest.key) { - return res.json(403, 'You cannot leave party during an active quest. Please leave the quest first'); + if (group.type === 'party') { + if (group.quest && group.quest.leader === user._id) { + return res.json(403, 'You cannot leave your party when you have started a quest. Abort the quest first.'); + } + + if (group.quest && group.quest.active && group.quest.members && group.quest.members[user._id]) { + return res.json(403, 'You cannot leave party during an active quest. Please leave the quest first'); + } } // When removing the user from challenges, should we keep the tasks? @@ -709,11 +715,14 @@ api.removeMember = function(req, res, next){ if(_.contains(group.members, uuid)){ var update = {$pull:{members:uuid}}; - if(group.quest && group.quest.members){ + if (group.quest && group.quest.leader === uuid) { + update['$set'] = { + quest: { key: null, leader: null } + }; + } else if(group.quest && group.quest.members){ // remove member from quest update['$unset'] = {}; update['$unset']['quest.members.' + uuid] = ""; - // TODO: run cleanQuestProgress and return scroll to member if member was quest owner } update['$inc'] = {memberCount: -1}; Group.update({_id:group._id},update, function(err, saved){ @@ -727,6 +736,10 @@ api.removeMember = function(req, res, next){ //Mark removed users messages as seen var update = {$unset:{}}; update.$unset['newMessages.' + group._id] = ''; + if (group.quest && group.quest.active && group.quest.leader === uuid) { + update['$inc'] = {}; + update['$inc']['items.quests.' + group.quest.key] = 1; + } User.update({_id: removedUser._id, apiToken: removedUser.apiToken}, update).exec(); // Sending an empty 204 because Group.update doesn't return the group diff --git a/website/views/options/social/quests/questActive.jade b/website/views/options/social/quests/questActive.jade index 3f920689d4..855379c7c6 100644 --- a/website/views/options/social/quests/questActive.jade +++ b/website/views/options/social/quests/questActive.jade @@ -1,4 +1,4 @@ -div(ng-if='group.quest.active==true') +div(ng-if='group.quest.active===true') unless tavern tabset tab(heading=env.t('questDetails')) @@ -20,7 +20,7 @@ div(ng-if='group.quest.active==true') include ./ianQuestInfo unless tavern - button.btn.btn-sm.btn-warning(ng-if=':: (group.quest.leader && group.quest.leader==user._id && isMemberOfRunningQuest(group.quest.leader,group))', - ng-click='questAbort(party)')=env.t('abort') + button.btn.btn-sm.btn-warning(ng-if='::canEditQuest(party)', + ng-click='questAbort()')=env.t('abort') button.btn.btn-sm.btn-warning(ng-if='!(group.quest.leader && group.quest.leader === user._id) && isMemberOfRunningQuest(user._id,group)', - ng-click='questLeave(party)')=env.t('leaveQuest') + ng-click='questLeave()')=env.t('leaveQuest') diff --git a/website/views/options/social/quests/questNotActive.jade b/website/views/options/social/quests/questNotActive.jade index 6b56e78983..b21845d59d 100644 --- a/website/views/options/social/quests/questNotActive.jade +++ b/website/views/options/social/quests/questNotActive.jade @@ -1,4 +1,4 @@ -div(ng-if='group.quest.active==false') +div(ng-if='group.quest.active===false') tabset tab(heading=env.t('invitations')) +participants(false) @@ -22,9 +22,9 @@ div(ng-if='group.quest.active==false') p=env.t('questStart') span(ng-if='user.party.quest.RSVPNeeded') - button.btn.btn-sm.btn-success(ng-click='questAccept(party)')=env.t('accept') - button.btn.btn-sm.btn-danger(ng-click='questReject(party)')=env.t('reject') + button.btn.btn-sm.btn-success(ng-click='questAccept()')=env.t('accept') + button.btn.btn-sm.btn-danger(ng-click='questReject()')=env.t('reject') - span(ng-if='::group.quest.leader && group.quest.leader==user._id && isMemberOfGroup(group.quest.leader,group) && isMemberOfPendingQuest(group.quest.leader,group)') - button.btn.btn-sm.btn-warning(ng-click='party.$questAccept({"force":true})')=env.t('begin') - button.btn.btn-sm.btn-danger(ng-click='questCancel(party)')=env.t('cancel') + span(ng-if='::canEditQuest(party)') + button.btn.btn-sm.btn-warning(ng-click='party.$startQuest({"force":true})')=env.t('begin') + button.btn.btn-sm.btn-danger(ng-click='questCancel()')=env.t('cancel') diff --git a/website/views/shared/modals/quests.jade b/website/views/shared/modals/quests.jade index 9dce32c5ff..c31963742b 100644 --- a/website/views/shared/modals/quests.jade +++ b/website/views/shared/modals/quests.jade @@ -43,7 +43,7 @@ script(type='text/ng-template', id='modals/showQuest.html') p=env.t('questWarning') .modal-footer button.btn.btn-default(ng-click='closeQuest(); $close()')=env.t('cancel') - button.btn.btn-primary(ng-click='questInit(); $close()') + button.btn.btn-primary(ng-click='questInit()') | {{:: party.memberCount > 1 ? env.t('inviteParty') : env.t('startQuest')}} script(type='text/ng-template', id='modals/buyQuest.html') @@ -79,8 +79,8 @@ script(type='text/ng-template', id='modals/questInvitation.html') quest-rewards(key='{{::user.party.quest.key}}', header=env.t('rewards')) .modal-footer button.btn.btn-default(ng-click='questHold = true; $close()')=env.t('askLater') - button.btn.btn-default(ng-click='questReject(party); $close()')=env.t('reject') - button.btn.btn-primary(ng-click='questAccept(party); $close()')=env.t('accept') + button.btn.btn-default(ng-click='questReject(); $close()')=env.t('reject') + button.btn.btn-primary(ng-click='questAccept(); $close()')=env.t('accept') script(type='text/ng-template', id='modals/questDrop.html') .quest-icon.pull-right(class='inventory_quest_scroll_{{::selectedQuest.key}}') @@ -95,7 +95,7 @@ script(type='text/ng-template', id='modals/questDrop.html') .modal-footer button.btn.btn-default(ng-click='closeQuest(); $close()')=env.t('questLater') button.btn.btn-primary(ng-click='inviteOrStartParty(group); $close()', ng-if='!party.members')=env.t('startAParty') - button.btn.btn-primary(ng-click='questInit(); $close()', ng-if='party.members')=env.t('inviteParty') + button.btn.btn-primary(ng-click='questInit()', ng-if='party.members')=env.t('inviteParty') script(type='text/ng-template', id='modals/ownedQuests.html') .modal-header