mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 06:07:21 +01:00
* Added initial bailey api
* wip
* implement new panel header
* Fixed lint
* add ability to mark notification as seen
* add notification count, remove top badge from user and add ability to mark multiple notifications as seen
* add support dismissall and mark all as read
* do not dismiss actionable notif
* mark as seen when menu is opened instead of closed
* implement ordering, list of actionable notifications
* add groups messages and fix badges count
* add notifications for received cards
* send card received notification to target not sender
* rename notificaion field
* fix integration tests
* mark cards notifications as read and update tests
* add mystery items notifications
* add unallocated stats points notifications
* fix linting
* simplify code
* refactoring and fixes
* fix dropdown opening
* start splitting notifications into their own component
* add notifications for inbox messages
* fix unit tests
* fix default buttons styles
* add initial bailey support
* add title and tests to new stuff notification
* add notification if a group task needs more work
* add tests and fixes for marking a task as needing more work
* make sure user._v is updated
* remove console.log
* notification: hover status and margins
* start styling notifications, add separate files and basic functionalities
* fix tests
* start adding mystery items notification
* wip card notification
* fix cards text
* initial implementation inbox messages
* initial implementation group messages
* disable inbox notifications until mobile is ready
* wip group chat messages
* finish mystery and card notifications
* add bailey notification and fix a lot of stuff
* start adding guilds and parties invitations
* misc invitation fixes
* fix lint issues
* remove old code and add key to notifications
* fix tests
* remove unused code
* add link for public guilds invite
* starts to implement needs work notification design and feature
* fixes to needs work, add group task approved notification
* finish needs work feature
* lots of fixes
* implement quest notification
* bailey fixes and static page
* routing fixes
* fixes # this.$store.dispatch(guilds:join, {groupId: group.id, type: party});
* read notifications on click
* chat notifications
* fix tests for chat notifications
* fix chat notification test
* fix tests
* fix tests (again)
* try awaiting
* remove only
* more sleep
* add bailey tests
* fix icons alignment
* fix issue with multiple points notifications
* remove merge code
* fix rejecting guild invitation
* make remove area bigger
* fix error with notifications and add migration
* fix migration
* fix typos
* add cleanup migration too
* notifications empty state, new counter color, fix marking messages as seen in guilds
* fixes
* add image and install correct packages
* fix mongoose version
* update bailey
* typo
* make sure chat is marked as read after other requests
523 lines
16 KiB
JavaScript
523 lines
16 KiB
JavaScript
import {
|
|
generateUser,
|
|
generateGroup,
|
|
translate as t,
|
|
} from '../../../../helpers/api-v3-integration.helper';
|
|
import { v4 as generateUUID } from 'uuid';
|
|
import nconf from 'nconf';
|
|
|
|
const INVITES_LIMIT = 100;
|
|
const PARTY_LIMIT_MEMBERS = 30;
|
|
const MAX_EMAIL_INVITES_BY_USER = 200;
|
|
|
|
describe('Post /groups/:groupId/invite', () => {
|
|
let inviter;
|
|
let group;
|
|
let groupName = 'Test Public Guild';
|
|
|
|
beforeEach(async () => {
|
|
inviter = await generateUser({balance: 4});
|
|
group = await inviter.post('/groups', {
|
|
name: groupName,
|
|
type: 'guild',
|
|
});
|
|
});
|
|
|
|
describe('user id invites', () => {
|
|
it('returns an error when invited user is not found', async () => {
|
|
let fakeID = generateUUID();
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [fakeID],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 404,
|
|
error: 'NotFound',
|
|
message: t('userWithIDNotFound', {userId: fakeID}),
|
|
});
|
|
});
|
|
|
|
it('returns an error when inviting yourself to a group', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [inviter._id],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('cannotInviteSelfToGroup'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when uuids is not an array', async () => {
|
|
let fakeID = generateUUID();
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: {fakeID},
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('uuidsMustBeAnArray'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when uuids and emails are empty', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
emails: [],
|
|
uuids: [],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('inviteMustNotBeEmpty'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when uuids is empty and emails is not passed', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('inviteMissingUuid'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when there are more than INVITES_LIMIT uuids', async () => {
|
|
let uuids = [];
|
|
|
|
for (let i = 0; i < 101; i += 1) {
|
|
uuids.push(generateUUID());
|
|
}
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids,
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}),
|
|
});
|
|
});
|
|
|
|
it('invites a user to a group by uuid', async () => {
|
|
let userToInvite = await generateUser();
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
})).to.eventually.deep.equal([{
|
|
id: group._id,
|
|
name: groupName,
|
|
inviter: inviter._id,
|
|
publicGuild: false,
|
|
}]);
|
|
|
|
await expect(userToInvite.get('/user'))
|
|
.to.eventually.have.deep.property('invitations.guilds[0].id', group._id);
|
|
});
|
|
|
|
it('invites multiple users to a group by uuid', async () => {
|
|
let userToInvite = await generateUser();
|
|
let userToInvite2 = await generateUser();
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInvite._id, userToInvite2._id],
|
|
})).to.eventually.deep.equal([
|
|
{
|
|
id: group._id,
|
|
name: groupName,
|
|
inviter: inviter._id,
|
|
publicGuild: false,
|
|
},
|
|
{
|
|
id: group._id,
|
|
name: groupName,
|
|
inviter: inviter._id,
|
|
publicGuild: false,
|
|
},
|
|
]);
|
|
|
|
await expect(userToInvite.get('/user')).to.eventually.have.deep.property('invitations.guilds[0].id', group._id);
|
|
await expect(userToInvite2.get('/user')).to.eventually.have.deep.property('invitations.guilds[0].id', group._id);
|
|
});
|
|
|
|
it('returns an error when inviting multiple users and a user is not found', async () => {
|
|
let userToInvite = await generateUser();
|
|
let fakeID = generateUUID();
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInvite._id, fakeID],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 404,
|
|
error: 'NotFound',
|
|
message: t('userWithIDNotFound', {userId: fakeID}),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('email invites', () => {
|
|
let testInvite = {name: 'test', email: 'test@habitica.com'};
|
|
|
|
it('returns an error when invite is missing an email', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
emails: [{name: 'test'}],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('inviteMissingEmail'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when emails is not an array', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
emails: {testInvite},
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('emailsMustBeAnArray'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when emails is empty and uuids is not passed', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
emails: [],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('inviteMissingEmail'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when there are more than INVITES_LIMIT emails', async () => {
|
|
let emails = [];
|
|
|
|
for (let i = 0; i < 101; i += 1) {
|
|
emails.push(`${generateUUID()}@habitica.com`);
|
|
}
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
emails,
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}),
|
|
});
|
|
});
|
|
|
|
it('returns an error when a user has sent the max number of email invites', async () => {
|
|
let inviterWithMax = await generateUser({
|
|
invitesSent: MAX_EMAIL_INVITES_BY_USER,
|
|
balance: 4,
|
|
});
|
|
let tmpGroup = await inviterWithMax.post('/groups', {
|
|
name: groupName,
|
|
type: 'guild',
|
|
});
|
|
|
|
await expect(inviterWithMax.post(`/groups/${tmpGroup._id}/invite`, {
|
|
emails: [testInvite],
|
|
inviter: 'inviter name',
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}),
|
|
});
|
|
});
|
|
|
|
it('invites a user to a group by email', async () => {
|
|
let res = await inviter.post(`/groups/${group._id}/invite`, {
|
|
emails: [testInvite],
|
|
inviter: 'inviter name',
|
|
});
|
|
|
|
let updatedUser = await inviter.sync();
|
|
|
|
expect(res).to.exist;
|
|
expect(updatedUser.invitesSent).to.eql(1);
|
|
});
|
|
|
|
it('invites multiple users to a group by email', async () => {
|
|
let res = await inviter.post(`/groups/${group._id}/invite`, {
|
|
emails: [testInvite, {name: 'test2', email: 'test2@habitica.com'}],
|
|
});
|
|
|
|
let updatedUser = await inviter.sync();
|
|
|
|
expect(res).to.exist;
|
|
expect(updatedUser.invitesSent).to.eql(2);
|
|
});
|
|
});
|
|
|
|
describe('user and email invites', () => {
|
|
it('returns an error when emails and uuids are not provided', async () => {
|
|
await expect(inviter.post(`/groups/${group._id}/invite`))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('canOnlyInviteEmailUuid'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when there are more than INVITES_LIMIT uuids and emails', async () => {
|
|
let emails = [];
|
|
let uuids = [];
|
|
|
|
for (let i = 0; i < 50; i += 1) {
|
|
emails.push(`${generateUUID()}@habitica.com`);
|
|
}
|
|
|
|
for (let i = 0; i < 51; i += 1) {
|
|
uuids.push(generateUUID());
|
|
}
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
emails,
|
|
uuids,
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}),
|
|
});
|
|
});
|
|
|
|
it('invites users to a group by uuid and email', async () => {
|
|
let newUser = await generateUser();
|
|
let invite = await inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [newUser._id],
|
|
emails: [{name: 'test', email: 'test@habitica.com'}],
|
|
});
|
|
let invitedUser = await newUser.get('/user');
|
|
|
|
expect(invitedUser.invitations.guilds[0].id).to.equal(group._id);
|
|
expect(invite).to.exist;
|
|
});
|
|
|
|
it('invites marks invite with cancelled plan', async () => {
|
|
let cancelledPlanGroup = await generateGroup(inviter, {
|
|
type: 'guild',
|
|
name: generateUUID(),
|
|
});
|
|
await cancelledPlanGroup.createCancelledSubscription();
|
|
|
|
let newUser = await generateUser();
|
|
let invite = await inviter.post(`/groups/${cancelledPlanGroup._id}/invite`, {
|
|
uuids: [newUser._id],
|
|
emails: [{name: 'test', email: 'test@habitica.com'}],
|
|
});
|
|
let invitedUser = await newUser.get('/user');
|
|
|
|
expect(invitedUser.invitations.guilds[0].id).to.equal(cancelledPlanGroup._id);
|
|
expect(invitedUser.invitations.guilds[0].cancelledPlan).to.be.true;
|
|
expect(invite).to.exist;
|
|
});
|
|
});
|
|
|
|
describe('guild invites', () => {
|
|
it('returns an error when invited user is already invited to the group', async () => {
|
|
let userToInivite = await generateUser();
|
|
await inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInivite._id],
|
|
});
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInivite._id],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('userAlreadyInvitedToGroup'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when invited user is already in the group', async () => {
|
|
let userToInvite = await generateUser();
|
|
await inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
});
|
|
await userToInvite.post(`/groups/${group._id}/join`);
|
|
|
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('userAlreadyInGroup'),
|
|
});
|
|
});
|
|
|
|
it('allows 30+ members in a guild', async () => {
|
|
let invitesToGenerate = [];
|
|
// Generate 30 users to invite (30 + leader = 31 members)
|
|
for (let i = 0; i < PARTY_LIMIT_MEMBERS; i++) {
|
|
invitesToGenerate.push(generateUser());
|
|
}
|
|
let generatedInvites = await Promise.all(invitesToGenerate);
|
|
// Invite users
|
|
expect(await inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: generatedInvites.map(invite => invite._id),
|
|
})).to.be.an('array');
|
|
});
|
|
|
|
// @TODO: Add this after we are able to mock the group plan route
|
|
xit('returns an error when a non-leader invites to a group plan', async () => {
|
|
let userToInvite = await generateUser();
|
|
|
|
let nonGroupLeader = await generateUser();
|
|
await inviter.post(`/groups/${group._id}/invite`, {
|
|
uuids: [nonGroupLeader._id],
|
|
});
|
|
await nonGroupLeader.post(`/groups/${group._id}/join`);
|
|
|
|
await expect(nonGroupLeader.post(`/groups/${group._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('onlyGroupLeaderCanInviteToGroupPlan'),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('party invites', () => {
|
|
let party;
|
|
|
|
beforeEach(async () => {
|
|
party = await inviter.post('/groups', {
|
|
name: 'Test Party',
|
|
type: 'party',
|
|
});
|
|
});
|
|
|
|
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`, {
|
|
uuids: [userToInvite._id],
|
|
});
|
|
|
|
await expect(inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('userAlreadyPendingInvitation'),
|
|
});
|
|
});
|
|
|
|
it('returns an error when invited user is already in a party of more than 1 member', async () => {
|
|
let userToInvite = await generateUser();
|
|
let userToInvite2 = await generateUser();
|
|
await inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: [userToInvite._id, userToInvite2._id],
|
|
});
|
|
await userToInvite.post(`/groups/${party._id}/join`);
|
|
await userToInvite2.post(`/groups/${party._id}/join`);
|
|
|
|
await expect(inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 401,
|
|
error: 'NotAuthorized',
|
|
message: t('userAlreadyInAParty'),
|
|
});
|
|
});
|
|
|
|
it('allow inviting a user to a party if they are partying solo', async () => {
|
|
let userToInvite = await generateUser();
|
|
await userToInvite.post('/groups', { // add user to a party
|
|
name: 'Another Test Party',
|
|
type: 'party',
|
|
});
|
|
|
|
await inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
});
|
|
expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id);
|
|
});
|
|
|
|
it('allow inviting a user to 2 different parties', async () => {
|
|
// Create another inviter
|
|
let inviter2 = await generateUser();
|
|
|
|
// Create user to invite
|
|
let userToInvite = await generateUser();
|
|
|
|
// Create second group
|
|
let party2 = await inviter2.post('/groups', {
|
|
name: 'Test Party 2',
|
|
type: 'party',
|
|
});
|
|
|
|
// Invite to first party
|
|
await inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
});
|
|
|
|
// Invite to second party
|
|
await inviter2.post(`/groups/${party2._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
});
|
|
|
|
// Get updated user
|
|
let invitedUser = await userToInvite.get('/user');
|
|
|
|
expect(invitedUser.invitations.parties.length).to.equal(2);
|
|
expect(invitedUser.invitations.parties[0].id).to.equal(party._id);
|
|
expect(invitedUser.invitations.parties[1].id).to.equal(party2._id);
|
|
});
|
|
|
|
it('allow inviting a user if party id is not associated with a real party', async () => {
|
|
let userToInvite = await generateUser({
|
|
party: { _id: generateUUID() },
|
|
});
|
|
|
|
await inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: [userToInvite._id],
|
|
});
|
|
expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id);
|
|
});
|
|
|
|
it('allows 30 members in a party', async () => {
|
|
let invitesToGenerate = [];
|
|
// Generate 29 users to invite (29 + leader = 30 members)
|
|
for (let i = 0; i < PARTY_LIMIT_MEMBERS - 1; i++) {
|
|
invitesToGenerate.push(generateUser());
|
|
}
|
|
let generatedInvites = await Promise.all(invitesToGenerate);
|
|
// Invite users
|
|
expect(await inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: generatedInvites.map(invite => invite._id),
|
|
})).to.be.an('array');
|
|
});
|
|
|
|
it('does not allow 30+ members in a party', async () => {
|
|
let invitesToGenerate = [];
|
|
// Generate 30 users to invite (30 + leader = 31 members)
|
|
for (let i = 0; i < PARTY_LIMIT_MEMBERS; i++) {
|
|
invitesToGenerate.push(generateUser());
|
|
}
|
|
let generatedInvites = await Promise.all(invitesToGenerate);
|
|
// Invite users
|
|
await expect(inviter.post(`/groups/${party._id}/invite`, {
|
|
uuids: generatedInvites.map(invite => invite._id),
|
|
}))
|
|
.to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('partyExceedsMembersLimit', {maxMembersParty: PARTY_LIMIT_MEMBERS}),
|
|
});
|
|
});
|
|
});
|
|
});
|