Merge branch 'develop' into party-chat-translations
# Conflicts: # website/server/controllers/api-v3/quests.js # website/server/controllers/api-v3/tasks/groups.js # website/server/controllers/api-v3/user/spells.js # website/server/models/group.js
This commit is contained in:
@@ -151,7 +151,10 @@ describe('GET challenges/groups/:groupId', () => {
|
||||
});
|
||||
|
||||
officialChallenge = await generateChallenge(user, group, {
|
||||
official: true,
|
||||
categories: [{
|
||||
name: 'habitica_official',
|
||||
slug: 'habitica_official',
|
||||
}],
|
||||
});
|
||||
|
||||
challenge = await generateChallenge(user, group);
|
||||
|
||||
@@ -193,7 +193,10 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
|
||||
officialChallenge = await generateChallenge(user, group, {
|
||||
official: true,
|
||||
categories: [{
|
||||
name: 'habitica_official',
|
||||
slug: 'habitica_official',
|
||||
}],
|
||||
});
|
||||
|
||||
challenge = await generateChallenge(user, group);
|
||||
@@ -224,4 +227,61 @@ describe('GET challenges/user', () => {
|
||||
expect(foundChallengeIndex).to.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
context('filters and paging', () => {
|
||||
let user, guild, member;
|
||||
const categories = [{
|
||||
slug: 'newCat',
|
||||
name: 'New Category',
|
||||
}];
|
||||
|
||||
before(async () => {
|
||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestGuild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
user = groupLeader;
|
||||
guild = group;
|
||||
member = members[0];
|
||||
|
||||
await user.update({balance: 20});
|
||||
|
||||
for (let i = 0; i < 11; i += 1) {
|
||||
await generateChallenge(user, group); // eslint-disable-line
|
||||
}
|
||||
});
|
||||
|
||||
it('returns public guilds filtered by category', async () => {
|
||||
const categoryChallenge = await generateChallenge(user, guild, {categories});
|
||||
const challenges = await user.get(`/challenges/user?categories=${categories[0].slug}`);
|
||||
|
||||
expect(challenges[0]._id).to.eql(categoryChallenge._id);
|
||||
expect(challenges.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('does not page challenges if page parameter is absent', async () => {
|
||||
const challenges = await user.get('/challenges/user');
|
||||
|
||||
expect(challenges.length).to.be.above(11);
|
||||
});
|
||||
|
||||
it('paginates challenges', async () => {
|
||||
const challenges = await user.get('/challenges/user?page=0');
|
||||
const challengesPaged = await user.get('/challenges/user?page=1&owned=owned');
|
||||
|
||||
expect(challenges.length).to.eql(10);
|
||||
expect(challengesPaged.length).to.eql(2);
|
||||
});
|
||||
|
||||
it('filters by owned', async () => {
|
||||
const challenges = await member.get('/challenges/user?owned=owned');
|
||||
|
||||
expect(challenges.length).to.eql(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,16 +53,26 @@ describe('DELETE /groups/:groupId/chat/:chatId', () => {
|
||||
|
||||
it('allows creator to delete a their message', async () => {
|
||||
await user.del(`/groups/${groupWithChat._id}/chat/${nextMessage.id}`);
|
||||
let messages = await user.get(`/groups/${groupWithChat._id}/chat/`);
|
||||
expect(messages).is.an('array');
|
||||
expect(messages).to.not.include(nextMessage);
|
||||
|
||||
const returnedMessages = await user.get(`/groups/${groupWithChat._id}/chat/`);
|
||||
const messageFromUser = returnedMessages.find(returnedMessage => {
|
||||
return returnedMessage.id === nextMessage.id;
|
||||
});
|
||||
|
||||
expect(returnedMessages).is.an('array');
|
||||
expect(messageFromUser).to.not.exist;
|
||||
});
|
||||
|
||||
it('allows admin to delete another user\'s message', async () => {
|
||||
await admin.del(`/groups/${groupWithChat._id}/chat/${nextMessage.id}`);
|
||||
let messages = await user.get(`/groups/${groupWithChat._id}/chat/`);
|
||||
expect(messages).is.an('array');
|
||||
expect(messages).to.not.include(nextMessage);
|
||||
|
||||
const returnedMessages = await user.get(`/groups/${groupWithChat._id}/chat/`);
|
||||
const messageFromUser = returnedMessages.find(returnedMessage => {
|
||||
return returnedMessage.id === nextMessage.id;
|
||||
});
|
||||
|
||||
expect(returnedMessages).is.an('array');
|
||||
expect(messageFromUser).to.not.exist;
|
||||
});
|
||||
|
||||
it('returns empty when previous message parameter is passed and the last message was deleted', async () => {
|
||||
@@ -71,9 +81,9 @@ describe('DELETE /groups/:groupId/chat/:chatId', () => {
|
||||
});
|
||||
|
||||
it('returns the update chat when previous message parameter is passed and the chat is updated', async () => {
|
||||
let deleteResult = await user.del(`/groups/${groupWithChat._id}/chat/${nextMessage.id}?previousMsg=${message.id}`);
|
||||
const updatedChat = await user.del(`/groups/${groupWithChat._id}/chat/${nextMessage.id}?previousMsg=${message.id}`);
|
||||
|
||||
expect(deleteResult[0].id).to.eql(message.id);
|
||||
expect(updatedChat[0].id).to.eql(message.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,14 +23,14 @@ describe('GET /groups/:groupId/chat', () => {
|
||||
privacy: 'public',
|
||||
}, {
|
||||
chat: [
|
||||
{text: 'Hello', flags: {}},
|
||||
{text: 'Welcome to the Guild', flags: {}},
|
||||
{text: 'Hello', flags: {}, id: 1},
|
||||
{text: 'Welcome to the Guild', flags: {}, id: 2},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns Guild chat', async () => {
|
||||
let chat = await user.get(`/groups/${group._id}/chat`);
|
||||
const chat = await user.get(`/groups/${group._id}/chat`);
|
||||
|
||||
expect(chat).to.eql(group.chat);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
TAVERN_ID,
|
||||
} from '../../../../../website/server/models/group';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { getMatchesByWordArray, removePunctuationFromString } from '../../../../../website/server/libs/stringUtils';
|
||||
import { getMatchesByWordArray } from '../../../../../website/server/libs/stringUtils';
|
||||
import bannedWords from '../../../../../website/server/libs/bannedWords';
|
||||
import guildsAllowingBannedWords from '../../../../../website/server/libs/guildsAllowingBannedWords';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
@@ -23,11 +23,11 @@ const BASE_URL = nconf.get('BASE_URL');
|
||||
describe('POST /chat', () => {
|
||||
let user, groupWithChat, member, additionalMember;
|
||||
let testMessage = 'Test Message';
|
||||
let testBannedWordMessage = 'TEST_PLACEHOLDER_SWEAR_WORD_HERE';
|
||||
let testSlurMessage = 'message with TEST_PLACEHOLDER_SLUR_WORD_HERE';
|
||||
let bannedWordErrorMessage = t('bannedWordUsed').split('.');
|
||||
bannedWordErrorMessage[0] += ` (${removePunctuationFromString(testBannedWordMessage.toLowerCase())})`;
|
||||
bannedWordErrorMessage = bannedWordErrorMessage.join('.');
|
||||
let testBannedWordMessage = 'TESTPLACEHOLDERSWEARWORDHERE';
|
||||
let testBannedWordMessage1 = 'TESTPLACEHOLDERSWEARWORDHERE1';
|
||||
let testSlurMessage = 'message with TESTPLACEHOLDERSLURWORDHERE';
|
||||
let testSlurMessage1 = 'TESTPLACEHOLDERSLURWORDHERE1';
|
||||
let bannedWordErrorMessage = t('bannedWordUsed', {swearWordsUsed: testBannedWordMessage});
|
||||
|
||||
before(async () => {
|
||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
@@ -39,6 +39,7 @@ describe('POST /chat', () => {
|
||||
members: 2,
|
||||
});
|
||||
user = groupLeader;
|
||||
await user.update({'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL}); // prevent tests accidentally throwing messageGroupChatSpam
|
||||
groupWithChat = group;
|
||||
member = members[0];
|
||||
additionalMember = members[1];
|
||||
@@ -136,9 +137,19 @@ describe('POST /chat', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('checks error message has the banned words used', async () => {
|
||||
let randIndex = Math.floor(Math.random() * (bannedWords.length + 1));
|
||||
let testBannedWords = bannedWords.slice(randIndex, randIndex + 2).map((w) => w.replace(/\\/g, ''));
|
||||
it('errors when word is typed in mixed case', async () => {
|
||||
let substrLength = Math.floor(testBannedWordMessage.length / 2);
|
||||
let chatMessage = testBannedWordMessage.substring(0, substrLength).toLowerCase() + testBannedWordMessage.substring(substrLength).toUpperCase();
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage }))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedWordUsed', {swearWordsUsed: chatMessage}),
|
||||
});
|
||||
});
|
||||
|
||||
it('checks error message has all the banned words used, regardless of case', async () => {
|
||||
let testBannedWords = [testBannedWordMessage.toUpperCase(), testBannedWordMessage1.toLowerCase()];
|
||||
let chatMessage = `Mixing ${testBannedWords[0]} and ${testBannedWords[1]} is bad for you.`;
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage}))
|
||||
.to.eventually.be.rejected
|
||||
@@ -320,6 +331,17 @@ describe('POST /chat', () => {
|
||||
members[0].flags.chatRevoked = false;
|
||||
await members[0].update({'flags.chatRevoked': false});
|
||||
});
|
||||
|
||||
it('errors when slur is typed in mixed case', async () => {
|
||||
let substrLength = Math.floor(testSlurMessage1.length / 2);
|
||||
let chatMessage = testSlurMessage1.substring(0, substrLength).toLowerCase() + testSlurMessage1.substring(substrLength).toUpperCase();
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage }))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedSlurUsed'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not error when sending a message to a private guild with a user with revoked chat', async () => {
|
||||
@@ -359,9 +381,11 @@ describe('POST /chat', () => {
|
||||
});
|
||||
|
||||
it('creates a chat', async () => {
|
||||
let message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
|
||||
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
|
||||
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
expect(newMessage.message.id).to.exist;
|
||||
expect(groupMessages[0].id).to.exist;
|
||||
});
|
||||
|
||||
it('creates a chat with user styles', async () => {
|
||||
|
||||
@@ -7,8 +7,7 @@ import util from 'util';
|
||||
let parseStringAsync = util.promisify(xml2js.parseString).bind(xml2js);
|
||||
|
||||
describe('GET /export/userdata.xml', () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid XML file with user data', async () => {
|
||||
it('should return a valid XML file with user data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
@@ -31,13 +30,21 @@ describe('GET /export/userdata.xml', () => {
|
||||
expect(res).to.contain.all.keys(['tasks', 'flags', 'tasksOrder', 'auth']);
|
||||
expect(res.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||
expect(res.tasks).to.have.all.keys(['dailys', 'habits', 'todos', 'rewards']);
|
||||
|
||||
expect(res.tasks.habits.length).to.equal(2);
|
||||
expect(res.tasks.habits[0]._id).to.equal(tasks[0]._id);
|
||||
let habitIds = _.map(res.tasks.habits, '_id');
|
||||
expect(habitIds).to.have.deep.members([tasks[0]._id, tasks[4]._id]);
|
||||
|
||||
expect(res.tasks.dailys.length).to.equal(2);
|
||||
expect(res.tasks.dailys[0]._id).to.equal(tasks[1]._id);
|
||||
let dailyIds = _.map(res.tasks.dailys, '_id');
|
||||
expect(dailyIds).to.have.deep.members([tasks[1]._id, tasks[5]._id]);
|
||||
|
||||
expect(res.tasks.rewards.length).to.equal(2);
|
||||
expect(res.tasks.rewards[0]._id).to.equal(tasks[2]._id);
|
||||
let rewardIds = _.map(res.tasks.rewards, '_id');
|
||||
expect(rewardIds).to.have.deep.members([tasks[2]._id, tasks[6]._id]);
|
||||
|
||||
expect(res.tasks.todos.length).to.equal(3);
|
||||
expect(res.tasks.todos[1]._id).to.equal(tasks[3]._id);
|
||||
let todoIds = _.map(res.tasks.todos, '_id');
|
||||
expect(todoIds).to.deep.include.members([tasks[3]._id, tasks[7]._id]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -201,8 +201,8 @@ describe('GET /groups', () => {
|
||||
await expect(user.get('/groups?type=publicGuilds&paginate=true&page=1'))
|
||||
.to.eventually.have.a.lengthOf(GUILD_PER_PAGE);
|
||||
let page2 = await expect(user.get('/groups?type=publicGuilds&paginate=true&page=2'))
|
||||
.to.eventually.have.a.lengthOf(1 + 3); // 1 created now, 3 by other tests
|
||||
expect(page2[3].name).to.equal('guild with less members');
|
||||
.to.eventually.have.a.lengthOf(1 + 4); // 1 created now, 4 by other tests
|
||||
expect(page2[4].name).to.equal('guild with less members');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -220,4 +220,18 @@ describe('GET /groups', () => {
|
||||
await expect(user.get('/groups?type=privateGuilds,publicGuilds,party,tavern'))
|
||||
.to.eventually.have.lengthOf(NUMBER_OF_GROUPS_USER_CAN_VIEW);
|
||||
});
|
||||
|
||||
it('returns a list of groups user has access to', async () => {
|
||||
let group = await generateGroup(user, {
|
||||
name: 'c++ coders',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
});
|
||||
|
||||
// search for 'c++ coders'
|
||||
await expect(user.get('/groups?type=publicGuilds&paginate=true&page=0&search=c%2B%2B+coders'))
|
||||
.to.eventually.have.lengthOf(1)
|
||||
.and.to.have.nested.property('[0]')
|
||||
.and.to.have.property('_id', group._id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('GET /groups/:groupId/members', () => {
|
||||
|
||||
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
|
||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
@@ -93,7 +93,7 @@ describe('GET /groups/:groupId/members', () => {
|
||||
|
||||
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
|
||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
|
||||
@@ -136,6 +136,22 @@ describe('POST /group', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when a user with no chat privileges attempts to create a public guild', async () => {
|
||||
await user.update({ 'flags.chatRevoked': true });
|
||||
|
||||
await expect(
|
||||
user.post('/groups', {
|
||||
name: 'Test Public Guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
})
|
||||
).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cannotCreatePublicGuildWhenMuted'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('private guild', () => {
|
||||
@@ -163,6 +179,17 @@ describe('POST /group', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a private guild when the user has no chat privileges', async () => {
|
||||
await user.update({ 'flags.chatRevoked': true });
|
||||
let privateGuild = await user.post('/groups', {
|
||||
name: groupName,
|
||||
type: groupType,
|
||||
privacy: groupPrivacy,
|
||||
});
|
||||
|
||||
expect(privateGuild._id).to.exist;
|
||||
});
|
||||
|
||||
it('deducts gems from user and adds them to guild bank', async () => {
|
||||
let privateGuild = await user.post('/groups', {
|
||||
name: groupName,
|
||||
@@ -201,6 +228,16 @@ describe('POST /group', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a party when the user has no chat privileges', async () => {
|
||||
await user.update({ 'flags.chatRevoked': true });
|
||||
let party = await user.post('/groups', {
|
||||
name: partyName,
|
||||
type: partyType,
|
||||
});
|
||||
|
||||
expect(party._id).to.exist;
|
||||
});
|
||||
|
||||
it('does not require gems to create a party', async () => {
|
||||
await user.update({ balance: 0 });
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ describe('POST /group/:groupId/join', () => {
|
||||
expect(res.leader.profile.name).to.eql(user.profile.name);
|
||||
});
|
||||
|
||||
it('returns an error is user was already a member', async () => {
|
||||
it('returns an error if user was already a member', async () => {
|
||||
await joiningUser.post(`/groups/${publicGuild._id}/join`);
|
||||
await expect(joiningUser.post(`/groups/${publicGuild._id}/join`)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('userAlreadyInGroup'),
|
||||
message: t('youAreAlreadyInGroup'),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -262,6 +262,30 @@ describe('POST /group/:groupId/join', () => {
|
||||
await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(false);
|
||||
});
|
||||
|
||||
it('does not allow user to leave a party if a quest was active and they were the only member', async () => {
|
||||
let userToInvite = await generateUser();
|
||||
let oldParty = await userToInvite.post('/groups', { // add user to a party
|
||||
name: 'Another Test Party',
|
||||
type: 'party',
|
||||
});
|
||||
|
||||
await userToInvite.update({
|
||||
[`items.quests.${PET_QUEST}`]: 1,
|
||||
});
|
||||
await userToInvite.post(`/groups/${oldParty._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true);
|
||||
await user.post(`/groups/${party._id}/invite`, {
|
||||
uuids: [userToInvite._id],
|
||||
});
|
||||
|
||||
await expect(userToInvite.post(`/groups/${party._id}/join`)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageCannotLeaveWhileQuesting'),
|
||||
});
|
||||
});
|
||||
|
||||
it('invites joining member to active quest', async () => {
|
||||
await user.update({
|
||||
[`items.quests.${PET_QUEST}`]: 1,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
each,
|
||||
} from 'lodash';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import * as payments from '../../../../../website/server/libs/payments';
|
||||
import * as payments from '../../../../../website/server/libs/payments/payments';
|
||||
|
||||
describe('POST /groups/:groupId/leave', () => {
|
||||
let typesOfGroups = {
|
||||
|
||||
@@ -24,6 +24,19 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
});
|
||||
|
||||
describe('user id invites', () => {
|
||||
it('returns an error when inviter has no chat privileges', async () => {
|
||||
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
||||
let userToInvite = await generateUser();
|
||||
await expect(inviterMuted.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [userToInvite._id],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cannotInviteWhenMuted'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when invited user is not found', async () => {
|
||||
let fakeID = generateUUID();
|
||||
|
||||
@@ -160,6 +173,19 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
describe('email invites', () => {
|
||||
let testInvite = {name: 'test', email: 'test@habitica.com'};
|
||||
|
||||
it('returns an error when inviter has no chat privileges', async () => {
|
||||
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
||||
await expect(inviterMuted.post(`/groups/${group._id}/invite`, {
|
||||
emails: [testInvite],
|
||||
inviter: 'inviter name',
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cannotInviteWhenMuted'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when invite is missing an email', async () => {
|
||||
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||
emails: [{name: 'test'}],
|
||||
@@ -321,6 +347,19 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
});
|
||||
|
||||
describe('guild invites', () => {
|
||||
it('returns an error when inviter has no chat privileges', async () => {
|
||||
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
||||
let userToInvite = await generateUser();
|
||||
await expect(inviterMuted.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [userToInvite._id],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cannotInviteWhenMuted'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when invited user is already invited to the group', async () => {
|
||||
let userToInvite = await generateUser();
|
||||
await inviter.post(`/groups/${group._id}/invite`, {
|
||||
@@ -398,6 +437,19 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when inviter has no chat privileges', async () => {
|
||||
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
||||
let userToInvite = await generateUser();
|
||||
await expect(inviterMuted.post(`/groups/${party._id}/invite`, {
|
||||
uuids: [userToInvite._id],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cannotInviteWhenMuted'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when invited user has a pending invitation to the party', async () => {
|
||||
let userToInvite = await generateUser();
|
||||
await inviter.post(`/groups/${party._id}/invite`, {
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('GET /members/:memberId', () => {
|
||||
let memberRes = await user.get(`/members/${member._id}`);
|
||||
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
|
||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import {
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
|
||||
describe('payments : amazon #subscribeCancel', () => {
|
||||
let endpoint = '/amazon/subscribe/cancel?noRedirect=true';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
|
||||
describe('payments - amazon - #checkout', () => {
|
||||
let endpoint = '/amazon/checkout';
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
|
||||
describe('payments - amazon - #subscribe', () => {
|
||||
let endpoint = '/amazon/subscribe';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||
import applePayments from '../../../../../../website/server/libs/applePayments';
|
||||
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||
|
||||
describe('payments : apple #cancelSubscribe', () => {
|
||||
let endpoint = '/iap/ios/subscribe/cancel?noRedirect=true';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||
import applePayments from '../../../../../../website/server/libs/applePayments';
|
||||
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||
|
||||
describe('payments : apple #verify', () => {
|
||||
let endpoint = '/iap/ios/verify';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||
import applePayments from '../../../../../../website/server/libs/applePayments';
|
||||
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||
|
||||
describe('payments : apple #subscribe', () => {
|
||||
let endpoint = '/iap/ios/subscribe';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||
|
||||
describe('payments : google #cancelSubscribe', () => {
|
||||
let endpoint = '/iap/android/subscribe/cancel?noRedirect=true';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||
|
||||
describe('payments : google #subscribe', () => {
|
||||
let endpoint = '/iap/android/subscribe';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||
|
||||
describe('payments : google #verify', () => {
|
||||
let endpoint = '/iap/android/verify';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
|
||||
describe('payments : paypal #checkout', () => {
|
||||
let endpoint = '/paypal/checkout';
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
|
||||
describe('payments : paypal #checkoutSuccess', () => {
|
||||
let endpoint = '/paypal/checkout/success';
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
import shared from '../../../../../../website/common';
|
||||
|
||||
describe('payments : paypal #subscribe', () => {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
|
||||
describe('payments : paypal #subscribeCancel', () => {
|
||||
let endpoint = '/paypal/subscribe/cancel';
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
|
||||
describe('payments : paypal #subscribeSuccess', () => {
|
||||
let endpoint = '/paypal/subscribe/success';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
|
||||
describe('payments - paypal - #ipn', () => {
|
||||
let endpoint = '/paypal/ipn';
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import {
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
|
||||
|
||||
describe('payments - stripe - #subscribeCancel', () => {
|
||||
let endpoint = '/stripe/subscribe/cancel?redirect=none';
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
|
||||
|
||||
describe('payments - stripe - #checkout', () => {
|
||||
let endpoint = '/stripe/checkout';
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
|
||||
|
||||
describe('payments - stripe - #subscribeEdit', () => {
|
||||
let endpoint = '/stripe/subscribe/edit';
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateUser,
|
||||
sleep,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
||||
|
||||
describe('POST /groups/:groupId/quests/accept', () => {
|
||||
const PET_QUEST = 'whale';
|
||||
@@ -155,10 +156,11 @@ describe('POST /groups/:groupId/quests/accept', () => {
|
||||
// quest will start after everyone has accepted
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
await questingGroup.sync();
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
const groupChat = await Chat.find({ groupId: questingGroup._id }).exec();
|
||||
|
||||
expect(groupChat[0].text).to.exist;
|
||||
expect(groupChat[0]._meta).to.exist;
|
||||
expect(groupChat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateUser,
|
||||
sleep,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
||||
|
||||
describe('POST /groups/:groupId/quests/force-start', () => {
|
||||
const PET_QUEST = 'whale';
|
||||
@@ -241,11 +242,13 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
||||
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
const groupChat = await Chat.find({ groupId: questingGroup._id }).exec();
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(groupChat[0].text).to.exist;
|
||||
expect(groupChat[0]._meta).to.exist;
|
||||
expect(groupChat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
const returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { quests as questScrolls } from '../../../../../website/common/script/content';
|
||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
||||
|
||||
describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
||||
let questingGroup;
|
||||
@@ -199,11 +200,11 @@ describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
||||
|
||||
await groupLeader.post(`/groups/${group._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await group.sync();
|
||||
const groupChat = await Chat.find({ groupId: group._id }).exec();
|
||||
|
||||
expect(group.chat[0].text).to.exist;
|
||||
expect(group.chat[0]._meta).to.exist;
|
||||
expect(group.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
expect(groupChat[0].text).to.exist;
|
||||
expect(groupChat[0]._meta).to.exist;
|
||||
expect(groupChat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await groupLeader.get(`/groups/${group._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
let stub = sandbox.stub(Group.prototype, 'sendChat');
|
||||
let stub = sandbox.spy(Group.prototype, 'sendChat');
|
||||
|
||||
let res = await leader.post(`/groups/${questingGroup._id}/quests/abort`);
|
||||
await Promise.all([
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
sleep,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
||||
|
||||
describe('POST /groups/:groupId/quests/reject', () => {
|
||||
let questingGroup;
|
||||
@@ -185,11 +186,12 @@ describe('POST /groups/:groupId/quests/reject', () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`);
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
const groupChat = await Chat.find({ groupId: questingGroup._id }).exec();
|
||||
|
||||
expect(groupChat[0].text).to.exist;
|
||||
expect(groupChat[0]._meta).to.exist;
|
||||
expect(groupChat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
|
||||
@@ -296,6 +296,16 @@ describe('PUT /tasks/:id', () => {
|
||||
|
||||
expect(fetchedDaily.text).to.eql('saved');
|
||||
});
|
||||
|
||||
// This is a special case for iOS requests
|
||||
it('will round a priority (difficulty)', async () => {
|
||||
daily = await user.put(`/tasks/${daily._id}`, {
|
||||
alias: 'alias',
|
||||
priority: 0.10000000000005,
|
||||
});
|
||||
|
||||
expect(daily.priority).to.eql(0.1);
|
||||
});
|
||||
});
|
||||
|
||||
context('habits', () => {
|
||||
|
||||
@@ -34,6 +34,8 @@ describe('GET /user', () => {
|
||||
expect(returnedUser._id).to.equal(user._id);
|
||||
expect(returnedUser.achievements).to.exist;
|
||||
expect(returnedUser.items.mounts).to.exist;
|
||||
// Notifications are always returned
|
||||
expect(returnedUser.notifications).to.exist;
|
||||
expect(returnedUser.stats).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('GET /user/in-app-rewards', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('returns the reward items available for purchase', async () => {
|
||||
let buyList = await user.get('/user/in-app-rewards');
|
||||
|
||||
expect(_.find(buyList, item => {
|
||||
return item.text === t('armorWarrior1Text');
|
||||
})).to.exist;
|
||||
|
||||
expect(_.find(buyList, item => {
|
||||
return item.text === t('armorWarrior2Text');
|
||||
})).to.not.exist;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('GET /user/toggle-pinned-item', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('cannot unpin potion', async () => {
|
||||
await expect(user.get('/user/toggle-pinned-item/potion/potion'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('cannotUnpinArmoirPotion'),
|
||||
});
|
||||
});
|
||||
|
||||
it('can pin shield_rogue_5', async () => {
|
||||
let result = await user.get('/user/toggle-pinned-item/marketGear/gear.flat.shield_rogue_5');
|
||||
|
||||
expect(result.pinnedItems.length).to.be.eql(user.pinnedItems.length + 1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,148 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
import getOfficialPinnedItems from '../../../../../website/common/script/libs/getOfficialPinnedItems.js';
|
||||
|
||||
describe('POST /user/move-pinned-item/:path/move/to/:position', () => {
|
||||
let user;
|
||||
let officialPinnedItems;
|
||||
let officialPinnedItemPaths;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
officialPinnedItems = getOfficialPinnedItems(user);
|
||||
|
||||
officialPinnedItemPaths = [];
|
||||
// officialPinnedItems are returned in { type: ..., path:... } format but we just need the paths for testPinnedItemsOrder
|
||||
if (officialPinnedItems.length > 0) {
|
||||
officialPinnedItemPaths = officialPinnedItems.map(item => item.path);
|
||||
}
|
||||
});
|
||||
|
||||
it('adjusts the order of pinned items with no order mismatch', async () => {
|
||||
let testPinnedItems = [
|
||||
{ type: 'armoire', path: 'armoire' },
|
||||
{ type: 'potion', path: 'potion' },
|
||||
{ type: 'marketGear', path: 'gear.flat.weapon_warrior_1' },
|
||||
{ type: 'marketGear', path: 'gear.flat.head_warrior_1' },
|
||||
{ type: 'marketGear', path: 'gear.flat.armor_warrior_1' },
|
||||
{ type: 'hatchingPotions', path: 'hatchingPotions.Golden' },
|
||||
{ type: 'marketGear', path: 'gear.flat.shield_warrior_1' },
|
||||
{ type: 'card', path: 'cardTypes.greeting' },
|
||||
{ type: 'potion', path: 'hatchingPotions.Golden' },
|
||||
{ type: 'card', path: 'cardTypes.thankyou' },
|
||||
{ type: 'food', path: 'food.Saddle' },
|
||||
];
|
||||
|
||||
let testPinnedItemsOrder = [
|
||||
'hatchingPotions.Golden',
|
||||
'cardTypes.greeting',
|
||||
'armoire',
|
||||
'gear.flat.weapon_warrior_1',
|
||||
'gear.flat.head_warrior_1',
|
||||
'cardTypes.thankyou',
|
||||
'gear.flat.armor_warrior_1',
|
||||
'food.Saddle',
|
||||
'gear.flat.shield_warrior_1',
|
||||
'potion',
|
||||
];
|
||||
|
||||
// For this test put seasonal items at the end so they stay out of the way
|
||||
testPinnedItemsOrder = testPinnedItemsOrder.concat(officialPinnedItemPaths);
|
||||
|
||||
await user.update({
|
||||
pinnedItems: testPinnedItems,
|
||||
pinnedItemsOrder: testPinnedItemsOrder,
|
||||
});
|
||||
|
||||
let res = await user.post('/user/move-pinned-item/armoire/move/to/5');
|
||||
await user.sync();
|
||||
|
||||
expect(user.pinnedItemsOrder[5]).to.equal('armoire');
|
||||
expect(user.pinnedItemsOrder[2]).to.equal('gear.flat.weapon_warrior_1');
|
||||
|
||||
// We have done nothing to change pinnedItems!
|
||||
expect(user.pinnedItems).to.deep.equal(testPinnedItems);
|
||||
|
||||
let expectedResponse = [
|
||||
'hatchingPotions.Golden',
|
||||
'cardTypes.greeting',
|
||||
'gear.flat.weapon_warrior_1',
|
||||
'gear.flat.head_warrior_1',
|
||||
'cardTypes.thankyou',
|
||||
'armoire',
|
||||
'gear.flat.armor_warrior_1',
|
||||
'food.Saddle',
|
||||
'gear.flat.shield_warrior_1',
|
||||
'potion',
|
||||
];
|
||||
expectedResponse = expectedResponse.concat(officialPinnedItemPaths);
|
||||
|
||||
expect(res).to.eql(expectedResponse);
|
||||
});
|
||||
|
||||
it('adjusts the order of pinned items with order mismatch', async () => {
|
||||
let testPinnedItems = [
|
||||
{ type: 'card', path: 'cardTypes.thankyou' },
|
||||
{ type: 'card', path: 'cardTypes.greeting' },
|
||||
{ type: 'potion', path: 'potion' },
|
||||
{ type: 'armoire', path: 'armoire' },
|
||||
];
|
||||
|
||||
let testPinnedItemsOrder = [
|
||||
'armoire',
|
||||
'potion',
|
||||
];
|
||||
|
||||
await user.update({
|
||||
pinnedItems: testPinnedItems,
|
||||
pinnedItemsOrder: testPinnedItemsOrder,
|
||||
});
|
||||
|
||||
let res = await user.post('/user/move-pinned-item/armoire/move/to/1');
|
||||
await user.sync();
|
||||
|
||||
// The basic test
|
||||
expect(user.pinnedItemsOrder[1]).to.equal('armoire');
|
||||
|
||||
// potion is now the last item because the 2 unacounted for cards show up
|
||||
// at the beginning of the order
|
||||
expect(user.pinnedItemsOrder[user.pinnedItemsOrder.length - 1]).to.equal('potion');
|
||||
|
||||
let expectedResponse = [
|
||||
'cardTypes.thankyou',
|
||||
'cardTypes.greeting',
|
||||
'potion',
|
||||
];
|
||||
// inAppRewards is used here and will by default put these seasonal items in the front like this:
|
||||
expectedResponse = officialPinnedItemPaths.concat(expectedResponse);
|
||||
// now put "armoire" in where we moved it:
|
||||
expectedResponse.splice(1, 0, 'armoire');
|
||||
|
||||
expect(res).to.eql(expectedResponse);
|
||||
});
|
||||
|
||||
it('cannot move pinned item that you do not have pinned', async () => {
|
||||
let testPinnedItems = [
|
||||
{ type: 'potion', path: 'potion' },
|
||||
{ type: 'armoire', path: 'armoire' },
|
||||
];
|
||||
|
||||
let testPinnedItemsOrder = [
|
||||
'armoire',
|
||||
'potion',
|
||||
];
|
||||
|
||||
await user.update({
|
||||
pinnedItems: testPinnedItems,
|
||||
pinnedItemsOrder: testPinnedItemsOrder,
|
||||
});
|
||||
|
||||
try {
|
||||
await user.post('/user/move-pinned-item/cardTypes.thankyou/move/to/1');
|
||||
} catch (err) {
|
||||
expect(err).to.exist;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -180,11 +180,42 @@ describe('POST /user/class/cast/:spellId', () => {
|
||||
members: 1,
|
||||
});
|
||||
await groupLeader.update({'stats.mp': 200, 'stats.class': 'wizard', 'stats.lvl': 13});
|
||||
|
||||
await groupLeader.post('/user/class/cast/earth');
|
||||
await sleep(1);
|
||||
await group.sync();
|
||||
expect(group.chat[0]).to.exist;
|
||||
expect(group.chat[0].uuid).to.equal('system');
|
||||
const groupMessages = await groupLeader.get(`/groups/${group._id}/chat`);
|
||||
|
||||
expect(groupMessages[0]).to.exist;
|
||||
expect(groupMessages[0].uuid).to.equal('system');
|
||||
});
|
||||
|
||||
it('Ethereal Surge does not recover mp of other mages', async () => {
|
||||
let group = await createAndPopulateGroup({
|
||||
groupDetails: { type: 'party', privacy: 'private' },
|
||||
members: 4,
|
||||
});
|
||||
|
||||
let promises = [];
|
||||
promises.push(group.groupLeader.update({'stats.mp': 200, 'stats.class': 'wizard', 'stats.lvl': 20}));
|
||||
promises.push(group.members[0].update({'stats.mp': 0, 'stats.class': 'warrior', 'stats.lvl': 20}));
|
||||
promises.push(group.members[1].update({'stats.mp': 0, 'stats.class': 'wizard', 'stats.lvl': 20}));
|
||||
promises.push(group.members[2].update({'stats.mp': 0, 'stats.class': 'rogue', 'stats.lvl': 20}));
|
||||
promises.push(group.members[3].update({'stats.mp': 0, 'stats.class': 'healer', 'stats.lvl': 20}));
|
||||
await Promise.all(promises);
|
||||
|
||||
await group.groupLeader.post('/user/class/cast/mpheal');
|
||||
|
||||
promises = [];
|
||||
promises.push(group.members[0].sync());
|
||||
promises.push(group.members[1].sync());
|
||||
promises.push(group.members[2].sync());
|
||||
promises.push(group.members[3].sync());
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(group.members[0].stats.mp).to.be.greaterThan(0); // warrior
|
||||
expect(group.members[1].stats.mp).to.equal(0); // wizard
|
||||
expect(group.members[2].stats.mp).to.be.greaterThan(0); // rogue
|
||||
expect(group.members[3].stats.mp).to.be.greaterThan(0); // healer
|
||||
});
|
||||
|
||||
it('cast bulk', async () => {
|
||||
@@ -197,7 +228,7 @@ describe('POST /user/class/cast/:spellId', () => {
|
||||
await groupLeader.post('/user/class/cast/earth', {quantity: 2});
|
||||
|
||||
await sleep(1);
|
||||
await group.sync();
|
||||
group = await groupLeader.get(`/groups/${group._id}`);
|
||||
|
||||
expect(group.chat[0]).to.exist;
|
||||
expect(group.chat[0].uuid).to.equal('system');
|
||||
@@ -258,11 +289,31 @@ describe('POST /user/class/cast/:spellId', () => {
|
||||
expect(user.achievements.birthday).to.equal(1);
|
||||
});
|
||||
|
||||
it('passes correct target to spell when targetType === \'task\'', async () => {
|
||||
await user.update({'stats.class': 'wizard', 'stats.lvl': 11});
|
||||
|
||||
let task = await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
let result = await user.post(`/user/class/cast/fireball?targetId=${task._id}`);
|
||||
|
||||
expect(result.task._id).to.equal(task._id);
|
||||
});
|
||||
|
||||
it('passes correct target to spell when targetType === \'self\'', async () => {
|
||||
await user.update({'stats.class': 'wizard', 'stats.lvl': 14, 'stats.mp': 50});
|
||||
|
||||
let result = await user.post('/user/class/cast/frost');
|
||||
|
||||
expect(result.user.stats.mp).to.equal(10);
|
||||
});
|
||||
|
||||
|
||||
// TODO find a way to have sinon working in integration tests
|
||||
// it doesn't work when tests are running separately from server
|
||||
it('passes correct target to spell when targetType === \'task\'');
|
||||
it('passes correct target to spell when targetType === \'tasks\'');
|
||||
it('passes correct target to spell when targetType === \'self\'');
|
||||
it('passes correct target to spell when targetType === \'party\'');
|
||||
it('passes correct target to spell when targetType === \'user\'');
|
||||
it('passes correct target to spell when targetType === \'party\' and user is not in a party');
|
||||
|
||||
@@ -30,10 +30,12 @@ describe('POST /user/release-both', () => {
|
||||
'items.currentPet': animal,
|
||||
'items.pets': loadPets(),
|
||||
'items.mounts': loadMounts(),
|
||||
'achievements.triadBingo': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when user balance is too low and user does not have triadBingo', async () => {
|
||||
// @TODO: Traid is now free. Add this back if we need
|
||||
xit('returns an error when user balance is too low and user does not have triadBingo', async () => {
|
||||
await expect(user.post('/user/release-both'))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
@@ -45,9 +47,7 @@ describe('POST /user/release-both', () => {
|
||||
// More tests in common code unit tests
|
||||
|
||||
it('grants triad bingo with gems', async () => {
|
||||
await user.update({
|
||||
balance: 1.5,
|
||||
});
|
||||
await user.update();
|
||||
|
||||
let response = await user.post('/user/release-both');
|
||||
await user.sync();
|
||||
|
||||
@@ -27,6 +27,33 @@ describe('PUT /user', () => {
|
||||
expect(user.stats.hp).to.eql(14);
|
||||
});
|
||||
|
||||
it('tags must be an array', async () => {
|
||||
await expect(user.put('/user', {
|
||||
tags: {
|
||||
tag: true,
|
||||
},
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'mustBeArray',
|
||||
});
|
||||
});
|
||||
|
||||
it('update tags', async () => {
|
||||
let userTags = user.tags;
|
||||
|
||||
await user.put('/user', {
|
||||
tags: [...user.tags, {
|
||||
name: 'new tag',
|
||||
}],
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.tags.length).to.be.eql(userTags.length + 1);
|
||||
});
|
||||
|
||||
|
||||
it('profile.name cannot be an empty string or null', async () => {
|
||||
await expect(user.put('/user', {
|
||||
'profile.name': ' ', // string should be trimmed
|
||||
|
||||
@@ -357,6 +357,21 @@ describe('POST /user/auth/local/register', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sanitizes email params to a lowercase string before creating the user', async () => {
|
||||
let username = generateRandomUserName();
|
||||
let email = 'ISANEmAiL@ExAmPle.coM';
|
||||
let password = 'password';
|
||||
|
||||
let user = await api.post('/user/auth/local/register', {
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
confirmPassword: password,
|
||||
});
|
||||
|
||||
expect(user.auth.local.email).to.equal(email.toLowerCase());
|
||||
});
|
||||
|
||||
it('fails on a habitica.com email', async () => {
|
||||
let username = generateRandomUserName();
|
||||
let email = `${username}@habitica.com`;
|
||||
|
||||
@@ -13,7 +13,7 @@ import nconf from 'nconf';
|
||||
const ENDPOINT = '/user/auth/update-email';
|
||||
|
||||
describe('PUT /user/auth/update-email', () => {
|
||||
let newEmail = 'some-new-email_2@example.net';
|
||||
let newEmail = 'SOmE-nEw-emAIl_2@example.net';
|
||||
let oldPassword = 'password'; // from habitrpg/test/helpers/api-integration/v3/object-generators.js
|
||||
|
||||
context('Local Authenticaion User', async () => {
|
||||
@@ -53,14 +53,15 @@ describe('PUT /user/auth/update-email', () => {
|
||||
});
|
||||
|
||||
it('changes email if new email and existing password are provided', async () => {
|
||||
let lowerCaseNewEmail = newEmail.toLowerCase();
|
||||
let response = await user.put(ENDPOINT, {
|
||||
newEmail,
|
||||
password: oldPassword,
|
||||
});
|
||||
expect(response).to.eql({ email: 'some-new-email_2@example.net' });
|
||||
expect(response.email).to.eql(lowerCaseNewEmail);
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.email).to.eql(newEmail);
|
||||
expect(user.auth.local.email).to.eql(lowerCaseNewEmail);
|
||||
});
|
||||
|
||||
it('rejects if email is already taken', async () => {
|
||||
|
||||
@@ -32,4 +32,11 @@ describe('GET /world-state', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a string representing the current season for NPC sprites', async () => {
|
||||
const res = await requester().get('/world-state');
|
||||
|
||||
expect(res).to.have.nested.property('npcImageSuffix');
|
||||
expect(res.npcImageSuffix).to.be.a('string');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('cron', () => {
|
||||
local: {
|
||||
username: 'username',
|
||||
lowerCaseUsername: 'username',
|
||||
email: 'email@email.email',
|
||||
email: 'email@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
@@ -82,7 +82,7 @@ describe('cron', () => {
|
||||
});
|
||||
|
||||
it('does not reset plan.gemsBought within the month', () => {
|
||||
let clock = sinon.useFakeTimers(moment().startOf('month').add(2, 'days').unix());
|
||||
let clock = sinon.useFakeTimers(moment().startOf('month').add(2, 'days').toDate());
|
||||
user.purchased.plan.dateUpdated = moment().startOf('month').toDate();
|
||||
|
||||
user.purchased.plan.gemsBought = 10;
|
||||
@@ -117,21 +117,6 @@ describe('cron', () => {
|
||||
expect(user.purchased.plan.consecutive.offset).to.equal(1);
|
||||
});
|
||||
|
||||
it('increments plan.consecutive.trinkets when user has reached a month that is a multiple of 3', () => {
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
user.purchased.plan.consecutive.offset = 1;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user.purchased.plan.consecutive.offset).to.equal(0);
|
||||
});
|
||||
|
||||
it('increments plan.consecutive.trinkets multiple times if user has been absent with continuous subscription', () => {
|
||||
user.purchased.plan.dateUpdated = moment().subtract(6, 'months').toDate();
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
});
|
||||
|
||||
it('does not award unearned plan.consecutive.trinkets if subscription ended during an absence', () => {
|
||||
user.purchased.plan.dateUpdated = moment().subtract(6, 'months').toDate();
|
||||
user.purchased.plan.dateTerminated = moment().subtract(3, 'months').toDate();
|
||||
@@ -143,21 +128,6 @@ describe('cron', () => {
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
});
|
||||
|
||||
it('increments plan.consecutive.gemCapExtra when user has reached a month that is a multiple of 3', () => {
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
user.purchased.plan.consecutive.offset = 1;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
expect(user.purchased.plan.consecutive.offset).to.equal(0);
|
||||
});
|
||||
|
||||
it('increments plan.consecutive.gemCapExtra multiple times if user has been absent with continuous subscription', () => {
|
||||
user.purchased.plan.dateUpdated = moment().subtract(6, 'months').toDate();
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(10);
|
||||
});
|
||||
|
||||
it('does not increment plan.consecutive.gemCapExtra when user has reached the gemCap limit', () => {
|
||||
user.purchased.plan.consecutive.gemCapExtra = 25;
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
@@ -184,6 +154,465 @@ describe('cron', () => {
|
||||
expect(user.purchased.plan.consecutive.count).to.equal(0);
|
||||
expect(user.purchased.plan.consecutive.offset).to.equal(0);
|
||||
});
|
||||
|
||||
describe('for a 1-month recurring subscription', () => {
|
||||
let clock;
|
||||
// create a user that will be used for all of these tests without a reset before each
|
||||
let user1 = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username1',
|
||||
lowerCaseUsername: 'username1',
|
||||
email: 'email1@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
});
|
||||
// user1 has a 1-month recurring subscription starting today
|
||||
user1.purchased.plan.customerId = 'subscribedId';
|
||||
user1.purchased.plan.dateUpdated = moment().toDate();
|
||||
user1.purchased.plan.planId = 'basic';
|
||||
user1.purchased.plan.consecutive.count = 0;
|
||||
user1.purchased.plan.consecutive.offset = 0;
|
||||
user1.purchased.plan.consecutive.trinkets = 0;
|
||||
user1.purchased.plan.consecutive.gemCapExtra = 0;
|
||||
|
||||
it('does not increment consecutive benefits after the first month', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(1, 'months').add(2, 'days').toDate());
|
||||
// Add 1 month to simulate what happens a month after the subscription was created.
|
||||
// Add 2 days so that we're sure we're not affected by any start-of-month effects e.g., from time zone oddness.
|
||||
cron({user: user1, tasksByType, daysMissed, analytics});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits after the second month', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(2, 'months').add(2, 'days').toDate());
|
||||
// Add 1 month to simulate what happens a month after the subscription was created.
|
||||
// Add 2 days so that we're sure we're not affected by any start-of-month effects e.g., from time zone oddness.
|
||||
cron({user: user1, tasksByType, daysMissed, analytics});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(2);
|
||||
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits after the third month', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(3, 'months').add(2, 'days').toDate());
|
||||
// Add 1 month to simulate what happens a month after the subscription was created.
|
||||
// Add 2 days so that we're sure we're not affected by any start-of-month effects e.g., from time zone oddness.
|
||||
cron({user: user1, tasksByType, daysMissed, analytics});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(3);
|
||||
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits after the fourth month', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(4, 'months').add(2, 'days').toDate());
|
||||
// Add 1 month to simulate what happens a month after the subscription was created.
|
||||
// Add 2 days so that we're sure we're not affected by any start-of-month effects e.g., from time zone oddness.
|
||||
cron({user: user1, tasksByType, daysMissed, analytics});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(4);
|
||||
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits correctly if user has been absent with continuous subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(10, 'months').add(2, 'days').toDate());
|
||||
cron({user: user1, tasksByType, daysMissed, analytics});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(10);
|
||||
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(3);
|
||||
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(15);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a 3-month recurring subscription', () => {
|
||||
let clock;
|
||||
let user3 = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username3',
|
||||
lowerCaseUsername: 'username3',
|
||||
email: 'email3@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
});
|
||||
// user3 has a 3-month recurring subscription starting today
|
||||
user3.purchased.plan.customerId = 'subscribedId';
|
||||
user3.purchased.plan.dateUpdated = moment().toDate();
|
||||
user3.purchased.plan.planId = 'basic_3mo';
|
||||
user3.purchased.plan.consecutive.count = 0;
|
||||
user3.purchased.plan.consecutive.offset = 3;
|
||||
user3.purchased.plan.consecutive.trinkets = 1;
|
||||
user3.purchased.plan.consecutive.gemCapExtra = 5;
|
||||
|
||||
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(1, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the middle of the period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(2, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the final month of the period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(3, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(3);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits the month after the second paid period has started', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(4, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(4);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the second month of the second period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(5, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(5);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the final month of the second period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(6, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(6);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits the month after the third paid period has started', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(7, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(7);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(3);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(15);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits correctly if user has been absent with continuous subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(10, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3, tasksByType, daysMissed, analytics});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(10);
|
||||
expect(user3.purchased.plan.consecutive.offset).to.equal(2);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(4);
|
||||
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(20);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a 6-month recurring subscription', () => {
|
||||
let clock;
|
||||
let user6 = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username6',
|
||||
lowerCaseUsername: 'username6',
|
||||
email: 'email6@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
});
|
||||
// user6 has a 6-month recurring subscription starting today
|
||||
user6.purchased.plan.customerId = 'subscribedId';
|
||||
user6.purchased.plan.dateUpdated = moment().toDate();
|
||||
user6.purchased.plan.planId = 'google_6mo';
|
||||
user6.purchased.plan.consecutive.count = 0;
|
||||
user6.purchased.plan.consecutive.offset = 6;
|
||||
user6.purchased.plan.consecutive.trinkets = 2;
|
||||
user6.purchased.plan.consecutive.gemCapExtra = 10;
|
||||
|
||||
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(1, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6, tasksByType, daysMissed, analytics});
|
||||
expect(user6.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
|
||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(10);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the final month of the period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(6, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6, tasksByType, daysMissed, analytics});
|
||||
expect(user6.purchased.plan.consecutive.count).to.equal(6);
|
||||
expect(user6.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(10);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits the month after the second paid period has started', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(7, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6, tasksByType, daysMissed, analytics});
|
||||
expect(user6.purchased.plan.consecutive.count).to.equal(7);
|
||||
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
|
||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(4);
|
||||
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits the month after the third paid period has started', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(13, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6, tasksByType, daysMissed, analytics});
|
||||
expect(user6.purchased.plan.consecutive.count).to.equal(13);
|
||||
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
|
||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(6);
|
||||
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits correctly if user has been absent with continuous subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(19, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6, tasksByType, daysMissed, analytics});
|
||||
expect(user6.purchased.plan.consecutive.count).to.equal(19);
|
||||
expect(user6.purchased.plan.consecutive.offset).to.equal(5);
|
||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(8);
|
||||
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a 12-month recurring subscription', () => {
|
||||
let clock;
|
||||
|
||||
let user12 = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username12',
|
||||
lowerCaseUsername: 'username12',
|
||||
email: 'email12@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
});
|
||||
// user12 has a 12-month recurring subscription starting today
|
||||
user12.purchased.plan.customerId = 'subscribedId';
|
||||
user12.purchased.plan.dateUpdated = moment().toDate();
|
||||
user12.purchased.plan.planId = 'basic_12mo';
|
||||
user12.purchased.plan.consecutive.count = 0;
|
||||
user12.purchased.plan.consecutive.offset = 12;
|
||||
user12.purchased.plan.consecutive.trinkets = 4;
|
||||
user12.purchased.plan.consecutive.gemCapExtra = 20;
|
||||
|
||||
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(1, 'months').add(2, 'days').toDate());
|
||||
cron({user: user12, tasksByType, daysMissed, analytics});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(4);
|
||||
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(20);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the final month of the period that they already have benefits for', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(12, 'months').add(2, 'days').toDate());
|
||||
cron({user: user12, tasksByType, daysMissed, analytics});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(12);
|
||||
expect(user12.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(4);
|
||||
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(20);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits the month after the second paid period has started', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(13, 'months').add(2, 'days').toDate());
|
||||
cron({user: user12, tasksByType, daysMissed, analytics});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(13);
|
||||
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(8);
|
||||
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits the month after the third paid period has started', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(25, 'months').add(2, 'days').toDate());
|
||||
cron({user: user12, tasksByType, daysMissed, analytics});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(25);
|
||||
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(12);
|
||||
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits correctly if user has been absent with continuous subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(37, 'months').add(2, 'days').toDate());
|
||||
cron({user: user12, tasksByType, daysMissed, analytics});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(37);
|
||||
expect(user12.purchased.plan.consecutive.offset).to.equal(11);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(16);
|
||||
expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a 3-month gift subscription (non-recurring)', () => {
|
||||
let clock;
|
||||
let user3g = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username3g',
|
||||
lowerCaseUsername: 'username3g',
|
||||
email: 'email3g@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
});
|
||||
// user3g has a 3-month gift subscription starting today
|
||||
user3g.purchased.plan.customerId = 'Gift';
|
||||
user3g.purchased.plan.dateUpdated = moment().toDate();
|
||||
user3g.purchased.plan.dateTerminated = moment().add(3, 'months').toDate();
|
||||
user3g.purchased.plan.planId = null;
|
||||
user3g.purchased.plan.consecutive.count = 0;
|
||||
user3g.purchased.plan.consecutive.offset = 3;
|
||||
user3g.purchased.plan.consecutive.trinkets = 1;
|
||||
user3g.purchased.plan.consecutive.gemCapExtra = 5;
|
||||
|
||||
it('does not increment consecutive benefits in the first month of the gift subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(1, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3g, tasksByType, daysMissed, analytics});
|
||||
expect(user3g.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user3g.purchased.plan.consecutive.offset).to.equal(2);
|
||||
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the second month of the gift subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(2, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3g, tasksByType, daysMissed, analytics});
|
||||
expect(user3g.purchased.plan.consecutive.count).to.equal(2);
|
||||
expect(user3g.purchased.plan.consecutive.offset).to.equal(1);
|
||||
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the third month of the gift subscription', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(3, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3g, tasksByType, daysMissed, analytics});
|
||||
expect(user3g.purchased.plan.consecutive.count).to.equal(3);
|
||||
expect(user3g.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the month after the gift subscription has ended', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(4, 'months').add(2, 'days').toDate());
|
||||
cron({user: user3g, tasksByType, daysMissed, analytics});
|
||||
expect(user3g.purchased.plan.consecutive.count).to.equal(0); // subscription has been erased by now
|
||||
expect(user3g.purchased.plan.consecutive.offset).to.equal(0);
|
||||
expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(0); // erased
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a 6-month recurring subscription where the user has incorrect consecutive month data from prior bugs', () => {
|
||||
let clock;
|
||||
let user6x = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username6x',
|
||||
lowerCaseUsername: 'username6x',
|
||||
email: 'email6x@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
});
|
||||
// user6x has a 6-month recurring subscription starting 8 months in the past before issue #4819 was fixed
|
||||
user6x.purchased.plan.customerId = 'subscribedId';
|
||||
user6x.purchased.plan.dateUpdated = moment().toDate();
|
||||
user6x.purchased.plan.planId = 'basic_6mo';
|
||||
user6x.purchased.plan.consecutive.count = 8;
|
||||
user6x.purchased.plan.consecutive.offset = 0;
|
||||
user6x.purchased.plan.consecutive.trinkets = 3;
|
||||
user6x.purchased.plan.consecutive.gemCapExtra = 15;
|
||||
|
||||
it('increments consecutive benefits in the first month since the fix for #4819 goes live', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(1, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6x, tasksByType, daysMissed, analytics});
|
||||
expect(user6x.purchased.plan.consecutive.count).to.equal(9);
|
||||
expect(user6x.purchased.plan.consecutive.offset).to.equal(5);
|
||||
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5);
|
||||
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the second month after the fix goes live', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(2, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6x, tasksByType, daysMissed, analytics});
|
||||
expect(user6x.purchased.plan.consecutive.count).to.equal(10);
|
||||
expect(user6x.purchased.plan.consecutive.offset).to.equal(4);
|
||||
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5);
|
||||
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not increment consecutive benefits in the third month after the fix goes live', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(3, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6x, tasksByType, daysMissed, analytics});
|
||||
expect(user6x.purchased.plan.consecutive.count).to.equal(11);
|
||||
expect(user6x.purchased.plan.consecutive.offset).to.equal(3);
|
||||
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5);
|
||||
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('increments consecutive benefits in the seventh month after the fix goes live', () => {
|
||||
clock = sinon.useFakeTimers(moment().zone(0).startOf('month').add(7, 'months').add(2, 'days').toDate());
|
||||
cron({user: user6x, tasksByType, daysMissed, analytics});
|
||||
expect(user6x.purchased.plan.consecutive.count).to.equal(15);
|
||||
expect(user6x.purchased.plan.consecutive.offset).to.equal(5);
|
||||
expect(user6x.purchased.plan.consecutive.trinkets).to.equal(7);
|
||||
expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25);
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('end of the month perks when user is not subscribed', () => {
|
||||
@@ -1348,7 +1777,7 @@ describe('recoverCron', () => {
|
||||
local: {
|
||||
username: 'username',
|
||||
lowerCaseUsername: 'username',
|
||||
email: 'email@email.email',
|
||||
email: 'email@example.com',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import amzLib from '../../../../../../../website/server/libs/amazonPayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import amzLib from '../../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
import { createNonLeaderGroupMember } from '../paymentHelpers';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import amzLib from '../../../../../../../website/server/libs/amazonPayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import amzLib from '../../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import { model as Coupon } from '../../../../../../../website/server/models/coupon';
|
||||
import amzLib from '../../../../../../../website/server/libs/amazonPayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import amzLib from '../../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../../website/server/models/group';
|
||||
import amzLib from '../../../../../../../website/server/libs/amazonPayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import amzLib from '../../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
|
||||
describe('#upgradeGroupPlan', () => {
|
||||
let spy, data, user, group, uuidString;
|
||||
|
||||
+154
-41
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable camelcase */
|
||||
import iapModule from '../../../../../website/server/libs/inAppPurchases';
|
||||
import payments from '../../../../../website/server/libs/payments';
|
||||
import applePayments from '../../../../../website/server/libs/applePayments';
|
||||
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||
import {model as User} from '../../../../../website/server/models/user';
|
||||
import common from '../../../../../website/common';
|
||||
import iapModule from '../../../../../../website/server/libs/inAppPurchases';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||
import iap from '../../../../../../website/server/libs/inAppPurchases';
|
||||
import {model as User} from '../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../website/common';
|
||||
import moment from 'moment';
|
||||
|
||||
const i18n = common.i18n;
|
||||
@@ -57,6 +57,18 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if getPurchaseData is invalid', async () => {
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
|
||||
|
||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: applePayments.constants.RESPONSE_NO_ITEM_PURCHASED,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors if the user cannot purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
||||
@@ -69,27 +81,76 @@ describe('Apple Payments', () => {
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('purchases gems', async () => {
|
||||
it('errors if amount does not exist', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
||||
await applePayments.verifyGemPurchase(user, receipt, headers);
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{productId: 'badProduct',
|
||||
transactionId: token,
|
||||
}]);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: applePayments.constants.RESPONSE_INVALID_ITEM,
|
||||
});
|
||||
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
amount: 5.25,
|
||||
headers,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
const gemsCanPurchase = [
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.4gems',
|
||||
amount: 1,
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.20gems',
|
||||
amount: 5.25,
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.21gems',
|
||||
amount: 5.25,
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.42gems',
|
||||
amount: 10.5,
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.84gems',
|
||||
amount: 21,
|
||||
},
|
||||
];
|
||||
|
||||
gemsCanPurchase.forEach(gemTest => {
|
||||
it(`purchases ${gemTest.productId} gems`, async () => {
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{productId: gemTest.productId,
|
||||
transactionId: token,
|
||||
}]);
|
||||
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
||||
await applePayments.verifyGemPurchase(user, receipt, headers);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
amount: gemTest.amount,
|
||||
headers,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscribe', () => {
|
||||
@@ -133,7 +194,16 @@ describe('Apple Payments', () => {
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iapModule.getPurchaseData.restore();
|
||||
payments.createSubscription.restore();
|
||||
if (payments.createSubscription.restore) payments.createSubscription.restore();
|
||||
});
|
||||
|
||||
it('should throw an error if sku is empty', async () => {
|
||||
await expect(applePayments.subscribe('', user, receipt, headers, nextPaymentProcessing))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
name: 'BadRequest',
|
||||
message: i18n.t('missingSubscriptionCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
@@ -149,26 +219,69 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a user subscription', async () => {
|
||||
const subOptions = [
|
||||
{
|
||||
sku: 'subscription1month',
|
||||
subKey: 'basic_earned',
|
||||
},
|
||||
{
|
||||
sku: 'com.habitrpg.ios.habitica.subscription.3month',
|
||||
subKey: 'basic_3mo',
|
||||
},
|
||||
{
|
||||
sku: 'com.habitrpg.ios.habitica.subscription.6month',
|
||||
subKey: 'basic_6mo',
|
||||
},
|
||||
{
|
||||
sku: 'com.habitrpg.ios.habitica.subscription.12month',
|
||||
subKey: 'basic_12mo',
|
||||
},
|
||||
];
|
||||
subOptions.forEach(option => {
|
||||
it(`creates a user subscription for ${option.sku}`, async () => {
|
||||
iapModule.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{
|
||||
expirationDate: moment.utc().add({day: 1}).toDate(),
|
||||
productId: option.sku,
|
||||
transactionId: token,
|
||||
}]);
|
||||
sub = common.content.subscriptionBlocks[option.subKey];
|
||||
|
||||
await applePayments.subscribe(option.sku, user, receipt, headers, nextPaymentProcessing);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||
user,
|
||||
customerId: token,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
sub,
|
||||
headers,
|
||||
additionalData: receipt,
|
||||
nextPaymentProcessing,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when a user is already subscribed', async () => {
|
||||
payments.createSubscription.restore();
|
||||
user = new User();
|
||||
|
||||
await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||
user,
|
||||
customerId: token,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
sub,
|
||||
headers,
|
||||
additionalData: receipt,
|
||||
nextPaymentProcessing,
|
||||
});
|
||||
await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: applePayments.constants.RESPONSE_ALREADY_USED,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable camelcase */
|
||||
import iapModule from '../../../../../website/server/libs/inAppPurchases';
|
||||
import payments from '../../../../../website/server/libs/payments';
|
||||
import googlePayments from '../../../../../website/server/libs/googlePayments';
|
||||
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||
import {model as User} from '../../../../../website/server/models/user';
|
||||
import common from '../../../../../website/common';
|
||||
import iapModule from '../../../../../../website/server/libs/inAppPurchases';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||
import iap from '../../../../../../website/server/libs/inAppPurchases';
|
||||
import {model as User} from '../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../website/common';
|
||||
import moment from 'moment';
|
||||
|
||||
const i18n = common.i18n;
|
||||
@@ -1,7 +1,7 @@
|
||||
import moment from 'moment';
|
||||
|
||||
import * as sender from '../../../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../../../website/server/libs/payments';
|
||||
import * as api from '../../../../../../../website/server/libs/payments/payments';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../../website/server/models/group';
|
||||
import {
|
||||
|
||||
@@ -3,10 +3,10 @@ import stripeModule from 'stripe';
|
||||
import nconf from 'nconf';
|
||||
|
||||
import * as sender from '../../../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../../../website/server/libs/payments';
|
||||
import amzLib from '../../../../../../../website/server/libs/amazonPayments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import * as api from '../../../../../../../website/server/libs/payments/payments';
|
||||
import amzLib from '../../../../../../../website/server/libs/payments/amazon';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../../website/server/models/group';
|
||||
import {
|
||||
|
||||
+7
-7
@@ -1,14 +1,14 @@
|
||||
import moment from 'moment';
|
||||
|
||||
import * as sender from '../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../website/server/libs/payments';
|
||||
import analytics from '../../../../../website/server/libs/analyticsService';
|
||||
import notifications from '../../../../../website/server/libs/pushNotifications';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { translate as t } from '../../../../helpers/api-v3-integration.helper';
|
||||
import * as sender from '../../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../../website/server/libs/payments/payments';
|
||||
import analytics from '../../../../../../website/server/libs/analyticsService';
|
||||
import notifications from '../../../../../../website/server/libs/pushNotifications';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import { translate as t } from '../../../../../helpers/api-v3-integration.helper';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../../helpers/api-unit.helper.js';
|
||||
|
||||
describe('payments/index', () => {
|
||||
let user, group, data, plan;
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
|
||||
describe('checkout success', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable camelcase */
|
||||
import nconf from 'nconf';
|
||||
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import moment from 'moment';
|
||||
import cc from 'coupon-code';
|
||||
|
||||
import paypalPayments from '../../../../../../../website/server/libs/paypalPayments';
|
||||
import paypalPayments from '../../../../../../../website/server/libs/payments/paypal';
|
||||
import { model as Coupon } from '../../../../../../../website/server/models/coupon';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import { model as Coupon } from '../../../../../../../website/server/models/coupon';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import stripeModule from 'stripe';
|
||||
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import common from '../../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
generateGroup,
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../../website/common';
|
||||
import logger from '../../../../../../../website/server/libs/logger';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from '../../../../../../helpers/api-unit.helper.js';
|
||||
import { model as User } from '../../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../../website/server/models/group';
|
||||
import stripePayments from '../../../../../../../website/server/libs/stripePayments';
|
||||
import payments from '../../../../../../../website/server/libs/payments';
|
||||
import stripePayments from '../../../../../../../website/server/libs/payments/stripe';
|
||||
import payments from '../../../../../../../website/server/libs/payments/payments';
|
||||
|
||||
describe('Stripe - Upgrade Group Plan', () => {
|
||||
const stripe = stripeModule('test');
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
generateRes,
|
||||
generateReq,
|
||||
} from '../../../../helpers/api-unit.helper';
|
||||
import { authWithHeaders as authWithHeadersFactory } from '../../../../../website/server/middlewares/auth';
|
||||
|
||||
describe('auth middleware', () => {
|
||||
let res, req, user;
|
||||
|
||||
beforeEach(async () => {
|
||||
res = generateRes();
|
||||
req = generateReq();
|
||||
user = await res.locals.user.save();
|
||||
});
|
||||
|
||||
describe('auth with headers', () => {
|
||||
it('allows to specify a list of user field that we do not want to load', (done) => {
|
||||
const authWithHeaders = authWithHeadersFactory(false, {
|
||||
userFieldsToExclude: ['items', 'flags', 'auth.timestamps'],
|
||||
});
|
||||
|
||||
req.headers['x-api-user'] = user._id;
|
||||
req.headers['x-api-key'] = user.apiToken;
|
||||
|
||||
authWithHeaders(req, res, (err) => {
|
||||
if (err) return done(err);
|
||||
|
||||
const userToJSON = res.locals.user.toJSON();
|
||||
expect(userToJSON.items).to.not.exist;
|
||||
expect(userToJSON.flags).to.not.exist;
|
||||
expect(userToJSON.auth.timestamps).to.not.exist;
|
||||
expect(userToJSON.auth).to.exist;
|
||||
expect(userToJSON.notifications).to.exist;
|
||||
expect(userToJSON.preferences).to.exist;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -182,7 +182,7 @@ describe('Group Model', () => {
|
||||
await party.startQuest(questLeader);
|
||||
await party.save();
|
||||
|
||||
sendChatStub = sandbox.stub(Group.prototype, 'sendChat');
|
||||
sendChatStub = sandbox.spy(Group.prototype, 'sendChat');
|
||||
});
|
||||
|
||||
afterEach(() => sendChatStub.restore());
|
||||
@@ -378,7 +378,7 @@ describe('Group Model', () => {
|
||||
await party.startQuest(questLeader);
|
||||
await party.save();
|
||||
|
||||
sendChatStub = sandbox.stub(Group.prototype, 'sendChat');
|
||||
sendChatStub = sandbox.spy(Group.prototype, 'sendChat');
|
||||
});
|
||||
|
||||
afterEach(() => sendChatStub.restore());
|
||||
@@ -1118,21 +1118,8 @@ describe('Group Model', () => {
|
||||
sandbox.spy(User, 'update');
|
||||
});
|
||||
|
||||
it('puts message at top of chat array', () => {
|
||||
let oldMessage = {
|
||||
text: 'a message',
|
||||
};
|
||||
party.chat.push(oldMessage, oldMessage, oldMessage);
|
||||
|
||||
party.sendChat('a new message', {_id: 'user-id', profile: { name: 'user name' }});
|
||||
|
||||
expect(party.chat).to.have.a.lengthOf(4);
|
||||
expect(party.chat[0].text).to.eql('a new message');
|
||||
expect(party.chat[0].uuid).to.eql('user-id');
|
||||
});
|
||||
|
||||
it('formats message', () => {
|
||||
party.sendChat('a new message', {
|
||||
const chatMessage = party.sendChat('a new message', {
|
||||
_id: 'user-id',
|
||||
profile: { name: 'user name' },
|
||||
contributor: {
|
||||
@@ -1147,11 +1134,11 @@ describe('Group Model', () => {
|
||||
},
|
||||
});
|
||||
|
||||
let chat = party.chat[0];
|
||||
const chat = chatMessage;
|
||||
|
||||
expect(chat.text).to.eql('a new message');
|
||||
expect(validator.isUUID(chat.id)).to.eql(true);
|
||||
expect(chat.timestamp).to.be.a('number');
|
||||
expect(chat.timestamp).to.be.a('date');
|
||||
expect(chat.likes).to.eql({});
|
||||
expect(chat.flags).to.eql({});
|
||||
expect(chat.flagCount).to.eql(0);
|
||||
@@ -1162,13 +1149,11 @@ describe('Group Model', () => {
|
||||
});
|
||||
|
||||
it('formats message as system if no user is passed in', () => {
|
||||
party.sendChat('a system message');
|
||||
|
||||
let chat = party.chat[0];
|
||||
const chat = party.sendChat('a system message');
|
||||
|
||||
expect(chat.text).to.eql('a system message');
|
||||
expect(validator.isUUID(chat.id)).to.eql(true);
|
||||
expect(chat.timestamp).to.be.a('number');
|
||||
expect(chat.timestamp).to.be.a('date');
|
||||
expect(chat.likes).to.eql({});
|
||||
expect(chat.flags).to.eql({});
|
||||
expect(chat.flagCount).to.eql(0);
|
||||
@@ -1575,7 +1560,8 @@ describe('Group Model', () => {
|
||||
expect(updatedParticipatingMember.achievements.quests[quest.key]).to.eql(1);
|
||||
});
|
||||
|
||||
it('gives out super awesome Masterclasser achievement to the deserving', async () => {
|
||||
// Disable test, it fails on TravisCI, but only there
|
||||
xit('gives out super awesome Masterclasser achievement to the deserving', async () => {
|
||||
quest = questScrolls.lostMasterclasser4;
|
||||
party.quest.key = quest.key;
|
||||
|
||||
@@ -1603,8 +1589,45 @@ describe('Group Model', () => {
|
||||
updatedLeader,
|
||||
updatedParticipatingMember,
|
||||
] = await Promise.all([
|
||||
User.findById(questLeader._id),
|
||||
User.findById(participatingMember._id),
|
||||
User.findById(questLeader._id).exec(),
|
||||
User.findById(participatingMember._id).exec(),
|
||||
]);
|
||||
|
||||
expect(updatedLeader.achievements.lostMasterclasser).to.eql(true);
|
||||
expect(updatedParticipatingMember.achievements.lostMasterclasser).to.not.eql(true);
|
||||
});
|
||||
|
||||
// Disable test, it fails on TravisCI, but only there
|
||||
xit('gives out super awesome Masterclasser achievement when quests done out of order', async () => {
|
||||
quest = questScrolls.lostMasterclasser1;
|
||||
party.quest.key = quest.key;
|
||||
|
||||
questLeader.achievements.quests = {
|
||||
mayhemMistiflying1: 1,
|
||||
mayhemMistiflying2: 1,
|
||||
mayhemMistiflying3: 1,
|
||||
stoikalmCalamity1: 1,
|
||||
stoikalmCalamity2: 1,
|
||||
stoikalmCalamity3: 1,
|
||||
taskwoodsTerror1: 1,
|
||||
taskwoodsTerror2: 1,
|
||||
taskwoodsTerror3: 1,
|
||||
dilatoryDistress1: 1,
|
||||
dilatoryDistress2: 1,
|
||||
dilatoryDistress3: 1,
|
||||
lostMasterclasser2: 1,
|
||||
lostMasterclasser3: 1,
|
||||
lostMasterclasser4: 1,
|
||||
};
|
||||
await questLeader.save();
|
||||
await party.finishQuest(quest);
|
||||
|
||||
let [
|
||||
updatedLeader,
|
||||
updatedParticipatingMember,
|
||||
] = await Promise.all([
|
||||
User.findById(questLeader._id).exec(),
|
||||
User.findById(participatingMember._id).exec(),
|
||||
]);
|
||||
|
||||
expect(updatedLeader.achievements.lostMasterclasser).to.eql(true);
|
||||
|
||||
Reference in New Issue
Block a user