mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-27 19:22:55 +01:00
* Links stay white on hover * Fixed task icon color * Disabled plus button when needed * Fixed difficulty color * Fixed task reward color * Updated create styles * Fixed group plan link * Fixed second group test modal * Added login incentives * Fixed group notification clear * Show baily correctly * Styled armoire notification * Fixed contributor achievement styles * Fixed death * Fixed drop styles * Fixed invited friend modal * Fixed joined challenge achievement style * Fixed joined guild style * Fixed level up styles * Updated low health styles * Fixed bailey styles * Updated quest completed * Added soem conditionals to hide modals * Added rebirth styles * Fixed rebirth enable styles * Fixed streak styles * Fixed testing modals * Fixed ultimate gear achievement * Fixed won challenge * Set user to welcomed if created on mobile * Removed old default tasks * Began adding more options to avatar * Added change class * Inbox to messages * Moved profile to menu * Added user modal for viewing a user and send message * Fixed conversations * Fixed lint * Fixed challenges sending to server * Added challenge progress view * Fixed group sync after pay * Fixed some group accepting features * Fixed initial chat loading * Fixed some exitence errors * Added user names to assigned * Added upgrade link * Began adding new payment flow * Added default tasks * Updated avatar styles * Updated tutorial styles * Rebuilt notifications and styles * Updated upload script * Fixed lint * Added default tasks back to mobile and added updated tests * More test fixes
352 lines
9.4 KiB
JavaScript
352 lines
9.4 KiB
JavaScript
import {
|
|
checkExistence,
|
|
createAndPopulateGroup,
|
|
generateGroup,
|
|
generateUser,
|
|
generateChallenge,
|
|
translate as t,
|
|
} from '../../../../helpers/api-integration/v3';
|
|
import {
|
|
find,
|
|
each,
|
|
map,
|
|
} from 'lodash';
|
|
import Bluebird from 'bluebird';
|
|
import {
|
|
sha1MakeSalt,
|
|
sha1Encrypt as sha1EncryptPassword,
|
|
} from '../../../../../website/server/libs/password';
|
|
import * as email from '../../../../../website/server/libs/email';
|
|
|
|
const DELETE_CONFIRMATION = 'DELETE';
|
|
|
|
describe('DELETE /user', () => {
|
|
let user;
|
|
let password = 'password'; // from habitrpg/test/helpers/api-integration/v3/object-generators.js
|
|
|
|
context('user with local auth', async () => {
|
|
beforeEach(async () => {
|
|
user = await generateUser({balance: 10});
|
|
});
|
|
|
|
it('returns an error if password is wrong', async () => {
|
|
await expect(user.del('/user', {
|
|
password: 'wrong-password',
|
|
})).to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('wrongPassword'),
|
|
});
|
|
});
|
|
|
|
it('returns an error if password is not supplied', async () => {
|
|
await expect(user.del('/user', {
|
|
password: '',
|
|
})).to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('missingPassword'),
|
|
});
|
|
});
|
|
|
|
it('deletes the user', async () => {
|
|
await user.del('/user', {
|
|
password,
|
|
});
|
|
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
|
});
|
|
|
|
it('returns an error if excessive feedback is supplied', async () => {
|
|
let feedbackText = 'spam feedback ';
|
|
let feedback = feedbackText;
|
|
while (feedback.length < 10000) {
|
|
feedback = feedback + feedbackText;
|
|
}
|
|
|
|
await expect(user.del('/user', {
|
|
password,
|
|
feedback,
|
|
})).to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: 'Account deletion feedback is limited to 10,000 characters. For lengthy feedback, email admin@habitica.com.',
|
|
});
|
|
});
|
|
|
|
it('returns an error if user has active subscription', async () => {
|
|
let userWithSubscription = await generateUser({'purchased.plan.customerId': 'fake-customer-id'});
|
|
|
|
await expect(userWithSubscription.del('/user', {
|
|
password,
|
|
})).to.be.rejected.and.to.eventually.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('cannotDeleteActiveAccount'),
|
|
});
|
|
});
|
|
|
|
it('deletes the user\'s tasks', async () => {
|
|
await user.post('/tasks/user', {
|
|
text: 'test habit',
|
|
type: 'habit',
|
|
});
|
|
await user.sync();
|
|
|
|
// gets the user's tasks ids
|
|
let ids = [];
|
|
each(user.tasksOrder, (idsForOrder) => {
|
|
ids.push(...idsForOrder);
|
|
});
|
|
|
|
expect(ids.length).to.be.above(0); // make sure the user has some task to delete
|
|
|
|
await user.del('/user', {
|
|
password,
|
|
});
|
|
|
|
await Bluebird.all(map(ids, id => {
|
|
return expect(checkExistence('tasks', id)).to.eventually.eql(false);
|
|
}));
|
|
});
|
|
|
|
it('reduces memberCount in challenges user is linked to', async () => {
|
|
let populatedGroup = await createAndPopulateGroup({
|
|
members: 2,
|
|
});
|
|
|
|
let group = populatedGroup.group;
|
|
let authorizedUser = populatedGroup.members[1];
|
|
|
|
let challenge = await generateChallenge(populatedGroup.groupLeader, group);
|
|
await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
|
|
|
await challenge.sync();
|
|
|
|
expect(challenge.memberCount).to.eql(2);
|
|
|
|
await authorizedUser.del('/user', {
|
|
password,
|
|
});
|
|
|
|
await challenge.sync();
|
|
|
|
expect(challenge.memberCount).to.eql(1);
|
|
});
|
|
|
|
it('sends feedback to the admin email', async () => {
|
|
sandbox.spy(email, 'sendTxn');
|
|
|
|
let feedback = 'Reasons for Deletion';
|
|
await user.del('/user', {
|
|
password,
|
|
feedback,
|
|
});
|
|
|
|
expect(email.sendTxn).to.be.calledOnce;
|
|
|
|
sandbox.restore();
|
|
});
|
|
|
|
it('does not send email if no feedback is supplied', async () => {
|
|
sandbox.spy(email, 'sendTxn');
|
|
|
|
await user.del('/user', {
|
|
password,
|
|
});
|
|
|
|
expect(email.sendTxn).to.not.be.called;
|
|
|
|
sandbox.restore();
|
|
});
|
|
|
|
it('deletes the user with a legacy sha1 password', async () => {
|
|
let textPassword = 'mySecretPassword';
|
|
let salt = sha1MakeSalt();
|
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
|
|
|
await user.update({
|
|
'auth.local.hashed_password': sha1HashedPassword,
|
|
'auth.local.passwordHashMethod': 'sha1',
|
|
'auth.local.salt': salt,
|
|
});
|
|
|
|
await user.sync();
|
|
|
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
|
expect(user.auth.local.salt).to.equal(salt);
|
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
|
|
|
// delete the user
|
|
await user.del('/user', {
|
|
password: textPassword,
|
|
});
|
|
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
|
});
|
|
|
|
context('last member of a party', () => {
|
|
let party;
|
|
|
|
beforeEach(async () => {
|
|
party = await generateGroup(user, {
|
|
type: 'party',
|
|
privacy: 'private',
|
|
});
|
|
});
|
|
|
|
it('deletes party when user is the only member', async () => {
|
|
await user.del('/user', {
|
|
password,
|
|
});
|
|
await expect(checkExistence('party', party._id)).to.eventually.eql(false);
|
|
});
|
|
});
|
|
|
|
context('last member of a private guild', () => {
|
|
let privateGuild;
|
|
|
|
beforeEach(async () => {
|
|
privateGuild = await generateGroup(user, {
|
|
type: 'guild',
|
|
privacy: 'private',
|
|
});
|
|
});
|
|
|
|
it('deletes guild when user is the only member', async () => {
|
|
await user.del('/user', {
|
|
password,
|
|
});
|
|
await expect(checkExistence('groups', privateGuild._id)).to.eventually.eql(false);
|
|
});
|
|
});
|
|
|
|
context('groups user is leader of', () => {
|
|
let guild, oldLeader, newLeader;
|
|
|
|
beforeEach(async () => {
|
|
let { group, groupLeader, members } = await createAndPopulateGroup({
|
|
groupDetails: {
|
|
type: 'guild',
|
|
privacy: 'public',
|
|
},
|
|
members: 1,
|
|
});
|
|
|
|
guild = group;
|
|
newLeader = members[0];
|
|
oldLeader = groupLeader;
|
|
});
|
|
|
|
it('chooses new group leader for any group user was the leader of', async () => {
|
|
await oldLeader.del('/user', {
|
|
password,
|
|
});
|
|
|
|
let updatedGuild = await newLeader.get(`/groups/${guild._id}`);
|
|
|
|
expect(updatedGuild.leader).to.exist;
|
|
expect(updatedGuild.leader._id).to.not.eql(oldLeader._id);
|
|
});
|
|
});
|
|
|
|
context('groups user is a part of', () => {
|
|
let group1, group2, userToDelete, otherUser;
|
|
|
|
beforeEach(async () => {
|
|
userToDelete = await generateUser({balance: 10});
|
|
|
|
group1 = await generateGroup(userToDelete, {
|
|
type: 'guild',
|
|
privacy: 'public',
|
|
});
|
|
|
|
let {group, members} = await createAndPopulateGroup({
|
|
groupDetails: {
|
|
type: 'guild',
|
|
privacy: 'public',
|
|
},
|
|
members: 3,
|
|
});
|
|
|
|
group2 = group;
|
|
otherUser = members[0];
|
|
|
|
await userToDelete.post(`/groups/${group2._id}/join`);
|
|
});
|
|
|
|
it('removes user from all groups user was a part of', async () => {
|
|
await userToDelete.del('/user', {
|
|
password,
|
|
});
|
|
|
|
let updatedGroup1Members = await otherUser.get(`/groups/${group1._id}/members`);
|
|
let updatedGroup2Members = await otherUser.get(`/groups/${group2._id}/members`);
|
|
let userInGroup = find(updatedGroup2Members, (member) => {
|
|
return member._id === userToDelete._id;
|
|
});
|
|
|
|
expect(updatedGroup1Members).to.be.empty;
|
|
expect(updatedGroup2Members).to.not.be.empty;
|
|
expect(userInGroup).to.not.exist;
|
|
});
|
|
});
|
|
});
|
|
|
|
context('user with Facebook auth', async () => {
|
|
beforeEach(async () => {
|
|
user = await generateUser({
|
|
auth: {
|
|
facebook: {
|
|
id: 'facebook-id',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it('returns an error if confirmation phrase is wrong', async () => {
|
|
await expect(user.del('/user', {
|
|
password: 'just-do-it',
|
|
})).to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('incorrectDeletePhrase'),
|
|
});
|
|
});
|
|
|
|
it('returns an error if confirmation phrase is not supplied', async () => {
|
|
await expect(user.del('/user', {
|
|
password: '',
|
|
})).to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('missingPassword'),
|
|
});
|
|
});
|
|
|
|
it('deletes a Facebook user', async () => {
|
|
await user.del('/user', {
|
|
password: DELETE_CONFIRMATION,
|
|
});
|
|
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
|
});
|
|
});
|
|
|
|
context('user with Google auth', async () => {
|
|
beforeEach(async () => {
|
|
user = await generateUser({
|
|
auth: {
|
|
google: {
|
|
id: 'google-id',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it('deletes a Google user', async () => {
|
|
await user.del('/user', {
|
|
password: DELETE_CONFIRMATION,
|
|
});
|
|
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
|
});
|
|
});
|
|
});
|