mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Merge branch 'develop' into 9796-keyboard-a11y
This commit is contained in:
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# Pull Request
|
# Pull Request
|
||||||
|
|
||||||
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
|
[Please see these instructions for adding a pull request](http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
|
||||||
|
|
||||||
# Requesting a feature
|
# Requesting a feature
|
||||||
|
|
||||||
@@ -12,4 +12,4 @@ Habitica uses [Trello](https://trello.com/b/EpoYEYod/habitica) to track feature
|
|||||||
|
|
||||||
# Contributing Code
|
# Contributing Code
|
||||||
|
|
||||||
See [Contributing to Habitica](http://habitica.wikia.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29)
|
See [Contributing to Habitica](http://habitica.fandom.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29)
|
||||||
|
|||||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
|||||||
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
|
[//]: # (Note: See http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
|
||||||
|
|
||||||
[//]: # (Put Issue # here, if applicable. This will automatically close the issue if your PR is merged in)
|
[//]: # (Put Issue # here, if applicable. This will automatically close the issue if your PR is merged in)
|
||||||
Fixes put_#_and_issue_numer_here
|
Fixes put_#_and_issue_numer_here
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -40,6 +40,7 @@ test/client/unit/coverage
|
|||||||
test/client/e2e/reports
|
test/client/e2e/reports
|
||||||
test/client-old/spec/mocks/translations.js
|
test/client-old/spec/mocks/translations.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
# Elastic Beanstalk Files
|
# Elastic Beanstalk Files
|
||||||
.elasticbeanstalk/*
|
.elasticbeanstalk/*
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- '8'
|
- '10'
|
||||||
services:
|
services:
|
||||||
- mongodb
|
- mongodb
|
||||||
cache:
|
cache:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:8
|
FROM node:10
|
||||||
|
|
||||||
ENV ADMIN_EMAIL admin@habitica.com
|
ENV ADMIN_EMAIL admin@habitica.com
|
||||||
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
||||||
@@ -8,6 +8,7 @@ ENV BASE_URL https://habitica.com
|
|||||||
ENV FACEBOOK_KEY 128307497299777
|
ENV FACEBOOK_KEY 128307497299777
|
||||||
ENV GA_ID UA-33510635-1
|
ENV GA_ID UA-33510635-1
|
||||||
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
||||||
|
ENV LOGGLY_CLIENT_TOKEN ab5663bf-241f-4d14-8783-7d80db77089a
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:8
|
FROM node:10
|
||||||
|
|
||||||
# Install global packages
|
# Install global packages
|
||||||
RUN npm install -g gulp-cli mocha
|
RUN npm install -g gulp-cli mocha
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ Habitica [.
|
For an introduction to the technologies used and how the software is organized, refer to [Guidance for Blacksmiths](http://habitica.fandom.com/wiki/Guidance_for_Blacksmiths).
|
||||||
|
|
||||||
To set up a local install of Habitica for development and testing on various platforms, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally).
|
To set up a local install of Habitica for development and testing on various platforms, see [Setting up Habitica Locally](http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally).
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ minimal dependencies on the developer's local platform. It can be used
|
|||||||
on a variety of systems including Windows, Mac OS X, and Linux.
|
on a variety of systems including Windows, Mac OS X, and Linux.
|
||||||
|
|
||||||
Instructions for using the Habitica Vagrant environment are in
|
Instructions for using the Habitica Vagrant environment are in
|
||||||
[Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally).
|
[Setting up Habitica Locally](http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally).
|
||||||
|
|||||||
@@ -1,115 +1,83 @@
|
|||||||
{
|
{
|
||||||
"PORT":3000,
|
|
||||||
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
|
||||||
"IP":"0.0.0.0",
|
|
||||||
"WEB_CONCURRENCY":1,
|
|
||||||
"BASE_URL":"http://localhost:3000",
|
|
||||||
"FACEBOOK_KEY":"123456789012345",
|
|
||||||
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"GOOGLE_CLIENT_ID":"123456789012345",
|
|
||||||
"GOOGLE_CLIENT_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"PLAY_API": {
|
|
||||||
"CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"ACCESS_TOKEN":"aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"REFRESH_TOKEN":"aaaabbbbccccddddeeeeffff00001111"
|
|
||||||
},
|
|
||||||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
|
||||||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
|
||||||
"NODE_ENV":"development",
|
|
||||||
"ENABLE_CONSOLE_LOGS_IN_TEST": "false",
|
|
||||||
"CRON_SAFE_MODE":"false",
|
|
||||||
"CRON_SEMI_SAFE_MODE":"false",
|
|
||||||
"MAINTENANCE_MODE": "false",
|
|
||||||
"SESSION_SECRET":"YOUR SECRET HERE",
|
|
||||||
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
|
|
||||||
"SESSION_SECRET_IV": "12345678912345678912345678912345",
|
|
||||||
"ADMIN_EMAIL": "you@example.com",
|
"ADMIN_EMAIL": "you@example.com",
|
||||||
"SMTP_USER":"user@example.com",
|
"AMAZON_PAYMENTS_CLIENT_ID": "CLIENT_ID",
|
||||||
"SMTP_PASS":"password",
|
"AMAZON_PAYMENTS_MODE": "sandbox",
|
||||||
"SMTP_SERVICE":"Gmail",
|
"AMAZON_PAYMENTS_MWS_KEY": "MWS_KEY",
|
||||||
"SMTP_HOST":"example.com",
|
"AMAZON_PAYMENTS_MWS_SECRET": "MWS_SECRET",
|
||||||
"SMTP_PORT": 587,
|
"AMAZON_PAYMENTS_SELLER_ID": "SELLER_ID",
|
||||||
"SMTP_TLS": true,
|
|
||||||
"STRIPE_API_KEY":"aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"STRIPE_PUB_KEY":"22223333444455556666777788889999",
|
|
||||||
"NEW_RELIC_LICENSE_KEY":"NEW_RELIC_LICENSE_KEY",
|
|
||||||
"NEW_RELIC_NO_CONFIG_FILE":"true",
|
|
||||||
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
|
||||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
|
||||||
"GA_ID": "GA_ID",
|
|
||||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||||
"AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
|
"AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
|
||||||
"AMAZON_PAYMENTS": {
|
"BASE_URL": "http://localhost:3000",
|
||||||
"SELLER_ID": "SELLER_ID",
|
"CRON_SAFE_MODE": "false",
|
||||||
"CLIENT_ID": "CLIENT_ID",
|
"CRON_SEMI_SAFE_MODE": "false",
|
||||||
"MWS_KEY": "",
|
"DISABLE_REQUEST_LOGGING": "true",
|
||||||
"MWS_SECRET": ""
|
"EMAILS_COMMUNITY_MANAGER_EMAIL": "admin@habitica.com",
|
||||||
},
|
"EMAILS_PRESS_ENQUIRY_EMAIL": "admin@habitica.com",
|
||||||
"FLAG_REPORT_EMAIL": "email@mod.com,email2@mod.com",
|
"EMAILS_TECH_ASSISTANCE_EMAIL": "admin@habitica.com",
|
||||||
"EMAIL_SERVER": {
|
"EMAIL_SERVER_AUTH_PASSWORD": "password",
|
||||||
"url": "http://example.com",
|
"EMAIL_SERVER_AUTH_USER": "user",
|
||||||
"authUser": "user",
|
"EMAIL_SERVER_URL": "http://example.com",
|
||||||
"authPassword": "password"
|
"ENABLE_CONSOLE_LOGS_IN_PROD": "false",
|
||||||
},
|
"ENABLE_CONSOLE_LOGS_IN_TEST": "false",
|
||||||
"S3":{
|
"FACEBOOK_KEY": "123456789012345",
|
||||||
"bucket":"bucket",
|
"FACEBOOK_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"accessKeyId":"accessKeyId",
|
"FLAG_REPORT_EMAIL": "email@example.com, email2@example.com",
|
||||||
"secretAccessKey":"secretAccessKey"
|
"GA_ID": "GA_ID",
|
||||||
},
|
"GOOGLE_CLIENT_ID": "123456789012345",
|
||||||
"SLACK_URL": "https://hooks.slack.com/services/some-url",
|
"GOOGLE_CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"TRANSIFEX_SLACK_CHANNEL": "transifex",
|
|
||||||
"PAYPAL":{
|
|
||||||
"billing_plans": {
|
|
||||||
"basic_earned":"basic_earned",
|
|
||||||
"basic_3mo":"basic_3mo",
|
|
||||||
"basic_6mo":"basic_6mo",
|
|
||||||
"google_6mo":"google_6mo",
|
|
||||||
"basic_12mo":"basic_12mo"
|
|
||||||
},
|
|
||||||
"mode":"sandbox",
|
|
||||||
"client_id":"client_id",
|
|
||||||
"client_secret":"client_secret",
|
|
||||||
"experience_profile_id": ""
|
|
||||||
},
|
|
||||||
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
||||||
"LOGGLY_TOKEN": "token",
|
"IGNORE_REDIRECT": "true",
|
||||||
"LOGGLY_CLIENT_TOKEN": "token",
|
|
||||||
"LOGGLY_ACCOUNT": "account",
|
|
||||||
"PUSH_CONFIGS": {
|
|
||||||
"GCM_SERVER_API_KEY": "",
|
|
||||||
"APN_ENABLED": "false",
|
|
||||||
"APN_KEY_ID": "xxxxxxxxxx",
|
|
||||||
"APN_KEY": "xxxxxxxxxx",
|
|
||||||
"APN_TEAM_ID": "aaabbbcccd",
|
|
||||||
"FCM_SERVER_API_KEY": ""
|
|
||||||
},
|
|
||||||
"SITE_HTTP_AUTH": {
|
|
||||||
"ENABLED": "false",
|
|
||||||
"USERNAME": "admin",
|
|
||||||
"PASSWORD": "password"
|
|
||||||
},
|
|
||||||
"SLACK": {
|
|
||||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
|
||||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
|
||||||
"SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id"
|
|
||||||
},
|
|
||||||
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"EMAILS" : {
|
"LOGGLY_CLIENT_TOKEN": "token",
|
||||||
"COMMUNITY_MANAGER_EMAIL" : "admin@habitica.com",
|
"LOGGLY_SUBDOMAIN": "example-subdomain",
|
||||||
"TECH_ASSISTANCE_EMAIL" : "admin@habitica.com",
|
"LOGGLY_TOKEN": "example-token",
|
||||||
"PRESS_ENQUIRY_EMAIL" : "admin@habitica.com"
|
"MAINTENANCE_MODE": "false",
|
||||||
},
|
"NODE_DB_URI": "mongodb://localhost/habitrpg",
|
||||||
"LOGGLY" : {
|
"NODE_ENV": "development",
|
||||||
"TOKEN" : "example-token",
|
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
|
||||||
"SUBDOMAIN" : "exmaple-subdomain"
|
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",
|
||||||
},
|
"PAYPAL_BILLING_PLANS_basic_3mo": "basic_3mo",
|
||||||
"KAFKA": {
|
"PAYPAL_BILLING_PLANS_basic_6mo": "basic_6mo",
|
||||||
"GROUP_ID": "",
|
"PAYPAL_BILLING_PLANS_basic_earned": "basic_earned",
|
||||||
"CLOUDKARAFKA_BROKERS": "",
|
"PAYPAL_BILLING_PLANS_google_6mo": "google_6mo",
|
||||||
"CLOUDKARAFKA_USERNAME": "",
|
"PAYPAL_CLIENT_ID": "client_id",
|
||||||
"CLOUDKARAFKA_PASSWORD": "",
|
"PAYPAL_CLIENT_SECRET": "client_secret",
|
||||||
"CLOUDKARAFKA_TOPIC_PREFIX": ""
|
"PAYPAL_EXPERIENCE_PROFILE_ID": "xp_profile_id",
|
||||||
},
|
"PAYPAL_MODE": "sandbox",
|
||||||
"MIGRATION_CONNECT_STRING": "mongodb://localhost:27017/habitrpg?auto_reconnect=true"
|
"PLAY_API_ACCESS_TOKEN": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"PLAY_API_CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"PLAY_API_CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"PLAY_API_REFRESH_TOKEN": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"PORT": 3000,
|
||||||
|
"PUSH_CONFIGS_APN_ENABLED": "false",
|
||||||
|
"PUSH_CONFIGS_APN_KEY": "xxxxxxxxxx",
|
||||||
|
"PUSH_CONFIGS_APN_KEY_ID": "xxxxxxxxxx",
|
||||||
|
"PUSH_CONFIGS_APN_TEAM_ID": "aaabbbcccd",
|
||||||
|
"PUSH_CONFIGS_FCM_SERVER_API_KEY": "aaabbbcccd",
|
||||||
|
"S3_ACCESS_KEY_ID": "accessKeyId",
|
||||||
|
"S3_BUCKET": "bucket",
|
||||||
|
"S3_SECRET_ACCESS_KEY": "secretAccessKey",
|
||||||
|
"SESSION_SECRET": "YOUR SECRET HERE",
|
||||||
|
"SESSION_SECRET_IV": "12345678912345678912345678912345",
|
||||||
|
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
|
||||||
|
"SITE_HTTP_AUTH_ENABLED": "false",
|
||||||
|
"SITE_HTTP_AUTH_PASSWORD": "password",
|
||||||
|
"SITE_HTTP_AUTH_USERNAME": "admin",
|
||||||
|
"SLACK_FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||||
|
"SLACK_FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||||
|
"SLACK_SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id",
|
||||||
|
"SLACK_URL": "https://hooks.slack.com/services/some-url",
|
||||||
|
"SMTP_HOST": "example.com",
|
||||||
|
"SMTP_PASS": "password",
|
||||||
|
"SMTP_PORT": 587,
|
||||||
|
"SMTP_SERVICE": "Gmail",
|
||||||
|
"SMTP_TLS": "true",
|
||||||
|
"SMTP_USER": "user@example.com",
|
||||||
|
"STRIPE_API_KEY": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"STRIPE_PUB_KEY": "22223333444455556666777788889999",
|
||||||
|
"TEST_DB_URI": "mongodb://localhost/habitrpg_test",
|
||||||
|
"TRANSIFEX_SLACK_CHANNEL": "transifex",
|
||||||
|
"WEB_CONCURRENCY": 1,
|
||||||
|
"SKIP_SSL_CHECK_KEY": "key",
|
||||||
|
"ENABLE_STACKDRIVER_TRACING": "false"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => {
|
|||||||
|
|
||||||
gulp.task('test:api:unit', (done) => {
|
gulp.task('test:api:unit', (done) => {
|
||||||
let runner = exec(
|
let runner = exec(
|
||||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
|
testBin('istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -180,12 +180,12 @@ gulp.task('test:api:unit', (done) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test:api:unit:watch', () => {
|
gulp.task('test:api:unit:watch', () => {
|
||||||
return gulp.watch(['website/server/libs/*', 'test/api/v3/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done()));
|
return gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done()));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test:api-v3:integration', (done) => {
|
gulp.task('test:api-v3:integration', (done) => {
|
||||||
let runner = exec(
|
let runner = exec(
|
||||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
testBin('istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||||
{maxBuffer: 500 * 1024},
|
{maxBuffer: 500 * 1024},
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -217,7 +217,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => {
|
|||||||
|
|
||||||
gulp.task('test:api-v4:integration', (done) => {
|
gulp.task('test:api-v4:integration', (done) => {
|
||||||
let runner = exec(
|
let runner = exec(
|
||||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
|
testBin('istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
|
||||||
{maxBuffer: 500 * 1024},
|
{maxBuffer: 500 * 1024},
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const Timer = require('./utils/timer');
|
|||||||
const connectToDb = require('./utils/connect').connectToDb;
|
const connectToDb = require('./utils/connect').connectToDb;
|
||||||
const closeDb = require('./utils/connect').closeDb;
|
const closeDb = require('./utils/connect').closeDb;
|
||||||
|
|
||||||
const message = '`This party\'s collection quest has been made easier! For details, refer to http://habitica.wikia.com/wiki/User_blog:LadyAlys/Collection_Quests_are_Now_Easier`';
|
const message = '`This party\'s collection quest has been made easier! For details, refer to http://habitica.fandom.com/wiki/User_blog:LadyAlys/Collection_Quests_are_Now_Easier`';
|
||||||
|
|
||||||
const timer = new Timer();
|
const timer = new Timer();
|
||||||
|
|
||||||
|
|||||||
66
migrations/archive/2018/20181023_veteran_pet_ladder.js
Normal file
66
migrations/archive/2018/20181023_veteran_pet_ladder.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181023_veteran_pet_ladder';
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {};
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (user.items.pets['Bear-Veteran']) {
|
||||||
|
set['items.pets.Fox-Veteran'] = 5;
|
||||||
|
} else if (user.items.pets['Lion-Veteran']) {
|
||||||
|
set['items.pets.Bear-Veteran'] = 5;
|
||||||
|
} else if (user.items.pets['Tiger-Veteran']) {
|
||||||
|
set['items.pets.Lion-Veteran'] = 5;
|
||||||
|
} else if (user.items.pets['Wolf-Veteran']) {
|
||||||
|
set['items.pets.Tiger-Veteran'] = 5;
|
||||||
|
} else {
|
||||||
|
set['items.pets.Wolf-Veteran'] = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'flags.verifiedUsername': true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
migration: 1,
|
||||||
|
flags: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
109
migrations/archive/2018/20181108_username_email.js
Normal file
109
migrations/archive/2018/20181108_username_email.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
const MIGRATION_NAME = '20181108_username_email.js';
|
||||||
|
const AUTHOR_NAME = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
const AUTHOR_UUID = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send emails to eligible users announcing upcoming username changes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
import { sendTxn } from '../../../website/server/libs/email';
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
const BASE_URL = nconf.get('BASE_URL');
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'flags.verifiedUsername': {$ne: true},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2018-10-25')},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 100,
|
||||||
|
fields: [
|
||||||
|
'_id',
|
||||||
|
'auth',
|
||||||
|
'preferences',
|
||||||
|
'profile',
|
||||||
|
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let userPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(() => delay(7000))
|
||||||
|
.then(() => {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}});
|
||||||
|
|
||||||
|
sendTxn(
|
||||||
|
user,
|
||||||
|
'username-change-follow-up',
|
||||||
|
[{name: 'LOGIN_NAME', content: user.auth.local.username},
|
||||||
|
{name: 'BASE_URL', content: BASE_URL}]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
if (user._id === AUTHOR_UUID) console.warn(`${AUTHOR_NAME} processed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay (t, v) {
|
||||||
|
return new Promise(function batchPause (resolve) {
|
||||||
|
setTimeout(resolve.bind(null, v), t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
109
migrations/archive/2018/20181122_turkey_day.js
Normal file
109
migrations/archive/2018/20181122_turkey_day.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181122_turkey_day';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.armor_special_turkeyArmorBase !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_turkeyHelmGilded'] = false;
|
||||||
|
set['items.gear.owned.armor_special_turkeyArmorGilded'] = false;
|
||||||
|
set['items.gear.owned.back_special_turkeyTailGilded'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_turkeyHelmGilded',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.armor_special_turkeyArmorGilded',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.back_special_turkeyTailGilded',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
|
||||||
|
set['items.gear.owned.head_special_turkeyHelmBase'] = false;
|
||||||
|
set['items.gear.owned.armor_special_turkeyArmorBase'] = false;
|
||||||
|
set['items.gear.owned.back_special_turkeyTailBase'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_turkeyHelmBase',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.armor_special_turkeyArmorBase',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.back_special_turkeyTailBase',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
|
||||||
|
set['items.mounts.Turkey-Gilded'] = true;
|
||||||
|
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
|
||||||
|
set['items.pets.Turkey-Gilded'] = 5;
|
||||||
|
} else if (user.items && user.items.pets && user.items.pets['Turkey-Base']) {
|
||||||
|
set['items.mounts.Turkey-Base'] = true;
|
||||||
|
} else {
|
||||||
|
set['items.pets.Turkey-Base'] = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
if (push) {
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||||
|
} else {
|
||||||
|
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
110
migrations/archive/2018/20181231_nye.js
Normal file
110
migrations/archive/2018/20181231_nye.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181231_nye';
|
||||||
|
import { model as User } from '../../../website/server/models/user';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {'flags.newStuff': true};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2018'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2018',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2017'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2017',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2016'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2016',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2015'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2015',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2014'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2014',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.head_special_nye'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
88
migrations/archive/2019/20190131_habit_birthday.js
Normal file
88
migrations/archive/2019/20190131_habit_birthday.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20190131_habit_birthday';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { model as User } from '../../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const inc = {
|
||||||
|
'items.food.Cake_Skeleton': 1,
|
||||||
|
'items.food.Cake_Base': 1,
|
||||||
|
'items.food.Cake_CottonCandyBlue': 1,
|
||||||
|
'items.food.Cake_CottonCandyPink': 1,
|
||||||
|
'items.food.Cake_Shade': 1,
|
||||||
|
'items.food.Cake_White': 1,
|
||||||
|
'items.food.Cake_Golden': 1,
|
||||||
|
'items.food.Cake_Zombie': 1,
|
||||||
|
'items.food.Cake_Desert': 1,
|
||||||
|
'items.food.Cake_Red': 1,
|
||||||
|
'achievements.habitBirthdays': 1,
|
||||||
|
};
|
||||||
|
const set = {};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.armor_special_birthday2018 !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_birthday2019'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2019', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_birthday2017 !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_birthday2018'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_birthday2016 !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_birthday2017'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_birthday2015 !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_birthday2016'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_birthday !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_birthday2015'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', _id: uuid()}};
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.armor_special_birthday'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', _id: uuid()}};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: push}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2019-01-15')},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
110
migrations/archive/mystery-items-old.js
Normal file
110
migrations/archive/mystery-items-old.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
|
||||||
|
const migrationName = 'mystery-items-201808.js'; // Update per month
|
||||||
|
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Award this month's mystery items to subscribers
|
||||||
|
*/
|
||||||
|
const MYSTERY_ITEMS = ['armor_mystery_201810', 'head_mystery_201810'];
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
let UserNotification = require('../../website/server/models/userNotification').model;
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: migrationName},
|
||||||
|
'purchased.plan.customerId': { $ne: null },
|
||||||
|
$or: [
|
||||||
|
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
||||||
|
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||||
|
{ 'purchased.plan.dateTerminated': { $eq: null } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [
|
||||||
|
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let userPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(() => {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const addToSet = {
|
||||||
|
'purchased.plan.mysteryItems': {
|
||||||
|
$each: MYSTERY_ITEMS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const push = {
|
||||||
|
notifications: (new UserNotification({
|
||||||
|
type: 'NEW_MYSTERY_ITEMS',
|
||||||
|
data: {
|
||||||
|
MYSTERY_ITEMS,
|
||||||
|
},
|
||||||
|
})).toJSON(),
|
||||||
|
};
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$addToSet: addToSet, $push: push});
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||||
|
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${ count } users processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
@@ -17,7 +17,7 @@ function setUpServer () {
|
|||||||
setUpServer();
|
setUpServer();
|
||||||
|
|
||||||
// Replace this with your migration
|
// Replace this with your migration
|
||||||
const processUsers = require('../scripts/gdpr-delete-users.js');
|
const processUsers = require('./users/mystery-items.js');
|
||||||
processUsers()
|
processUsers()
|
||||||
.then(function success () {
|
.then(function success () {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
/* eslint-disable no-console */
|
|
||||||
const MIGRATION_NAME = '20181023_veteran_pet_ladder';
|
|
||||||
import { model as User } from '../../website/server/models/user';
|
|
||||||
|
|
||||||
function processUsers (lastId) {
|
|
||||||
let query = {
|
|
||||||
migration: {$ne: MIGRATION_NAME},
|
|
||||||
'flags.verifiedUsername': true,
|
|
||||||
};
|
|
||||||
|
|
||||||
let fields = {
|
|
||||||
'items.pets': 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (lastId) {
|
|
||||||
query._id = {
|
|
||||||
$gt: lastId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return User.find(query)
|
|
||||||
.limit(250)
|
|
||||||
.sort({_id: 1})
|
|
||||||
.select(fields)
|
|
||||||
.then(updateUsers)
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err);
|
|
||||||
return exiting(1, `ERROR! ${err}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let progressCount = 1000;
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
function updateUsers (users) {
|
|
||||||
if (!users || users.length === 0) {
|
|
||||||
console.warn('All appropriate users found and modified.');
|
|
||||||
displayData();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let userPromises = users.map(updateUser);
|
|
||||||
let lastUser = users[users.length - 1];
|
|
||||||
|
|
||||||
return Promise.all(userPromises)
|
|
||||||
.then(() => {
|
|
||||||
processUsers(lastUser._id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUser (user) {
|
|
||||||
count++;
|
|
||||||
|
|
||||||
let set = {migration: MIGRATION_NAME};
|
|
||||||
|
|
||||||
if (user.items.pets['Bear-Veteran']) {
|
|
||||||
set['items.pets.Fox-Veteran'] = 5;
|
|
||||||
} else if (user.items.pets['Lion-Veteran']) {
|
|
||||||
set['items.pets.Bear-Veteran'] = 5;
|
|
||||||
} else if (user.items.pets['Tiger-Veteran']) {
|
|
||||||
set['items.pets.Lion-Veteran'] = 5;
|
|
||||||
} else if (user.items.pets['Wolf-Veteran']) {
|
|
||||||
set['items.pets.Tiger-Veteran'] = 5;
|
|
||||||
} else {
|
|
||||||
set['items.pets.Wolf-Veteran'] = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
|
||||||
|
|
||||||
return user.update({_id: user._id}, {$set: set}).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayData () {
|
|
||||||
console.warn(`\n${count} users processed\n`);
|
|
||||||
return exiting(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function exiting (code, msg) {
|
|
||||||
code = code || 0; // 0 = success
|
|
||||||
if (code && !msg) {
|
|
||||||
msg = 'ERROR!';
|
|
||||||
}
|
|
||||||
if (msg) {
|
|
||||||
if (code) {
|
|
||||||
console.error(msg);
|
|
||||||
} else {
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process.exit(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = processUsers;
|
|
||||||
61
migrations/users/bulk-email.js
Normal file
61
migrations/users/bulk-email.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
import { sendTxn } from '../../../website/server/libs/email';
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
import moment from 'moment';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
const BASE_URL = nconf.get('BASE_URL');
|
||||||
|
const EMAIL_SLUG = 'mandrill-email-slug'; // Set email template to send
|
||||||
|
const MIGRATION_NAME = 'bulk-email';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
sendTxn(
|
||||||
|
user,
|
||||||
|
EMAIL_SLUG,
|
||||||
|
[{name: 'BASE_URL', content: BASE_URL}] // Add variables from template
|
||||||
|
);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'auth.timestamps.loggedin': {$gt: moment().subtract(2, 'weeks').toDate()}, // customize or remove to target different populations
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
auth: 1,
|
||||||
|
preferences: 1,
|
||||||
|
profile: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,70 +1,13 @@
|
|||||||
import monk from 'monk';
|
/* eslint-disable no-console */
|
||||||
import nconf from 'nconf';
|
const MIGRATION_NAME = 'mystery_items_201902';
|
||||||
|
const MYSTERY_ITEMS = ['eyewear_mystery_201902', 'shield_mystery_201902'];
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
import { model as UserNotification } from '../../website/server/models/userNotification';
|
||||||
|
|
||||||
const migrationName = 'mystery-items-201808.js'; // Update per month
|
const progressCount = 1000;
|
||||||
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
|
||||||
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Award this month's mystery items to subscribers
|
|
||||||
*/
|
|
||||||
const MYSTERY_ITEMS = ['armor_mystery_201810', 'head_mystery_201810'];
|
|
||||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
|
||||||
|
|
||||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
|
||||||
let UserNotification = require('../../website/server/models/userNotification').model;
|
|
||||||
|
|
||||||
function processUsers (lastId) {
|
|
||||||
// specify a query to limit the affected users (empty for all users):
|
|
||||||
let query = {
|
|
||||||
migration: {$ne: migrationName},
|
|
||||||
'purchased.plan.customerId': { $ne: null },
|
|
||||||
$or: [
|
|
||||||
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
|
||||||
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
|
||||||
{ 'purchased.plan.dateTerminated': { $eq: null } },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (lastId) {
|
|
||||||
query._id = {
|
|
||||||
$gt: lastId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
dbUsers.find(query, {
|
|
||||||
sort: {_id: 1},
|
|
||||||
limit: 250,
|
|
||||||
fields: [
|
|
||||||
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
|
||||||
})
|
|
||||||
.then(updateUsers)
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err);
|
|
||||||
return exiting(1, `ERROR! ${ err}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let progressCount = 1000;
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
function updateUsers (users) {
|
async function updateUser (user) {
|
||||||
if (!users || users.length === 0) {
|
|
||||||
console.warn('All appropriate users found and modified.');
|
|
||||||
displayData();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let userPromises = users.map(updateUser);
|
|
||||||
let lastUser = users[users.length - 1];
|
|
||||||
|
|
||||||
return Promise.all(userPromises)
|
|
||||||
.then(() => {
|
|
||||||
processUsers(lastUser._id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUser (user) {
|
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
const addToSet = {
|
const addToSet = {
|
||||||
@@ -80,31 +23,49 @@ function updateUser (user) {
|
|||||||
},
|
},
|
||||||
})).toJSON(),
|
})).toJSON(),
|
||||||
};
|
};
|
||||||
|
const set = {
|
||||||
dbUsers.update({_id: user._id}, {$addToSet: addToSet, $push: push});
|
migration: MIGRATION_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: push, $addToSet: addToSet}).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayData () {
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'purchased.plan.customerId': { $ne: null },
|
||||||
|
$or: [
|
||||||
|
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
||||||
|
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||||
|
{ 'purchased.plan.dateTerminated': { $eq: null } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
console.warn(`\n${count} users processed\n`);
|
console.warn(`\n${count} users processed\n`);
|
||||||
return exiting(0);
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
function exiting (code, msg) {
|
|
||||||
code = code || 0; // 0 = success
|
|
||||||
if (code && !msg) {
|
|
||||||
msg = 'ERROR!';
|
|
||||||
}
|
|
||||||
if (msg) {
|
|
||||||
if (code) {
|
|
||||||
console.error(msg);
|
|
||||||
} else {
|
} else {
|
||||||
console.log(msg);
|
query._id = {
|
||||||
}
|
$gt: users[users.length - 1],
|
||||||
}
|
};
|
||||||
process.exit(code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = processUsers;
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
73
migrations/users/pi-day.js
Normal file
73
migrations/users/pi-day.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20190314_pi_day';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const inc = {
|
||||||
|
'items.food.Pie_Skeleton': 1,
|
||||||
|
'items.food.Pie_Base': 1,
|
||||||
|
'items.food.Pie_CottonCandyBlue': 1,
|
||||||
|
'items.food.Pie_CottonCandyPink': 1,
|
||||||
|
'items.food.Pie_Shade': 1,
|
||||||
|
'items.food.Pie_White': 1,
|
||||||
|
'items.food.Pie_Golden': 1,
|
||||||
|
'items.food.Pie_Zombie': 1,
|
||||||
|
'items.food.Pie_Desert': 1,
|
||||||
|
'items.food.Pie_Red': 1,
|
||||||
|
};
|
||||||
|
const set = {};
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
set['items.gear.owned.head_special_piDay'] = false;
|
||||||
|
set['items.gear.owned.shield_special_piDay'] = false;
|
||||||
|
const push = [
|
||||||
|
{type: 'marketGear', path: 'gear.flat.head_special_piDay', _id: uuid()},
|
||||||
|
{type: 'marketGear', path: 'gear.flat.shield_special_piDay', _id: uuid()},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2019-02-15')},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
81
migrations/users/take-this.js
Normal file
81
migrations/users/take-this.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181203_take_this';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') {
|
||||||
|
push = false;
|
||||||
|
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.back_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.body_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.weapon_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: uuid()}};
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.shield_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: uuid()}};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
if (push) {
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: push}).exec();
|
||||||
|
} else {
|
||||||
|
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
challenges: '00708425-d477-41a5-bf27-6270466e7976',
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
5957
package-lock.json
generated
5957
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
49
package.json
49
package.json
@@ -1,17 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "habitica",
|
"name": "habitica",
|
||||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||||
"version": "4.69.1",
|
"version": "4.88.0",
|
||||||
"main": "./website/server/index.js",
|
"main": "./website/server/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@google-cloud/trace-agent": "^3.5.2",
|
||||||
"@slack/client": "^3.8.1",
|
"@slack/client": "^3.8.1",
|
||||||
"accepts": "^1.3.5",
|
"accepts": "^1.3.5",
|
||||||
"amazon-payments": "^0.2.7",
|
"amazon-payments": "^0.2.7",
|
||||||
"amplitude": "^3.5.0",
|
"amplitude": "^3.5.0",
|
||||||
|
"amplitude-js": "^4.6.0-beta.2",
|
||||||
"apidoc": "^0.17.5",
|
"apidoc": "^0.17.5",
|
||||||
"apn": "^2.2.0",
|
"apn": "^2.2.0",
|
||||||
"autoprefixer": "^8.5.0",
|
"autoprefixer": "^8.5.0",
|
||||||
"aws-sdk": "^2.329.0",
|
"aws-sdk": "^2.400.0",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"axios-progress-bar": "^1.2.0",
|
"axios-progress-bar": "^1.2.0",
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
@@ -29,11 +31,11 @@
|
|||||||
"bcrypt": "^3.0.1",
|
"bcrypt": "^3.0.1",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"bootstrap": "^4.1.1",
|
"bootstrap": "^4.1.1",
|
||||||
"bootstrap-vue": "^2.0.0-rc.9",
|
"bootstrap-vue": "^2.0.0-rc.13",
|
||||||
"compression": "^1.7.2",
|
"compression": "^1.7.2",
|
||||||
"cookie-session": "^1.2.0",
|
"cookie-session": "^1.2.0",
|
||||||
"coupon-code": "^0.4.5",
|
"coupon-code": "^0.4.5",
|
||||||
"cross-env": "^5.1.5",
|
"cross-env": "^5.2.0",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"csv-stringify": "^4.3.1",
|
"csv-stringify": "^4.3.1",
|
||||||
"cwait": "^1.1.1",
|
"cwait": "^1.1.1",
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
"got": "^9.0.0",
|
"got": "^9.0.0",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"gulp-babel": "^7.0.1",
|
"gulp-babel": "^7.0.1",
|
||||||
"gulp-imagemin": "^4.1.0",
|
"gulp-imagemin": "^5.0.3",
|
||||||
"gulp-nodemon": "^2.4.1",
|
"gulp-nodemon": "^2.4.1",
|
||||||
"gulp.spritesmith": "^6.9.0",
|
"gulp.spritesmith": "^6.9.0",
|
||||||
"habitica-markdown": "^1.3.0",
|
"habitica-markdown": "^1.3.0",
|
||||||
@@ -62,12 +64,12 @@
|
|||||||
"method-override": "^3.0.0",
|
"method-override": "^3.0.0",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
"moment-recur": "^1.0.7",
|
"moment-recur": "^1.0.7",
|
||||||
"mongoose": "^5.3.4",
|
"mongoose": "^5.4.11",
|
||||||
"morgan": "^1.7.0",
|
"morgan": "^1.7.0",
|
||||||
"nconf": "^0.10.0",
|
"nconf": "^0.10.0",
|
||||||
"node-gcm": "^1.0.2",
|
"node-gcm": "^1.0.2",
|
||||||
"node-sass": "^4.9.0",
|
"node-sass": "^4.9.0",
|
||||||
"nodemailer": "^4.6.4",
|
"nodemailer": "^5.0.0",
|
||||||
"ora": "^3.0.0",
|
"ora": "^3.0.0",
|
||||||
"pageres": "^4.1.1",
|
"pageres": "^4.1.1",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
@@ -80,17 +82,17 @@
|
|||||||
"ps-tree": "^1.0.0",
|
"ps-tree": "^1.0.0",
|
||||||
"pug": "^2.0.3",
|
"pug": "^2.0.3",
|
||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
"sass-loader": "^7.0.0",
|
"sass-loader": "^7.0.3",
|
||||||
"shelljs": "^0.8.2",
|
"shelljs": "^0.8.2",
|
||||||
"short-uuid": "^3.0.0",
|
"short-uuid": "^3.0.0",
|
||||||
"smartbanner.js": "^1.9.1",
|
"smartbanner.js": "^1.9.1",
|
||||||
"stripe": "^5.9.0",
|
"stripe": "^5.9.0",
|
||||||
"superagent": "^3.8.3",
|
"superagent": "^4.0.0",
|
||||||
"svg-inline-loader": "^0.8.0",
|
"svg-inline-loader": "^0.8.0",
|
||||||
"svg-url-loader": "^2.3.2",
|
"svg-url-loader": "^2.3.2",
|
||||||
"svgo": "^1.0.5",
|
"svgo": "^1.0.5",
|
||||||
"svgo-loader": "^2.1.0",
|
"svgo-loader": "^2.1.0",
|
||||||
"universal-analytics": "^0.4.16",
|
"universal-analytics": "^0.4.17",
|
||||||
"update": "^0.7.4",
|
"update": "^0.7.4",
|
||||||
"upgrade": "^1.1.0",
|
"upgrade": "^1.1.0",
|
||||||
"url-loader": "^1.0.0",
|
"url-loader": "^1.0.0",
|
||||||
@@ -98,24 +100,24 @@
|
|||||||
"uuid": "^3.0.1",
|
"uuid": "^3.0.1",
|
||||||
"validator": "^10.5.0",
|
"validator": "^10.5.0",
|
||||||
"vinyl-buffer": "^1.0.1",
|
"vinyl-buffer": "^1.0.1",
|
||||||
"vue": "^2.5.16",
|
"vue": "^2.6.4",
|
||||||
"vue-loader": "^14.2.2",
|
"vue-loader": "^14.2.2",
|
||||||
"vue-mugen-scroll": "^0.2.1",
|
"vue-mugen-scroll": "^0.2.1",
|
||||||
"vue-router": "^3.0.0",
|
"vue-router": "^3.0.0",
|
||||||
"vue-style-loader": "^4.1.0",
|
"vue-style-loader": "^4.1.0",
|
||||||
"vue-template-compiler": "^2.5.16",
|
"vue-template-compiler": "^2.6.4",
|
||||||
"vuedraggable": "^2.15.0",
|
"vuedraggable": "^2.15.0",
|
||||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
|
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
|
||||||
"webpack": "^3.12.0",
|
"webpack": "^3.12.0",
|
||||||
"webpack-merge": "^4.0.0",
|
"webpack-merge": "^4.1.3",
|
||||||
"winston": "^2.4.2",
|
"winston": "^2.4.3",
|
||||||
"winston-loggly-bulk": "^2.0.2",
|
"winston-loggly-bulk": "^2.0.2",
|
||||||
"xml2js": "^0.4.4"
|
"xml2js": "^0.4.4"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.9.4",
|
"node": "^10",
|
||||||
"npm": "^5.6.0"
|
"npm": "^6"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.vue .",
|
"lint": "eslint --ext .js,.vue .",
|
||||||
@@ -144,13 +146,13 @@
|
|||||||
"apidoc": "gulp apidoc"
|
"apidoc": "gulp apidoc"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/test-utils": "^1.0.0-beta.16",
|
"@vue/test-utils": "^1.0.0-beta.29",
|
||||||
"babel-plugin-istanbul": "^4.1.6",
|
"babel-plugin-istanbul": "^4.1.6",
|
||||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"chalk": "^2.4.1",
|
"chalk": "^2.4.1",
|
||||||
"chromedriver": "^2.38.3",
|
"chromedriver": "^2.40.0",
|
||||||
"connect-history-api-fallback": "^1.1.0",
|
"connect-history-api-fallback": "^1.1.0",
|
||||||
"coveralls": "^3.0.1",
|
"coveralls": "^3.0.1",
|
||||||
"cross-spawn": "^6.0.5",
|
"cross-spawn": "^6.0.5",
|
||||||
@@ -162,9 +164,9 @@
|
|||||||
"eslint-plugin-mocha": "^5.0.0",
|
"eslint-plugin-mocha": "^5.0.0",
|
||||||
"eventsource-polyfill": "^0.9.6",
|
"eventsource-polyfill": "^0.9.6",
|
||||||
"expect.js": "^0.3.1",
|
"expect.js": "^0.3.1",
|
||||||
"http-proxy-middleware": "^0.18.0",
|
"http-proxy-middleware": "^0.19.0",
|
||||||
"istanbul": "^1.1.0-alpha.1",
|
"istanbul": "^1.1.0-alpha.1",
|
||||||
"karma": "^3.0.0",
|
"karma": "^3.1.3",
|
||||||
"karma-babel-preprocessor": "^7.0.0",
|
"karma-babel-preprocessor": "^7.0.0",
|
||||||
"karma-chai-plugins": "^0.9.0",
|
"karma-chai-plugins": "^0.9.0",
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
@@ -180,7 +182,7 @@
|
|||||||
"mocha": "^5.1.1",
|
"mocha": "^5.1.1",
|
||||||
"monk": "^6.0.6",
|
"monk": "^6.0.6",
|
||||||
"nightwatch": "^0.9.21",
|
"nightwatch": "^0.9.21",
|
||||||
"puppeteer": "^1.4.0",
|
"puppeteer": "^1.5.0",
|
||||||
"require-again": "^2.0.0",
|
"require-again": "^2.0.0",
|
||||||
"selenium-server": "^3.12.0",
|
"selenium-server": "^3.12.0",
|
||||||
"sinon": "^6.3.5",
|
"sinon": "^6.3.5",
|
||||||
@@ -190,8 +192,5 @@
|
|||||||
"webpack-dev-middleware": "^2.0.5",
|
"webpack-dev-middleware": "^2.0.5",
|
||||||
"webpack-hot-middleware": "^2.22.2"
|
"webpack-hot-middleware": "^2.22.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {}
|
||||||
"memwatch-next": "^0.3.0",
|
|
||||||
"node-rdkafka": "^2.3.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ async function _deleteAmplitudeData (userId, email) {
|
|||||||
if (response) console.log(`${response.status} ${response.statusText}`);
|
if (response) console.log(`${response.status} ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _deleteHabiticaData (user) {
|
async function _deleteHabiticaData (user, email) {
|
||||||
await User.update(
|
await User.update(
|
||||||
{_id: user._id},
|
{_id: user._id},
|
||||||
{$set: {
|
{$set: {
|
||||||
'auth.local.passwordHashMethod': 'bcrypt',
|
'auth.local.email': email,
|
||||||
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
||||||
|
'auth.local.passwordHashMethod': 'bcrypt',
|
||||||
}}
|
}}
|
||||||
);
|
);
|
||||||
const response = await axios.delete(
|
const response = await axios.delete(
|
||||||
@@ -75,7 +76,7 @@ async function _processEmailAddress (email) {
|
|||||||
} else {
|
} else {
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
||||||
await _deleteHabiticaData(user); // eslint-disable-line no-await-in-loop
|
await _deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,21 @@ const nconf = require('nconf');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const paypal = require('paypal-rest-sdk');
|
const paypal = require('paypal-rest-sdk');
|
||||||
const blocks = require('../website/common').content.subscriptionBlocks;
|
const blocks = require('../website/common').content.subscriptionBlocks;
|
||||||
const live = nconf.get('PAYPAL:mode') === 'live';
|
const live = nconf.get('PAYPAL_MODE') === 'live';
|
||||||
|
|
||||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
||||||
|
|
||||||
let OP = 'create'; // list create update remove
|
let OP = 'create'; // list get update create create-webprofile
|
||||||
|
|
||||||
paypal.configure({
|
paypal.configure({
|
||||||
mode: nconf.get('PAYPAL:mode'), // sandbox or live
|
mode: nconf.get('PAYPAL_MODE'), // sandbox or live
|
||||||
client_id: nconf.get('PAYPAL:client_id'),
|
client_id: nconf.get('PAYPAL_CLIENT_ID'),
|
||||||
client_secret: nconf.get('PAYPAL:client_secret'),
|
client_secret: nconf.get('PAYPAL_CLIENT_SECRET'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
|
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
|
||||||
let billingPlanTitle = 'Habitica Subscription';
|
let billingPlanTitle = 'Habitica Subscription';
|
||||||
let billingPlanAttributes = {
|
let billingPlanAttributes = {
|
||||||
name: billingPlanTitle,
|
|
||||||
description: billingPlanTitle,
|
description: billingPlanTitle,
|
||||||
type: 'INFINITE',
|
type: 'INFINITE',
|
||||||
merchant_preferences: {
|
merchant_preferences: {
|
||||||
@@ -63,7 +62,7 @@ switch (OP) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'get':
|
case 'get':
|
||||||
paypal.billingPlan.get(nconf.get('PAYPAL:billing_plans:12'), (err, plan) => {
|
paypal.billingPlan.get(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), (err, plan) => {
|
||||||
console.log({err, plan});
|
console.log({err, plan});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -75,7 +74,7 @@ switch (OP) {
|
|||||||
cancel_url: 'https://habitica.com',
|
cancel_url: 'https://habitica.com',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
paypal.billingPlan.update(nconf.get('PAYPAL:billing_plans:12'), updatePayload, (err, res) => {
|
paypal.billingPlan.update(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), updatePayload, (err, res) => {
|
||||||
console.log({err, plan: res});
|
console.log({err, plan: res});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -101,9 +100,6 @@ switch (OP) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'remove': break;
|
|
||||||
|
|
||||||
case 'create-webprofile':
|
case 'create-webprofile':
|
||||||
let webexpinfo = {
|
let webexpinfo = {
|
||||||
name: 'HabiticaProfile',
|
name: 'HabiticaProfile',
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
For information about writing and running tests, see [Using Your Local Install to Modify Habitica's Website and API](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API).
|
For information about writing and running tests, see [Using Your Local Install to Modify Habitica's Website and API](http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API).
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
BadRequest,
|
BadRequest,
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
NotFound,
|
NotFound,
|
||||||
|
NotificationNotFound,
|
||||||
} from '../../../../website/server/libs/errors';
|
} from '../../../../website/server/libs/errors';
|
||||||
|
import i18n from '../../../../website/common/script/i18n';
|
||||||
|
|
||||||
describe('Custom Errors', () => {
|
describe('Custom Errors', () => {
|
||||||
describe('CustomError', () => {
|
describe('CustomError', () => {
|
||||||
@@ -66,6 +68,23 @@ describe('Custom Errors', () => {
|
|||||||
|
|
||||||
expect(notAuthorizedError.message).to.eql('Custom Error Message');
|
expect(notAuthorizedError.message).to.eql('Custom Error Message');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('NotificationNotFound', () => {
|
||||||
|
it('is an instance of NotFound', () => {
|
||||||
|
const notificationNotFoundErr = new NotificationNotFound();
|
||||||
|
expect(notificationNotFoundErr).to.be.an.instanceOf(NotFound);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it returns an http code of 404', () => {
|
||||||
|
const notificationNotFoundErr = new NotificationNotFound();
|
||||||
|
expect(notificationNotFoundErr.httpCode).to.eql(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a standard message', () => {
|
||||||
|
const notificationNotFoundErr = new NotificationNotFound();
|
||||||
|
expect(notificationNotFoundErr.message).to.eql(i18n.t('messageNotificationNotFound'));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BadRequest', () => {
|
describe('BadRequest', () => {
|
||||||
|
|||||||
@@ -245,7 +245,9 @@ describe('Password Utilities', () => {
|
|||||||
|
|
||||||
it('returns false if the user has no local auth', async () => {
|
it('returns false if the user has no local auth', async () => {
|
||||||
let user = await generateUser({
|
let user = await generateUser({
|
||||||
auth: 'not an object with valid fields',
|
auth: {
|
||||||
|
facebook: {},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
let res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import iap from '../../../../../website/server/libs/inAppPurchases';
|
|||||||
import {model as User} from '../../../../../website/server/models/user';
|
import {model as User} from '../../../../../website/server/models/user';
|
||||||
import common from '../../../../../website/common';
|
import common from '../../../../../website/common';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import {mockFindById, restoreFindById} from '../../../../helpers/mongoose.helper';
|
||||||
|
|
||||||
const i18n = common.i18n;
|
const i18n = common.i18n;
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ describe('Apple Payments', () => {
|
|||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(false);
|
.returns(false);
|
||||||
|
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -61,7 +62,7 @@ describe('Apple Payments', () => {
|
|||||||
iapGetPurchaseDataStub.restore();
|
iapGetPurchaseDataStub.restore();
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
|
||||||
|
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -71,7 +72,7 @@ describe('Apple Payments', () => {
|
|||||||
|
|
||||||
it('errors if the user cannot purchase gems', async () => {
|
it('errors if the user cannot purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -89,7 +90,7 @@ describe('Apple Payments', () => {
|
|||||||
transactionId: token,
|
transactionId: token,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -131,7 +132,7 @@ describe('Apple Payments', () => {
|
|||||||
}]);
|
}]);
|
||||||
|
|
||||||
sinon.stub(user, 'canGetGems').resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await applePayments.verifyGemPurchase(user, receipt, headers);
|
await applePayments.verifyGemPurchase({user, receipt, headers});
|
||||||
|
|
||||||
expect(iapSetupStub).to.be.calledOnce;
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
expect(iapValidateStub).to.be.calledOnce;
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
@@ -151,6 +152,38 @@ describe('Apple Payments', () => {
|
|||||||
user.canGetGems.restore();
|
user.canGetGems.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('gifts gems', async () => {
|
||||||
|
const receivingUser = new User();
|
||||||
|
await receivingUser.save();
|
||||||
|
|
||||||
|
mockFindById(receivingUser);
|
||||||
|
|
||||||
|
iapGetPurchaseDataStub.restore();
|
||||||
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
|
.returns([{productId: gemsCanPurchase[0].productId,
|
||||||
|
transactionId: token,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const gift = {uuid: receivingUser._id};
|
||||||
|
await applePayments.verifyGemPurchase({user, gift, 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: receivingUser,
|
||||||
|
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||||
|
amount: gemsCanPurchase[0].amount,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
restoreFindById();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('subscribe', () => {
|
describe('subscribe', () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import iap from '../../../../../website/server/libs/inAppPurchases';
|
|||||||
import {model as User} from '../../../../../website/server/models/user';
|
import {model as User} from '../../../../../website/server/models/user';
|
||||||
import common from '../../../../../website/common';
|
import common from '../../../../../website/common';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import {mockFindById, restoreFindById} from '../../../../helpers/mongoose.helper';
|
||||||
|
|
||||||
const i18n = common.i18n;
|
const i18n = common.i18n;
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ describe('Google Payments', () => {
|
|||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(false);
|
.returns(false);
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -55,7 +56,7 @@ describe('Google Payments', () => {
|
|||||||
it('should throw an error if productId is invalid', async () => {
|
it('should throw an error if productId is invalid', async () => {
|
||||||
receipt = `{"token": "${token}", "productId": "invalid"}`;
|
receipt = `{"token": "${token}", "productId": "invalid"}`;
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -66,7 +67,7 @@ describe('Google Payments', () => {
|
|||||||
it('should throw an error if user cannot purchase gems', async () => {
|
it('should throw an error if user cannot purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -78,7 +79,7 @@ describe('Google Payments', () => {
|
|||||||
|
|
||||||
it('purchases gems', async () => {
|
it('purchases gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await googlePayments.verifyGemPurchase(user, receipt, signature, headers);
|
await googlePayments.verifyGemPurchase({user, receipt, signature, headers});
|
||||||
|
|
||||||
expect(iapSetupStub).to.be.calledOnce;
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
expect(iapValidateStub).to.be.calledOnce;
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
@@ -99,6 +100,34 @@ describe('Google Payments', () => {
|
|||||||
expect(user.canGetGems).to.be.calledOnce;
|
expect(user.canGetGems).to.be.calledOnce;
|
||||||
user.canGetGems.restore();
|
user.canGetGems.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('gifts gems', async () => {
|
||||||
|
const receivingUser = new User();
|
||||||
|
await receivingUser.save();
|
||||||
|
|
||||||
|
mockFindById(receivingUser);
|
||||||
|
|
||||||
|
const gift = {uuid: receivingUser._id};
|
||||||
|
await googlePayments.verifyGemPurchase({user, gift, receipt, signature, headers});
|
||||||
|
|
||||||
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledWith(iap.GOOGLE, {
|
||||||
|
data: receipt,
|
||||||
|
signature,
|
||||||
|
});
|
||||||
|
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||||
|
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||||
|
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user: receivingUser,
|
||||||
|
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||||
|
amount: 5.25,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
restoreFindById();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('subscribe', () => {
|
describe('subscribe', () => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ describe('checkout', () => {
|
|||||||
|
|
||||||
function getPaypalCreateOptions (description, amount) {
|
function getPaypalCreateOptions (description, amount) {
|
||||||
return {
|
return {
|
||||||
|
experience_profile_id: 'xp_profile_id',
|
||||||
intent: 'sale',
|
intent: 'sale',
|
||||||
payer: { payment_method: 'Paypal' },
|
payer: { payment_method: 'Paypal' },
|
||||||
redirect_urls: {
|
redirect_urls: {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ describe('slack', () => {
|
|||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
id: 'chat-id',
|
id: 'chat-id',
|
||||||
|
username: 'author',
|
||||||
user: 'Author',
|
user: 'Author',
|
||||||
uuid: 'author-id',
|
uuid: 'author-id',
|
||||||
text: 'some text',
|
text: 'some text',
|
||||||
@@ -50,11 +51,11 @@ describe('slack', () => {
|
|||||||
|
|
||||||
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
|
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
|
||||||
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
||||||
text: 'flagger (flagger-id; language: flagger-lang) flagged a message',
|
text: 'flagger (flagger-id; language: flagger-lang) flagged a group message',
|
||||||
attachments: [{
|
attachments: [{
|
||||||
fallback: 'Flag Message',
|
fallback: 'Flag Message',
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
author_name: `Author - author@example.com - author-id\n${timestamp}`,
|
author_name: `@author Author (author@example.com; author-id)\n${timestamp}`,
|
||||||
title: 'Flag in Some group - (private guild)',
|
title: 'Flag in Some group - (private guild)',
|
||||||
title_link: undefined,
|
title_link: undefined,
|
||||||
text: 'some text',
|
text: 'some text',
|
||||||
@@ -110,7 +111,7 @@ describe('slack', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('noops if no flagging url is provided', () => {
|
it('noops if no flagging url is provided', () => {
|
||||||
sandbox.stub(nconf, 'get').withArgs('SLACK:FLAGGING_URL').returns('');
|
sandbox.stub(nconf, 'get').withArgs('SLACK_FLAGGING_URL').returns('');
|
||||||
sandbox.stub(logger, 'error');
|
sandbox.stub(logger, 'error');
|
||||||
let reRequiredSlack = requireAgain('../../../../website/server/libs/slack');
|
let reRequiredSlack = requireAgain('../../../../website/server/libs/slack');
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,56 @@ describe('redirects middleware', () => {
|
|||||||
|
|
||||||
expect(res.redirect).to.have.not.been.called;
|
expect(res.redirect).to.have.not.been.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not redirect if passed skip ssl request param is passed with corrrect key', () => {
|
||||||
|
let nconfStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfStub.withArgs('BASE_URL').returns('https://habitica.com');
|
||||||
|
nconfStub.withArgs('IS_PROD').returns(true);
|
||||||
|
nconfStub.withArgs('SKIP_SSL_CHECK_KEY').returns('test-key');
|
||||||
|
|
||||||
|
req.header = sandbox.stub().withArgs('x-forwarded-proto').returns('http');
|
||||||
|
req.originalUrl = '/static/front';
|
||||||
|
req.query.skipSSLCheck = 'test-key';
|
||||||
|
|
||||||
|
const attachRedirects = requireAgain(pathToRedirectsMiddleware);
|
||||||
|
attachRedirects.forceSSL(req, res, next);
|
||||||
|
|
||||||
|
expect(res.redirect).to.have.not.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does redirect if skip ssl request param is passed with incorrrect key', () => {
|
||||||
|
let nconfStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfStub.withArgs('BASE_URL').returns('https://habitica.com');
|
||||||
|
nconfStub.withArgs('IS_PROD').returns(true);
|
||||||
|
nconfStub.withArgs('SKIP_SSL_CHECK_KEY').returns('test-key');
|
||||||
|
|
||||||
|
req.header = sandbox.stub().withArgs('x-forwarded-proto').returns('http');
|
||||||
|
req.originalUrl = '/static/front?skipSSLCheck=INVALID';
|
||||||
|
req.query.skipSSLCheck = 'INVALID';
|
||||||
|
|
||||||
|
const attachRedirects = requireAgain(pathToRedirectsMiddleware);
|
||||||
|
attachRedirects.forceSSL(req, res, next);
|
||||||
|
|
||||||
|
expect(res.redirect).to.be.calledOnce;
|
||||||
|
expect(res.redirect).to.be.calledWith('https://habitica.com/static/front?skipSSLCheck=INVALID');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does redirect if skip ssl check key is not set', () => {
|
||||||
|
let nconfStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfStub.withArgs('BASE_URL').returns('https://habitica.com');
|
||||||
|
nconfStub.withArgs('IS_PROD').returns(true);
|
||||||
|
nconfStub.withArgs('SKIP_SSL_CHECK_KEY').returns(null);
|
||||||
|
|
||||||
|
req.header = sandbox.stub().withArgs('x-forwarded-proto').returns('http');
|
||||||
|
req.originalUrl = '/static/front';
|
||||||
|
req.query.skipSSLCheck = 'INVALID';
|
||||||
|
|
||||||
|
const attachRedirects = requireAgain(pathToRedirectsMiddleware);
|
||||||
|
attachRedirects.forceSSL(req, res, next);
|
||||||
|
|
||||||
|
expect(res.redirect).to.be.calledOnce;
|
||||||
|
expect(res.redirect).to.be.calledWith('https://habitica.com/static/front');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('forceHabitica', () => {
|
context('forceHabitica', () => {
|
||||||
|
|||||||
@@ -32,8 +32,19 @@ describe('Group Model', () => {
|
|||||||
privacy: 'private',
|
privacy: 'private',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let _progress = {
|
||||||
|
up: 10,
|
||||||
|
down: 8,
|
||||||
|
collectedItems: 5,
|
||||||
|
};
|
||||||
|
|
||||||
questLeader = new User({
|
questLeader = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Quest Leader' },
|
profile: { name: 'Quest Leader' },
|
||||||
items: {
|
items: {
|
||||||
quests: {
|
quests: {
|
||||||
@@ -45,20 +56,40 @@ describe('Group Model', () => {
|
|||||||
party.leader = questLeader._id;
|
party.leader = questLeader._id;
|
||||||
|
|
||||||
participatingMember = new User({
|
participatingMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Participating Member' },
|
profile: { name: 'Participating Member' },
|
||||||
});
|
});
|
||||||
sleepingParticipatingMember = new User({
|
sleepingParticipatingMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Sleeping Participating Member' },
|
profile: { name: 'Sleeping Participating Member' },
|
||||||
preferences: { sleep: true },
|
preferences: { sleep: true },
|
||||||
});
|
});
|
||||||
nonParticipatingMember = new User({
|
nonParticipatingMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Non-Participating Member' },
|
profile: { name: 'Non-Participating Member' },
|
||||||
});
|
});
|
||||||
undecidedMember = new User({
|
undecidedMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Undecided Member' },
|
profile: { name: 'Undecided Member' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -569,7 +600,7 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if no uuids or emails are passed in', async () => {
|
it('throws an error if no uuids or emails are passed in', async () => {
|
||||||
await expect(Group.validateInvitations(null, null, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -579,7 +610,7 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if only uuids are passed in, but they are not an array', async () => {
|
it('throws an error if only uuids are passed in, but they are not an array', async () => {
|
||||||
await expect(Group.validateInvitations({ uuid: 'user-id'}, null, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({ uuids: 'user-id'}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -589,7 +620,7 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if only emails are passed in, but they are not an array', async () => {
|
it('throws an error if only emails are passed in, but they are not an array', async () => {
|
||||||
await expect(Group.validateInvitations(null, { emails: 'user@example.com'}, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({emails: 'user@example.com'}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -599,27 +630,27 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if emails are not passed in, and uuid array is empty', async () => {
|
it('throws an error if emails are not passed in, and uuid array is empty', async () => {
|
||||||
await expect(Group.validateInvitations([], null, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({uuids: []}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
});
|
});
|
||||||
expect(res.t).to.be.calledOnce;
|
expect(res.t).to.be.calledOnce;
|
||||||
expect(res.t).to.be.calledWith('inviteMissingUuid');
|
expect(res.t).to.be.calledWith('inviteMustNotBeEmpty');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if uuids are not passed in, and email array is empty', async () => {
|
it('throws an error if uuids are not passed in, and email array is empty', async () => {
|
||||||
await expect(Group.validateInvitations(null, [], res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({emails: []}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
});
|
});
|
||||||
expect(res.t).to.be.calledOnce;
|
expect(res.t).to.be.calledOnce;
|
||||||
expect(res.t).to.be.calledWith('inviteMissingEmail');
|
expect(res.t).to.be.calledWith('inviteMustNotBeEmpty');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if uuids and emails are passed in as empty arrays', async () => {
|
it('throws an error if uuids and emails are passed in as empty arrays', async () => {
|
||||||
await expect(Group.validateInvitations([], [], res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({emails: [], uuids: []}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -639,7 +670,7 @@ describe('Group Model', () => {
|
|||||||
|
|
||||||
uuids.push('one-more-uuid'); // to put it over the limit
|
uuids.push('one-more-uuid'); // to put it over the limit
|
||||||
|
|
||||||
await expect(Group.validateInvitations(uuids, emails, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({uuids, emails}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -657,33 +688,33 @@ describe('Group Model', () => {
|
|||||||
emails.push(`user-${i}@example.com`);
|
emails.push(`user-${i}@example.com`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Group.validateInvitations(uuids, emails, res);
|
await Group.validateInvitations({uuids, emails}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('does not throw an error if only user ids are passed in', async () => {
|
it('does not throw an error if only user ids are passed in', async () => {
|
||||||
await Group.validateInvitations(['user-id', 'user-id2'], null, res);
|
await Group.validateInvitations({uuids: ['user-id', 'user-id2']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if only emails are passed in', async () => {
|
it('does not throw an error if only emails are passed in', async () => {
|
||||||
await Group.validateInvitations(null, ['user1@example.com', 'user2@example.com'], res);
|
await Group.validateInvitations({emails: ['user1@example.com', 'user2@example.com']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if both uuids and emails are passed in', async () => {
|
it('does not throw an error if both uuids and emails are passed in', async () => {
|
||||||
await Group.validateInvitations(['user-id', 'user-id2'], ['user1@example.com', 'user2@example.com'], res);
|
await Group.validateInvitations({uuids: ['user-id', 'user-id2'], emails: ['user1@example.com', 'user2@example.com']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if uuids are passed in and emails are an empty array', async () => {
|
it('does not throw an error if uuids are passed in and emails are an empty array', async () => {
|
||||||
await Group.validateInvitations(['user-id', 'user-id2'], [], res);
|
await Group.validateInvitations({uuids: ['user-id', 'user-id2'], emails: []}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if emails are passed in and uuids are an empty array', async () => {
|
it('does not throw an error if emails are passed in and uuids are an empty array', async () => {
|
||||||
await Group.validateInvitations([], ['user1@example.com', 'user2@example.com'], res);
|
await Group.validateInvitations({uuids: [], emails: ['user1@example.com', 'user2@example.com']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1163,16 +1194,17 @@ describe('Group Model', () => {
|
|||||||
expect(party.quest.members).to.eql(expectedQuestMembers);
|
expect(party.quest.members).to.eql(expectedQuestMembers);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies updates to user object directly if user is participating', async () => {
|
it('applies updates to user object directly if user is participating (without resetting progress, except progress.down)', async () => {
|
||||||
await party.startQuest(participatingMember);
|
await party.startQuest(participatingMember);
|
||||||
|
|
||||||
expect(participatingMember.party.quest.key).to.eql('whale');
|
expect(participatingMember.party.quest.key).to.eql('whale');
|
||||||
|
expect(participatingMember.party.quest.progress.up).to.eql(10);
|
||||||
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
||||||
expect(participatingMember.party.quest.progress.collectedItems).to.eql(0);
|
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(participatingMember.party.quest.completed).to.eql(null);
|
expect(participatingMember.party.quest.completed).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies updates to other participating members', async () => {
|
it('applies updates to other participating members (without resetting progress, except progress.down)', async () => {
|
||||||
await party.startQuest(nonParticipatingMember);
|
await party.startQuest(nonParticipatingMember);
|
||||||
|
|
||||||
questLeader = await User.findById(questLeader._id);
|
questLeader = await User.findById(questLeader._id);
|
||||||
@@ -1180,18 +1212,21 @@ describe('Group Model', () => {
|
|||||||
sleepingParticipatingMember = await User.findById(sleepingParticipatingMember._id);
|
sleepingParticipatingMember = await User.findById(sleepingParticipatingMember._id);
|
||||||
|
|
||||||
expect(participatingMember.party.quest.key).to.eql('whale');
|
expect(participatingMember.party.quest.key).to.eql('whale');
|
||||||
|
expect(participatingMember.party.quest.progress.up).to.eql(10);
|
||||||
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
||||||
expect(participatingMember.party.quest.progress.collectedItems).to.eql(0);
|
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(participatingMember.party.quest.completed).to.eql(null);
|
expect(participatingMember.party.quest.completed).to.eql(null);
|
||||||
|
|
||||||
expect(sleepingParticipatingMember.party.quest.key).to.eql('whale');
|
expect(sleepingParticipatingMember.party.quest.key).to.eql('whale');
|
||||||
|
expect(sleepingParticipatingMember.party.quest.progress.up).to.eql(10);
|
||||||
expect(sleepingParticipatingMember.party.quest.progress.down).to.eql(0);
|
expect(sleepingParticipatingMember.party.quest.progress.down).to.eql(0);
|
||||||
expect(sleepingParticipatingMember.party.quest.progress.collectedItems).to.eql(0);
|
expect(sleepingParticipatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(sleepingParticipatingMember.party.quest.completed).to.eql(null);
|
expect(sleepingParticipatingMember.party.quest.completed).to.eql(null);
|
||||||
|
|
||||||
expect(questLeader.party.quest.key).to.eql('whale');
|
expect(questLeader.party.quest.key).to.eql('whale');
|
||||||
|
expect(questLeader.party.quest.progress.up).to.eql(10);
|
||||||
expect(questLeader.party.quest.progress.down).to.eql(0);
|
expect(questLeader.party.quest.progress.down).to.eql(0);
|
||||||
expect(questLeader.party.quest.progress.collectedItems).to.eql(0);
|
expect(questLeader.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(questLeader.party.quest.completed).to.eql(null);
|
expect(questLeader.party.quest.completed).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1202,6 +1237,9 @@ describe('Group Model', () => {
|
|||||||
undecidedMember = await User.findById(undecidedMember._id);
|
undecidedMember = await User.findById(undecidedMember._id);
|
||||||
|
|
||||||
expect(nonParticipatingMember.party.quest.key).to.not.eql('whale');
|
expect(nonParticipatingMember.party.quest.key).to.not.eql('whale');
|
||||||
|
expect(nonParticipatingMember.party.quest.progress.up).to.eql(10);
|
||||||
|
expect(nonParticipatingMember.party.quest.progress.down).to.eql(8);
|
||||||
|
expect(nonParticipatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(undecidedMember.party.quest.key).to.not.eql('whale');
|
expect(undecidedMember.party.quest.key).to.not.eql('whale');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1369,8 +1407,9 @@ describe('Group Model', () => {
|
|||||||
let userQuest = participatingMember.party.quest;
|
let userQuest = participatingMember.party.quest;
|
||||||
|
|
||||||
expect(userQuest.key).to.eql('whale');
|
expect(userQuest.key).to.eql('whale');
|
||||||
|
expect(userQuest.progress.up).to.eql(10);
|
||||||
expect(userQuest.progress.down).to.eql(0);
|
expect(userQuest.progress.down).to.eql(0);
|
||||||
expect(userQuest.progress.collectedItems).to.eql(0);
|
expect(userQuest.progress.collectedItems).to.eql(5);
|
||||||
expect(userQuest.completed).to.eql(null);
|
expect(userQuest.completed).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1670,16 +1709,23 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets user quest object to a clean state', async () => {
|
it('updates participating members quest object to a clean state (except for progress)', async () => {
|
||||||
await party.finishQuest(quest);
|
await party.finishQuest(quest);
|
||||||
|
|
||||||
let updatedLeader = await User.findById(questLeader._id);
|
questLeader = await User.findById(questLeader._id);
|
||||||
|
participatingMember = await User.findById(participatingMember._id);
|
||||||
|
|
||||||
expect(updatedLeader.party.quest.completed).to.eql('whale');
|
expect(questLeader.party.quest.completed).to.eql('whale');
|
||||||
expect(updatedLeader.party.quest.progress.up).to.eql(0);
|
expect(questLeader.party.quest.progress.up).to.eql(10);
|
||||||
expect(updatedLeader.party.quest.progress.down).to.eql(0);
|
expect(questLeader.party.quest.progress.down).to.eql(8);
|
||||||
expect(updatedLeader.party.quest.progress.collectedItems).to.eql(0);
|
expect(questLeader.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(updatedLeader.party.quest.RSVPNeeded).to.eql(false);
|
expect(questLeader.party.quest.RSVPNeeded).to.eql(false);
|
||||||
|
|
||||||
|
expect(participatingMember.party.quest.completed).to.eql('whale');
|
||||||
|
expect(participatingMember.party.quest.progress.up).to.eql(10);
|
||||||
|
expect(participatingMember.party.quest.progress.down).to.eql(8);
|
||||||
|
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
|
expect(participatingMember.party.quest.RSVPNeeded).to.eql(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1843,6 +1889,62 @@ describe('Group Model', () => {
|
|||||||
expect(options.chat).to.eql(chat);
|
expect(options.chat).to.eql(chat);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sends webhooks for users with webhooks triggered by system messages', async () => {
|
||||||
|
let guild = new Group({
|
||||||
|
name: 'some guild',
|
||||||
|
type: 'guild',
|
||||||
|
});
|
||||||
|
|
||||||
|
let memberWithWebhook = new User({
|
||||||
|
guilds: [guild._id],
|
||||||
|
webhooks: [{
|
||||||
|
type: 'groupChatReceived',
|
||||||
|
url: 'http://someurl.com',
|
||||||
|
options: {
|
||||||
|
groupId: guild._id,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
let memberWithoutWebhook = new User({
|
||||||
|
guilds: [guild._id],
|
||||||
|
});
|
||||||
|
let nonMemberWithWebhooks = new User({
|
||||||
|
webhooks: [{
|
||||||
|
type: 'groupChatReceived',
|
||||||
|
url: 'http://a-different-url.com',
|
||||||
|
options: {
|
||||||
|
groupId: generateUUID(),
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
memberWithWebhook.save(),
|
||||||
|
memberWithoutWebhook.save(),
|
||||||
|
nonMemberWithWebhooks.save(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
guild.leader = memberWithWebhook._id;
|
||||||
|
|
||||||
|
await guild.save();
|
||||||
|
|
||||||
|
const groupMessage = guild.sendChat('Test message.');
|
||||||
|
await groupMessage.save();
|
||||||
|
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
expect(groupChatReceivedWebhook.send).to.be.calledOnce;
|
||||||
|
|
||||||
|
let args = groupChatReceivedWebhook.send.args[0];
|
||||||
|
let webhooks = args[0].webhooks;
|
||||||
|
let options = args[1];
|
||||||
|
|
||||||
|
expect(webhooks).to.have.a.lengthOf(1);
|
||||||
|
expect(webhooks[0].id).to.eql(memberWithWebhook.webhooks[0].id);
|
||||||
|
expect(options.group).to.eql(guild);
|
||||||
|
expect(options.chat).to.eql(groupMessage);
|
||||||
|
});
|
||||||
|
|
||||||
it('sends webhooks for each user with webhooks in group', async () => {
|
it('sends webhooks for each user with webhooks in group', async () => {
|
||||||
let guild = new Group({
|
let guild = new Group({
|
||||||
name: 'some guild',
|
name: 'some guild',
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
_id: groupLeader._id,
|
_id: groupLeader._id,
|
||||||
id: groupLeader._id,
|
id: groupLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: groupLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: groupLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(chal.group).to.eql({
|
expect(chal.group).to.eql({
|
||||||
_id: group._id,
|
_id: group._id,
|
||||||
@@ -105,6 +113,14 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
_id: challengeLeader._id,
|
_id: challengeLeader._id,
|
||||||
id: challengeLeader._id,
|
id: challengeLeader._id,
|
||||||
profile: {name: challengeLeader.profile.name},
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(chal.group).to.eql({
|
expect(chal.group).to.eql({
|
||||||
_id: group._id,
|
_id: group._id,
|
||||||
@@ -131,6 +147,14 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
_id: challengeLeader._id,
|
_id: challengeLeader._id,
|
||||||
id: challengeLeader._id,
|
id: challengeLeader._id,
|
||||||
profile: {name: challengeLeader.profile.name},
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -179,6 +203,14 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
_id: challengeLeader._id,
|
_id: challengeLeader._id,
|
||||||
id: challengeLeader._id,
|
id: challengeLeader._id,
|
||||||
profile: {name: challengeLeader.profile.name},
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(chal.group).to.eql({
|
expect(chal.group).to.eql({
|
||||||
_id: group._id,
|
_id: group._id,
|
||||||
@@ -205,6 +237,14 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
_id: challengeLeader._id,
|
_id: challengeLeader._id,
|
||||||
id: challengeLeader._id,
|
id: challengeLeader._id,
|
||||||
profile: {name: challengeLeader.profile.name},
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,11 +65,11 @@ describe('GET /challenges/:challengeId/export/csv', () => {
|
|||||||
const sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id');
|
const sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id');
|
||||||
const splitRes = res.split('\n');
|
const splitRes = res.split('\n');
|
||||||
|
|
||||||
expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
expect(splitRes[0]).to.equal('UUID,Display Name,Username,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
||||||
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},${sortedMembers[0].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},${sortedMembers[1].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},${sortedMembers[2].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},${sortedMembers[3].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[5]).to.equal('');
|
expect(splitRes[5]).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -78,10 +78,10 @@ describe('GET /challenges/:challengeId/export/csv', () => {
|
|||||||
const res = await members[1].get(`/challenges/${challenge._id}/export/csv`);
|
const res = await members[1].get(`/challenges/${challenge._id}/export/csv`);
|
||||||
const sortedMembers = _.sortBy([members[1], members[2], groupLeader], '_id');
|
const sortedMembers = _.sortBy([members[1], members[2], groupLeader], '_id');
|
||||||
const splitRes = res.split('\n');
|
const splitRes = res.split('\n');
|
||||||
expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
expect(splitRes[0]).to.equal('UUID,Display Name,Username,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
||||||
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},${sortedMembers[0].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},${sortedMembers[1].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},${sortedMembers[2].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[4]).to.equal('');
|
expect(splitRes[4]).to.equal('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
_id: groupLeader._id,
|
_id: groupLeader._id,
|
||||||
id: groupLeader._id,
|
id: groupLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: groupLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: groupLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,8 +81,16 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
_id: leader._id,
|
_id: leader._id,
|
||||||
id: leader._id,
|
id: leader._id,
|
||||||
profile: {name: leader.profile.name},
|
profile: {name: leader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: leader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,8 +104,16 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
_id: anotherUser._id,
|
_id: anotherUser._id,
|
||||||
id: anotherUser._id,
|
id: anotherUser._id,
|
||||||
profile: {name: anotherUser.profile.name},
|
profile: {name: anotherUser.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: anotherUser.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,7 +131,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=not-true`);
|
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=not-true`);
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -126,7 +150,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let res = await user.get(`/challenges/${challenge._id}/members`);
|
let res = await user.get(`/challenges/${challenge._id}/members`);
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -145,7 +169,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=true`);
|
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=true`);
|
||||||
expect(res.length).to.equal(32);
|
expect(res.length).to.equal(32);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ describe('GET /challenges/:challengeId/members/:memberId', () => {
|
|||||||
await groupLeader.post(`/tasks/challenge/${challenge._id}`, [{type: 'habit', text: taskText}]);
|
await groupLeader.post(`/tasks/challenge/${challenge._id}`, [{type: 'habit', text: taskText}]);
|
||||||
|
|
||||||
let memberProgress = await user.get(`/challenges/${challenge._id}/members/${groupLeader._id}`);
|
let memberProgress = await user.get(`/challenges/${challenge._id}/members/${groupLeader._id}`);
|
||||||
expect(memberProgress).to.have.all.keys(['_id', 'id', 'profile', 'tasks']);
|
expect(memberProgress).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile', 'tasks']);
|
||||||
expect(memberProgress.profile).to.have.all.keys(['name']);
|
expect(memberProgress.profile).to.have.all.keys(['name']);
|
||||||
expect(memberProgress.tasks.length).to.equal(1);
|
expect(memberProgress.tasks.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -46,6 +54,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,6 +74,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -65,6 +89,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -125,6 +157,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: privateGuild.leader._id,
|
_id: privateGuild.leader._id,
|
||||||
id: privateGuild.leader._id,
|
id: privateGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -132,6 +172,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: privateGuild.leader._id,
|
_id: privateGuild.leader._id,
|
||||||
id: privateGuild.leader._id,
|
id: privateGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -235,6 +283,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -242,6 +298,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -254,6 +318,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -261,6 +333,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -288,6 +368,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -295,6 +383,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -307,6 +403,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -314,6 +418,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge.group).to.eql({
|
expect(foundChallenge.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -62,6 +70,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge1.group).to.eql({
|
expect(foundChallenge1.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -79,6 +95,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge2.group).to.eql({
|
expect(foundChallenge2.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -101,6 +125,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge1.group).to.eql({
|
expect(foundChallenge1.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -118,6 +150,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge2.group).to.eql({
|
expect(foundChallenge2.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
|
|||||||
@@ -79,6 +79,14 @@ describe('POST /challenges/:challengeId/join', () => {
|
|||||||
_id: groupLeader._id,
|
_id: groupLeader._id,
|
||||||
id: groupLeader._id,
|
id: groupLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: groupLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: groupLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res.name).to.equal(challenge.name);
|
expect(res.name).to.equal(challenge.name);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -79,6 +79,14 @@ describe('PUT /challenges/:challengeId', () => {
|
|||||||
_id: member._id,
|
_id: member._id,
|
||||||
id: member._id,
|
id: member._id,
|
||||||
profile: {name: member.profile.name},
|
profile: {name: member.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: member.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res.name).to.equal('New Challenge Name');
|
expect(res.name).to.equal('New Challenge Name');
|
||||||
expect(res.description).to.equal('New challenge description.');
|
expect(res.description).to.equal('New challenge description.');
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ describe('POST /chat/:chatId/flag', () => {
|
|||||||
|
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
||||||
text: `${user.profile.name} (${user.id}; language: en) flagged a message`,
|
text: `${user.profile.name} (${user.id}; language: en) flagged a group message`,
|
||||||
attachments: [{
|
attachments: [{
|
||||||
fallback: 'Flag Message',
|
fallback: 'Flag Message',
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
author_name: `${anotherUser.profile.name} - ${anotherUser.auth.local.email} - ${anotherUser._id}\n${timestamp}`,
|
author_name: `@${anotherUser.auth.local.username} ${anotherUser.profile.name} (${anotherUser.auth.local.email}; ${anotherUser._id})\n${timestamp}`,
|
||||||
title: 'Flag in Test Guild',
|
title: 'Flag in Test Guild',
|
||||||
title_link: `${BASE_URL}/groups/guild/${group._id}`,
|
title_link: `${BASE_URL}/groups/guild/${group._id}`,
|
||||||
text: TEST_MESSAGE,
|
text: TEST_MESSAGE,
|
||||||
@@ -98,11 +98,11 @@ describe('POST /chat/:chatId/flag', () => {
|
|||||||
|
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
||||||
text: `${newUser.profile.name} (${newUser.id}; language: en) flagged a message`,
|
text: `${newUser.profile.name} (${newUser.id}; language: en) flagged a group message`,
|
||||||
attachments: [{
|
attachments: [{
|
||||||
fallback: 'Flag Message',
|
fallback: 'Flag Message',
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
author_name: `${newUser.profile.name} - ${newUser.auth.local.email} - ${newUser._id}\n${timestamp}`,
|
author_name: `@${newUser.auth.local.username} ${newUser.profile.name} (${newUser.auth.local.email}; ${newUser._id})\n${timestamp}`,
|
||||||
title: 'Flag in Test Guild',
|
title: 'Flag in Test Guild',
|
||||||
title_link: `${BASE_URL}/groups/guild/${group._id}`,
|
title_link: `${BASE_URL}/groups/guild/${group._id}`,
|
||||||
text: TEST_MESSAGE,
|
text: TEST_MESSAGE,
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ describe('POST /chat', () => {
|
|||||||
attachments: [{
|
attachments: [{
|
||||||
fallback: 'Slur Message',
|
fallback: 'Slur Message',
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
author_name: `${user.profile.name} - ${user.auth.local.email} - ${user._id}`,
|
author_name: `@${user.auth.local.username} ${user.profile.name} (${user.auth.local.email}; ${user._id})`,
|
||||||
title: 'Slur in Test Guild',
|
title: 'Slur in Test Guild',
|
||||||
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
|
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
|
||||||
text: testSlurMessage,
|
text: testSlurMessage,
|
||||||
@@ -310,7 +310,7 @@ describe('POST /chat', () => {
|
|||||||
attachments: [{
|
attachments: [{
|
||||||
fallback: 'Slur Message',
|
fallback: 'Slur Message',
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
author_name: `${members[0].profile.name} - ${members[0].auth.local.email} - ${members[0]._id}`,
|
author_name: `@${members[0].auth.local.username} ${members[0].profile.name} (${members[0].auth.local.email}; ${members[0]._id})`,
|
||||||
title: 'Slur in Party - (private party)',
|
title: 'Slur in Party - (private party)',
|
||||||
title_link: undefined,
|
title_link: undefined,
|
||||||
text: testSlurMessage,
|
text: testSlurMessage,
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('messageCannotFlagSystemMessages', {communityManagerEmail: config.EMAILS.COMMUNITY_MANAGER_EMAIL}),
|
message: t('messageCannotFlagSystemMessages', {communityManagerEmail: config.EMAILS_COMMUNITY_MANAGER_EMAIL}),
|
||||||
});
|
});
|
||||||
// let messages = await members[0].get(`/groups/${group._id}/chat`);
|
// let messages = await members[0].get(`/groups/${group._id}/chat`);
|
||||||
// expect(messages[0].id).to.eql(skillMsg.id);
|
// expect(messages[0].id).to.eql(skillMsg.id);
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ describe('GET /groups/:groupId/invites', () => {
|
|||||||
_id: invited._id,
|
_id: invited._id,
|
||||||
id: invited._id,
|
id: invited._id,
|
||||||
profile: {name: invited.profile.name},
|
profile: {name: invited.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: invited.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +66,7 @@ describe('GET /groups/:groupId/invites', () => {
|
|||||||
let invited = await generateUser();
|
let invited = await generateUser();
|
||||||
await user.post(`/groups/${group._id}/invite`, {uuids: [invited._id]});
|
await user.post(`/groups/${group._id}/invite`, {uuids: [invited._id]});
|
||||||
let res = await user.get('/groups/party/invites');
|
let res = await user.get('/groups/party/invites');
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,7 +84,7 @@ describe('GET /groups/:groupId/invites', () => {
|
|||||||
let res = await leader.get(`/groups/${group._id}/invites`);
|
let res = await leader.get(`/groups/${group._id}/invites`);
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
}).timeout(10000);
|
}).timeout(10000);
|
||||||
|
|||||||
@@ -56,13 +56,21 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('populates only some fields', async () => {
|
it('populates only some fields', async () => {
|
||||||
await generateGroup(user, {type: 'party', name: generateUUID()});
|
await generateGroup(user, {type: 'party', name: generateUUID()});
|
||||||
let res = await user.get('/groups/party/members');
|
let res = await user.get('/groups/party/members');
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,7 +82,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||||
]);
|
]);
|
||||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||||
'size', 'hair', 'skin', 'shirt',
|
'size', 'hair', 'skin', 'shirt',
|
||||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||||
@@ -95,7 +103,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||||
]);
|
]);
|
||||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||||
'size', 'hair', 'skin', 'shirt',
|
'size', 'hair', 'skin', 'shirt',
|
||||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||||
@@ -120,7 +128,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
let res = await user.get('/groups/party/members');
|
let res = await user.get('/groups/party/members');
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -137,7 +145,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
let res = await user.get('/groups/party/members?includeAllMembers=true');
|
let res = await user.get('/groups/party/members?includeAllMembers=true');
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,73 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('username invites', () => {
|
||||||
|
it('returns an error when invited user is not found', async () => {
|
||||||
|
const fakeID = 'fakeuserid';
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [fakeID],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('userWithUsernameNotFound', {username: fakeID}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when inviting yourself to a group', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [inviter.auth.local.lowerCaseUsername],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('cannotInviteSelfToGroup'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites a user to a group by username', async () => {
|
||||||
|
const userToInvite = await generateUser();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [userToInvite.auth.local.lowerCaseUsername],
|
||||||
|
})).to.eventually.deep.equal([{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
publicGuild: false,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
await expect(userToInvite.get('/user'))
|
||||||
|
.to.eventually.have.nested.property('invitations.guilds[0].id', group._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites multiple users to a group by uuid', async () => {
|
||||||
|
const userToInvite = await generateUser();
|
||||||
|
const userToInvite2 = await generateUser();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [userToInvite.auth.local.lowerCaseUsername, userToInvite2.auth.local.lowerCaseUsername],
|
||||||
|
})).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.nested.property('invitations.guilds[0].id', group._id);
|
||||||
|
await expect(userToInvite2.get('/user')).to.eventually.have.nested.property('invitations.guilds[0].id', group._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('user id invites', () => {
|
describe('user id invites', () => {
|
||||||
it('returns an error when inviter has no chat privileges', async () => {
|
it('returns an error when inviter has no chat privileges', async () => {
|
||||||
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
||||||
@@ -93,7 +160,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('inviteMissingUuid'),
|
message: t('inviteMustNotBeEmpty'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -228,7 +295,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('inviteMissingEmail'),
|
message: t('inviteMustNotBeEmpty'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -266,7 +333,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 401,
|
code: 401,
|
||||||
error: 'NotAuthorized',
|
error: 'NotAuthorized',
|
||||||
message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}),
|
message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL')}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ describe('GET /heroes/:heroId', () => {
|
|||||||
|
|
||||||
it('validates req.params.heroId', async () => {
|
it('validates req.params.heroId', async () => {
|
||||||
await expect(user.get('/hall/heroes/invalidUUID')).to.eventually.be.rejected.and.eql({
|
await expect(user.get('/hall/heroes/invalidUUID')).to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 404,
|
||||||
error: 'BadRequest',
|
error: 'NotFound',
|
||||||
message: t('invalidReqParams'),
|
message: t('userWithIDNotFound', {userId: 'invalidUUID'}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ describe('GET /heroes/:heroId', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns only necessary hero data', async () => {
|
it('returns only necessary hero data given user id', async () => {
|
||||||
let hero = await generateUser({
|
let hero = await generateUser({
|
||||||
contributor: {tier: 23},
|
contributor: {tier: 23},
|
||||||
});
|
});
|
||||||
@@ -53,4 +53,18 @@ describe('GET /heroes/:heroId', () => {
|
|||||||
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns only necessary hero data given username', async () => {
|
||||||
|
let hero = await generateUser({
|
||||||
|
contributor: {tier: 23},
|
||||||
|
});
|
||||||
|
let heroRes = await user.get(`/hall/heroes/${hero.auth.local.username}`);
|
||||||
|
|
||||||
|
expect(heroRes).to.have.all.keys([ // works as: object has all and only these keys
|
||||||
|
'_id', 'id', 'balance', 'profile', 'purchased',
|
||||||
|
'contributor', 'auth', 'items',
|
||||||
|
]);
|
||||||
|
expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']);
|
||||||
|
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe('GET /members/:memberId', () => {
|
|||||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||||
]);
|
]);
|
||||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||||
'size', 'hair', 'skin', 'shirt',
|
'size', 'hair', 'skin', 'shirt',
|
||||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe('POST /notifications/:notificationId/read', () => {
|
|||||||
|
|
||||||
await expect(user.post(`/notifications/${dummyId}/read`)).to.eventually.be.rejected.and.eql({
|
await expect(user.post(`/notifications/${dummyId}/read`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe('POST /notifications/:notificationId/see', () => {
|
|||||||
|
|
||||||
await expect(user.post(`/notifications/${dummyId}/see`)).to.eventually.be.rejected.and.eql({
|
await expect(user.post(`/notifications/${dummyId}/see`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('POST /notifications/read', () => {
|
|||||||
notificationIds: [dummyId],
|
notificationIds: [dummyId],
|
||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('POST /notifications/see', () => {
|
|||||||
notificationIds: [dummyId],
|
notificationIds: [dummyId],
|
||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||||
|
|
||||||
|
describe('payments : apple #norenewsubscribe', () => {
|
||||||
|
let endpoint = '/iap/ios/norenew-subscribe';
|
||||||
|
let sku = 'com.habitrpg.ios.habitica.subscription.3month';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies sub key', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingSubscriptionCode'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeStub = sinon.stub(applePayments, 'noRenewSubscribe').resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
applePayments.noRenewSubscribe.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
transaction: {receipt: 'receipt'},
|
||||||
|
gift: {
|
||||||
|
uuid: '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][0].sku).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||||
|
|
||||||
describe('payments : apple #verify', () => {
|
describe('payments : apple #verify', () => {
|
||||||
@@ -9,6 +9,14 @@ describe('payments : apple #verify', () => {
|
|||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
let verifyStub;
|
let verifyStub;
|
||||||
|
|
||||||
@@ -31,10 +39,31 @@ describe('payments : apple #verify', () => {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
expect(verifyStub).to.be.calledOnce;
|
expect(verifyStub).to.be.calledOnce;
|
||||||
expect(verifyStub.args[0][0]._id).to.eql(user._id);
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
expect(verifyStub.args[0][1]).to.eql('receipt');
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
expect(verifyStub.args[0][2]['x-api-key']).to.eql(user.apiToken);
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
expect(verifyStub.args[0][2]['x-api-user']).to.eql(user._id);
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
transaction: {
|
||||||
|
receipt: 'receipt',
|
||||||
|
},
|
||||||
|
gift: {
|
||||||
|
uuid: '1',
|
||||||
|
}});
|
||||||
|
|
||||||
|
expect(verifyStub).to.be.calledOnce;
|
||||||
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(verifyStub.args[0][0].gift.uuid).to.eql('1');
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||||
|
|
||||||
|
describe('payments : google #norenewsubscribe', () => {
|
||||||
|
let endpoint = '/iap/android/norenew-subscribe';
|
||||||
|
let sku = 'com.habitrpg.android.habitica.subscription.3month';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies sub key', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingSubscriptionCode'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeStub = sinon.stub(googlePayments, 'noRenewSubscribe').resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
googlePayments.noRenewSubscribe.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
transaction: {
|
||||||
|
receipt: 'receipt',
|
||||||
|
signature: 'signature',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][0].sku).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][0].signature).to.eql('signature');
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
transaction: {
|
||||||
|
receipt: 'receipt',
|
||||||
|
signature: 'signature',
|
||||||
|
},
|
||||||
|
gift: {
|
||||||
|
uuid: '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][0].sku).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][0].signature).to.eql('signature');
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||||
|
|
||||||
describe('payments : google #verify', () => {
|
describe('payments : google #verify', () => {
|
||||||
@@ -9,6 +9,14 @@ describe('payments : google #verify', () => {
|
|||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
let verifyStub;
|
let verifyStub;
|
||||||
|
|
||||||
@@ -30,11 +38,30 @@ describe('payments : google #verify', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(verifyStub).to.be.calledOnce;
|
expect(verifyStub).to.be.calledOnce;
|
||||||
expect(verifyStub.args[0][0]._id).to.eql(user._id);
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
expect(verifyStub.args[0][1]).to.eql('receipt');
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
expect(verifyStub.args[0][2]).to.eql('signature');
|
expect(verifyStub.args[0][0].signature).to.eql('signature');
|
||||||
expect(verifyStub.args[0][3]['x-api-key']).to.eql(user.apiToken);
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
expect(verifyStub.args[0][3]['x-api-user']).to.eql(user._id);
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
transaction: {receipt: 'receipt', signature: 'signature'},
|
||||||
|
gift: {uuid: '1'},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(verifyStub).to.be.calledOnce;
|
||||||
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(verifyStub.args[0][0].signature).to.eql('signature');
|
||||||
|
expect(verifyStub.args[0][0].gift.uuid).to.eql('1');
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,12 @@ describe('GET /user/anonymized', () => {
|
|||||||
'profile.name': 'profile',
|
'profile.name': 'profile',
|
||||||
'purchased.plan': 'purchased plan',
|
'purchased.plan': 'purchased plan',
|
||||||
contributor: 'contributor',
|
contributor: 'contributor',
|
||||||
invitations: 'invitations',
|
invitations: {
|
||||||
|
guilds: ['guild1', 'guild2'],
|
||||||
|
party: {
|
||||||
|
_id: 'partyid',
|
||||||
|
},
|
||||||
|
},
|
||||||
'items.special.nyeReceived': 'some',
|
'items.special.nyeReceived': 'some',
|
||||||
'items.special.valentineReceived': 'some',
|
'items.special.valentineReceived': 'some',
|
||||||
webhooks: [{url: 'https://somurl.com'}],
|
webhooks: [{url: 'https://somurl.com'}],
|
||||||
|
|||||||
@@ -94,9 +94,6 @@ describe('POST /user/auth/reset-password-set-new-one', () => {
|
|||||||
userId: user._id,
|
userId: user._id,
|
||||||
expiresAt: moment().add({days: 1}),
|
expiresAt: moment().add({days: 1}),
|
||||||
}));
|
}));
|
||||||
await user.update({
|
|
||||||
auth: 'not an object with valid fields',
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(api.post(`${endpoint}`, {
|
await expect(api.post(`${endpoint}`, {
|
||||||
code,
|
code,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ describe('POST /user/auth/local/login', () => {
|
|||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 401,
|
code: 401,
|
||||||
error: 'NotAuthorized',
|
error: 'NotAuthorized',
|
||||||
message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL'), userId: user._id }),
|
message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'), userId: user._id }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ describe('PUT /user/auth/update-email', () => {
|
|||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 401,
|
code: 401,
|
||||||
error: 'NotAuthorized',
|
error: 'NotAuthorized',
|
||||||
message: t('cannotFulfillReq', { techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL') }),
|
message: t('cannotFulfillReq', { techAssistanceEmail: nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL') }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ const ENDPOINT = '/user/auth/update-username';
|
|||||||
|
|
||||||
describe('PUT /user/auth/update-username', async () => {
|
describe('PUT /user/auth/update-username', async () => {
|
||||||
let user;
|
let user;
|
||||||
let newUsername = 'new-username';
|
let password = 'password'; // from habitrpg/test/helpers/api-integration/v4/object-generators.js
|
||||||
let password = 'password'; // from habitrpg/test/helpers/api-integration/v3/object-generators.js
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successfully changes username', async () => {
|
it('successfully changes username with password', async () => {
|
||||||
|
let newUsername = 'new-username';
|
||||||
let response = await user.put(ENDPOINT, {
|
let response = await user.put(ENDPOINT, {
|
||||||
username: newUsername,
|
username: newUsername,
|
||||||
password,
|
password,
|
||||||
@@ -29,6 +29,38 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
expect(user.auth.local.username).to.eql(newUsername);
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('successfully changes username without password', async () => {
|
||||||
|
let newUsername = 'new-username-nopw';
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: newUsername,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: newUsername });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('successfully changes username containing number and underscore', async () => {
|
||||||
|
let newUsername = 'new_username9';
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: newUsername,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: newUsername });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets verifiedUsername when changing username', async () => {
|
||||||
|
user.flags.verifiedUsername = false;
|
||||||
|
await user.sync();
|
||||||
|
let newUsername = 'new-username-verify';
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: newUsername,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: newUsername });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.flags.verifiedUsername).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
let myNewUsername = 'my-new-username';
|
let myNewUsername = 'my-new-username';
|
||||||
let textPassword = 'mySecretPassword';
|
let textPassword = 'mySecretPassword';
|
||||||
@@ -80,6 +112,7 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('errors if password is wrong', async () => {
|
it('errors if password is wrong', async () => {
|
||||||
|
let newUsername = 'new-username';
|
||||||
await expect(user.put(ENDPOINT, {
|
await expect(user.put(ENDPOINT, {
|
||||||
username: newUsername,
|
username: newUsername,
|
||||||
password: 'wrong-password',
|
password: 'wrong-password',
|
||||||
@@ -90,19 +123,6 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents social-only user from changing username', async () => {
|
|
||||||
let socialUser = await generateUser({ 'auth.local': { ok: true } });
|
|
||||||
|
|
||||||
await expect(socialUser.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
password,
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('userHasNoLocalRegistration'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username is not provided', async () => {
|
it('errors if new username is not provided', async () => {
|
||||||
await expect(user.put(ENDPOINT, {
|
await expect(user.put(ENDPOINT, {
|
||||||
password,
|
password,
|
||||||
@@ -112,5 +132,93 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
message: t('invalidReqParams'),
|
message: t('invalidReqParams'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('errors if new username is a slur', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username contains a slur', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'something_TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username is not allowed', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'support',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueForbidden'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username is not allowed regardless of casing', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'SUppORT',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueForbidden'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if username has incorrect length', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'thisisaverylongusernameover20characters',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueLength'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username contains invalid characters', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'Eichhörnchen',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueInvalidCharacters'),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'test.name',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueInvalidCharacters'),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: '🤬',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueInvalidCharacters'),
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,4 +53,15 @@ describe('POST /user/buy-gear/:key', () => {
|
|||||||
message: 'You need to purchase a lower level gear before this one.',
|
message: 'You need to purchase a lower level gear before this one.',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an error if tries to buy gear from a different class', async () => {
|
||||||
|
let key = 'armor_rogue_1';
|
||||||
|
|
||||||
|
return expect(user.post(`/user/buy-gear/${key}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: 'You can\'t buy this item.',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
74
test/api/v4/members/POST-flag_private_message.test.js
Normal file
74
test/api/v4/members/POST-flag_private_message.test.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
describe('POST /members/flag-private-message/:messageId', () => {
|
||||||
|
let userToSendMessage;
|
||||||
|
let messageToSend = 'Test Private Message';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
userToSendMessage = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allows players to flag their own private message', async () => {
|
||||||
|
let receiver = await generateUser();
|
||||||
|
|
||||||
|
await userToSendMessage.post('/members/send-private-message', {
|
||||||
|
message: messageToSend,
|
||||||
|
toUserId: receiver._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let senderMessages = await userToSendMessage.get('/inbox/messages');
|
||||||
|
|
||||||
|
let sendersMessageInSendersInbox = _.find(senderMessages, (message) => {
|
||||||
|
return message.uuid === receiver._id && message.text === messageToSend;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendersMessageInSendersInbox).to.exist;
|
||||||
|
await expect(userToSendMessage.post(`/members/flag-private-message/${sendersMessageInSendersInbox.id}`)).to.eventually.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Flags a private message', async () => {
|
||||||
|
let receiver = await generateUser();
|
||||||
|
|
||||||
|
await userToSendMessage.post('/members/send-private-message', {
|
||||||
|
message: messageToSend,
|
||||||
|
toUserId: receiver._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let receiversMessages = await receiver.get('/inbox/messages');
|
||||||
|
|
||||||
|
let sendersMessageInReceiversInbox = _.find(receiversMessages, (message) => {
|
||||||
|
return message.uuid === userToSendMessage._id && message.text === messageToSend;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendersMessageInReceiversInbox).to.exist;
|
||||||
|
await expect(receiver.post(`/members/flag-private-message/${sendersMessageInReceiversInbox.id}`)).to.eventually.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Returns an error when user tries to flag a private message that is already flagged', async () => {
|
||||||
|
let receiver = await generateUser();
|
||||||
|
|
||||||
|
await userToSendMessage.post('/members/send-private-message', {
|
||||||
|
message: messageToSend,
|
||||||
|
toUserId: receiver._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let receiversMessages = await receiver.get('/inbox/messages');
|
||||||
|
|
||||||
|
let sendersMessageInReceiversInbox = _.find(receiversMessages, (message) => {
|
||||||
|
return message.uuid === userToSendMessage._id && message.text === messageToSend;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendersMessageInReceiversInbox).to.exist;
|
||||||
|
await expect(receiver.post(`/members/flag-private-message/${sendersMessageInReceiversInbox.id}`)).to.eventually.be.ok;
|
||||||
|
|
||||||
|
await expect(receiver.post(`/members/flag-private-message/${sendersMessageInReceiversInbox.id}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('messageGroupChatFlagAlreadyReported'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
test/api/v4/user/auth/POST-user_verify_display_name.test.js
Normal file
57
test/api/v4/user/auth/POST-user_verify_display_name.test.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
const ENDPOINT = '/user/auth/verify-display-name';
|
||||||
|
|
||||||
|
describe('POST /user/auth/verify-display-name', async () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('successfully verifies display name including funky characters', async () => {
|
||||||
|
let newDisplayName = 'Sabé 🤬';
|
||||||
|
let response = await user.post(ENDPOINT, {
|
||||||
|
displayName: newDisplayName,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ isUsable: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
context('errors', async () => {
|
||||||
|
it('errors if display name is not provided', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if display name is a slur', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueSlur')] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if display name contains a slur', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength'), t('displaynameIssueSlur')] });
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'something_TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength'), t('displaynameIssueSlur')] });
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength'), t('displaynameIssueSlur')] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if display name has incorrect length', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'this is a very long display name over 30 characters',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength')] });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v4';
|
|
||||||
import {
|
|
||||||
bcryptCompare,
|
|
||||||
sha1MakeSalt,
|
|
||||||
sha1Encrypt as sha1EncryptPassword,
|
|
||||||
} from '../../../../../website/server/libs/password';
|
|
||||||
|
|
||||||
const ENDPOINT = '/user/auth/update-username';
|
|
||||||
|
|
||||||
describe('PUT /user/auth/update-username', async () => {
|
|
||||||
let user;
|
|
||||||
let password = 'password'; // from habitrpg/test/helpers/api-integration/v4/object-generators.js
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successfully changes username with password', async () => {
|
|
||||||
let newUsername = 'new-username';
|
|
||||||
let response = await user.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
password,
|
|
||||||
});
|
|
||||||
expect(response).to.eql({ username: newUsername });
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.local.username).to.eql(newUsername);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successfully changes username without password', async () => {
|
|
||||||
let newUsername = 'new-username-nopw';
|
|
||||||
let response = await user.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
});
|
|
||||||
expect(response).to.eql({ username: newUsername });
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.local.username).to.eql(newUsername);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successfully changes username containing number and underscore', async () => {
|
|
||||||
let newUsername = 'new_username9';
|
|
||||||
let response = await user.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
});
|
|
||||||
expect(response).to.eql({ username: newUsername });
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.local.username).to.eql(newUsername);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets verifiedUsername when changing username', async () => {
|
|
||||||
user.flags.verifiedUsername = false;
|
|
||||||
await user.sync();
|
|
||||||
let newUsername = 'new-username-verify';
|
|
||||||
let response = await user.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
});
|
|
||||||
expect(response).to.eql({ username: newUsername });
|
|
||||||
await user.sync();
|
|
||||||
expect(user.flags.verifiedUsername).to.eql(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
|
||||||
let myNewUsername = 'my-new-username';
|
|
||||||
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);
|
|
||||||
|
|
||||||
// update email
|
|
||||||
let response = await user.put(ENDPOINT, {
|
|
||||||
username: myNewUsername,
|
|
||||||
password: textPassword,
|
|
||||||
});
|
|
||||||
expect(response).to.eql({ username: myNewUsername });
|
|
||||||
|
|
||||||
await user.sync();
|
|
||||||
|
|
||||||
expect(user.auth.local.username).to.eql(myNewUsername);
|
|
||||||
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
|
||||||
expect(user.auth.local.salt).to.be.undefined;
|
|
||||||
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
|
||||||
|
|
||||||
let isValidPassword = await bcryptCompare(textPassword, user.auth.local.hashed_password);
|
|
||||||
expect(isValidPassword).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
context('errors', async () => {
|
|
||||||
it('prevents username update if new username is already taken', async () => {
|
|
||||||
let existingUsername = 'existing-username';
|
|
||||||
await generateUser({'auth.local.username': existingUsername, 'auth.local.lowerCaseUsername': existingUsername });
|
|
||||||
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: existingUsername,
|
|
||||||
password,
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameTaken'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if password is wrong', async () => {
|
|
||||||
let newUsername = 'new-username';
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
password: 'wrong-password',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('wrongPassword'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username is not provided', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
password,
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('invalidReqParams'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username is a slur', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'TESTPLACEHOLDERSLURWORDHERE',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username contains a slur', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
|
||||||
});
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'something_TESTPLACEHOLDERSLURWORDHERE',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
|
||||||
});
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username is not allowed', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'support',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameIssueForbidden'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username is not allowed regardless of casing', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'SUppORT',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameIssueForbidden'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if username has incorrect length', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'thisisaverylongusernameover20characters',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameIssueLength'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username contains invalid characters', async () => {
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'Eichhörnchen',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameIssueInvalidCharacters'),
|
|
||||||
});
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: 'test.name',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameIssueInvalidCharacters'),
|
|
||||||
});
|
|
||||||
await expect(user.put(ENDPOINT, {
|
|
||||||
username: '🤬',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('usernameIssueInvalidCharacters'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
300
test/client/unit/specs/components/avatar.vue.test.js
Normal file
300
test/client/unit/specs/components/avatar.vue.test.js
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
import Avatar from 'client/components/avatar';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import generateStore from 'client/store';
|
||||||
|
|
||||||
|
context('avatar.vue', () => {
|
||||||
|
let Constructr;
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Constructr = Vue.extend(Avatar);
|
||||||
|
|
||||||
|
vm = new Constructr({
|
||||||
|
propsData: {
|
||||||
|
member: {
|
||||||
|
stats: {
|
||||||
|
buffs: {},
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
hair: {},
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
equipped: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).$mount();
|
||||||
|
|
||||||
|
vm.$store = generateStore();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
stats: { lvl: 17 },
|
||||||
|
preferences: { disableClasses: true },
|
||||||
|
flags: { classSelected: false },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accurately reports class status', () => {
|
||||||
|
expect(vm.hasClass).to.equal(false);
|
||||||
|
|
||||||
|
vm.member.preferences.disableClasses = false;
|
||||||
|
vm.member.flags.classSelected = true;
|
||||||
|
|
||||||
|
expect(vm.hasClass).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isBuffed', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
buffs: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accurately reports if buffed', () => {
|
||||||
|
expect(vm.isBuffed).to.equal(undefined);
|
||||||
|
|
||||||
|
vm.member.stats.buffs = { str: 1 };
|
||||||
|
|
||||||
|
expect(vm.isBuffed).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('paddingTop', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
items: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to 28px', () => {
|
||||||
|
vm.avatarOnly = true;
|
||||||
|
expect(vm.paddingTop).to.equal('28px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is 24.5px if user has a pet', () => {
|
||||||
|
vm.member.items = {
|
||||||
|
currentPet: { name: 'Foo' },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.paddingTop).to.equal('24.5px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is 0px if user has a mount', () => {
|
||||||
|
vm.member.items = {
|
||||||
|
currentMount: { name: 'Bar' },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.paddingTop).to.equal('0px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be overriden', () => {
|
||||||
|
vm.overrideTopPadding = '27px';
|
||||||
|
expect(vm.paddingTop).to.equal('27px');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('costumeClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
preferences: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns if showing equiped gear', () => {
|
||||||
|
expect(vm.costumeClass).to.equal('equipped');
|
||||||
|
});
|
||||||
|
it('returns if wearing a costume', () => {
|
||||||
|
vm.member.preferences = { costume: true };
|
||||||
|
expect(vm.costumeClass).to.equal('costume');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('visualBuffs', () => {
|
||||||
|
it('returns an array of buffs', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
class: 'Warrior',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.visualBuffs).to.include({snowball: 'snowman'});
|
||||||
|
expect(vm.visualBuffs).to.include({spookySparkles: 'ghost'});
|
||||||
|
expect(vm.visualBuffs).to.include({shinySeed: 'avatar_floral_Warrior'});
|
||||||
|
expect(vm.visualBuffs).to.include({seafoam: 'seafoam_star'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('backgroundClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member.preferences = { background: 'pony' };
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows the background', () => {
|
||||||
|
expect(vm.backgroundClass).to.equal('background_pony');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be overridden', () => {
|
||||||
|
vm.overrideAvatarGear = { background: 'character' };
|
||||||
|
|
||||||
|
expect(vm.backgroundClass).to.equal('background_character');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns to a blank string if not showing background', () => {
|
||||||
|
vm.withBackground = false;
|
||||||
|
vm.avatarOnly = true;
|
||||||
|
|
||||||
|
expect(vm.backgroundClass).to.equal('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('specialMountClass', () => {
|
||||||
|
it('checks if riding a Kangaroo', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
class: 'None',
|
||||||
|
},
|
||||||
|
items: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.specialMountClass).to.equal(undefined);
|
||||||
|
|
||||||
|
vm.member.items = {
|
||||||
|
currentMount: ['Kangaroo'],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.specialMountClass).to.equal('offset-kangaroo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('skinClass', () => {
|
||||||
|
it('returns current skin color', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {},
|
||||||
|
preferences: {
|
||||||
|
skin: 'blue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.skinClass).to.equal('skin_blue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns if sleep or not', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {},
|
||||||
|
preferences: {
|
||||||
|
skin: 'blue',
|
||||||
|
sleep: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.skinClass).to.equal('skin_blue');
|
||||||
|
|
||||||
|
vm.member.preferences.sleep = true;
|
||||||
|
|
||||||
|
expect(vm.skinClass).to.equal('skin_blue_sleep');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('methods', () => {
|
||||||
|
describe('getGearClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
equipped: { Hat: 'Fancy Tophat' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preferences: { costume: false },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined if no match', () => {
|
||||||
|
expect(vm.getGearClass('foo')).to.equal(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the matching gear', () => {
|
||||||
|
expect(vm.getGearClass('Hat')).to.equal('Fancy Tophat');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be overridden', () => {
|
||||||
|
vm.overrideAvatarGear = { Hat: 'Dapper Bowler' };
|
||||||
|
|
||||||
|
expect(vm.getGearClass('Hat')).to.equal('Dapper Bowler');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hideGear', () => {
|
||||||
|
it('returns no weapon equipped', () => {
|
||||||
|
vm.member.items.gear.equipped = {};
|
||||||
|
expect(vm.hideGear('weapon')).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
equipped: {
|
||||||
|
weapon: {
|
||||||
|
baseWeapon: 'Spoon',
|
||||||
|
twoHanded: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preferences: { costume: false },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('show avatar', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
buffs: {
|
||||||
|
snowball: false,
|
||||||
|
seafoam: false,
|
||||||
|
spookySparkles: false,
|
||||||
|
shinySeed: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('does if not showing visual buffs', () => {
|
||||||
|
expect(vm.showAvatar()).to.equal(true);
|
||||||
|
|
||||||
|
let buffs = vm.member.stats.buffs;
|
||||||
|
|
||||||
|
buffs.snowball = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.snowball = false;
|
||||||
|
buffs.spookySparkles = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.spookySparkles = false;
|
||||||
|
buffs.shinySeed = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.shinySeed = false;
|
||||||
|
buffs.seafoam = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.seafoam = false;
|
||||||
|
vm.showVisualBuffs = false;
|
||||||
|
expect(vm.showAvatar()).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import {shallow} from '@vue/test-utils';
|
import {mount} from '@vue/test-utils';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
import CategoryTags from 'client/components/categories/categoryTags.vue';
|
import CategoryTags from 'client/components/categories/categoryTags.vue';
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ describe('Category Tags', () => {
|
|||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
wrapper = shallow(CategoryTags, {
|
wrapper = mount(CategoryTags, {
|
||||||
propsData: {
|
propsData: {
|
||||||
categories: [],
|
categories: [],
|
||||||
},
|
},
|
||||||
@@ -27,9 +28,11 @@ describe('Category Tags', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
expect(wrapper.contains('.category-label')).to.eq(true);
|
expect(wrapper.contains('.category-label')).to.eq(true);
|
||||||
expect(wrapper.find('.category-label').text()).to.eq('test');
|
expect(wrapper.find('.category-label').text()).to.eq('test');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('displays a habitica official in purple', () => {
|
it('displays a habitica official in purple', () => {
|
||||||
wrapper.setProps({
|
wrapper.setProps({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { shallow } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
|
|
||||||
import SidebarSection from 'client/components/sidebarSection.vue';
|
import SidebarSection from 'client/components/sidebarSection.vue';
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ describe('Sidebar Section', () => {
|
|||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
wrapper = shallow(SidebarSection, {
|
wrapper = mount(SidebarSection, {
|
||||||
propsData: {
|
propsData: {
|
||||||
title: 'Hello World',
|
title: 'Hello World',
|
||||||
},
|
},
|
||||||
@@ -39,7 +39,7 @@ describe('Sidebar Section', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('can hide contents by default', () => {
|
it('can hide contents by default', () => {
|
||||||
wrapper = shallow(SidebarSection, {
|
wrapper = mount(SidebarSection, {
|
||||||
propsData: {
|
propsData: {
|
||||||
title: 'Hello World',
|
title: 'Hello World',
|
||||||
show: false,
|
show: false,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { shallow, createLocalVue } from '@vue/test-utils';
|
import { mount, createLocalVue } from '@vue/test-utils';
|
||||||
|
|
||||||
import TaskColumn from 'client/components/tasks/column.vue';
|
import TaskColumn from 'client/components/tasks/column.vue';
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ describe('Task Column', () => {
|
|||||||
};
|
};
|
||||||
let stubs = ['b-modal']; // <b-modal> is a custom component and not tested here
|
let stubs = ['b-modal']; // <b-modal> is a custom component and not tested here
|
||||||
|
|
||||||
return shallow(TaskColumn, {
|
return mount(TaskColumn, {
|
||||||
propsData: {
|
propsData: {
|
||||||
type,
|
type,
|
||||||
},
|
},
|
||||||
|
|||||||
39
test/client/unit/specs/libs/highlightUsers.js
Normal file
39
test/client/unit/specs/libs/highlightUsers.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {highlightUsers} from '../../../../../website/client/libs/highlightUsers';
|
||||||
|
import habiticaMarkdown from 'habitica-markdown';
|
||||||
|
|
||||||
|
describe('highlightUserAndEmail', () => {
|
||||||
|
it('highlights displayname', () => {
|
||||||
|
const text = 'hello @displayedUser with text after';
|
||||||
|
|
||||||
|
const result = highlightUsers(text, 'user', 'displayedUser');
|
||||||
|
|
||||||
|
expect(result).to.contain('<span class="at-text at-highlight">@displayedUser</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights username', () => {
|
||||||
|
const text = 'hello @user';
|
||||||
|
|
||||||
|
const result = highlightUsers(text, 'user', 'displayedUser');
|
||||||
|
expect(result).to.contain('<span class="at-text at-highlight">@user</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('not highlights any email', () => {
|
||||||
|
const text = habiticaMarkdown.render('hello@example.com');
|
||||||
|
|
||||||
|
const result = highlightUsers(text, 'example', 'displayedUser');
|
||||||
|
expect(result).to.not.contain('<span class="at-highlight">@example</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('complex highlight', () => {
|
||||||
|
const plainText = 'a bit more @mentions to @use my@mentions.com broken.@mail.com';
|
||||||
|
|
||||||
|
const text = habiticaMarkdown.render(plainText);
|
||||||
|
|
||||||
|
const result = highlightUsers(text, 'use', 'mentions');
|
||||||
|
|
||||||
|
expect(result).to.contain('<span class="at-text at-highlight">@mentions</span>');
|
||||||
|
expect(result).to.contain('<span class="at-text at-highlight">@use</span>');
|
||||||
|
expect(result).to.not.contain('<span class="at-text at-highlight">@mentions</span>.com');
|
||||||
|
});
|
||||||
|
});
|
||||||
76
test/client/unit/specs/store/getters/user/user.test.js
Normal file
76
test/client/unit/specs/store/getters/user/user.test.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { data, gems, buffs, preferences, tasksOrder } from 'client/store/getters/user';
|
||||||
|
|
||||||
|
context('user getters', () => {
|
||||||
|
describe('data', () => {
|
||||||
|
it('returns the user\'s data', () => {
|
||||||
|
expect(data({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: {
|
||||||
|
lvl: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).lvl).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('gems', () => {
|
||||||
|
it('returns the user\'s gems', () => {
|
||||||
|
expect(gems({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: { balance: 4.5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})).to.equal(18);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buffs', () => {
|
||||||
|
it('returns the user\'s buffs', () => {
|
||||||
|
expect(buffs({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: {
|
||||||
|
stats: {
|
||||||
|
buffs: [1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})(0)).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('preferences', () => {
|
||||||
|
it('returns the user\'s preferences', () => {
|
||||||
|
expect(preferences({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: {
|
||||||
|
preferences: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tasksOrder', () => {
|
||||||
|
it('returns the user\'s tasksOrder', () => {
|
||||||
|
expect(tasksOrder({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
tasksOrder: {
|
||||||
|
masters: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})('master')).to.equal(1);
|
||||||
|
|
||||||
|
expect(tasksOrder()).to.not.equal('null');
|
||||||
|
expect(tasksOrder()).to.not.equal('undefined');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { gems as userGems } from 'client/store/getters/user';
|
|
||||||
|
|
||||||
describe('userGems getter', () => {
|
|
||||||
it('returns the user\'s gems', () => {
|
|
||||||
expect(userGems({
|
|
||||||
state: {
|
|
||||||
user: {
|
|
||||||
data: {balance: 4.5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})).to.equal(18);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -158,7 +158,7 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
|
|
||||||
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
||||||
expect(user.stats.gp).to.eql(100);
|
expect(user.stats.gp).to.eql(100);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledTwice;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -246,5 +246,14 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
|
|
||||||
expect(user.items.gear.owned).to.have.property('head_special_2', true);
|
expect(user.items.gear.owned).to.have.property('head_special_2', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does buyGear equipment if it is an armoire item that an user previously lost', () => {
|
||||||
|
user.stats.gp = 200;
|
||||||
|
user.items.gear.owned.shield_armoire_ramHornShield = false;
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'shield_armoire_ramHornShield'}});
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.have.property('shield_armoire_ramHornShield', true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
20
test/helpers/mongoose.helper.js
Normal file
20
test/helpers/mongoose.helper.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
export async function mockFindById (response) {
|
||||||
|
const mockFind = {
|
||||||
|
select () {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
lean () {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
exec () {
|
||||||
|
return Promise.resolve(response);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
sinon.stub(mongoose.Model, 'findById').returns(mockFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreFindById () {
|
||||||
|
return mongoose.Model.findById.restore();
|
||||||
|
}
|
||||||
@@ -65,7 +65,7 @@ apt-get install -qq ntp
|
|||||||
echo Installing nvm, node and global node modules...
|
echo Installing nvm, node and global node modules...
|
||||||
/vagrant/vagrant_scripts/install_node.sh
|
/vagrant/vagrant_scripts/install_node.sh
|
||||||
|
|
||||||
echo "'vagrant up' is finished. Continue with the instructions at http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally"
|
echo "'vagrant up' is finished. Continue with the instructions at http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally"
|
||||||
|
|
||||||
# Uncomment both lines to autostart the habitica server when provisioning
|
# Uncomment both lines to autostart the habitica server when provisioning
|
||||||
# echo Starting Habitica server...
|
# echo Starting Habitica server...
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ setupNconf(configFile);
|
|||||||
// @TODO: Do we need? const CLIENT_VARS = ['language', 'isStaticPage', 'availableLanguages', 'translations',
|
// @TODO: Do we need? const CLIENT_VARS = ['language', 'isStaticPage', 'availableLanguages', 'translations',
|
||||||
// 'FACEBOOK_KEY', 'GOOGLE_CLIENT_ID', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
// 'FACEBOOK_KEY', 'GOOGLE_CLIENT_ID', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
||||||
// 'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
// 'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
||||||
// 'worldDmg', 'mods', 'IS_MOBILE', 'PUSHER:KEY', 'PUSHER:ENABLED'];
|
// 'worldDmg', 'mods', 'IS_MOBILE'];
|
||||||
|
|
||||||
const AMAZON_SELLER_ID = nconf.get('AMAZON_PAYMENTS:SELLER_ID') || nconf.get('AMAZON_PAYMENTS_SELLER_ID');
|
const AMAZON_SELLER_ID = nconf.get('AMAZON_PAYMENTS_SELLER_ID');
|
||||||
const AMAZON_CLIENT_ID = nconf.get('AMAZON_PAYMENTS:CLIENT_ID') || nconf.get('AMAZON_PAYMENTS_CLIENT_ID');
|
const AMAZON_CLIENT_ID = nconf.get('AMAZON_PAYMENTS_CLIENT_ID');
|
||||||
|
const AMAZON_MODE = nconf.get('AMAZON_PAYMENTS_MODE');
|
||||||
|
|
||||||
let env = {
|
let env = {
|
||||||
NODE_ENV: '"production"',
|
NODE_ENV: '"production"',
|
||||||
@@ -26,15 +27,16 @@ let env = {
|
|||||||
AMAZON_PAYMENTS: {
|
AMAZON_PAYMENTS: {
|
||||||
SELLER_ID: `"${AMAZON_SELLER_ID}"`,
|
SELLER_ID: `"${AMAZON_SELLER_ID}"`,
|
||||||
CLIENT_ID: `"${AMAZON_CLIENT_ID}"`,
|
CLIENT_ID: `"${AMAZON_CLIENT_ID}"`,
|
||||||
|
MODE: `"${AMAZON_MODE}"`,
|
||||||
},
|
},
|
||||||
EMAILS: {
|
EMAILS: {
|
||||||
COMMUNITY_MANAGER_EMAIL: `"${nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL')}"`,
|
COMMUNITY_MANAGER_EMAIL: `"${nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL')}"`,
|
||||||
TECH_ASSISTANCE_EMAIL: `"${nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}"`,
|
TECH_ASSISTANCE_EMAIL: `"${nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL')}"`,
|
||||||
PRESS_ENQUIRY_EMAIL: `"${nconf.get('EMAILS:PRESS_ENQUIRY_EMAIL')}"`,
|
PRESS_ENQUIRY_EMAIL: `"${nconf.get('EMAILS_PRESS_ENQUIRY_EMAIL')}"`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY GOOGLE_CLIENT_ID AMPLITUDE_KEY PUSHER:KEY PUSHER:ENABLED LOGGLY_CLIENT_TOKEN'
|
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY GOOGLE_CLIENT_ID AMPLITUDE_KEY LOGGLY_CLIENT_TOKEN'
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.forEach(key => {
|
.forEach(key => {
|
||||||
env[key] = `"${nconf.get(key)}"`;
|
env[key] = `"${nconf.get(key)}"`;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Running
|
# Running
|
||||||
For information about installing and running Habitica locally, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally).
|
For information about installing and running Habitica locally, see [Setting up Habitica Locally](http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally).
|
||||||
|
|
||||||
# Preparation Reading
|
# Preparation Reading
|
||||||
- Vue 2 (https://vuejs.org)
|
- Vue 2 (https://vuejs.org)
|
||||||
@@ -18,4 +18,4 @@ The project is developed directly in the `develop` branch as long as we'll be ab
|
|||||||
|
|
||||||
So far most of the work has been on the template, so there's no complex logic to understand. The only thing I would suggest you to read about is Vuex for data management: it's basically a Flux implementation: there's a central store that hold the data for the entire app, and every change to the data must happen through an action, the data cannot be mutated directly.
|
So far most of the work has been on the template, so there's no complex logic to understand. The only thing I would suggest you to read about is Vuex for data management: it's basically a Flux implementation: there's a central store that hold the data for the entire app, and every change to the data must happen through an action, the data cannot be mutated directly.
|
||||||
|
|
||||||
For further resources, see [Guidance for Blacksmiths](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths), and in particular the ["Website Technology Stack" section](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths#Website_Technology_Stack).
|
For further resources, see [Guidance for Blacksmiths](http://habitica.fandom.com/wiki/Guidance_for_Blacksmiths), and in particular the ["Website Technology Stack" section](http://habitica.fandom.com/wiki/Guidance_for_Blacksmiths#Website_Technology_Stack).
|
||||||
|
|||||||
@@ -11,22 +11,23 @@ div
|
|||||||
#app(:class='{"casting-spell": castingSpell}')
|
#app(:class='{"casting-spell": castingSpell}')
|
||||||
banned-account-modal
|
banned-account-modal
|
||||||
amazon-payments-modal(v-if='!isStaticPage')
|
amazon-payments-modal(v-if='!isStaticPage')
|
||||||
|
payments-success-modal
|
||||||
snackbars
|
snackbars
|
||||||
router-view(v-if="!isUserLoggedIn || isStaticPage")
|
router-view(v-if="!isUserLoggedIn || isStaticPage")
|
||||||
template(v-else)
|
template(v-else)
|
||||||
template(v-if="isUserLoaded")
|
template(v-if="isUserLoaded")
|
||||||
div.resting-banner(v-show="showRestingBanner", ref="restingBanner")
|
.resting-banner(v-show="showRestingBanner", ref="restingBanner")
|
||||||
span.content
|
span.content
|
||||||
span.label.d-inline.d-sm-none {{ $t('innCheckOutBannerShort') }}
|
span.label.d-inline.d-sm-none {{ $t('innCheckOutBannerShort') }}
|
||||||
span.label.d-none.d-sm-inline {{ $t('innCheckOutBanner') }}
|
span.label.d-none.d-sm-inline {{ $t('innCheckOutBanner') }}
|
||||||
span.separator |
|
span.separator |
|
||||||
span.resume(@click="resumeDamage()") {{ $t('resumeDamage') }}
|
span.resume(@click="resumeDamage()") {{ $t('resumeDamage') }}
|
||||||
div.closepadding(@click="hideBanner()")
|
.closepadding(@click="hideBanner()")
|
||||||
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close")
|
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close")
|
||||||
notifications-display
|
notifications-display
|
||||||
app-menu(:class='{"restingInn": showRestingBanner}' :style="{ marginTop: bannerHeight + 'px' }")
|
app-menu
|
||||||
.container-fluid
|
.container-fluid
|
||||||
app-header(:class='{"restingInn": showRestingBanner}')
|
app-header
|
||||||
buyModal(
|
buyModal(
|
||||||
:item="selectedItemToBuy || {}",
|
:item="selectedItemToBuy || {}",
|
||||||
:withPin="true",
|
:withPin="true",
|
||||||
@@ -49,6 +50,13 @@ div
|
|||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
@import '~client/assets/scss/colors.scss';
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: calc(100% - 56px); /* 56px is the menu */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
#loading-screen-inapp {
|
#loading-screen-inapp {
|
||||||
#melior {
|
#melior {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -78,6 +86,11 @@ div
|
|||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
overflow-x: hidden;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
border-radius: 1000px;
|
border-radius: 1000px;
|
||||||
background-color: $green-10;
|
background-color: $green-10;
|
||||||
@@ -88,42 +101,10 @@ div
|
|||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-fluid {
|
|
||||||
overflow-x: hidden;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
|
||||||
height: calc(100% - 56px); /* 56px is the menu */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang='scss'>
|
|
||||||
@import '~client/assets/scss/colors.scss';
|
|
||||||
|
|
||||||
/* @TODO: The modal-open class is not being removed. Let's try this for now */
|
|
||||||
.modal {
|
|
||||||
overflow-y: scroll !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-backdrop.show {
|
|
||||||
opacity: .9 !important;
|
|
||||||
background-color: $purple-100 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Push progress bar above modals */
|
|
||||||
#nprogress .bar {
|
|
||||||
z-index: 1600 !important; /* Must stay above nav bar */
|
|
||||||
}
|
|
||||||
|
|
||||||
.resting-banner {
|
.resting-banner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
background-color: $blue-10;
|
background-color: $blue-10;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1300;
|
z-index: 1300;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -139,14 +120,10 @@ div
|
|||||||
.closepadding {
|
.closepadding {
|
||||||
margin: 11px 24px;
|
margin: 11px 24px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: relative;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
span svg path {
|
|
||||||
stroke: $blue-500;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
@@ -169,6 +146,30 @@ div
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.closepadding span svg path {
|
||||||
|
stroke: #FFF;
|
||||||
|
opacity: 0.48;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @TODO: The modal-open class is not being removed. Let's try this for now */
|
||||||
|
.modal {
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-backdrop.show {
|
||||||
|
opacity: .9 !important;
|
||||||
|
background-color: $purple-100 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push progress bar above modals */
|
||||||
|
#nprogress .bar {
|
||||||
|
z-index: 1600 !important; /* Must stay above nav bar */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { loadProgressBar } from 'axios-progress-bar';
|
import { loadProgressBar } from 'axios-progress-bar';
|
||||||
@@ -185,7 +186,10 @@ import SelectMembersModal from 'client/components/selectMembersModal.vue';
|
|||||||
import notifications from 'client/mixins/notifications';
|
import notifications from 'client/mixins/notifications';
|
||||||
import { setup as setupPayments } from 'client/libs/payments';
|
import { setup as setupPayments } from 'client/libs/payments';
|
||||||
import amazonPaymentsModal from 'client/components/payments/amazonModal';
|
import amazonPaymentsModal from 'client/components/payments/amazonModal';
|
||||||
|
import paymentsSuccessModal from 'client/components/payments/successModal';
|
||||||
|
|
||||||
import spellsMixin from 'client/mixins/spells';
|
import spellsMixin from 'client/mixins/spells';
|
||||||
|
import { CONSTANTS, getLocalSetting, removeLocalSetting } from 'client/libs/userlocalManager';
|
||||||
|
|
||||||
import svgClose from 'assets/svg/close.svg';
|
import svgClose from 'assets/svg/close.svg';
|
||||||
import bannedAccountModal from 'client/components/bannedAccountModal';
|
import bannedAccountModal from 'client/components/bannedAccountModal';
|
||||||
@@ -205,6 +209,7 @@ export default {
|
|||||||
SelectMembersModal,
|
SelectMembersModal,
|
||||||
amazonPaymentsModal,
|
amazonPaymentsModal,
|
||||||
bannedAccountModal,
|
bannedAccountModal,
|
||||||
|
paymentsSuccessModal,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -220,7 +225,6 @@ export default {
|
|||||||
loading: true,
|
loading: true,
|
||||||
currentTipNumber: 0,
|
currentTipNumber: 0,
|
||||||
bannerHidden: false,
|
bannerHidden: false,
|
||||||
bannerHeight: 0,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -313,6 +317,7 @@ export default {
|
|||||||
const errorMessage = errorData.message || errorData;
|
const errorMessage = errorData.message || errorData;
|
||||||
|
|
||||||
// Check for conditions to reset the user auth
|
// Check for conditions to reset the user auth
|
||||||
|
// TODO use a specific error like NotificationNotFound instead of checking for the string
|
||||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
||||||
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
||||||
this.$store.dispatch('auth:logout');
|
this.$store.dispatch('auth:logout');
|
||||||
@@ -322,12 +327,6 @@ export default {
|
|||||||
let snackbarTimeout = false;
|
let snackbarTimeout = false;
|
||||||
if (error.response.status === 502) snackbarTimeout = true;
|
if (error.response.status === 502) snackbarTimeout = true;
|
||||||
|
|
||||||
const notificationNotFoundMessage = [
|
|
||||||
this.$t('messageNotificationNotFound'),
|
|
||||||
this.$t('messageNotificationNotFound', 'en'),
|
|
||||||
];
|
|
||||||
if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true;
|
|
||||||
|
|
||||||
let errorsToShow = [];
|
let errorsToShow = [];
|
||||||
// show only the first error for each param
|
// show only the first error for each param
|
||||||
let paramErrorsFound = {};
|
let paramErrorsFound = {};
|
||||||
@@ -341,6 +340,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
errorsToShow.push(errorMessage);
|
errorsToShow.push(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore NotificationNotFound errors, see https://github.com/HabitRPG/habitica/issues/10391
|
||||||
|
if (errorData.error !== 'NotificationNotFound') {
|
||||||
// dispatch as one snackbar notification
|
// dispatch as one snackbar notification
|
||||||
this.$store.dispatch('snackbars:add', {
|
this.$store.dispatch('snackbars:add', {
|
||||||
title: 'Habitica',
|
title: 'Habitica',
|
||||||
@@ -349,6 +351,7 @@ export default {
|
|||||||
timeout: snackbarTimeout,
|
timeout: snackbarTimeout,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
@@ -418,14 +421,6 @@ export default {
|
|||||||
|
|
||||||
this.hideLoadingScreen();
|
this.hideLoadingScreen();
|
||||||
|
|
||||||
window.addEventListener('resize', this.setBannerOffset);
|
|
||||||
// Adjust the positioning of the header banners
|
|
||||||
this.$watch('showRestingBanner', () => {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.setBannerOffset();
|
|
||||||
});
|
|
||||||
}, {immediate: true});
|
|
||||||
|
|
||||||
// Adjust the timezone offset
|
// Adjust the timezone offset
|
||||||
if (this.user.preferences.timezoneOffset !== this.browserTimezoneOffset) {
|
if (this.user.preferences.timezoneOffset !== this.browserTimezoneOffset) {
|
||||||
this.$store.dispatch('user:set', {
|
this.$store.dispatch('user:set', {
|
||||||
@@ -433,6 +428,14 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let appState = getLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
|
||||||
|
if (appState) {
|
||||||
|
appState = JSON.parse(appState);
|
||||||
|
if (appState.paymentCompleted) {
|
||||||
|
removeLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
|
||||||
|
this.$root.$emit('habitica:payment-success', appState);
|
||||||
|
}
|
||||||
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
// Load external scripts after the app has been rendered
|
// Load external scripts after the app has been rendered
|
||||||
setupPayments();
|
setupPayments();
|
||||||
@@ -452,7 +455,6 @@ export default {
|
|||||||
this.$root.$off('bv::show::modal');
|
this.$root.$off('bv::show::modal');
|
||||||
this.$root.$off('buyModal::showItem');
|
this.$root.$off('buyModal::showItem');
|
||||||
this.$root.$off('selectMembersModal::showItem');
|
this.$root.$off('selectMembersModal::showItem');
|
||||||
window.removeEventListener('resize', this.setBannerOffset);
|
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
// Remove the index.html loading screen and now show the inapp loading
|
// Remove the index.html loading screen and now show the inapp loading
|
||||||
@@ -611,22 +613,10 @@ export default {
|
|||||||
},
|
},
|
||||||
hideBanner () {
|
hideBanner () {
|
||||||
this.bannerHidden = true;
|
this.bannerHidden = true;
|
||||||
this.setBannerOffset();
|
|
||||||
},
|
},
|
||||||
resumeDamage () {
|
resumeDamage () {
|
||||||
this.$store.dispatch('user:sleep');
|
this.$store.dispatch('user:sleep');
|
||||||
},
|
},
|
||||||
setBannerOffset () {
|
|
||||||
let contentPlacement = 0;
|
|
||||||
if (this.showRestingBanner && this.$refs.restingBanner !== undefined) {
|
|
||||||
contentPlacement = this.$refs.restingBanner.clientHeight;
|
|
||||||
}
|
|
||||||
this.bannerHeight = contentPlacement;
|
|
||||||
let smartBanner = document.getElementsByClassName('smartbanner')[0];
|
|
||||||
if (smartBanner !== undefined) {
|
|
||||||
smartBanner.style.top = `${contentPlacement}px`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -659,5 +649,6 @@ export default {
|
|||||||
<style src="assets/css/sprites/spritesmith-main-21.css"></style>
|
<style src="assets/css/sprites/spritesmith-main-21.css"></style>
|
||||||
<style src="assets/css/sprites/spritesmith-main-22.css"></style>
|
<style src="assets/css/sprites/spritesmith-main-22.css"></style>
|
||||||
<style src="assets/css/sprites/spritesmith-main-23.css"></style>
|
<style src="assets/css/sprites/spritesmith-main-23.css"></style>
|
||||||
|
<style src="assets/css/sprites/spritesmith-main-24.css"></style>
|
||||||
<style src="assets/css/sprites.css"></style>
|
<style src="assets/css/sprites.css"></style>
|
||||||
<style src="smartbanner.js/dist/smartbanner.min.css"></style>
|
<style src="smartbanner.js/dist/smartbanner.min.css"></style>
|
||||||
|
|||||||
@@ -1,66 +1,54 @@
|
|||||||
.achievement-costumeContest6x {
|
.promo_armoire_backgrounds_201903 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -142px -727px;
|
background-position: 0px -490px;
|
||||||
width: 144px;
|
|
||||||
height: 156px;
|
|
||||||
}
|
|
||||||
.promo_alligator {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: -477px 0px;
|
|
||||||
width: 480px;
|
|
||||||
height: 360px;
|
|
||||||
}
|
|
||||||
.promo_animal_tails {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: 0px -727px;
|
|
||||||
width: 141px;
|
|
||||||
height: 441px;
|
|
||||||
}
|
|
||||||
.promo_armoire_backgrounds_201811 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: -376px -574px;
|
|
||||||
width: 423px;
|
width: 423px;
|
||||||
height: 147px;
|
height: 147px;
|
||||||
}
|
}
|
||||||
.promo_ios {
|
.promo_classes_spring2019 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: 0px -365px;
|
background-position: 0px -327px;
|
||||||
width: 375px;
|
width: 432px;
|
||||||
height: 361px;
|
height: 162px;
|
||||||
}
|
}
|
||||||
.promo_mystery_201810 {
|
.promo_egg_hunt {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -958px -428px;
|
background-position: -769px -148px;
|
||||||
width: 294px;
|
width: 354px;
|
||||||
height: 168px;
|
height: 147px;
|
||||||
}
|
}
|
||||||
.promo_seaserpent {
|
.promo_mystery_201902 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: 0px 0px;
|
background-position: -769px -296px;
|
||||||
width: 476px;
|
width: 240px;
|
||||||
height: 364px;
|
height: 147px;
|
||||||
|
}
|
||||||
|
.promo_mythical_marvels_bundle {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
|
background-position: -769px 0px;
|
||||||
|
width: 423px;
|
||||||
|
height: 147px;
|
||||||
|
}
|
||||||
|
.promo_seasonalshop_spring {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
|
background-position: -1010px -296px;
|
||||||
|
width: 162px;
|
||||||
|
height: 138px;
|
||||||
}
|
}
|
||||||
.promo_take_this {
|
.promo_take_this {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -958px -597px;
|
background-position: -769px -444px;
|
||||||
width: 96px;
|
width: 96px;
|
||||||
height: 69px;
|
height: 69px;
|
||||||
}
|
}
|
||||||
.promo_veteran_pets {
|
.scene_dailies {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -958px -286px;
|
background-position: -441px 0px;
|
||||||
width: 363px;
|
width: 327px;
|
||||||
height: 141px;
|
height: 276px;
|
||||||
}
|
}
|
||||||
.scene_nametag {
|
.scene_tavern {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -376px -365px;
|
background-position: 0px 0px;
|
||||||
width: 512px;
|
width: 440px;
|
||||||
height: 208px;
|
height: 326px;
|
||||||
}
|
|
||||||
.scene_tools {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: -958px 0px;
|
|
||||||
width: 366px;
|
|
||||||
height: 285px;
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user