Compare commits
274 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69cc134fff | |||
| ffd9400cb7 | |||
| 5be91ef842 | |||
| 3123183e46 | |||
| 49cca7a601 | |||
| 7fbd38d18c | |||
| 1f95376d39 | |||
| 2da0a1e88c | |||
| afacd3e1cf | |||
| a69b9e6705 | |||
| e4e5d10316 | |||
| 27c38bdf45 | |||
| ea24eeb019 | |||
| 55a8eef3e1 | |||
| 92cbb4a07d | |||
| 3f96d05365 | |||
| 0b72f6a613 | |||
| 5e1e6be518 | |||
| 472ec99291 | |||
| 0284e9a4e3 | |||
| 1a0721c078 | |||
| 6b6b548ac5 | |||
| 30f3d786bb | |||
| 07bbba6789 | |||
| 6afb2bd0d4 | |||
| f1a3bd5001 | |||
| 3f6a13d209 | |||
| 3658e41fec | |||
| c69d5c7ae6 | |||
| 747f9e6a99 | |||
| 7755ab090b | |||
| 9ed17df1e3 | |||
| faeb040a83 | |||
| 0a1ae1375e | |||
| 9756030fa2 | |||
| c66172b74b | |||
| 281f6d1806 | |||
| 237095d109 | |||
| fa788f49fc | |||
| 0817cf96e1 | |||
| 97e1d75dce | |||
| 52bf20c27d | |||
| 5dbaf39aba | |||
| 66d402c985 | |||
| 8048146223 | |||
| e2c07e458d | |||
| 90a9e8e192 | |||
| f8039f48a6 | |||
| 04337f8e83 | |||
| 45297f8bf9 | |||
| 6f112c29f2 | |||
| 4d1edb363c | |||
| 4e303cc592 | |||
| 798a975185 | |||
| eb2b46fc5d | |||
| 29854d3bdb | |||
| f8751b002c | |||
| cd545e08d5 | |||
| f69bb4f023 | |||
| 847081d2b2 | |||
| 8112d46ea4 | |||
| d13bded647 | |||
| 1de4ab3612 | |||
| f9f22f313f | |||
| f57eed85a8 | |||
| 10dd3318ab | |||
| cbef83c14a | |||
| 59709a8590 | |||
| f85f2a0c6d | |||
| 605a5a1d5c | |||
| 2d5d786c8e | |||
| 5efe5b7b10 | |||
| 3e92bb22fa | |||
| 1249b9d410 | |||
| 197aafe092 | |||
| 79829ca128 | |||
| adaa1d9a3e | |||
| 3e6691dbbb | |||
| 046761b9aa | |||
| 0b0466b960 | |||
| f8d4a2bd6b | |||
| 1af59a3770 | |||
| bbcb13c91b | |||
| d27dc46c50 | |||
| 679459b83b | |||
| 5a619773d5 | |||
| ad76ab1315 | |||
| 15eb8db925 | |||
| a0e92c5605 | |||
| eac3e36c07 | |||
| 0b8def555b | |||
| 5f5fa5c2eb | |||
| 1eac8bbbbe | |||
| 49c7580cd4 | |||
| dca958f2e2 | |||
| eae5f0d605 | |||
| 6ab091645c | |||
| d66041c280 | |||
| de070a450a | |||
| eaaab35f31 | |||
| 6a63f080ad | |||
| c42f81b629 | |||
| 9a78a7b896 | |||
| 8b70721137 | |||
| 44ffbd716d | |||
| 5bfc3a5ff4 | |||
| 0ba5df4164 | |||
| 52a59c8192 | |||
| c1a860494d | |||
| 395dafa127 | |||
| bab41647f5 | |||
| 8582a67308 | |||
| 0d58fb0fd3 | |||
| 1d2482f8bc | |||
| f4cf906127 | |||
| 559f9b1825 | |||
| c7039bc9ea | |||
| f929d36e1a | |||
| 254d1a3465 | |||
| 442aae8a35 | |||
| bcb0ed0a5c | |||
| a48b8f0e34 | |||
| 7eeeda2aae | |||
| a5ad9c30f0 | |||
| ac732b2c85 | |||
| a56b2d68fb | |||
| 25b0ff38c4 | |||
| dcc06931cc | |||
| bc3ebbd095 | |||
| e5b9581743 | |||
| 4b9fe49e3a | |||
| ab4c8b0a46 | |||
| f6c26fe869 | |||
| 80e9735b28 | |||
| aa6f188bd9 | |||
| e8b7660376 | |||
| 7d76622410 | |||
| 928e5f66c4 | |||
| 6a343535c0 | |||
| f58f6acb44 | |||
| 64754777ed | |||
| 3b5e4b6d84 | |||
| 9383578cb8 | |||
| 474672ec64 | |||
| 25c6691793 | |||
| 3ea7b72024 | |||
| 2d6f05a9a4 | |||
| 28637286d6 | |||
| 874887b790 | |||
| c977e5ebb5 | |||
| f040e668f3 | |||
| 55a15f938c | |||
| 8c4f35daf4 | |||
| 8f38ce3424 | |||
| b8f57a74d0 | |||
| 7ed26c0dbe | |||
| e8f5b26d4d | |||
| 0273648b6b | |||
| b6fdac8885 | |||
| 00e6389672 | |||
| e02c669b61 | |||
| f0cb7c6bf3 | |||
| 571ef0b309 | |||
| 74328d1bcc | |||
| d34a9d828c | |||
| 2fd35b3a40 | |||
| e27512f626 | |||
| dbf9cb3b4e | |||
| 34c1245519 | |||
| f602bfe438 | |||
| 9aa4b8aa64 | |||
| 5a150ebc5b | |||
| cbe1892b50 | |||
| 13df60e0dd | |||
| 3ff7692528 | |||
| 111bba84dc | |||
| b0d2b72b88 | |||
| 696317ea8a | |||
| 593178a46a | |||
| f8fe16482d | |||
| 5108480ec5 | |||
| 95968b1b1c | |||
| 566569af98 | |||
| 6693e9fca9 | |||
| 431bde56d2 | |||
| 7cf17c0e63 | |||
| 49561bfc8c | |||
| 8cbbb58e78 | |||
| 905549e379 | |||
| 5d45c7209a | |||
| 371cddfe17 | |||
| fcfac30caa | |||
| b094fb1e52 | |||
| a2dd82b6db | |||
| e6071610e4 | |||
| bdd0e2bb79 | |||
| 054a9a6f2b | |||
| 35b9ed6273 | |||
| e65277baa5 | |||
| 421bd8624c | |||
| 4562c6422a | |||
| a5cd9f2473 | |||
| 18bbdfa84b | |||
| d8c37f6e2d | |||
| 7f38c61c70 | |||
| 1c018cedb1 | |||
| 80892bd6a8 | |||
| 6801dae75d | |||
| 59e1de6771 | |||
| 5b240a1950 | |||
| 3ec3722038 | |||
| d798ebadfe | |||
| 6cbddef627 | |||
| 016de411c9 | |||
| 2173f53883 | |||
| f2e5bc52e5 | |||
| 393a9290e9 | |||
| ad5045bc09 | |||
| 9b515ebdd1 | |||
| 97bf9ee8e8 | |||
| f5ba636579 | |||
| 4dd7e49552 | |||
| d2f673ef1e | |||
| e198dd551a | |||
| 0bfc9d9516 | |||
| d4e20ee4aa | |||
| a751a367fc | |||
| d323be19c6 | |||
| be3f61a94b | |||
| f1bb2db73b | |||
| a622344d44 | |||
| e279a3550b | |||
| 70aab3059c | |||
| c264e37182 | |||
| b31bc15493 | |||
| ba19c00617 | |||
| 93aa92de7c | |||
| d021680945 | |||
| f9595af8a5 | |||
| d2756278c3 | |||
| 2e2dc179c4 | |||
| acf7b811ab | |||
| d5170251c0 | |||
| c9ba9054e3 | |||
| d4aac1ee4b | |||
| 9615a332a5 | |||
| 417455e5ef | |||
| 136502a110 | |||
| 425887c1e4 | |||
| cfa8a5190f | |||
| df5be81706 | |||
| 08b3491047 | |||
| e73c3147c1 | |||
| a43254000e | |||
| 4e3c984baf | |||
| c112e923f1 | |||
| 540353f024 | |||
| 2b9b5e369e | |||
| cb38475765 | |||
| 8bb92577b0 | |||
| fb26cbd26d | |||
| a0de5cd8f8 | |||
| 9fe10b1818 | |||
| d8dd39422a | |||
| 3f9b710773 | |||
| 8a8bab4be1 | |||
| 2a0747ed72 | |||
| a5196e94f6 | |||
| 009ab26711 | |||
| 3fabf3391f | |||
| 8020990264 | |||
| a2cfeafc02 | |||
| d04a4fb1ed | |||
| b16da35585 |
@@ -14,7 +14,7 @@ files:
|
||||
owner: root
|
||||
group: users
|
||||
content: |
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@3
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@4
|
||||
container_commands:
|
||||
01_makeBabel:
|
||||
command: "touch /tmp/.babel.json"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg/es6",
|
||||
"habitrpg"
|
||||
"habitrpg",
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ Fixes put_issue_url_here
|
||||
|
||||
|
||||
[//]: # (Put User ID in here - found in Settings -> API)
|
||||
|
||||
----
|
||||
UUID:
|
||||
|
||||
+16
-11
@@ -1,17 +1,22 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '4.3.1'
|
||||
- '6'
|
||||
before_install:
|
||||
- "npm install -g npm@3"
|
||||
- "npm install -g gulp"
|
||||
- "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10"
|
||||
- "echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list"
|
||||
- "sudo apt-get update"
|
||||
- "sudo apt-get install mongodb-org-server"
|
||||
- npm install -g npm@4
|
||||
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
|
||||
before_script:
|
||||
- 'npm install -g grunt-cli mocha'
|
||||
- npm run test:build
|
||||
- cp config.json.example config.json
|
||||
- "until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done"
|
||||
- "export DISPLAY=:99"
|
||||
- if [ $REQUIRES_SERVER ]; then until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done; export DISPLAY=:99; fi
|
||||
after_script:
|
||||
- "./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js"
|
||||
- ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js
|
||||
script: npm run $TEST
|
||||
env:
|
||||
matrix:
|
||||
- TEST="lint"
|
||||
- TEST="test:api-v3" REQUIRES_SERVER=true
|
||||
- TEST="test:sanity"
|
||||
- TEST="test:content"
|
||||
- TEST="test:common"
|
||||
- TEST="test:karma"
|
||||
- TEST="client:unit"
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ RUN apt-get install -y \
|
||||
python
|
||||
|
||||
# Install NodeJS
|
||||
RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
|
||||
RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Clean up package management
|
||||
@@ -25,7 +25,7 @@ RUN apt-get clean
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g npm@3
|
||||
RUN npm install -g npm@4
|
||||
RUN npm install -g gulp grunt-cli bower
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
|
||||
+3
-10
@@ -57,7 +57,7 @@ module.exports = function(grunt) {
|
||||
files: [
|
||||
{expand: true, cwd: 'website/client-old/', src: 'favicon.ico', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/client-old/', src: 'favicon_192x192.png', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/static/sprites'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'backer-only/*.gif', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'npc_ian.gif', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'quest_*.gif', dest: 'website/build/'},
|
||||
@@ -78,6 +78,7 @@ module.exports = function(grunt) {
|
||||
'website/build/favicon.ico',
|
||||
'website/build/favicon_192x192.png',
|
||||
'website/build/*.png',
|
||||
'website/build/static/sprites/*.png',
|
||||
'website/build/*.gif',
|
||||
'website/build/bower_components/bootstrap/dist/fonts/*'
|
||||
],
|
||||
@@ -126,15 +127,7 @@ module.exports = function(grunt) {
|
||||
// Register tasks.
|
||||
grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']);
|
||||
grunt.registerTask('build:dev', ['cssmin', 'stylus']);
|
||||
grunt.registerTask('build:test', ['test:prepare:translations', 'build:dev']);
|
||||
|
||||
grunt.registerTask('test:prepare:translations', function() {
|
||||
var i18n = require('./website/server/libs/i18n'),
|
||||
fs = require('fs');
|
||||
fs.writeFileSync('test/client-old/spec/mocks/translations.js',
|
||||
"if(!window.env) window.env = {};\n" +
|
||||
"window.env.translations = " + JSON.stringify(i18n.translations['en']) + ';');
|
||||
});
|
||||
grunt.registerTask('build:test', ['build:dev']);
|
||||
|
||||
// Load tasks
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
"jquery-ui": "1.10.3",
|
||||
"jquery.cookie": "1.4.0",
|
||||
"js-emoji": "snicker/js-emoji#f25d8a303f",
|
||||
"ngInfiniteScroll": "1.0.0",
|
||||
"ngInfiniteScroll": "1.1.0",
|
||||
"pnotify": "1.3.1",
|
||||
"sticky": "1.0.3",
|
||||
"swagger-ui": "wordnik/swagger-ui#v2.0.24",
|
||||
|
||||
+2
-1
@@ -79,6 +79,7 @@
|
||||
},
|
||||
"SLACK": {
|
||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/"
|
||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||
"SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id"
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -12,6 +12,9 @@ import {each} from 'lodash';
|
||||
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
|
||||
const DIST_PATH = 'website/assets/sprites/dist/';
|
||||
|
||||
const IMG_DIST_PATH_NEW_CLIENT = 'website/static/sprites/';
|
||||
const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/';
|
||||
|
||||
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
@@ -25,7 +28,7 @@ gulp.task('sprites:largeSprites', () => {
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', (done) => {
|
||||
clean(`${DIST_PATH}spritesmith*`, done);
|
||||
clean(`{${DIST_PATH}spritesmith*,${IMG_DIST_PATH_NEW_CLIENT}spritesmith*,${CSS_DIST_PATH_NEW_CLIENT}spritesmith*}`, done);
|
||||
});
|
||||
|
||||
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
|
||||
@@ -66,14 +69,16 @@ function createSpritesStream (name, src) {
|
||||
algorithm: 'binary-tree',
|
||||
padding: 1,
|
||||
cssTemplate: 'website/assets/sprites/css/css.template.handlebars',
|
||||
cssVarMap: cssVarMap
|
||||
cssVarMap: cssVarMap,
|
||||
}));
|
||||
|
||||
let imgStream = spriteData.img
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
let cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
stream.add(imgStream);
|
||||
@@ -148,4 +153,9 @@ function cssVarMap (sprite) {
|
||||
}
|
||||
if (~sprite.name.indexOf('shirt'))
|
||||
sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts
|
||||
if (~sprite.name.indexOf('hair_base')) {
|
||||
let styleArray = sprite.name.split('_').slice(2,3);
|
||||
if (Number(styleArray[0]) > 14)
|
||||
sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos
|
||||
}
|
||||
}
|
||||
|
||||
+15
-5
@@ -13,6 +13,9 @@ import Bluebird from 'bluebird';
|
||||
import runSequence from 'run-sequence';
|
||||
import os from 'os';
|
||||
import nconf from 'nconf';
|
||||
import fs from 'fs';
|
||||
|
||||
const i18n = require('../website/server/libs/i18n');
|
||||
|
||||
// TODO rewrite
|
||||
|
||||
@@ -72,10 +75,17 @@ gulp.task('test:prepare:server', ['test:prepare:mongo'], () => {
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:build', ['build'], (cb) => {
|
||||
exec(testBin('grunt build:test'), cb);
|
||||
gulp.task('test:prepare:translations', (cb) => {
|
||||
fs.writeFile(
|
||||
'test/client-old/spec/mocks/translations.js',
|
||||
`if(!window.env) window.env = {};
|
||||
window.env.translations = ${JSON.stringify(i18n.translations['en'])};`, cb);
|
||||
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:build', ['build', 'test:prepare:translations']);
|
||||
// exec(testBin('grunt build:test'), cb);
|
||||
|
||||
gulp.task('test:prepare:webdriver', (cb) => {
|
||||
exec('npm run test:prepare:webdriver', cb);
|
||||
});
|
||||
@@ -270,7 +280,7 @@ gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => {
|
||||
|
||||
gulp.task('test:api-v3:unit', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/unit --recursive'),
|
||||
testBin('mocha test/api/v3/unit --recursive --require ./test/helpers/start-server'),
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
@@ -288,7 +298,7 @@ gulp.task('test:api-v3:unit:watch', () => {
|
||||
|
||||
gulp.task('test:api-v3:integration', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive'),
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
@@ -308,7 +318,7 @@ gulp.task('test:api-v3:integration:watch', () => {
|
||||
|
||||
gulp.task('test:api-v3:integration:separate-server', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive', 'LOAD_SERVER=0'),
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => done(err)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
var migrationName = '20161030-jackolanterns.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* set the newStuff flag in all user accounts so they see a Bailey message
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-10-01')} // remove when running migration a second time
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'migration': 1,
|
||||
'items.pets.JackOLantern-Base': 1,
|
||||
'items.mounts.JackOLantern-Base': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
var inc = {};
|
||||
if (user.migration !== migrationName) {
|
||||
if (user.items.mounts['JackOLantern-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost':5};
|
||||
} else if (user.items.pets['JackOLantern-Base']) {
|
||||
set = {'migration':migrationName, 'items.mounts.JackOLantern-Base':true};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.pets.JackOLantern-Base':5};
|
||||
}
|
||||
inc = {
|
||||
'items.food.Candy_Base': 1,
|
||||
'items.food.Candy_CottonCandyBlue': 1,
|
||||
'items.food.Candy_CottonCandyPink': 1,
|
||||
'items.food.Candy_Desert': 1,
|
||||
'items.food.Candy_Golden': 1,
|
||||
'items.food.Candy_Red': 1,
|
||||
'items.food.Candy_Shade': 1,
|
||||
'items.food.Candy_Skeleton': 1,
|
||||
'items.food.Candy_White': 1,
|
||||
'items.food.Candy_Zombie': 1,
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set, $inc:inc});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
var migrationName = '20161102_takeThis.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['d1be0965-e909-4d30-82fa-9a0011f885b2']}
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
var migrationName = '20161122_turkey_ladder.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Yearly Turkey Day award. Turkey pet, Turkey mount, Gilded Turkey pet, Gilded Turkey mount
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-10-31')} // Extend timeframe each run of migration
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'migration': 1,
|
||||
'items.mounts': 1,
|
||||
'items.pets': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (user.items.pets['Turkey-Gilded']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true};
|
||||
} else if (user.items.mounts['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5};
|
||||
} else if (user.items.pets['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Base':true};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Base':5};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
var migrationName = '20161201_takeThis.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['ff674aba-a114-4a6f-8ebc-1de27ffb646e']}
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ var _id = '';
|
||||
var update = {
|
||||
$addToSet: {
|
||||
'purchased.plan.mysteryItems':{
|
||||
$each:['head_mystery_201609','armor_mystery_201609']
|
||||
$each:['head_mystery_201612','armor_mystery_201612']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,14 +6,11 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
* Remove flag stating that the Enchanted Armoire is empty, for when new equipment is added
|
||||
*/
|
||||
|
||||
var dbserver = 'localhost:27017'; // FOR TEST DATABASE
|
||||
// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE
|
||||
var dbname = 'habitrpg';
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
@@ -22,7 +19,6 @@ var query = {
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'flags.armoireEmpty':1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
@@ -32,7 +28,8 @@ dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
return displayData();
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
|
||||
Generated
+4850
-1133
File diff suppressed because it is too large
Load Diff
+20
-14
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "3.47.2",
|
||||
"version": "3.64.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "3.6.0",
|
||||
@@ -15,8 +15,10 @@
|
||||
"aws-sdk": "^2.0.25",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-loader": "^6.0.0",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-async-to-module-method": "^6.8.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.16.0",
|
||||
"babel-plugin-transform-regenerator": "^6.16.1",
|
||||
"babel-polyfill": "^6.6.1",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"babel-register": "^6.6.0",
|
||||
@@ -29,13 +31,13 @@
|
||||
"compression": "^1.6.1",
|
||||
"connect-ratelimit": "0.0.7",
|
||||
"cookie-session": "^1.2.0",
|
||||
"coupon-code": "^0.4.3",
|
||||
"coupon-code": "^0.4.5",
|
||||
"css-loader": "^0.23.1",
|
||||
"csv-stringify": "^1.0.2",
|
||||
"cwait": "^1.0.0",
|
||||
"domain-middleware": "~0.1.0",
|
||||
"estraverse": "^4.1.1",
|
||||
"express": "~4.13.3",
|
||||
"express": "~4.14.0",
|
||||
"express-csv": "~0.6.0",
|
||||
"express-validator": "^2.18.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
@@ -64,16 +66,18 @@
|
||||
"image-size": "~0.3.2",
|
||||
"in-app-purchase": "^1.1.6",
|
||||
"jade": "~1.11.0",
|
||||
"jquery": "https://registry.npmjs.org/jquery/-/jquery-3.1.1.tgz",
|
||||
"js2xmlparser": "~1.0.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"lodash": "^3.10.1",
|
||||
"lodash.pickby": "^4.2.0",
|
||||
"lodash.setwith": "^4.2.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"method-override": "^2.3.5",
|
||||
"moment": "^2.13.0",
|
||||
"mongoose": "^4.4.16",
|
||||
"mongoose": "^4.7.1",
|
||||
"mongoose-id-autoinc": "~2013.7.14-4",
|
||||
"morgan": "^1.7.0",
|
||||
"nconf": "~0.8.2",
|
||||
@@ -89,12 +93,13 @@
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
"paypal-ipn": "3.0.0",
|
||||
"paypal-rest-sdk": "^1.2.1",
|
||||
"postcss-easy-import": "^1.0.1",
|
||||
"pretty-data": "^0.40.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.0-beta6",
|
||||
"push-notify": "habitrpg/push-notify#v1.2.0",
|
||||
"pusher": "^1.3.0",
|
||||
"request": "~2.72.0",
|
||||
"request": "~2.74.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"run-sequence": "^1.1.4",
|
||||
"s3-upload-stream": "^1.0.6",
|
||||
@@ -110,11 +115,12 @@
|
||||
"validator": "^4.9.0",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"vue": "^2.0.0-rc.6",
|
||||
"vue": "^2.1.0",
|
||||
"vue-hot-reload-api": "^1.2.0",
|
||||
"vue-loader": "^9.4.0",
|
||||
"vue-loader": "^10.0.0",
|
||||
"vue-resource": "^1.0.2",
|
||||
"vue-router": "^2.0.0-rc.5",
|
||||
"vue-template-compiler": "^2.1.0",
|
||||
"webpack": "^1.12.2",
|
||||
"webpack-merge": "^0.8.3",
|
||||
"winston": "^2.1.0",
|
||||
@@ -122,12 +128,13 @@
|
||||
},
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "^4.3.1",
|
||||
"npm": "^3.8.9"
|
||||
"node": "^6.9.1",
|
||||
"npm": "^4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"test": "npm run lint && gulp test && npm run client:unit",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
"test:api-v3:unit": "gulp test:api-v3:unit",
|
||||
"test:api-v3:integration": "gulp test:api-v3:integration",
|
||||
@@ -153,7 +160,6 @@
|
||||
"postinstall": "bower --config.interactive=false install -f; gulp build; npm run client:build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^6.0.0",
|
||||
"chai": "^3.4.0",
|
||||
"chai-as-promised": "^5.1.0",
|
||||
"chalk": "^1.1.3",
|
||||
@@ -163,13 +169,12 @@
|
||||
"cross-spawn": "^2.1.5",
|
||||
"csv": "~0.3.6",
|
||||
"deep-diff": "~0.1.4",
|
||||
"eslint": "~2.12.0",
|
||||
"eslint-config-habitrpg": "^1.0.0",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-config-habitrpg": "^2.0.0",
|
||||
"eslint-friendly-formatter": "^2.0.5",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"eslint-plugin-babel": "^3.0.0",
|
||||
"eslint-plugin-html": "^1.3.0",
|
||||
"eslint-plugin-mocha": "^2.1.0",
|
||||
"eslint-plugin-mocha": "^4.7.0",
|
||||
"event-stream": "^3.2.2",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"expect.js": "~0.2.0",
|
||||
@@ -202,6 +207,7 @@
|
||||
"selenium-server": "2.53.0",
|
||||
"sinon": "^1.17.2",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"sinon-stub-promise": "^4.0.0",
|
||||
"superagent-defaults": "^0.1.13",
|
||||
"vinyl-transform": "^1.0.0",
|
||||
"webpack-dev-middleware": "^1.4.0",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": [
|
||||
"habitrpg/mocha",
|
||||
"habitrpg/babel"
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
describe('GET challenges/group/:groupId', () => {
|
||||
describe('GET challenges/groups/:groupId', () => {
|
||||
context('Public Guild', () => {
|
||||
let publicGuild, user, nonMember, challenge, challenge2;
|
||||
|
||||
|
||||
@@ -29,14 +29,6 @@ describe('POST /coupons/generate/:event', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if event is missing', async () => {
|
||||
await expect(user.post('/coupons/generate')).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Not found.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if event is invalid', async () => {
|
||||
await expect(user.post('/coupons/generate/notValid?count=1')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
|
||||
@@ -7,15 +7,21 @@ import {
|
||||
import moment from 'moment';
|
||||
|
||||
describe('GET /export/history.csv', () => {
|
||||
it('should return a valid CSV file with tasks history data', async () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid CSV file with tasks history data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'daily', text: 'daily 1'},
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'habit', text: 'habit 2'},
|
||||
{type: 'todo', text: 'todo 1'},
|
||||
]);
|
||||
|
||||
// to handle occasional inconsistency in task creation order
|
||||
tasks.sort(function (a, b) {
|
||||
return a.text.localeCompare(b.text);
|
||||
});
|
||||
|
||||
// score all the tasks twice
|
||||
await user.post(`/tasks/${tasks[0]._id}/score/up`);
|
||||
await user.post(`/tasks/${tasks[1]._id}/score/up`);
|
||||
@@ -28,7 +34,7 @@ describe('GET /export/history.csv', () => {
|
||||
await user.post(`/tasks/${tasks[3]._id}/score/up`);
|
||||
|
||||
// adding an history entry to daily 1 manually because cron didn't run yet
|
||||
await updateDocument('tasks', tasks[1], {
|
||||
await updateDocument('tasks', tasks[0], {
|
||||
history: [{value: 3.2, date: Number(new Date())}],
|
||||
});
|
||||
|
||||
@@ -41,11 +47,11 @@ describe('GET /export/history.csv', () => {
|
||||
let splitRes = res.split('\n');
|
||||
|
||||
expect(splitRes[0]).to.equal('Task Name,Task ID,Task Type,Date,Value');
|
||||
expect(splitRes[1]).to.equal(`habit 1,${tasks[0]._id},habit,${moment(tasks[0].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[0].value}`);
|
||||
expect(splitRes[2]).to.equal(`habit 1,${tasks[0]._id},habit,${moment(tasks[0].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[1].value}`);
|
||||
expect(splitRes[3]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[0].value}`);
|
||||
expect(splitRes[4]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[1].value}`);
|
||||
expect(splitRes[5]).to.equal(`daily 1,${tasks[1]._id},daily,${moment(tasks[1].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[0].value}`);
|
||||
expect(splitRes[1]).to.equal(`daily 1,${tasks[0]._id},daily,${moment(tasks[0].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[0].value}`);
|
||||
expect(splitRes[2]).to.equal(`habit 1,${tasks[1]._id},habit,${moment(tasks[1].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[0].value}`);
|
||||
expect(splitRes[3]).to.equal(`habit 1,${tasks[1]._id},habit,${moment(tasks[1].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[1].value}`);
|
||||
expect(splitRes[4]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[0].value}`);
|
||||
expect(splitRes[5]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[1].value}`);
|
||||
expect(splitRes[6]).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,8 @@ import Bluebird from 'bluebird';
|
||||
let parseStringAsync = Bluebird.promisify(xml2js.parseString, {context: xml2js});
|
||||
|
||||
describe('GET /export/userdata.xml', () => {
|
||||
it('should return a valid XML file with user data', async () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid XML file with user data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
|
||||
@@ -82,8 +82,10 @@ describe('GET /groups/:groupId/members', () => {
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql(['size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background'].sort());
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
|
||||
@@ -134,6 +134,22 @@ describe('POST /group/:groupId/join', () => {
|
||||
|
||||
await expect(user.get('/user')).to.eventually.have.deep.property('items.quests.basilist', 1);
|
||||
});
|
||||
|
||||
it('notifies inviting user that their invitation was accepted', async () => {
|
||||
await invitedUser.post(`/groups/${guild._id}/join`);
|
||||
|
||||
let inviter = await user.get('/user');
|
||||
let expectedData = {
|
||||
headerText: t('invitationAcceptedHeader'),
|
||||
bodyText: t('invitationAcceptedBody', {
|
||||
username: invitedUser.auth.local.username,
|
||||
groupName: guild.name,
|
||||
}),
|
||||
};
|
||||
|
||||
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
|
||||
expect(inviter.notifications[0].data).to.eql(expectedData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,6 +188,23 @@ describe('POST /group/:groupId/join', () => {
|
||||
await expect(invitedUser.get('/user')).to.eventually.have.deep.property('party._id', party._id);
|
||||
});
|
||||
|
||||
it('notifies inviting user that their invitation was accepted', async () => {
|
||||
await invitedUser.post(`/groups/${party._id}/join`);
|
||||
|
||||
let inviter = await user.get('/user');
|
||||
|
||||
let expectedData = {
|
||||
headerText: t('invitationAcceptedHeader'),
|
||||
bodyText: t('invitationAcceptedBody', {
|
||||
username: invitedUser.auth.local.username,
|
||||
groupName: party.name,
|
||||
}),
|
||||
};
|
||||
|
||||
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
|
||||
expect(inviter.notifications[0].data).to.eql(expectedData);
|
||||
});
|
||||
|
||||
it('clears invitation from user when joining party', async () => {
|
||||
await invitedUser.post(`/groups/${party._id}/join`);
|
||||
|
||||
|
||||
@@ -65,6 +65,19 @@ describe('POST /groups/:groupId/leave', () => {
|
||||
expect(groupToLeave.leader).to.equal(member._id);
|
||||
});
|
||||
|
||||
it('removes new messages for that group from user', async () => {
|
||||
await member.post(`/groups/${groupToLeave._id}/chat`, { message: 'Some message' });
|
||||
|
||||
await leader.sync();
|
||||
|
||||
expect(leader.newMessages[groupToLeave._id]).to.not.be.empty;
|
||||
|
||||
await leader.post(`/groups/${groupToLeave._id}/leave`);
|
||||
await leader.sync();
|
||||
|
||||
expect(leader.newMessages[groupToLeave._id]).to.be.empty;
|
||||
});
|
||||
|
||||
context('With challenges', () => {
|
||||
let challenge;
|
||||
|
||||
@@ -122,6 +135,8 @@ describe('POST /groups/:groupId/leave', () => {
|
||||
privateGuild = group;
|
||||
leader = groupLeader;
|
||||
invitedUser = invitees[0];
|
||||
|
||||
await leader.post(`/groups/${group._id}/chat`, { message: 'Some message' });
|
||||
});
|
||||
|
||||
it('removes a group when the last member leaves', async () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
|
||||
describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
let leader;
|
||||
@@ -60,6 +61,14 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
});
|
||||
|
||||
context('Guilds', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('can remove other members', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${member._id}`);
|
||||
let memberRemoved = await member.get('/user');
|
||||
@@ -80,6 +89,22 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
|
||||
expect(_.findIndex(invitedUserWithoutInvite.invitations.guilds, {id: guild._id})).eql(-1);
|
||||
});
|
||||
|
||||
it('sends email to user with rescinded invite', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${invitedUser._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(invitedUser._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('guild-invite-rescinded');
|
||||
});
|
||||
|
||||
it('sends email to removed user', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${member._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(member._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('kicked-from-guild');
|
||||
});
|
||||
});
|
||||
|
||||
context('Party', () => {
|
||||
@@ -87,6 +112,7 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
let partyLeader;
|
||||
let partyInvitedUser;
|
||||
let partyMember;
|
||||
let removedMember;
|
||||
|
||||
beforeEach(async () => {
|
||||
let { group, groupLeader, invitees, members } = await createAndPopulateGroup({
|
||||
@@ -96,13 +122,19 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
privacy: 'private',
|
||||
},
|
||||
invites: 1,
|
||||
members: 1,
|
||||
members: 2,
|
||||
});
|
||||
|
||||
party = group;
|
||||
partyLeader = groupLeader;
|
||||
partyInvitedUser = invitees[0];
|
||||
partyMember = members[0];
|
||||
removedMember = members[1];
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('can remove other members', async () => {
|
||||
@@ -129,6 +161,18 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
expect(invitedUserWithoutInvite.invitations.party).to.be.empty;
|
||||
});
|
||||
|
||||
it('removes new messages from a member who is removed', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/chat`, { message: 'Some message' });
|
||||
await removedMember.sync();
|
||||
|
||||
expect(removedMember.newMessages[party._id]).to.not.be.empty;
|
||||
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${removedMember._id}`);
|
||||
await removedMember.sync();
|
||||
|
||||
expect(removedMember.newMessages[party._id]).to.be.empty;
|
||||
});
|
||||
|
||||
it('removes user from quest when removing user from party after quest starts', async () => {
|
||||
let petQuest = 'whale';
|
||||
await partyLeader.update({
|
||||
@@ -173,5 +217,21 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
expect(party.quest.members[partyLeader._id]).to.be.true;
|
||||
expect(party.quest.members[partyMember._id]).to.not.exist;
|
||||
});
|
||||
|
||||
it('sends email to user with rescinded invite', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${partyInvitedUser._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(partyInvitedUser._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('party-invite-rescinded');
|
||||
});
|
||||
|
||||
it('sends email to removed user', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${partyMember._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(partyMember._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('kicked-from-party');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('GET /members/:memberId/achievements', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('validates req.params.memberId', async () => {
|
||||
await expect(user.get('/members/invalidUUID/achievements')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns achievements based on given user', async () => {
|
||||
let member = await generateUser({
|
||||
contributor: {level: 1},
|
||||
backer: {tier: 3},
|
||||
});
|
||||
let achievementsRes = await user.get(`/members/${member._id}/achievements`);
|
||||
|
||||
expect(achievementsRes.special.achievements.contributor.earned).to.equal(true);
|
||||
expect(achievementsRes.special.achievements.contributor.value).to.equal(1);
|
||||
|
||||
expect(achievementsRes.special.achievements.kickstarter.earned).to.equal(true);
|
||||
expect(achievementsRes.special.achievements.kickstarter.value).to.equal(3);
|
||||
});
|
||||
|
||||
it('handles non-existing members', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.get(`/members/${dummyId}/achievements`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('userWithIDNotFound', {userId: dummyId}),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -35,8 +35,10 @@ describe('GET /members/:memberId', () => {
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql(['size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background'].sort());
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
|
||||
@@ -4,6 +4,14 @@ import {
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
function findMessage (messages, receiverId) {
|
||||
let message = _.find(messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiverId;
|
||||
});
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
describe('POST /members/transfer-gems', () => {
|
||||
let userToSendMessage;
|
||||
let receiver;
|
||||
@@ -116,19 +124,14 @@ describe('POST /members/transfer-gems', () => {
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === userToSendMessage._id;
|
||||
});
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiver._id;
|
||||
});
|
||||
|
||||
let messageSentContent = t('privateMessageGiftIntro', {
|
||||
let messageSentContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
});
|
||||
messageSentContent += t('privateMessageGiftGemsMessage', {gemAmount});
|
||||
messageSentContent = `\`${messageSentContent}\` `;
|
||||
messageSentContent += message;
|
||||
|
||||
@@ -150,19 +153,14 @@ describe('POST /members/transfer-gems', () => {
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === userToSendMessage._id;
|
||||
});
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiver._id;
|
||||
});
|
||||
|
||||
let messageSentContent = t('privateMessageGiftIntro', {
|
||||
let messageSentContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
});
|
||||
messageSentContent += t('privateMessageGiftGemsMessage', {gemAmount});
|
||||
messageSentContent = `\`${messageSentContent}\` `;
|
||||
|
||||
expect(sendersMessageInReceiversInbox).to.exist;
|
||||
@@ -173,4 +171,40 @@ describe('POST /members/transfer-gems', () => {
|
||||
expect(sendersMessageInSendersInbox.text).to.equal(messageSentContent);
|
||||
expect(updatedSender.balance).to.equal(0);
|
||||
});
|
||||
|
||||
it('sends transfer gems message in each participant\'s language', async () => {
|
||||
await receiver.update({
|
||||
'preferences.language': 'es',
|
||||
});
|
||||
await userToSendMessage.update({
|
||||
'preferences.language': 'cs',
|
||||
});
|
||||
await userToSendMessage.post('/members/transfer-gems', {
|
||||
gemAmount,
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let [receieversMessageContent, sendersMessageContent] = ['es', 'cs'].map((lang) => {
|
||||
let messageContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
}, lang);
|
||||
|
||||
return `\`${messageContent}\` `;
|
||||
});
|
||||
|
||||
expect(sendersMessageInReceiversInbox).to.exist;
|
||||
expect(sendersMessageInReceiversInbox.text).to.equal(receieversMessageContent);
|
||||
|
||||
expect(sendersMessageInSendersInbox).to.exist;
|
||||
expect(sendersMessageInSendersInbox.text).to.equal(sendersMessageContent);
|
||||
expect(updatedSender.balance).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /notifications/:notificationId/read', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('errors when notification is not found', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.post(`/notifications/${dummyId}/read`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('messageNotificationNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
xit('removes a notification', async () => {
|
||||
});
|
||||
});
|
||||
+6
-8
@@ -10,13 +10,11 @@ describe('payments - amazon - #createOrderReferenceId', () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies billingAgreementId', async (done) => {
|
||||
try {
|
||||
await user.post(endpoint);
|
||||
} catch (e) {
|
||||
// Parameter AWSAccessKeyId cannot be empty.
|
||||
expect(e.error).to.eql('BadRequest');
|
||||
done();
|
||||
}
|
||||
it('verifies billingAgreementId', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.billingAgreementId',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
|
||||
describe('POST /groups/:groupId/quests/abort', () => {
|
||||
let questingGroup;
|
||||
@@ -85,6 +86,7 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
});
|
||||
|
||||
it('aborts a quest', async () => {
|
||||
sandbox.stub(Group.prototype, 'sendChat');
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
@@ -123,5 +125,8 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
},
|
||||
members: {},
|
||||
});
|
||||
expect(Group.prototype.sendChat).to.be.calledOnce;
|
||||
expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/);
|
||||
Group.prototype.sendChat.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('GET /shops/seasonal', () => {
|
||||
|
||||
expect(shop.identifier).to.equal('seasonalShop');
|
||||
expect(shop.text).to.eql(t('seasonalShop'));
|
||||
expect(shop.notes).to.eql(t('seasonalShopFallText'));
|
||||
expect(shop.notes).to.be.a('string');
|
||||
expect(shop.imageName).to.be.a('string');
|
||||
expect(shop.categories).to.be.an('array');
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('GET /tasks/user', () => {
|
||||
for (let i = 0; i < numberOfTodos; i++) {
|
||||
let id = todos[i]._id;
|
||||
|
||||
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line babel/no-await-in-loop
|
||||
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
await user.sync();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
||||
|
||||
for (let task of tasks) {
|
||||
if (['todo 2', 'todo 3', 'todo 6'].indexOf(task.text) !== -1) {
|
||||
await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line babel/no-await-in-loop
|
||||
await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ describe('PUT /tasks/:id', () => {
|
||||
checklist: [
|
||||
{text: 123, completed: false},
|
||||
],
|
||||
collapseChecklist: false,
|
||||
});
|
||||
await sleep(2);
|
||||
|
||||
@@ -111,6 +112,7 @@ describe('PUT /tasks/:id', () => {
|
||||
{text: 123, completed: false},
|
||||
{text: 456, completed: true},
|
||||
],
|
||||
collapseChecklist: true,
|
||||
notes: 'new notes',
|
||||
attribute: 'per',
|
||||
tags: [challengeUserTaskId],
|
||||
@@ -143,6 +145,8 @@ describe('PUT /tasks/:id', () => {
|
||||
expect(savedChallengeUserTask.streak).to.equal(25);
|
||||
expect(savedChallengeUserTask.reminders.length).to.equal(2);
|
||||
expect(savedChallengeUserTask.checklist.length).to.equal(2);
|
||||
expect(savedChallengeUserTask.alias).to.equal('a-short-task-name');
|
||||
expect(savedChallengeUserTask.collapseChecklist).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,12 +5,17 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/challenge/:challengeId', () => {
|
||||
let user;
|
||||
let guild;
|
||||
let challenge;
|
||||
|
||||
function findUserChallengeTask (memberTask) {
|
||||
return memberTask.challenge.id === challenge._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({balance: 1});
|
||||
guild = await generateGroup(user);
|
||||
@@ -88,6 +93,9 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.habits.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test habit');
|
||||
@@ -95,6 +103,8 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
expect(task.type).to.eql('habit');
|
||||
expect(task.up).to.eql(false);
|
||||
expect(task.down).to.eql(true);
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('creates a todo', async () => {
|
||||
@@ -105,11 +115,16 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.todos.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test todo');
|
||||
expect(task.notes).to.eql('1976');
|
||||
expect(task.type).to.eql('todo');
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('creates a daily', async () => {
|
||||
@@ -124,6 +139,9 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.dailys.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test daily');
|
||||
@@ -132,5 +150,7 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
expect(task.frequency).to.eql('daily');
|
||||
expect(task.everyX).to.eql(5);
|
||||
expect(new Date(task.startDate)).to.eql(now);
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,4 +69,16 @@ describe('DELETE /tasks/:id', () => {
|
||||
expect(syncedTask.group.broken).to.equal('TASK_DELETED');
|
||||
expect(member2SyncedTask.group.broken).to.equal('TASK_DELETED');
|
||||
});
|
||||
|
||||
it('prevents a user from deleting a task they are assigned to', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.del(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cantDeleteAssignedGroupTasks'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('GET /approvals/group/:groupId', () => {
|
||||
let user, guild, member, task, syncedTask;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
try {
|
||||
await member.post(`/tasks/${syncedTask._id}/score/up`);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-empty
|
||||
}
|
||||
});
|
||||
|
||||
it('errors when user is not the group leader', async () => {
|
||||
await expect(member.get(`/approvals/group/${guild._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanEditTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a list of task that need approval', async () => {
|
||||
let approvals = await user.get(`/approvals/group/${guild._id}`);
|
||||
expect(approvals[0]._id).to.equal(syncedTask._id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/:id/approve/:userId', () => {
|
||||
let user, guild, member, task;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when user is not assigned', async () => {
|
||||
await expect(user.post(`/tasks/${task._id}/approve/${member._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('taskNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when user is not the group leader', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
await expect(member.post(`/tasks/${task._id}/approve/${member._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanEditTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('approves an assigned user', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
await user.post(`/tasks/${task._id}/approve/${member._id}`);
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await member.sync();
|
||||
|
||||
expect(member.notifications.length).to.equal(2);
|
||||
expect(member.notifications[0].type).to.equal('GROUP_TASK_APPROVED');
|
||||
expect(member.notifications[0].data.message).to.equal(t('yourTaskHasBeenApproved', {taskText: task.text}));
|
||||
expect(member.notifications[1].type).to.equal('SCORED_TASK');
|
||||
expect(member.notifications[1].data.message).to.equal(t('yourTaskHasBeenApproved', {taskText: task.text}));
|
||||
|
||||
expect(syncedTask.group.approval.approved).to.be.true;
|
||||
expect(syncedTask.group.approval.approvingUser).to.equal(user._id);
|
||||
expect(syncedTask.group.approval.dateApproved).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/:id/score/:direction', () => {
|
||||
let user, guild, member, task;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
});
|
||||
|
||||
it('prevents user from scoring a task that needs to be approved', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
let updatedTask = await member.get(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].type).to.equal('GROUP_TASK_APPROVAL');
|
||||
expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', {
|
||||
user: member.auth.local.username,
|
||||
taskName: updatedTask.text,
|
||||
}));
|
||||
expect(user.notifications[0].data.groupId).to.equal(guild._id);
|
||||
|
||||
expect(updatedTask.group.approval.requested).to.equal(true);
|
||||
expect(updatedTask.group.approval.requestedDate).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
|
||||
it('errors when approval has already been requested', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskRequiresApproval'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to score an apporoved task', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await user.post(`/tasks/${task._id}/approve/${member._id}`);
|
||||
|
||||
await member.post(`/tasks/${syncedTask._id}/score/up`);
|
||||
let updatedTask = await member.get(`/tasks/${syncedTask._id}`);
|
||||
|
||||
expect(updatedTask.completed).to.equal(true);
|
||||
expect(updatedTask.dateCompleted).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
});
|
||||
@@ -82,7 +82,7 @@ describe('POST /tasks/:taskId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('allows user to assign themselves', async () => {
|
||||
it('allows user to assign themselves (claim)', async () => {
|
||||
await member.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let groupTask = await user.get(`/tasks/group/${guild._id}`);
|
||||
@@ -93,6 +93,14 @@ describe('POST /tasks/:taskId', () => {
|
||||
expect(syncedTask).to.exist;
|
||||
});
|
||||
|
||||
it('sends a message to the group when a user claims a task', async () => {
|
||||
await member.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let updateGroup = await user.get(`/groups/${guild._id}`);
|
||||
|
||||
expect(updateGroup.chat[0].text).to.equal(t('userIsClamingTask', {username: member.profile.name, task: task.text}));
|
||||
});
|
||||
|
||||
it('assigns a task to a user', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
|
||||
@@ -20,6 +20,6 @@ describe('POST /user/purchase-hourglass/:type/:key', () => {
|
||||
|
||||
expect(response.message).to.eql(t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.pets).to.eql({'MantisShrimp-Base': 5});
|
||||
expect(user.items.pets['MantisShrimp-Base']).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,6 +66,7 @@ describe('POST /user/auth/local/register', () => {
|
||||
});
|
||||
|
||||
await expect(getProperty('users', user._id, '_ABtest')).to.eventually.be.a('string');
|
||||
await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object');
|
||||
});
|
||||
|
||||
it('requires password and confirmPassword to match', async () => {
|
||||
|
||||
@@ -278,6 +278,7 @@ describe('analyticsService', () => {
|
||||
todos: [{_id: 'todo'}],
|
||||
rewards: [{_id: 'reward'}],
|
||||
balance: 12,
|
||||
loginIncentives: 1,
|
||||
};
|
||||
|
||||
data.user = user;
|
||||
@@ -302,6 +303,7 @@ describe('analyticsService', () => {
|
||||
contributorLevel: 1,
|
||||
subscription: 'foo-plan',
|
||||
balance: 12,
|
||||
loginIncentives: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -351,7 +353,8 @@ describe('analyticsService', () => {
|
||||
purchaseType: 'checkout',
|
||||
gift: false,
|
||||
quantity: 1,
|
||||
headers: {'x-client': 'habitica-web',
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { model as User } from '../../../../../website/server/models/user';
|
||||
import * as Tasks from '../../../../../website/server/models/task';
|
||||
import { clone } from 'lodash';
|
||||
import common from '../../../../../website/common';
|
||||
import analytics from '../../../../../website/server/libs/analyticsService';
|
||||
|
||||
// const scoreTask = common.ops.scoreTask;
|
||||
|
||||
@@ -17,9 +18,6 @@ describe('cron', () => {
|
||||
let user;
|
||||
let tasksByType = {habits: [], dailys: [], todos: [], rewards: []};
|
||||
let daysMissed = 0;
|
||||
let analytics = {
|
||||
track: sinon.spy(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
user = new User({
|
||||
@@ -34,11 +32,17 @@ describe('cron', () => {
|
||||
},
|
||||
});
|
||||
|
||||
sinon.spy(analytics, 'track');
|
||||
|
||||
user._statsComputed = {
|
||||
mp: 10,
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('updates user.preferences.timezoneOffsetAtLastCron', () => {
|
||||
let timezoneOffsetFromUserPrefs = 1;
|
||||
|
||||
@@ -59,6 +63,11 @@ describe('cron', () => {
|
||||
expect(user.flags.cronCount).to.be.greaterThan(cronCountBefore);
|
||||
});
|
||||
|
||||
it('calls analytics', () => {
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(analytics.track.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
describe('end of the month perks', () => {
|
||||
beforeEach(() => {
|
||||
user.purchased.plan.customerId = 'subscribedId';
|
||||
@@ -257,6 +266,11 @@ describe('cron', () => {
|
||||
user.preferences.sleep = true;
|
||||
});
|
||||
|
||||
it('calls analytics', () => {
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(analytics.track.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('clears user buffs', () => {
|
||||
user.stats.buffs = {
|
||||
str: 1,
|
||||
@@ -286,7 +300,7 @@ describe('cron', () => {
|
||||
startDate: new Date(),
|
||||
};
|
||||
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line new-cap
|
||||
tasksByType.dailys.push(task);
|
||||
tasksByType.dailys[0].completed = true;
|
||||
|
||||
@@ -307,7 +321,7 @@ describe('cron', () => {
|
||||
value: 0,
|
||||
};
|
||||
|
||||
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
tasksByType.todos.push(task);
|
||||
});
|
||||
|
||||
@@ -333,7 +347,7 @@ describe('cron', () => {
|
||||
type: 'daily',
|
||||
};
|
||||
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line new-cap
|
||||
tasksByType.dailys = [];
|
||||
tasksByType.dailys.push(task);
|
||||
|
||||
@@ -435,7 +449,7 @@ describe('cron', () => {
|
||||
type: 'habit',
|
||||
};
|
||||
|
||||
let task = new Tasks.habit(Tasks.Task.sanitize(habit)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.habit(Tasks.Task.sanitize(habit)); // eslint-disable-line new-cap
|
||||
tasksByType.habits = [];
|
||||
tasksByType.habits.push(task);
|
||||
});
|
||||
@@ -476,7 +490,7 @@ describe('cron', () => {
|
||||
type: 'daily',
|
||||
};
|
||||
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line new-cap
|
||||
tasksByType.dailys = [];
|
||||
tasksByType.dailys.push(task);
|
||||
|
||||
@@ -619,7 +633,7 @@ describe('cron', () => {
|
||||
type: 'daily',
|
||||
};
|
||||
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line new-cap
|
||||
tasksByType.dailys = [];
|
||||
tasksByType.dailys.push(task);
|
||||
|
||||
@@ -656,9 +670,9 @@ describe('cron', () => {
|
||||
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].type).to.equal('CRON');
|
||||
expect(user.notifications[0].data).to.eql({
|
||||
expect(user.notifications.length).to.be.greaterThan(0);
|
||||
expect(user.notifications[1].type).to.equal('CRON');
|
||||
expect(user.notifications[1].data).to.eql({
|
||||
hp: user.stats.hp - hpBefore,
|
||||
mp: user.stats.mp - mpBefore,
|
||||
});
|
||||
@@ -675,13 +689,14 @@ describe('cron', () => {
|
||||
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].type).to.equal('CRON');
|
||||
expect(user.notifications[0].data).to.eql({
|
||||
expect(user.notifications.length).to.be.greaterThan(0);
|
||||
expect(user.notifications[1].type).to.equal('CRON');
|
||||
expect(user.notifications[1].data).to.eql({
|
||||
hp: user.stats.hp - hpBefore1,
|
||||
mp: user.stats.mp - mpBefore1,
|
||||
});
|
||||
|
||||
let notifsBefore2 = user.notifications.length;
|
||||
let hpBefore2 = user.stats.hp;
|
||||
let mpBefore2 = user.stats.mp;
|
||||
|
||||
@@ -689,12 +704,14 @@ describe('cron', () => {
|
||||
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].type).to.equal('CRON');
|
||||
expect(user.notifications[0].data).to.eql({
|
||||
expect(user.notifications.length - notifsBefore2).to.equal(0);
|
||||
expect(user.notifications[0].type).to.not.equal('CRON');
|
||||
expect(user.notifications[1].type).to.equal('CRON');
|
||||
expect(user.notifications[1].data).to.eql({
|
||||
hp: user.stats.hp - hpBefore2 - (hpBefore2 - hpBefore1),
|
||||
mp: user.stats.mp - mpBefore2 - (mpBefore2 - mpBefore1),
|
||||
});
|
||||
expect(user.notifications[0].type).to.not.equal('CRON');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -747,6 +764,188 @@ describe('cron', () => {
|
||||
expect(user.inbox.messages[messageId]).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('login incentives', () => {
|
||||
it('increments incentive counter each cron', () => {
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
user.lastCron = moment(new Date()).subtract({days: 1});
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(2);
|
||||
});
|
||||
|
||||
it('pushes a notification of the day\'s incentive each cron', () => {
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.notifications.length).to.be.greaterThan(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('replaces previous notifications', () => {
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
|
||||
let filteredNotifications = user.notifications.filter(n => n.type === 'LOGIN_INCENTIVE');
|
||||
|
||||
expect(filteredNotifications.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('increments loginIncentives by 1 even if days are skipped in between', () => {
|
||||
daysMissed = 3;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
});
|
||||
|
||||
it('increments loginIncentives by 1 even if user has Dailies paused', () => {
|
||||
user.preferences.sleep = true;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
});
|
||||
|
||||
it('awards user bard robes if login incentive is 1', () => {
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
expect(user.items.gear.owned.armor_special_bardRobes).to.eql(true);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user incentive backgrounds if login incentive is 2', () => {
|
||||
user.loginIncentives = 1;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(2);
|
||||
expect(user.purchased.background.blue).to.eql(true);
|
||||
expect(user.purchased.background.green).to.eql(true);
|
||||
expect(user.purchased.background.purple).to.eql(true);
|
||||
expect(user.purchased.background.red).to.eql(true);
|
||||
expect(user.purchased.background.yellow).to.eql(true);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user Bard Hat if login incentive is 3', () => {
|
||||
user.loginIncentives = 2;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(3);
|
||||
expect(user.items.gear.owned.head_special_bardHat).to.eql(true);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user RoyalPurple Hatching Potion if login incentive is 4', () => {
|
||||
user.loginIncentives = 3;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(4);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a Chocolate, Meat and Pink Contton Candy if login incentive is 5', () => {
|
||||
user.loginIncentives = 4;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(5);
|
||||
|
||||
expect(user.items.food.Chocolate).to.eql(1);
|
||||
expect(user.items.food.Meat).to.eql(1);
|
||||
expect(user.items.food.CottonCandyPink).to.eql(1);
|
||||
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user moon quest if login incentive is 7', () => {
|
||||
user.loginIncentives = 6;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(7);
|
||||
expect(user.items.quests.moon1).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user RoyalPurple Hatching Potion if login incentive is 10', () => {
|
||||
user.loginIncentives = 9;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(10);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a Strawberry, Patato and Blue Contton Candy if login incentive is 14', () => {
|
||||
user.loginIncentives = 13;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(14);
|
||||
|
||||
expect(user.items.food.Strawberry).to.eql(1);
|
||||
expect(user.items.food.Potatoe).to.eql(1);
|
||||
expect(user.items.food.CottonCandyBlue).to.eql(1);
|
||||
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a bard instrument if login incentive is 18', () => {
|
||||
user.loginIncentives = 17;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(18);
|
||||
expect(user.items.gear.owned.weapon_special_bardInstrument).to.eql(true);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user second moon quest if login incentive is 22', () => {
|
||||
user.loginIncentives = 21;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(22);
|
||||
expect(user.items.quests.moon2).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a RoyalPurple hatching potion if login incentive is 26', () => {
|
||||
user.loginIncentives = 25;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(26);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user Fish, Milk, Rotten Meat and Honey if login incentive is 30', () => {
|
||||
user.loginIncentives = 29;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(30);
|
||||
|
||||
expect(user.items.food.Fish).to.eql(1);
|
||||
expect(user.items.food.Milk).to.eql(1);
|
||||
expect(user.items.food.RottenMeat).to.eql(1);
|
||||
expect(user.items.food.Honey).to.eql(1);
|
||||
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a RoyalPurple hatching potion if login incentive is 35', () => {
|
||||
user.loginIncentives = 34;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(35);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user the third moon quest if login incentive is 40', () => {
|
||||
user.loginIncentives = 39;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(40);
|
||||
expect(user.items.quests.moon3).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a RoyalPurple hatching potion if login incentive is 45', () => {
|
||||
user.loginIncentives = 44;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(45);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
|
||||
it('awards user a saddle if login incentive is 50', () => {
|
||||
user.loginIncentives = 49;
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
expect(user.loginIncentives).to.eql(50);
|
||||
expect(user.items.food.Saddle).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('recoverCron', () => {
|
||||
|
||||
@@ -3,15 +3,33 @@ import * as api from '../../../../../website/server/libs/payments';
|
||||
import analytics from '../../../../../website/server/libs/analyticsService';
|
||||
import notifications from '../../../../../website/server/libs/pushNotifications';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
import stripeModule from 'stripe';
|
||||
import moment from 'moment';
|
||||
import { translate as t } from '../../../../helpers/api-v3-integration.helper';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../helpers/api-unit.helper.js';
|
||||
import i18n from '../../../../../website/common/script/i18n';
|
||||
import amzLib from '../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments/index', () => {
|
||||
let user, data, plan;
|
||||
let user, group, data, plan;
|
||||
|
||||
beforeEach(() => {
|
||||
let stripe = stripeModule('test');
|
||||
|
||||
beforeEach(async () => {
|
||||
user = new User();
|
||||
user.profile.name = 'sender';
|
||||
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
leader: user._id,
|
||||
});
|
||||
await group.save();
|
||||
|
||||
sandbox.stub(sender, 'sendTxn');
|
||||
sandbox.stub(user, 'sendMessage');
|
||||
sandbox.stub(analytics, 'trackPurchase');
|
||||
@@ -133,6 +151,14 @@ describe('payments/index', () => {
|
||||
expect(recipient.purchased.plan.dateUpdated).to.exist;
|
||||
});
|
||||
|
||||
it('sets plan.dateCreated if it did not previously exist', async () => {
|
||||
expect(recipient.purchased.plan.dateCreated).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(recipient.purchased.plan.dateCreated).to.exist;
|
||||
});
|
||||
|
||||
it('does not change plan.customerId if it already exists', async () => {
|
||||
recipient.purchased.plan = plan;
|
||||
data.customerId = 'purchaserCustomerId';
|
||||
@@ -161,9 +187,10 @@ describe('payments/index', () => {
|
||||
|
||||
it('sends a private message about the gift', async () => {
|
||||
await api.createSubscription(data);
|
||||
let msg = '\`Hello recipient, sender has sent you 3 months of subscription!\`';
|
||||
|
||||
expect(user.sendMessage).to.be.calledOnce;
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, '\`Hello recipient, sender has sent you 3 months of subscription!\`');
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: msg, senderMsg: msg });
|
||||
});
|
||||
|
||||
it('sends an email about the gift', async () => {
|
||||
@@ -186,6 +213,7 @@ describe('payments/index', () => {
|
||||
expect(analytics.trackPurchase).to.be.calledOnce;
|
||||
expect(analytics.trackPurchase).to.be.calledWith({
|
||||
uuid: user._id,
|
||||
groupId: undefined,
|
||||
itemPurchased: 'Subscription',
|
||||
sku: 'payment method-subscription',
|
||||
purchaseType: 'subscribe',
|
||||
@@ -276,6 +304,7 @@ describe('payments/index', () => {
|
||||
expect(analytics.trackPurchase).to.be.calledOnce;
|
||||
expect(analytics.trackPurchase).to.be.calledWith({
|
||||
uuid: user._id,
|
||||
groupId: undefined,
|
||||
itemPurchased: 'Subscription',
|
||||
sku: 'payment method-subscription',
|
||||
purchaseType: 'subscribe',
|
||||
@@ -291,6 +320,53 @@ describe('payments/index', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('Purchasing a subscription for group', () => {
|
||||
it('creates a subscription', async () => {
|
||||
expect(group.purchased.plan.planId).to.not.exist;
|
||||
data.groupId = group._id;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
|
||||
expect(updatedGroup.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(updatedGroup.purchased.plan.customerId).to.eql('customer-id');
|
||||
expect(updatedGroup.purchased.plan.dateUpdated).to.exist;
|
||||
expect(updatedGroup.purchased.plan.gemsBought).to.eql(0);
|
||||
expect(updatedGroup.purchased.plan.paymentMethod).to.eql('Payment Method');
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.eql(0);
|
||||
expect(updatedGroup.purchased.plan.dateTerminated).to.eql(null);
|
||||
expect(updatedGroup.purchased.plan.lastBillingDate).to.not.exist;
|
||||
expect(updatedGroup.purchased.plan.dateCreated).to.exist;
|
||||
});
|
||||
|
||||
it('sets extraMonths if plan has dateTerminated date', async () => {
|
||||
group.purchased.plan = plan;
|
||||
group.purchased.plan.dateTerminated = moment(new Date()).add(2, 'months');
|
||||
await group.save();
|
||||
expect(group.purchased.plan.extraMonths).to.eql(0);
|
||||
data.groupId = group._id;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.within(1.9, 2);
|
||||
});
|
||||
|
||||
it('does not set negative extraMonths if plan has past dateTerminated date', async () => {
|
||||
group.purchased.plan = plan;
|
||||
group.purchased.plan.dateTerminated = moment(new Date()).subtract(2, 'months');
|
||||
await group.save();
|
||||
expect(group.purchased.plan.extraMonths).to.eql(0);
|
||||
data.groupId = group._id;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
context('Block subscription perks', () => {
|
||||
it('adds block months to plan.consecutive.offset', async () => {
|
||||
await api.createSubscription(data);
|
||||
@@ -426,61 +502,169 @@ describe('payments/index', () => {
|
||||
data = { user };
|
||||
});
|
||||
|
||||
it('adds a month termination date by default', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
context('Canceling a subscription for self', () => {
|
||||
it('adds a month termination date by default', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||
});
|
||||
|
||||
it('adds extraMonths to dateTerminated value', async () => {
|
||||
user.purchased.plan.extraMonths = 2;
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(89, 90); // 3 months +/- 1 days
|
||||
});
|
||||
|
||||
it('handles extra month fractions', async () => {
|
||||
user.purchased.plan.extraMonths = 0.3;
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(38, 39); // should be about 1 month + 1/3 month
|
||||
});
|
||||
|
||||
it('terminates at next billing date if it exists', async () => {
|
||||
data.nextBill = moment().add({ days: 15 });
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(13, 15);
|
||||
});
|
||||
|
||||
it('resets plan.extraMonths', async () => {
|
||||
user.purchased.plan.extraMonths = 5;
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.extraMonths).to.eql(0);
|
||||
});
|
||||
|
||||
it('sends an email', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
expect(sender.sendTxn).to.be.calledOnce;
|
||||
expect(sender.sendTxn).to.be.calledWith(user, 'cancel-subscription');
|
||||
});
|
||||
});
|
||||
|
||||
it('adds extraMonths to dateTerminated value', async () => {
|
||||
user.purchased.plan.extraMonths = 2;
|
||||
context('Canceling a subscription for group', () => {
|
||||
it('adds a month termination date by default', async () => {
|
||||
data.groupId = group._id;
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||
});
|
||||
|
||||
expect(daysTillTermination).to.be.within(89, 90); // 3 months +/- 1 days
|
||||
});
|
||||
it('adds extraMonths to dateTerminated value', async () => {
|
||||
group.purchased.plan.extraMonths = 2;
|
||||
await group.save();
|
||||
data.groupId = group._id;
|
||||
|
||||
it('handles extra month fractions', async () => {
|
||||
user.purchased.plan.extraMonths = 0.3;
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
expect(daysTillTermination).to.be.within(89, 90); // 3 months +/- 1 days
|
||||
});
|
||||
|
||||
expect(daysTillTermination).to.be.within(38, 39); // should be about 1 month + 1/3 month
|
||||
});
|
||||
it('handles extra month fractions', async () => {
|
||||
group.purchased.plan.extraMonths = 0.3;
|
||||
await group.save();
|
||||
data.groupId = group._id;
|
||||
|
||||
it('terminates at next billing date if it exists', async () => {
|
||||
data.nextBill = moment().add({ days: 15 });
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
expect(daysTillTermination).to.be.within(38, 39); // should be about 1 month + 1/3 month
|
||||
});
|
||||
|
||||
expect(daysTillTermination).to.be.within(13, 15);
|
||||
});
|
||||
it('terminates at next billing date if it exists', async () => {
|
||||
data.nextBill = moment().add({ days: 15 });
|
||||
data.groupId = group._id;
|
||||
|
||||
it('resets plan.extraMonths', async () => {
|
||||
user.purchased.plan.extraMonths = 5;
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(user.purchased.plan.extraMonths).to.eql(0);
|
||||
});
|
||||
expect(daysTillTermination).to.be.within(13, 15);
|
||||
});
|
||||
|
||||
it('sends an email', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
it('resets plan.extraMonths', async () => {
|
||||
group.purchased.plan.extraMonths = 5;
|
||||
await group.save();
|
||||
data.groupId = group._id;
|
||||
|
||||
expect(sender.sendTxn).to.be.calledOnce;
|
||||
expect(sender.sendTxn).to.be.calledWith(user, 'cancel-subscription');
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.eql(0);
|
||||
});
|
||||
|
||||
it('sends an email', async () => {
|
||||
data.groupId = group._id;
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
expect(sender.sendTxn).to.be.calledOnce;
|
||||
expect(sender.sendTxn).to.be.calledWith(user, 'group-cancel-subscription');
|
||||
});
|
||||
|
||||
it('prevents non group leader from manging subscription', async () => {
|
||||
let groupMember = new User();
|
||||
data.user = groupMember;
|
||||
data.groupId = group._id;
|
||||
|
||||
await expect(api.cancelSubscription(data))
|
||||
.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('onlyGroupLeaderCanManageSubscription'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
});
|
||||
|
||||
it('allows old group leader to cancel if they created the subscription', async () => {
|
||||
data.groupId = group._id;
|
||||
data.sub = {
|
||||
key: 'group_monthly',
|
||||
};
|
||||
data.paymentMethod = 'Payment Method';
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let newLeader = new User();
|
||||
updatedGroup.leader = newLeader._id;
|
||||
await updatedGroup.save();
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
updatedGroup = await Group.findById(group._id).exec();
|
||||
|
||||
expect(updatedGroup.purchased.plan.dateTerminated).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -553,14 +737,190 @@ describe('payments/index', () => {
|
||||
|
||||
it('sends a message from purchaser to recipient', async () => {
|
||||
await api.buyGems(data);
|
||||
let msg = '\`Hello recipient, sender has sent you 4 gems!\`';
|
||||
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, '\`Hello recipient, sender has sent you 4 gems!\`');
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: msg, senderMsg: msg });
|
||||
});
|
||||
|
||||
it('sends a push notification if user did not gift to self', async () => {
|
||||
await api.buyGems(data);
|
||||
expect(notifications.sendNotification).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('sends gem donation message in each participant\'s language', async () => {
|
||||
// TODO using english for both users because other languages are not loaded
|
||||
// for api.buyGems
|
||||
await recipient.update({
|
||||
'preferences.language': 'en',
|
||||
});
|
||||
await user.update({
|
||||
'preferences.language': 'en',
|
||||
});
|
||||
await api.buyGems(data);
|
||||
|
||||
let [recipientsMessageContent, sendersMessageContent] = ['en', 'en'].map((lang) => {
|
||||
let messageContent = t('giftedGemsFull', {
|
||||
username: recipient.profile.name,
|
||||
sender: user.profile.name,
|
||||
gemAmount: data.gift.gems.amount,
|
||||
}, lang);
|
||||
|
||||
return `\`${messageContent}\``;
|
||||
});
|
||||
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: recipientsMessageContent, senderMsg: sendersMessageContent });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#upgradeGroupPlan', () => {
|
||||
let spy;
|
||||
|
||||
beforeEach(function () {
|
||||
spy = sinon.stub(stripe.subscriptions, 'update');
|
||||
spy.returnsPromise().resolves([]);
|
||||
data.groupId = group._id;
|
||||
data.sub.quantity = 3;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore(stripe.subscriptions.update);
|
||||
});
|
||||
|
||||
it('updates a group plan quantity', async () => {
|
||||
data.paymentMethod = 'Stripe';
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.quantity).to.eql(3);
|
||||
|
||||
updatedGroup.memberCount += 1;
|
||||
await updatedGroup.save();
|
||||
|
||||
await api.updateStripeGroupPlan(updatedGroup, stripe);
|
||||
|
||||
expect(spy.calledOnce).to.be.true;
|
||||
expect(updatedGroup.purchased.plan.quantity).to.eql(4);
|
||||
});
|
||||
|
||||
it('does not update a group plan quantity that has a payment method other than stripe', async () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.quantity).to.eql(3);
|
||||
|
||||
updatedGroup.memberCount += 1;
|
||||
await updatedGroup.save();
|
||||
|
||||
await api.updateStripeGroupPlan(updatedGroup, stripe);
|
||||
|
||||
expect(spy.calledOnce).to.be.false;
|
||||
expect(updatedGroup.purchased.plan.quantity).to.eql(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('payWithStripe', () => {
|
||||
let spy;
|
||||
let stripeCreateCustomerSpy;
|
||||
let createSubSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
spy = sinon.stub(stripe.subscriptions, 'update');
|
||||
spy.returnsPromise().resolves;
|
||||
|
||||
stripeCreateCustomerSpy = sinon.stub(stripe.customers, 'create');
|
||||
let stripCustomerResponse = {
|
||||
subscriptions: {
|
||||
data: [{id: 'test-id'}],
|
||||
},
|
||||
};
|
||||
stripeCreateCustomerSpy.returnsPromise().resolves(stripCustomerResponse);
|
||||
|
||||
createSubSpy = sinon.stub(api, 'createSubscription');
|
||||
createSubSpy.returnsPromise().resolves({});
|
||||
|
||||
data.groupId = group._id;
|
||||
data.sub.quantity = 3;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore(stripe.subscriptions.update);
|
||||
stripe.customers.create.restore();
|
||||
api.createSubscription.restore();
|
||||
});
|
||||
|
||||
it('subscribes with stripe', async () => {
|
||||
let token = 'test-token';
|
||||
let gift;
|
||||
let sub = data.sub;
|
||||
let groupId = group._id;
|
||||
let email = 'test@test.com';
|
||||
let headers = {};
|
||||
let coupon;
|
||||
|
||||
await api.payWithStripe({
|
||||
token,
|
||||
user,
|
||||
gift,
|
||||
sub,
|
||||
groupId,
|
||||
email,
|
||||
headers,
|
||||
coupon,
|
||||
}, stripe);
|
||||
|
||||
expect(stripeCreateCustomerSpy.calledOnce).to.be.true;
|
||||
expect(createSubSpy.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscribeWithAmazon', () => {
|
||||
let amazonSetBillingAgreementDetailsSpy;
|
||||
let amazonConfirmBillingAgreementSpy;
|
||||
let amazongAuthorizeOnBillingAgreementSpy;
|
||||
let createSubSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
amazonSetBillingAgreementDetailsSpy = sinon.stub(amzLib, 'setBillingAgreementDetails');
|
||||
amazonSetBillingAgreementDetailsSpy.returnsPromise().resolves({});
|
||||
|
||||
amazonConfirmBillingAgreementSpy = sinon.stub(amzLib, 'confirmBillingAgreement');
|
||||
amazonConfirmBillingAgreementSpy.returnsPromise().resolves({});
|
||||
|
||||
amazongAuthorizeOnBillingAgreementSpy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
||||
amazongAuthorizeOnBillingAgreementSpy.returnsPromise().resolves({});
|
||||
|
||||
createSubSpy = sinon.stub(api, 'createSubscription');
|
||||
createSubSpy.returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
amzLib.setBillingAgreementDetails.restore();
|
||||
amzLib.confirmBillingAgreement.restore();
|
||||
amzLib.authorizeOnBillingAgreement.restore();
|
||||
api.createSubscription.restore();
|
||||
});
|
||||
|
||||
it('subscribes with amazon', async () => {
|
||||
let billingAgreementId = 'billingAgreementId';
|
||||
let sub = data.sub;
|
||||
let coupon;
|
||||
let groupId = group._id;
|
||||
let headers = {};
|
||||
|
||||
await api.subscribeWithAmazon({
|
||||
billingAgreementId,
|
||||
sub,
|
||||
coupon,
|
||||
user,
|
||||
groupId,
|
||||
headers,
|
||||
});
|
||||
|
||||
expect(amazonSetBillingAgreementDetailsSpy.calledOnce).to.be.true;
|
||||
expect(amazonConfirmBillingAgreementSpy.calledOnce).to.be.true;
|
||||
expect(amazongAuthorizeOnBillingAgreementSpy.calledOnce).to.be.true;
|
||||
expect(createSubSpy.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,9 @@ describe('slack', () => {
|
||||
profile: {
|
||||
name: 'flagger',
|
||||
},
|
||||
preferences: {
|
||||
language: 'flagger-lang',
|
||||
},
|
||||
},
|
||||
group: {
|
||||
id: 'group-id',
|
||||
@@ -44,7 +47,7 @@ describe('slack', () => {
|
||||
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
||||
text: 'flagger (flagger-id) flagged a message',
|
||||
text: 'flagger (flagger-id) flagged a message (language: flagger-lang)',
|
||||
attachments: [{
|
||||
fallback: 'Flag Message',
|
||||
color: 'danger',
|
||||
|
||||
@@ -98,7 +98,6 @@ describe('response middleware', () => {
|
||||
{
|
||||
type: notification.type,
|
||||
id: notification.id,
|
||||
createdAt: notification.createdAt,
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -6,32 +6,36 @@ import common from '../../../../../website/common/';
|
||||
import { each, find } from 'lodash';
|
||||
|
||||
describe('Challenge Model', () => {
|
||||
let guild, leader, challenge, task, tasksToTest;
|
||||
let guild, leader, challenge, task;
|
||||
let tasksToTest = {
|
||||
habit: {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
up: false,
|
||||
down: true,
|
||||
notes: 'test notes',
|
||||
},
|
||||
todo: {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
notes: 'test notes',
|
||||
},
|
||||
daily: {
|
||||
text: 'test daily',
|
||||
type: 'daily',
|
||||
frequency: 'daily',
|
||||
everyX: 5,
|
||||
startDate: new Date(),
|
||||
notes: 'test notes',
|
||||
},
|
||||
reward: {
|
||||
text: 'test reward',
|
||||
type: 'reward',
|
||||
notes: 'test notes',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
tasksToTest = {
|
||||
habit: {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
up: false,
|
||||
down: true,
|
||||
},
|
||||
todo: {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
},
|
||||
daily: {
|
||||
text: 'test daily',
|
||||
type: 'daily',
|
||||
frequency: 'daily',
|
||||
everyX: 5,
|
||||
startDate: new Date(),
|
||||
},
|
||||
reward: {
|
||||
text: 'test reward',
|
||||
type: 'reward',
|
||||
},
|
||||
};
|
||||
guild = new Group({
|
||||
name: 'test party',
|
||||
type: 'guild',
|
||||
@@ -77,6 +81,7 @@ describe('Challenge Model', () => {
|
||||
});
|
||||
|
||||
expect(syncedTask).to.exist;
|
||||
expect(syncedTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('syncs a challenge to a user', async () => {
|
||||
@@ -96,8 +101,8 @@ describe('Challenge Model', () => {
|
||||
});
|
||||
|
||||
expect(updatedNewMember.challenges).to.contain(challenge._id);
|
||||
expect(updatedNewMember.tags[3].id).to.equal(challenge._id);
|
||||
expect(updatedNewMember.tags[3].name).to.equal(challenge.shortName);
|
||||
expect(updatedNewMember.tags[7].id).to.equal(challenge._id);
|
||||
expect(updatedNewMember.tags[7].name).to.equal(challenge.shortName);
|
||||
expect(syncedTask).to.exist;
|
||||
});
|
||||
|
||||
@@ -161,7 +166,7 @@ describe('Challenge Model', () => {
|
||||
|
||||
context('type specific updates', () => {
|
||||
it('updates habit specific field to challenge and challenge members', async () => {
|
||||
task = new Tasks.habit(Tasks.Task.sanitize(tasksToTest.habit)); // eslint-disable-line babel/new-cap
|
||||
task = new Tasks.habit(Tasks.Task.sanitize(tasksToTest.habit)); // eslint-disable-line new-cap
|
||||
task.challenge.id = challenge._id;
|
||||
await task.save();
|
||||
|
||||
@@ -180,7 +185,7 @@ describe('Challenge Model', () => {
|
||||
});
|
||||
|
||||
it('updates todo specific field to challenge and challenge members', async () => {
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(tasksToTest.todo)); // eslint-disable-line babel/new-cap
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(tasksToTest.todo)); // eslint-disable-line new-cap
|
||||
task.challenge.id = challenge._id;
|
||||
await task.save();
|
||||
|
||||
@@ -196,7 +201,7 @@ describe('Challenge Model', () => {
|
||||
});
|
||||
|
||||
it('does not update checklists on the user task', async () => {
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(tasksToTest.todo)); // eslint-disable-line babel/new-cap
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(tasksToTest.todo)); // eslint-disable-line new-cap
|
||||
task.challenge.id = challenge._id;
|
||||
await task.save();
|
||||
|
||||
@@ -214,7 +219,7 @@ describe('Challenge Model', () => {
|
||||
});
|
||||
|
||||
it('updates daily specific field to challenge and challenge members', async () => {
|
||||
task = new Tasks.daily(Tasks.Task.sanitize(tasksToTest.daily)); // eslint-disable-line babel/new-cap
|
||||
task = new Tasks.daily(Tasks.Task.sanitize(tasksToTest.daily)); // eslint-disable-line new-cap
|
||||
task.challenge.id = challenge._id;
|
||||
await task.save();
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { sleep } from '../../../../helpers/api-unit.helper';
|
||||
import { model as Group, INVITES_LIMIT } from '../../../../../website/server/models/group';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { BadRequest } from '../../../../../website/server/libs/errors';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../../../../../website/server/libs/errors';
|
||||
import { quests as questScrolls } from '../../../../../website/common/script/content';
|
||||
import { groupChatReceivedWebhook } from '../../../../../website/server/libs/webhook';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
import validator from 'validator';
|
||||
import { TAVERN_ID } from '../../../../../website/common/script/';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import shared from '../../../../../website/common';
|
||||
|
||||
describe('Group Model', () => {
|
||||
let party, questLeader, participatingMember, nonParticipatingMember, undecidedMember;
|
||||
@@ -628,24 +631,105 @@ describe('Group Model', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes a private group when the last member leaves', async () => {
|
||||
party.memberCount = 1;
|
||||
|
||||
it('deletes a private party when the last member leaves', async () => {
|
||||
await party.leave(participatingMember);
|
||||
await party.leave(questLeader);
|
||||
await party.leave(nonParticipatingMember);
|
||||
await party.leave(undecidedMember);
|
||||
|
||||
party = await Group.findOne({_id: party._id});
|
||||
expect(party).to.not.exist;
|
||||
});
|
||||
|
||||
it('does not delete a public group when the last member leaves', async () => {
|
||||
it('does not delete a private group when the last member leaves and a subscription is active', async () => {
|
||||
party.memberCount = 1;
|
||||
party.purchased.plan.customerId = '110002222333';
|
||||
|
||||
await expect(party.leave(participatingMember))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
name: 'NotAuthorized',
|
||||
httpCode: 401,
|
||||
message: shared.i18n.t('cannotDeleteActiveGroup'),
|
||||
});
|
||||
|
||||
party = await Group.findOne({_id: party._id});
|
||||
expect(party).to.exist;
|
||||
expect(party.memberCount).to.eql(1);
|
||||
});
|
||||
|
||||
it('does not delete a public group when the last member leaves', async () => {
|
||||
party.privacy = 'public';
|
||||
|
||||
await party.leave(participatingMember);
|
||||
await party.leave(questLeader);
|
||||
await party.leave(nonParticipatingMember);
|
||||
await party.leave(undecidedMember);
|
||||
|
||||
party = await Group.findOne({_id: party._id});
|
||||
expect(party).to.exist;
|
||||
});
|
||||
|
||||
it('does not delete a private party when the member count reaches zero if there are still members', async () => {
|
||||
party.memberCount = 1;
|
||||
|
||||
await party.leave(participatingMember);
|
||||
|
||||
party = await Group.findOne({_id: party._id});
|
||||
expect(party).to.exist;
|
||||
});
|
||||
|
||||
it('deletes a private guild when the last member leaves', async () => {
|
||||
let guild = new Group({
|
||||
name: 'test guild',
|
||||
type: 'guild',
|
||||
memberCount: 1,
|
||||
});
|
||||
|
||||
let leader = new User({
|
||||
guilds: [guild._id],
|
||||
});
|
||||
|
||||
guild.leader = leader._id;
|
||||
|
||||
await Promise.all([
|
||||
guild.save(),
|
||||
leader.save(),
|
||||
]);
|
||||
|
||||
await guild.leave(leader);
|
||||
|
||||
guild = await Group.findOne({_id: guild._id});
|
||||
expect(guild).to.not.exist;
|
||||
});
|
||||
|
||||
it('does not delete a private guild when the member count reaches zero if there are still members', async () => {
|
||||
let guild = new Group({
|
||||
name: 'test guild',
|
||||
type: 'guild',
|
||||
memberCount: 1,
|
||||
});
|
||||
|
||||
let leader = new User({
|
||||
guilds: [guild._id],
|
||||
});
|
||||
|
||||
let member = new User({
|
||||
guilds: [guild._id],
|
||||
});
|
||||
|
||||
guild.leader = leader._id;
|
||||
|
||||
await Promise.all([
|
||||
guild.save(),
|
||||
leader.save(),
|
||||
member.save(),
|
||||
]);
|
||||
|
||||
await guild.leave(member);
|
||||
|
||||
guild = await Group.findOne({_id: guild._id});
|
||||
expect(guild).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#sendChat', () => {
|
||||
@@ -1061,8 +1145,45 @@ describe('Group Model', () => {
|
||||
[nonParticipatingMember._id]: false,
|
||||
[undecidedMember._id]: null,
|
||||
};
|
||||
});
|
||||
|
||||
sandbox.spy(User, 'update');
|
||||
describe('user update retry failures', () => {
|
||||
let successfulMock = {
|
||||
exec: () => {
|
||||
return Promise.resolve({raw: 'sucess'});
|
||||
},
|
||||
};
|
||||
let failedMock = {
|
||||
exec: () => {
|
||||
return Promise.reject(new Error('error'));
|
||||
},
|
||||
};
|
||||
|
||||
it('doesn\'t retry successful operations', async () => {
|
||||
sandbox.stub(User, 'update').returns(successfulMock);
|
||||
|
||||
await party.finishQuest(quest);
|
||||
|
||||
expect(User.update).to.be.calledTwice;
|
||||
});
|
||||
|
||||
it('stops retrying when a successful update has occurred', async () => {
|
||||
let updateStub = sandbox.stub(User, 'update');
|
||||
updateStub.onCall(0).returns(failedMock);
|
||||
updateStub.returns(successfulMock);
|
||||
|
||||
await party.finishQuest(quest);
|
||||
|
||||
expect(User.update).to.be.calledThrice;
|
||||
});
|
||||
|
||||
it('retries failed updates at most five times per user', async () => {
|
||||
sandbox.stub(User, 'update').returns(failedMock);
|
||||
|
||||
await expect(party.finishQuest(quest)).to.eventually.be.rejected;
|
||||
|
||||
expect(User.update.callCount).to.eql(10);
|
||||
});
|
||||
});
|
||||
|
||||
it('gives out achievements', async () => {
|
||||
@@ -1171,13 +1292,15 @@ describe('Group Model', () => {
|
||||
|
||||
context('Party quests', () => {
|
||||
it('updates participating members with rewards', async () => {
|
||||
sandbox.spy(User, 'update');
|
||||
await party.finishQuest(quest);
|
||||
|
||||
expect(User.update).to.be.calledOnce;
|
||||
expect(User.update).to.be.calledTwice;
|
||||
expect(User.update).to.be.calledWithMatch({
|
||||
_id: {
|
||||
$in: [questLeader._id, participatingMember._id],
|
||||
},
|
||||
_id: questLeader._id,
|
||||
});
|
||||
expect(User.update).to.be.calledWithMatch({
|
||||
_id: participatingMember._id,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1204,6 +1327,7 @@ describe('Group Model', () => {
|
||||
});
|
||||
|
||||
it('updates all users with rewards', async () => {
|
||||
sandbox.spy(User, 'update');
|
||||
await party.finishQuest(tavernQuest);
|
||||
|
||||
expect(User.update).to.be.calledOnce;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { model as Challenge } from '../../../../../website/server/models/challen
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import * as Tasks from '../../../../../website/server/models/task';
|
||||
import { each, find } from 'lodash';
|
||||
import { each, find, findIndex } from 'lodash';
|
||||
|
||||
describe('Group Task Methods', () => {
|
||||
let guild, leader, challenge, task;
|
||||
@@ -68,11 +68,29 @@ describe('Group Task Methods', () => {
|
||||
task = new Tasks[`${taskType}`](Tasks.Task.sanitize(taskValue));
|
||||
task.group.id = guild._id;
|
||||
await task.save();
|
||||
if (task.checklist) {
|
||||
task.checklist.push({
|
||||
text: 'Checklist Item 1',
|
||||
completed: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('syncs an assigned task to a user', async () => {
|
||||
await guild.syncTask(task, leader);
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let tagIndex = findIndex(updatedLeader.tags, {id: guild._id});
|
||||
let newTag = updatedLeader.tags[tagIndex];
|
||||
|
||||
expect(newTag.id).to.equal(guild._id);
|
||||
expect(newTag.name).to.equal(guild.name);
|
||||
expect(newTag.group).to.equal(guild._id);
|
||||
});
|
||||
|
||||
it('create tags for a user when task is synced', async () => {
|
||||
await guild.syncTask(task, leader);
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let updatedLeadersTasks = await Tasks.Task.find({_id: { $in: updatedLeader.tasksOrder[`${taskType}s`]}});
|
||||
let syncedTask = find(updatedLeadersTasks, findLinkedTask);
|
||||
@@ -96,35 +114,124 @@ describe('Group Task Methods', () => {
|
||||
expect(syncedTask.text).to.equal(task.text);
|
||||
});
|
||||
|
||||
it('syncs updated info for assigned task to all users', async () => {
|
||||
let newMember = new User({
|
||||
guilds: [guild._id],
|
||||
});
|
||||
await newMember.save();
|
||||
|
||||
it('syncs checklist items to an assigned user', async () => {
|
||||
await guild.syncTask(task, leader);
|
||||
await guild.syncTask(task, newMember);
|
||||
|
||||
let updatedTaskName = 'Update Task name';
|
||||
task.text = updatedTaskName;
|
||||
|
||||
await guild.updateTask(task);
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let updatedLeadersTasks = await Tasks.Task.find({_id: { $in: updatedLeader.tasksOrder[`${taskType}s`]}});
|
||||
let syncedTask = find(updatedLeadersTasks, findLinkedTask);
|
||||
|
||||
let updatedMember = await User.findOne({_id: newMember._id});
|
||||
let updatedMemberTasks = await Tasks.Task.find({_id: { $in: updatedMember.tasksOrder[`${taskType}s`]}});
|
||||
let syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
|
||||
if (task.type !== 'daily' && task.type !== 'todo') return;
|
||||
|
||||
expect(task.group.assignedUsers).to.contain(leader._id);
|
||||
expect(syncedTask).to.exist;
|
||||
expect(syncedTask.text).to.equal(task.text);
|
||||
expect(syncedTask.checklist.length).to.equal(task.checklist.length);
|
||||
expect(syncedTask.checklist[0].text).to.equal(task.checklist[0].text);
|
||||
});
|
||||
|
||||
expect(task.group.assignedUsers).to.contain(newMember._id);
|
||||
expect(syncedMemberTask).to.exist;
|
||||
expect(syncedMemberTask.text).to.equal(task.text);
|
||||
describe('syncs updated info', async() => {
|
||||
let newMember;
|
||||
|
||||
beforeEach(async () => {
|
||||
newMember = new User({
|
||||
guilds: [guild._id],
|
||||
});
|
||||
await newMember.save();
|
||||
|
||||
await guild.syncTask(task, leader);
|
||||
await guild.syncTask(task, newMember);
|
||||
});
|
||||
|
||||
it('syncs updated info for assigned task to all users', async () => {
|
||||
let updatedTaskName = 'Update Task name';
|
||||
task.text = updatedTaskName;
|
||||
task.group.approval.required = true;
|
||||
|
||||
await guild.updateTask(task);
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let updatedLeadersTasks = await Tasks.Task.find({_id: { $in: updatedLeader.tasksOrder[`${taskType}s`]}});
|
||||
let syncedTask = find(updatedLeadersTasks, findLinkedTask);
|
||||
|
||||
let updatedMember = await User.findOne({_id: newMember._id});
|
||||
let updatedMemberTasks = await Tasks.Task.find({_id: { $in: updatedMember.tasksOrder[`${taskType}s`]}});
|
||||
let syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
|
||||
|
||||
expect(task.group.assignedUsers).to.contain(leader._id);
|
||||
expect(syncedTask).to.exist;
|
||||
expect(syncedTask.text).to.equal(task.text);
|
||||
expect(syncedTask.group.approval.required).to.equal(true);
|
||||
|
||||
expect(task.group.assignedUsers).to.contain(newMember._id);
|
||||
expect(syncedMemberTask).to.exist;
|
||||
expect(syncedMemberTask.text).to.equal(task.text);
|
||||
expect(syncedMemberTask.group.approval.required).to.equal(true);
|
||||
});
|
||||
|
||||
it('syncs a new checklist item to all assigned users', async () => {
|
||||
if (task.type !== 'daily' && task.type !== 'todo') return;
|
||||
|
||||
let newCheckListItem = {
|
||||
text: 'Checklist Item 1',
|
||||
completed: false,
|
||||
};
|
||||
|
||||
task.checklist.push(newCheckListItem);
|
||||
|
||||
await guild.updateTask(task, {newCheckListItem});
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let updatedLeadersTasks = await Tasks.Task.find({_id: { $in: updatedLeader.tasksOrder[`${taskType}s`]}});
|
||||
let syncedTask = find(updatedLeadersTasks, findLinkedTask);
|
||||
|
||||
let updatedMember = await User.findOne({_id: newMember._id});
|
||||
let updatedMemberTasks = await Tasks.Task.find({_id: { $in: updatedMember.tasksOrder[`${taskType}s`]}});
|
||||
let syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
|
||||
|
||||
expect(syncedTask.checklist.length).to.equal(task.checklist.length);
|
||||
expect(syncedTask.checklist[1].text).to.equal(task.checklist[1].text);
|
||||
expect(syncedMemberTask.checklist.length).to.equal(task.checklist.length);
|
||||
expect(syncedMemberTask.checklist[1].text).to.equal(task.checklist[1].text);
|
||||
});
|
||||
|
||||
it('syncs updated info for checklist in assigned task to all users when flag is passed', async () => {
|
||||
if (task.type !== 'daily' && task.type !== 'todo') return;
|
||||
|
||||
let updateCheckListText = 'Updated checklist item';
|
||||
if (task.checklist) {
|
||||
task.checklist[0].text = updateCheckListText;
|
||||
}
|
||||
|
||||
await guild.updateTask(task, {updateCheckListItems: [task.checklist[0]]});
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let updatedLeadersTasks = await Tasks.Task.find({_id: { $in: updatedLeader.tasksOrder[`${taskType}s`]}});
|
||||
let syncedTask = find(updatedLeadersTasks, findLinkedTask);
|
||||
|
||||
let updatedMember = await User.findOne({_id: newMember._id});
|
||||
let updatedMemberTasks = await Tasks.Task.find({_id: { $in: updatedMember.tasksOrder[`${taskType}s`]}});
|
||||
let syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
|
||||
|
||||
expect(syncedTask.checklist.length).to.equal(task.checklist.length);
|
||||
expect(syncedTask.checklist[0].text).to.equal(updateCheckListText);
|
||||
expect(syncedMemberTask.checklist.length).to.equal(task.checklist.length);
|
||||
expect(syncedMemberTask.checklist[0].text).to.equal(updateCheckListText);
|
||||
});
|
||||
|
||||
it('removes a checklist item in assigned task to all users when flag is passed with checklist id', async () => {
|
||||
if (task.type !== 'daily' && task.type !== 'todo') return;
|
||||
|
||||
await guild.updateTask(task, {removedCheckListItemId: task.checklist[0].id});
|
||||
|
||||
let updatedLeader = await User.findOne({_id: leader._id});
|
||||
let updatedLeadersTasks = await Tasks.Task.find({_id: { $in: updatedLeader.tasksOrder[`${taskType}s`]}});
|
||||
let syncedTask = find(updatedLeadersTasks, findLinkedTask);
|
||||
|
||||
let updatedMember = await User.findOne({_id: newMember._id});
|
||||
let updatedMemberTasks = await Tasks.Task.find({_id: { $in: updatedMember.tasksOrder[`${taskType}s`]}});
|
||||
let syncedMemberTask = find(updatedMemberTasks, findLinkedTask);
|
||||
|
||||
expect(syncedTask.checklist.length).to.equal(0);
|
||||
expect(syncedMemberTask.checklist.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('removes an assigned task and unlinks assignees', async () => {
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('Task Model', () => {
|
||||
user = new User();
|
||||
await user.save();
|
||||
|
||||
taskWithAlias = new Tasks.todo({ // eslint-disable-line babel/new-cap
|
||||
taskWithAlias = new Tasks.todo({ // eslint-disable-line new-cap
|
||||
text: 'some text',
|
||||
alias: 'short-name',
|
||||
userId: user.id,
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('User Model', () => {
|
||||
|
||||
let userToJSON = user.toJSON();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'createdAt']);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||
expect(userToJSON.notifications[0].data).to.eql({});
|
||||
});
|
||||
@@ -67,7 +67,7 @@ describe('User Model', () => {
|
||||
|
||||
let userToJSON = user.toJSON();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'createdAt']);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||
expect(userToJSON.notifications[0].data).to.eql({field: 1});
|
||||
});
|
||||
|
||||
@@ -275,7 +275,8 @@ describe('Challenges Controller', function() {
|
||||
describe('editTask', function() {
|
||||
it('is Tasks.editTask', function() {
|
||||
inject(function(Tasks) {
|
||||
expect(scope.editTask).to.eql(Tasks.editTask);
|
||||
// @TODO: Currently we override the task function in the challenge ctrl, but we should abstract it again
|
||||
// expect(scope.editTask).to.eql(Tasks.editTask);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,10 @@ describe('Inventory Controller', function() {
|
||||
},
|
||||
preferences: {
|
||||
suppressModals: {}
|
||||
}
|
||||
},
|
||||
purchased: {
|
||||
plan: {}
|
||||
},
|
||||
});
|
||||
|
||||
Shared.wrap(user);
|
||||
@@ -450,4 +453,84 @@ describe('Inventory Controller', function() {
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('mounts')).to.eql(true);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Gear search filter', function() {
|
||||
var wrap = function(text) {
|
||||
return {'text': function() {return text;}};
|
||||
}
|
||||
|
||||
var toText = function(list) {
|
||||
return _.map(list, function(ele) { return ele.text(); });
|
||||
}
|
||||
|
||||
var gearByClass, gearByType;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.$digest();
|
||||
gearByClass = {'raw': [wrap('kale'), wrap('sashimi')],
|
||||
'cooked': [wrap('chicken'), wrap('potato')]};
|
||||
|
||||
gearByType = {'veg': [wrap('kale'), wrap('potato')],
|
||||
'not': [wrap('chicken'), wrap('sashimi')]};
|
||||
scope.gearByClass = gearByClass;
|
||||
scope.gearByType = gearByType;
|
||||
scope.equipmentQuery.query = 'a';
|
||||
});
|
||||
|
||||
it('filters nothing if equipmentQuery is nothing', function() {
|
||||
scope.equipmentQuery.query = '';
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['chicken', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['chicken', 'sashimi']);
|
||||
});
|
||||
|
||||
it('filters out gear if class gear changes', function() {
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['potato']);
|
||||
|
||||
scope.gearByClass['raw'].push(wrap('zucchini'));
|
||||
scope.gearByClass['cooked'].push(wrap('pizza'));
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['potato', 'pizza']);
|
||||
});
|
||||
|
||||
it('filters out gear if typed gear changes', function() {
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['sashimi']);
|
||||
|
||||
scope.gearByType['veg'].push(wrap('zucchini'));
|
||||
scope.gearByType['not'].push(wrap('pizza'));
|
||||
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['sashimi', 'pizza']);
|
||||
});
|
||||
|
||||
it('filters out gear if filter query changes', function() {
|
||||
scope.equipmentQuery.query = 'c';
|
||||
scope.$digest();
|
||||
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql([]);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['chicken']);
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql([]);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['chicken']);
|
||||
});
|
||||
|
||||
it('returns the right filtered gear', function() {
|
||||
var equipment = [wrap('spicy tuna'), wrap('dragon'), wrap('rainbow'), wrap('caterpillar')];
|
||||
expect(toText(scope.equipmentSearch(equipment, 'ra'))).to.eql(['dragon', 'rainbow']);
|
||||
});
|
||||
|
||||
it('returns the right filtered gear if the source gear has unicode', function() {
|
||||
// blue hat, red hat, red shield
|
||||
var equipment = [wrap('藍色軟帽'), wrap('紅色軟帽'), wrap('紅色盾牌')];
|
||||
// searching for 'red' gives red hat, red shield
|
||||
expect(toText(scope.equipmentSearch(equipment, '紅色'))).to.eql(['紅色軟帽', '紅色盾牌']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('Notification Controller', function() {
|
||||
let User = {
|
||||
user,
|
||||
readNotification: function noop () {},
|
||||
readNotifications: function noop () {},
|
||||
sync: userSync
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ describe('Tasks Controller', function() {
|
||||
describe('editTask', function() {
|
||||
it('is Tasks.editTask', function() {
|
||||
inject(function(Tasks) {
|
||||
expect(scope.editTask).to.eql(Tasks.editTask);
|
||||
// @TODO: Currently we override the task function in the challenge ctrl, but we should abstract it again
|
||||
// expect(scope.editTask).to.eql(Tasks.editTask);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,8 @@ describe('Tasks Service', function() {
|
||||
rootScope.charts = {};
|
||||
tasks = Tasks;
|
||||
});
|
||||
|
||||
rootScope.openModal = function () {};
|
||||
});
|
||||
|
||||
it('calls get user tasks endpoint', function() {
|
||||
@@ -151,7 +153,6 @@ describe('Tasks Service', function() {
|
||||
});
|
||||
|
||||
describe('editTask', function() {
|
||||
|
||||
var task;
|
||||
|
||||
beforeEach(function(){
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": ["transform-object-rest-spread"],
|
||||
"plugins": [
|
||||
"transform-object-rest-spread",
|
||||
"syntax-async-functions",
|
||||
"transform-regenerator",
|
||||
],
|
||||
"comments": false
|
||||
}
|
||||
@@ -36,7 +36,10 @@ webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || [];
|
||||
webpackConfig.module.preLoaders.unshift({
|
||||
test: /\.js$/,
|
||||
loader: 'isparta',
|
||||
include: path.resolve(projectRoot, 'website/client'),
|
||||
include: [
|
||||
path.resolve(projectRoot, 'website/client'),
|
||||
path.resolve(projectRoot, 'website/common'),
|
||||
],
|
||||
});
|
||||
|
||||
// only apply babel for test files when using isparta
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import floorFilter from 'client/filters/floor';
|
||||
|
||||
describe('floor filter', () => {
|
||||
it('can floor a decimal number', () => {
|
||||
expect(floorFilter(4.567)).to.equal(4.56);
|
||||
expect(floorFilter(4.562)).to.equal(4.56);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import roundFilter from 'client/filters/round';
|
||||
|
||||
describe('round filter', () => {
|
||||
it('can round a decimal number', () => {
|
||||
expect(roundFilter(4.567)).to.equal(4.57);
|
||||
expect(roundFilter(4.562)).to.equal(4.56);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { gems as userGems } from 'client/store/getters/user';
|
||||
|
||||
describe('userGems getter', () => {
|
||||
it('returns the user\'s gems', () => {
|
||||
expect(userGems({
|
||||
state: {
|
||||
user: {
|
||||
balance: 4.5,
|
||||
},
|
||||
},
|
||||
})).to.equal(18);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import Vue from 'vue';
|
||||
import storeInjector from 'inject?-vue!client/store';
|
||||
import { mapState, mapGetters, mapActions } from 'client/store';
|
||||
import { flattenAndNamespace } from 'client/store/helpers/internals';
|
||||
|
||||
describe('Store', () => {
|
||||
let injectedStore;
|
||||
@@ -14,11 +15,25 @@ describe('Store', () => {
|
||||
computedName ({ state }) {
|
||||
return `${state.name} computed!`;
|
||||
},
|
||||
...flattenAndNamespace({
|
||||
nested: {
|
||||
computedName ({ state }) {
|
||||
return `${state.name} computed!`;
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
'./actions': {
|
||||
getName ({ state }, ...args) {
|
||||
return [state.name, ...args];
|
||||
},
|
||||
...flattenAndNamespace({
|
||||
nested: {
|
||||
getName ({ state }, ...args) {
|
||||
return [state.name, ...args];
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}).default;
|
||||
});
|
||||
@@ -41,17 +56,29 @@ describe('Store', () => {
|
||||
injectedStore.state.name = 'test updated';
|
||||
});
|
||||
|
||||
it('supports getters', () => {
|
||||
expect(injectedStore.getters.computedName).to.equal('test computed!');
|
||||
injectedStore.state.name = 'test updated';
|
||||
expect(injectedStore.getters.computedName).to.equal('test updated computed!');
|
||||
describe('getters', () => {
|
||||
it('supports getters', () => {
|
||||
expect(injectedStore.getters.computedName).to.equal('test computed!');
|
||||
injectedStore.state.name = 'test updated';
|
||||
expect(injectedStore.getters.computedName).to.equal('test updated computed!');
|
||||
});
|
||||
|
||||
it('supports nested getters', () => {
|
||||
expect(injectedStore.getters['nested:computedName']).to.equal('test computed!');
|
||||
injectedStore.state.name = 'test updated';
|
||||
expect(injectedStore.getters['nested:computedName']).to.equal('test updated computed!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
it('can be dispatched', () => {
|
||||
it('can dispatch an action', () => {
|
||||
expect(injectedStore.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('can dispatch a nested action', () => {
|
||||
expect(injectedStore.dispatch('nested:getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('throws an error is the action doesn\'t exists', () => {
|
||||
expect(() => injectedStore.dispatched('wrong')).to.throw;
|
||||
});
|
||||
@@ -116,5 +143,26 @@ describe('Store', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('flattenAndNamespace', () => {
|
||||
let result = flattenAndNamespace({
|
||||
nested: {
|
||||
computed ({ state }, ...args) {
|
||||
return [state.name, ...args];
|
||||
},
|
||||
getName ({ state }, ...args) {
|
||||
return [state.name, ...args];
|
||||
},
|
||||
},
|
||||
nested2: {
|
||||
getName ({ state }, ...args) {
|
||||
return [state.name, ...args];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(Object.keys(result).length).to.equal(3);
|
||||
expect(Object.keys(result).sort()).to.deep.equal(['nested2:getName', 'nested:computed', 'nested:getName']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,8 @@ describe('shared.fns.ultimateGear', () => {
|
||||
expect(user.addNotification).to.be.calledWith('ULTIMATE_GEAR_ACHIEVEMENT');
|
||||
});
|
||||
|
||||
it('does not set armoirEnabled when gear is not owned', () => {
|
||||
it('does not set armoireEnabled when gear is not owned', () => {
|
||||
user.flags.armoireEnabled = false;
|
||||
let items = {
|
||||
gear: {
|
||||
owned: {
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
import shared from '../../../website/common';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
|
||||
describe('achievements', () => {
|
||||
describe('general well-formedness', () => {
|
||||
let user = generateUser();
|
||||
let achievements = shared.achievements.getAchievementsForProfile(user);
|
||||
|
||||
it('each category has \'label\' and \'achievements\' fields', () => {
|
||||
_.each(achievements, (category) => {
|
||||
expect(category).to.have.property('label')
|
||||
.that.is.a('string');
|
||||
expect(category).to.have.property('achievements')
|
||||
.that.is.a('object');
|
||||
});
|
||||
});
|
||||
|
||||
it('each achievement has all required fields of correct types', () => {
|
||||
_.each(achievements, (category) => {
|
||||
_.each(category.achievements, (achiev) => {
|
||||
// May have additional fields (such as 'value' and 'optionalCount').
|
||||
expect(achiev).to.contain.all.keys(['title', 'text', 'icon', 'earned', 'index']);
|
||||
expect(achiev.title).to.be.a('string');
|
||||
expect(achiev.text).to.be.a('string');
|
||||
expect(achiev.icon).to.be.a('string');
|
||||
expect(achiev.earned).to.be.a('boolean');
|
||||
expect(achiev.index).to.be.a('number');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('categories have unique labels', () => {
|
||||
let achievementsArray = _.values(achievements).map(cat => cat.label);
|
||||
let labels = _.uniq(achievementsArray);
|
||||
|
||||
expect(labels.length).to.be.greaterThan(0);
|
||||
expect(labels.length).to.eql(_.size(achievements));
|
||||
});
|
||||
|
||||
it('achievements have unique keys', () => {
|
||||
let keysSoFar = {};
|
||||
|
||||
_.each(achievements, (category) => {
|
||||
_.keys(category.achievements).forEach((key) => {
|
||||
expect(keysSoFar[key]).to.be.undefined;
|
||||
keysSoFar[key] = key;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('achievements have unique indices', () => {
|
||||
let indicesSoFar = {};
|
||||
|
||||
_.each(achievements, (category) => {
|
||||
_.each(category.achievements, (achiev) => {
|
||||
let i = achiev.index;
|
||||
expect(indicesSoFar[i]).to.be.undefined;
|
||||
indicesSoFar[i] = i;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('all categories have at least 1 achievement', () => {
|
||||
_.each(achievements, (category) => {
|
||||
expect(_.size(category.achievements)).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unearned basic achievements', () => {
|
||||
let user = generateUser();
|
||||
let basicAchievs = shared.achievements.getAchievementsForProfile(user).basic.achievements;
|
||||
|
||||
it('streak and perfect day achievements exist with counts', () => {
|
||||
let streak = basicAchievs.streak;
|
||||
let perfect = basicAchievs.perfect;
|
||||
|
||||
expect(streak).to.exist;
|
||||
expect(streak).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
expect(perfect).to.exist;
|
||||
expect(perfect).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
|
||||
it('party up/on achievements exist with no counts', () => {
|
||||
let partyUp = basicAchievs.partyUp;
|
||||
let partyOn = basicAchievs.partyOn;
|
||||
|
||||
expect(partyUp).to.exist;
|
||||
expect(partyUp.optionalCount).to.be.undefined;
|
||||
expect(partyOn).to.exist;
|
||||
expect(partyOn.optionalCount).to.be.undefined;
|
||||
});
|
||||
|
||||
it('pet/mount master and triad bingo achievements exist with counts', () => {
|
||||
let beastMaster = basicAchievs.beastMaster;
|
||||
let mountMaster = basicAchievs.mountMaster;
|
||||
let triadBingo = basicAchievs.triadBingo;
|
||||
|
||||
expect(beastMaster).to.exist;
|
||||
expect(beastMaster).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
expect(mountMaster).to.exist;
|
||||
expect(mountMaster).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
expect(triadBingo).to.exist;
|
||||
expect(triadBingo).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
|
||||
it('ultimate gear achievements exist with no counts', () => {
|
||||
let gearTypes = ['healer', 'rogue', 'warrior', 'mage'];
|
||||
gearTypes.forEach((gear) => {
|
||||
let gearAchiev = basicAchievs[`${gear}UltimateGear`];
|
||||
|
||||
expect(gearAchiev).to.exist;
|
||||
expect(gearAchiev.optionalCount).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
it('rebirth achievement exists with no count', () => {
|
||||
let rebirth = basicAchievs.rebirth;
|
||||
|
||||
expect(rebirth).to.exist;
|
||||
expect(rebirth.optionalCount).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('unearned seasonal achievements', () => {
|
||||
let user = generateUser();
|
||||
let seasonalAchievs = shared.achievements.getAchievementsForProfile(user).seasonal.achievements;
|
||||
|
||||
it('habiticaDays and habitBirthdays achievements exist with counts', () => {
|
||||
let habiticaDays = seasonalAchievs.habiticaDays;
|
||||
let habitBirthdays = seasonalAchievs.habitBirthdays;
|
||||
|
||||
expect(habiticaDays).to.exist;
|
||||
expect(habiticaDays).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
expect(habitBirthdays).to.exist;
|
||||
expect(habitBirthdays).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
|
||||
it('spell achievements exist with counts', () => {
|
||||
let spellTypes = ['snowball', 'spookySparkles', 'shinySeed', 'seafoam'];
|
||||
spellTypes.forEach((spell) => {
|
||||
let spellAchiev = seasonalAchievs[spell];
|
||||
|
||||
expect(spellAchiev).to.exist;
|
||||
expect(spellAchiev).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
});
|
||||
|
||||
it('quest achievements do not exist', () => {
|
||||
let quests = ['dilatory', 'stressbeast', 'burnout', 'bewilder'];
|
||||
quests.forEach((quest) => {
|
||||
let questAchiev = seasonalAchievs[`${quest}Quest`];
|
||||
|
||||
expect(questAchiev).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('costumeContests achievement exists with count', () => {
|
||||
let costumeContests = seasonalAchievs.costumeContests;
|
||||
|
||||
expect(costumeContests).to.exist;
|
||||
expect(costumeContests).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
|
||||
it('card achievements exist with counts', () => {
|
||||
let cardTypes = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday'];
|
||||
cardTypes.forEach((card) => {
|
||||
let cardAchiev = seasonalAchievs[`${card}Cards`];
|
||||
|
||||
expect(cardAchiev).to.exist;
|
||||
expect(cardAchiev).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unearned special achievements', () => {
|
||||
let user = generateUser();
|
||||
let specialAchievs = shared.achievements.getAchievementsForProfile(user).special.achievements;
|
||||
|
||||
it('habitSurveys achievement exists with count', () => {
|
||||
let habitSurveys = specialAchievs.habitSurveys;
|
||||
|
||||
expect(habitSurveys).to.exist;
|
||||
expect(habitSurveys).to.have.property('optionalCount')
|
||||
.that.is.a('number');
|
||||
});
|
||||
|
||||
it('contributor achievement exists with value and no count', () => {
|
||||
let contributor = specialAchievs.contributor;
|
||||
|
||||
expect(contributor).to.exist;
|
||||
expect(contributor).to.have.property('value')
|
||||
.that.is.a('number');
|
||||
expect(contributor.optionalCount).to.be.undefined;
|
||||
});
|
||||
|
||||
it('npc achievement is hidden if unachieved', () => {
|
||||
let npc = specialAchievs.npc;
|
||||
expect(npc).to.not.exist;
|
||||
});
|
||||
|
||||
it('kickstarter achievement is hidden if unachieved', () => {
|
||||
let kickstarter = specialAchievs.kickstarter;
|
||||
expect(kickstarter).to.not.exist;
|
||||
});
|
||||
|
||||
it('veteran achievement is hidden if unachieved', () => {
|
||||
let veteran = specialAchievs.veteran;
|
||||
expect(veteran).to.not.exist;
|
||||
});
|
||||
|
||||
it('originalUser achievement is hidden if unachieved', () => {
|
||||
let originalUser = specialAchievs.originalUser;
|
||||
expect(originalUser).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('earned seasonal achievements', () => {
|
||||
let user = generateUser();
|
||||
let quests = ['dilatory', 'stressbeast', 'burnout', 'bewilder'];
|
||||
quests.forEach((quest) => {
|
||||
user.achievements.quests[quest] = 1;
|
||||
});
|
||||
let seasonalAchievs = shared.achievements.getAchievementsForProfile(user).seasonal.achievements;
|
||||
|
||||
it('quest achievements exist', () => {
|
||||
quests.forEach((quest) => {
|
||||
let questAchiev = seasonalAchievs[`${quest}Quest`];
|
||||
|
||||
expect(questAchiev).to.exist;
|
||||
expect(questAchiev.optionalCount).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('earned special achievements', () => {
|
||||
let user = generateUser({
|
||||
achievements: {
|
||||
habitSurveys: 2,
|
||||
veteran: true,
|
||||
originalUser: true,
|
||||
},
|
||||
backer: {tier: 3},
|
||||
contributor: {level: 1},
|
||||
});
|
||||
let specialAchievs = shared.achievements.getAchievementsForProfile(user).special.achievements;
|
||||
|
||||
it('habitSurveys achievement is earned with correct value', () => {
|
||||
let habitSurveys = specialAchievs.habitSurveys;
|
||||
|
||||
expect(habitSurveys).to.exist;
|
||||
expect(habitSurveys.earned).to.eql(true);
|
||||
expect(habitSurveys.value).to.eql(2);
|
||||
});
|
||||
|
||||
it('contributor achievement is earned with correct value', () => {
|
||||
let contributor = specialAchievs.contributor;
|
||||
|
||||
expect(contributor).to.exist;
|
||||
expect(contributor.earned).to.eql(true);
|
||||
expect(contributor.value).to.eql(1);
|
||||
});
|
||||
|
||||
it('npc achievement is earned with correct value', () => {
|
||||
let npcUser = generateUser({
|
||||
backer: {npc: 'test'},
|
||||
});
|
||||
let npc = shared.achievements.getAchievementsForProfile(npcUser).special.achievements.npc;
|
||||
|
||||
expect(npc).to.exist;
|
||||
expect(npc.earned).to.eql(true);
|
||||
expect(npc.value).to.eql('test');
|
||||
});
|
||||
|
||||
it('kickstarter achievement is earned with correct value', () => {
|
||||
let kickstarter = specialAchievs.kickstarter;
|
||||
|
||||
expect(kickstarter).to.exist;
|
||||
expect(kickstarter.earned).to.eql(true);
|
||||
expect(kickstarter.value).to.eql(3);
|
||||
});
|
||||
|
||||
it('veteran achievement is earned', () => {
|
||||
let veteran = specialAchievs.veteran;
|
||||
|
||||
expect(veteran).to.exist;
|
||||
expect(veteran.earned).to.eql(true);
|
||||
});
|
||||
|
||||
it('originalUser achievement is earned', () => {
|
||||
let originalUser = specialAchievs.originalUser;
|
||||
|
||||
expect(originalUser).to.exist;
|
||||
expect(originalUser.earned).to.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mountMaster, beastMaster, and triadBingo achievements', () => {
|
||||
it('master and triad bingo achievements do not include *Text2 strings if no keys have been used', () => {
|
||||
let user = generateUser();
|
||||
let basicAchievs = shared.achievements.getAchievementsForProfile(user).basic.achievements;
|
||||
|
||||
let beastMaster = basicAchievs.beastMaster;
|
||||
let mountMaster = basicAchievs.mountMaster;
|
||||
let triadBingo = basicAchievs.triadBingo;
|
||||
|
||||
expect(beastMaster.text).to.not.match(/released/);
|
||||
expect(beastMaster.text).to.not.match(/0 time\(s\)/);
|
||||
expect(mountMaster.text).to.not.match(/released/);
|
||||
expect(mountMaster.text).to.not.match(/0 time\(s\)/);
|
||||
expect(triadBingo.text).to.not.match(/released/);
|
||||
expect(triadBingo.text).to.not.match(/0 time\(s\)/);
|
||||
});
|
||||
|
||||
it('master and triad bingo achievements includes *Text2 strings if keys have been used', () => {
|
||||
let user = generateUser({
|
||||
achievements: {
|
||||
beastMasterCount: 1,
|
||||
mountMasterCount: 2,
|
||||
triadBingoCount: 3,
|
||||
},
|
||||
});
|
||||
let basicAchievs = shared.achievements.getAchievementsForProfile(user).basic.achievements;
|
||||
|
||||
let beastMaster = basicAchievs.beastMaster;
|
||||
let mountMaster = basicAchievs.mountMaster;
|
||||
let triadBingo = basicAchievs.triadBingo;
|
||||
|
||||
expect(beastMaster.text).to.match(/released/);
|
||||
expect(beastMaster.text).to.match(/1 time\(s\)/);
|
||||
expect(mountMaster.text).to.match(/released/);
|
||||
expect(mountMaster.text).to.match(/2 time\(s\)/);
|
||||
expect(triadBingo.text).to.match(/released/);
|
||||
expect(triadBingo.text).to.match(/3 time\(s\)/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ultimateGear achievements', () => {
|
||||
it('title and text contain localized class info', () => {
|
||||
let user = generateUser();
|
||||
let basicAchievs = shared.achievements.getAchievementsForProfile(user).basic.achievements;
|
||||
let gearTypes = ['healer', 'rogue', 'warrior', 'mage'];
|
||||
|
||||
gearTypes.forEach((gear) => {
|
||||
let gearAchiev = basicAchievs[`${gear}UltimateGear`];
|
||||
let classNameRegex = new RegExp(gear.charAt(0).toUpperCase() + gear.slice(1));
|
||||
|
||||
expect(gearAchiev.title).to.match(classNameRegex);
|
||||
expect(gearAchiev.text).to.match(classNameRegex);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import randomVal from '../../../website/common/script/libs/randomVal';
|
||||
import {times} from 'lodash';
|
||||
|
||||
describe('randomVal', () => {
|
||||
let obj;
|
||||
@@ -21,23 +22,12 @@ describe('randomVal', () => {
|
||||
expect(result).to.be.oneOf([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('uses Math.random to determine the property', () => {
|
||||
sandbox.spy(Math, 'random');
|
||||
|
||||
randomVal(obj);
|
||||
|
||||
expect(Math.random).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('can pass in a predictable random value', () => {
|
||||
sandbox.spy(Math, 'random');
|
||||
|
||||
let result = randomVal(obj, {
|
||||
predictableRandom: 0.3,
|
||||
times(30, () => {
|
||||
expect(randomVal(obj, {
|
||||
predictableRandom: 0.3,
|
||||
})).to.equal(2);
|
||||
});
|
||||
|
||||
expect(Math.random).to.not.be.called;
|
||||
expect(result).to.equal(2);
|
||||
});
|
||||
|
||||
it('returns a random key when the key option is passed in', () => {
|
||||
|
||||
@@ -22,7 +22,9 @@ describe('shops', () => {
|
||||
it('items contain required fields', () => {
|
||||
_.each(shopCategories, (category) => {
|
||||
_.each(category.items, (item) => {
|
||||
expect(item).to.have.all.keys(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'class']);
|
||||
_.each(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'class'], (key) => {
|
||||
expect(_.has(item, key)).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -46,7 +48,9 @@ describe('shops', () => {
|
||||
it('items contain required fields', () => {
|
||||
_.each(shopCategories, (category) => {
|
||||
_.each(category.items, (item) => {
|
||||
expect(item).to.have.all.keys('key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'boss', 'class', 'collect', 'drop', 'unlockCondition', 'lvl');
|
||||
_.each(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'boss', 'class', 'collect', 'drop', 'unlockCondition', 'lvl'], (key) => {
|
||||
expect(_.has(item, key)).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -70,7 +74,9 @@ describe('shops', () => {
|
||||
it('items contain required fields', () => {
|
||||
_.each(shopCategories, (category) => {
|
||||
_.each(category.items, (item) => {
|
||||
expect(item).to.have.all.keys('key', 'text', 'value', 'currency', 'locked', 'purchaseType', 'class', 'notes', 'class');
|
||||
_.each(['key', 'text', 'value', 'currency', 'locked', 'purchaseType', 'class', 'notes', 'class'], (key) => {
|
||||
expect(_.has(item, key)).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -94,7 +100,9 @@ describe('shops', () => {
|
||||
it('items contain required fields', () => {
|
||||
_.each(shopCategories, (category) => {
|
||||
_.each(category.items, (item) => {
|
||||
expect(item).to.have.all.keys('key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'specialClass', 'type');
|
||||
_.each(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'type'], (key) => {
|
||||
expect(_.has(item, key)).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('shared.ops.blockUser', () => {
|
||||
|
||||
it('validates uuid', (done) => {
|
||||
try {
|
||||
blockUser(user, { params: { uuid: 1 } });
|
||||
blockUser(user, { params: { uuid: '1' } });
|
||||
} catch (error) {
|
||||
expect(error.message).to.eql(i18n.t('invalidUUID'));
|
||||
done();
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '../../helpers/common.helper';
|
||||
import count from '../../../website/common/script/count';
|
||||
import buyArmoire from '../../../website/common/script/ops/buyArmoire';
|
||||
import randomVal from '../../../website/common/script/libs/randomVal';
|
||||
import content from '../../../website/common/script/content/index';
|
||||
import {
|
||||
NotAuthorized,
|
||||
@@ -43,11 +44,11 @@ describe('shared.ops.buyArmoire', () => {
|
||||
user.stats.exp = 0;
|
||||
user.items.food = {};
|
||||
|
||||
sandbox.stub(Math, 'random');
|
||||
sandbox.stub(randomVal, 'trueRandom');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Math.random.restore();
|
||||
randomVal.trueRandom.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
@@ -89,7 +90,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
context('non-gear awards', () => {
|
||||
it('gives Experience', () => {
|
||||
let previousExp = user.stats.exp;
|
||||
Math.random.returns(YIELD_EXP);
|
||||
randomVal.trueRandom.returns(YIELD_EXP);
|
||||
|
||||
buyArmoire(user);
|
||||
|
||||
@@ -102,7 +103,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
it('gives food', () => {
|
||||
let previousExp = user.stats.exp;
|
||||
|
||||
Math.random.returns(YIELD_FOOD);
|
||||
randomVal.trueRandom.returns(YIELD_FOOD);
|
||||
|
||||
buyArmoire(user);
|
||||
|
||||
@@ -113,7 +114,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
});
|
||||
|
||||
it('does not give equipment if all equipment has been found', () => {
|
||||
Math.random.returns(YIELD_EQUIPMENT);
|
||||
randomVal.trueRandom.returns(YIELD_EQUIPMENT);
|
||||
user.items.gear.owned = getFullArmoire();
|
||||
user.stats.gp = 150;
|
||||
|
||||
@@ -131,7 +132,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
context('gear awards', () => {
|
||||
it('always drops equipment the first time', () => {
|
||||
delete user.flags.armoireOpened;
|
||||
Math.random.returns(YIELD_EXP);
|
||||
randomVal.trueRandom.returns(YIELD_EXP);
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(1);
|
||||
|
||||
@@ -148,7 +149,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
});
|
||||
|
||||
it('gives more equipment', () => {
|
||||
Math.random.returns(YIELD_EQUIPMENT);
|
||||
randomVal.trueRandom.returns(YIELD_EQUIPMENT);
|
||||
user.items.gear.owned = {
|
||||
weapon_warrior_0: true,
|
||||
head_armoire_hornedIronHelm: true,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '../../helpers/common.helper';
|
||||
|
||||
describe('shared.ops.purchase', () => {
|
||||
const SEASONAL_FOOD = 'Candy_Base';
|
||||
const SEASONAL_FOOD = 'Meat';
|
||||
let user;
|
||||
let goldPoints = 40;
|
||||
let gemsBought = 40;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import releaseBoth from '../../../website/common/script/ops/releaseBoth';
|
||||
import content from '../../../website/common/script/content/index';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
@@ -65,19 +66,41 @@ describe('shared.ops.releaseBoth', () => {
|
||||
expect(user.items.mounts[animal]).to.equal(null);
|
||||
});
|
||||
|
||||
it('removes currentPet', () => {
|
||||
it('removes drop currentPet', () => {
|
||||
let petInfo = content.petInfo[user.items.currentPet];
|
||||
expect(petInfo.type).to.equal('drop');
|
||||
releaseBoth(user);
|
||||
|
||||
expect(user.items.currentMount).to.be.empty;
|
||||
expect(user.items.currentPet).to.be.empty;
|
||||
});
|
||||
|
||||
it('removes currentMount', () => {
|
||||
it('removes drop currentMount', () => {
|
||||
let mountInfo = content.mountInfo[user.items.currentMount];
|
||||
expect(mountInfo.type).to.equal('drop');
|
||||
releaseBoth(user);
|
||||
|
||||
expect(user.items.currentMount).to.be.empty;
|
||||
});
|
||||
|
||||
it('leaves non-drop pets and mounts equipped', () => {
|
||||
let questAnimal = 'Gryphon-Base';
|
||||
user.items.currentMount = questAnimal;
|
||||
user.items.currentPet = questAnimal;
|
||||
user.items.pets[questAnimal] = 5;
|
||||
user.items.mounts[questAnimal] = true;
|
||||
|
||||
let petInfo = content.petInfo[user.items.currentPet];
|
||||
expect(petInfo.type).to.not.equal('drop');
|
||||
let mountInfo = content.mountInfo[user.items.currentMount];
|
||||
expect(mountInfo.type).to.not.equal('drop');
|
||||
|
||||
releaseBoth(user);
|
||||
|
||||
expect(user.items.currentMount).to.equal(questAnimal);
|
||||
expect(user.items.currentPet).to.equal(questAnimal);
|
||||
});
|
||||
|
||||
it('decreases user\'s balance', () => {
|
||||
releaseBoth(user);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import releaseMounts from '../../../website/common/script/ops/releaseMounts';
|
||||
import content from '../../../website/common/script/content/index';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
@@ -37,12 +38,26 @@ describe('shared.ops.releaseMounts', () => {
|
||||
expect(user.items.mounts[animal]).to.equal(null);
|
||||
});
|
||||
|
||||
it('removes currentMount', () => {
|
||||
it('removes drop currentMount', () => {
|
||||
let mountInfo = content.mountInfo[user.items.currentMount];
|
||||
expect(mountInfo.type).to.equal('drop');
|
||||
releaseMounts(user);
|
||||
|
||||
expect(user.items.currentMount).to.be.empty;
|
||||
});
|
||||
|
||||
it('leaves non-drop mount equipped', () => {
|
||||
let questAnimal = 'Gryphon-Base';
|
||||
user.items.currentMount = questAnimal;
|
||||
user.items.mounts[questAnimal] = true;
|
||||
|
||||
let mountInfo = content.mountInfo[user.items.currentMount];
|
||||
expect(mountInfo.type).to.not.equal('drop');
|
||||
releaseMounts(user);
|
||||
|
||||
expect(user.items.currentMount).to.equal(questAnimal);
|
||||
});
|
||||
|
||||
it('increases mountMasterCount achievement', () => {
|
||||
releaseMounts(user);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import releasePets from '../../../website/common/script/ops/releasePets';
|
||||
import content from '../../../website/common/script/content/index';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
@@ -37,12 +38,26 @@ describe('shared.ops.releasePets', () => {
|
||||
expect(user.items.pets[animal]).to.equal(0);
|
||||
});
|
||||
|
||||
it('removes currentPet', () => {
|
||||
it('removes drop currentPet', () => {
|
||||
let petInfo = content.petInfo[user.items.currentPet];
|
||||
expect(petInfo.type).to.equal('drop');
|
||||
releasePets(user);
|
||||
|
||||
expect(user.items.currentPet).to.be.empty;
|
||||
});
|
||||
|
||||
it('leaves non-drop pets equipped', () => {
|
||||
let questAnimal = 'Gryphon-Base';
|
||||
user.items.currentPet = questAnimal;
|
||||
user.items.pets[questAnimal] = 5;
|
||||
|
||||
let petInfo = content.petInfo[user.items.currentPet];
|
||||
expect(petInfo.type).to.not.equal('drop');
|
||||
releasePets(user);
|
||||
|
||||
expect(user.items.currentPet).to.equal(questAnimal);
|
||||
});
|
||||
|
||||
it('decreases user\'s balance', () => {
|
||||
releasePets(user);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import revive from '../../../website/common/script/ops/revive';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import {
|
||||
@@ -53,7 +55,21 @@ describe('shared.ops.revive', () => {
|
||||
expect(user.stats.str).to.equal(1);
|
||||
});
|
||||
|
||||
it('TODO: test actual ways stats are affected');
|
||||
it('it decreases a random stat from str, con, per, int by one', () => {
|
||||
let stats = ['str', 'con', 'per', 'int'];
|
||||
|
||||
_.each(stats, (s) => {
|
||||
user.stats[s] = 1;
|
||||
});
|
||||
|
||||
revive(user);
|
||||
|
||||
let statSum = _.reduce(stats, (m, k) => {
|
||||
return m + user.stats[k];
|
||||
}, 0);
|
||||
|
||||
expect(statSum).to.equal(3);
|
||||
});
|
||||
|
||||
it('removes a random item from user gear owned', () => {
|
||||
let weaponKey = 'weapon_warrior_0';
|
||||
@@ -65,15 +81,58 @@ describe('shared.ops.revive', () => {
|
||||
expect(user.items.gear.owned[weaponKey]).to.be.false;
|
||||
});
|
||||
|
||||
it('does not remove 0 value items');
|
||||
it('does not remove 0 value items', () => {
|
||||
user.items.gear.owned = {
|
||||
eyewear_special_yellowTopFrame: true,
|
||||
};
|
||||
|
||||
it('allows removing warrior sword (0 value item)');
|
||||
revive(user);
|
||||
|
||||
it('does not remove items of a different class');
|
||||
expect(user.items.gear.owned.eyewear_special_yellowTopFrame).to.be.true;
|
||||
});
|
||||
|
||||
it('removes "special" items');
|
||||
it('allows removing warrior sword (0 value item)', () => {
|
||||
user.items.gear.owned = {
|
||||
weapon_warrior_0: true,
|
||||
};
|
||||
|
||||
it('removes "armoire" items');
|
||||
let weaponKey = 'weapon_warrior_0';
|
||||
|
||||
let [, message] = revive(user);
|
||||
|
||||
expect(message).to.equal(i18n.t('messageLostItem', { itemText: content.gear.flat[weaponKey].text()}));
|
||||
expect(user.items.gear.owned[weaponKey]).to.be.false;
|
||||
});
|
||||
|
||||
it('does not remove items of a different class', () => {
|
||||
let weaponKey = 'weapon_wizard_1';
|
||||
user.items.gear.owned[weaponKey] = true;
|
||||
|
||||
let [, message] = revive(user);
|
||||
|
||||
expect(message).to.equal('');
|
||||
expect(user.items.gear.owned[weaponKey]).to.be.true;
|
||||
});
|
||||
|
||||
it('removes "special" items', () => {
|
||||
let weaponKey = 'weapon_special_1';
|
||||
user.items.gear.owned[weaponKey] = true;
|
||||
|
||||
let [, message] = revive(user);
|
||||
|
||||
expect(message).to.equal(i18n.t('messageLostItem', { itemText: content.gear.flat[weaponKey].text()}));
|
||||
expect(user.items.gear.owned[weaponKey]).to.be.false;
|
||||
});
|
||||
|
||||
it('removes "armoire" items', () => {
|
||||
let weaponKey = 'armor_armoire_goldenToga';
|
||||
user.items.gear.owned[weaponKey] = true;
|
||||
|
||||
let [, message] = revive(user);
|
||||
|
||||
expect(message).to.equal(i18n.t('messageLostItem', { itemText: content.gear.flat[weaponKey].text()}));
|
||||
expect(user.items.gear.owned[weaponKey]).to.be.false;
|
||||
});
|
||||
|
||||
it('dequips lost item from user if user had it equipped', () => {
|
||||
let weaponKey = 'weapon_warrior_0';
|
||||
|
||||
@@ -157,6 +157,16 @@ describe('shared.ops.scoreTask', () => {
|
||||
expect(ref.afterUser._tmp.quest.progressDelta).to.eql(secondTaskDelta);
|
||||
});
|
||||
|
||||
it('does not modify stats when task need approval', () => {
|
||||
todo.group.approval.required = true;
|
||||
options = { user: ref.afterUser, task: todo, direction: 'up', times: 5, cron: false };
|
||||
scoreTask(options);
|
||||
|
||||
expect(ref.afterUser.stats.hp).to.eql(50);
|
||||
expect(ref.afterUser.stats.exp).to.equal(ref.beforeUser.stats.exp);
|
||||
expect(ref.afterUser.stats.gp).to.equal(ref.beforeUser.stats.gp);
|
||||
});
|
||||
|
||||
context('habits', () => {
|
||||
it('up', () => {
|
||||
options = { user: ref.afterUser, task: habit, direction: 'up', times: 5, cron: false };
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../helpers/common.helper';
|
||||
|
||||
import timeTravelers from '../../website/common/script/content/time-travelers'
|
||||
|
||||
describe('time-travelers store', () => {
|
||||
let user;
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
});
|
||||
|
||||
it('removes owned sets from the time travelers store', () => {
|
||||
user.items.gear.owned['head_mystery_201602'] = true;
|
||||
expect(timeTravelers.timeTravelerStore(user)['201602']).to.not.exist;
|
||||
expect(timeTravelers.timeTravelerStore(user)['201603']).to.exist;
|
||||
});
|
||||
|
||||
it('removes unopened mystery item sets from the time travelers store', () => {
|
||||
user.purchased = {
|
||||
plan: {
|
||||
mysteryItems: ['head_mystery_201602'],
|
||||
},
|
||||
};
|
||||
expect(timeTravelers.timeTravelerStore(user)['201602']).to.not.exist;
|
||||
expect(timeTravelers.timeTravelerStore(user)['201603']).to.exist;
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
// Import requester function, set it up for v3, export it
|
||||
import { requester } from '../requester';
|
||||
requester.setApiVersion('v3');
|
||||
|
||||
@@ -86,7 +86,7 @@ export function generateTodo (user) {
|
||||
completed: false,
|
||||
};
|
||||
|
||||
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
task.userId = user._id;
|
||||
task.save();
|
||||
|
||||
@@ -101,7 +101,7 @@ export function generateDaily (user) {
|
||||
completed: false,
|
||||
};
|
||||
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line babel/new-cap
|
||||
let task = new Tasks.daily(Tasks.Task.sanitize(daily)); // eslint-disable-line new-cap
|
||||
task.userId = user._id;
|
||||
task.save();
|
||||
|
||||
|
||||
@@ -13,27 +13,7 @@ chai.use(require('sinon-chai'));
|
||||
chai.use(require('chai-as-promised'));
|
||||
global.expect = chai.expect;
|
||||
global.sinon = require('sinon');
|
||||
let sinonStubPromise = require('sinon-stub-promise');
|
||||
sinonStubPromise(global.sinon);
|
||||
global.sandbox = sinon.sandbox.create();
|
||||
global.Promise = Bluebird;
|
||||
|
||||
import nconf from 'nconf';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
//------------------------------
|
||||
// Load nconf for unit tests
|
||||
//------------------------------
|
||||
if (process.env.LOAD_SERVER === '0') { // when the server is in a different process we simply connect to mongoose
|
||||
require('../../website/server/libs/setupNconf')('./config.json');
|
||||
// Use Q promises instead of mpromise in mongoose
|
||||
mongoose.Promise = Bluebird;
|
||||
mongoose.connect(nconf.get('TEST_DB_URI'));
|
||||
} else { // When running tests and the server in the same process
|
||||
require('../../website/server/libs/setupNconf')('./config.json.example');
|
||||
nconf.set('NODE_DB_URI', nconf.get('TEST_DB_URI'));
|
||||
nconf.set('NODE_ENV', 'test');
|
||||
nconf.set('IS_TEST', true);
|
||||
// We require src/server and npt src/index because
|
||||
// 1. nconf is already setup
|
||||
// 2. we don't need clustering
|
||||
require('../../website/server/server');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* eslint-disable no-process-env */
|
||||
import nconf from 'nconf';
|
||||
import mongoose from 'mongoose';
|
||||
import Bluebird from 'bluebird';
|
||||
import setupNconf from '../../website/server/libs/setupNconf';
|
||||
|
||||
if (process.env.LOAD_SERVER === '0') { // when the server is in a different process we simply connect to mongoose
|
||||
setupNconf('./config.json');
|
||||
// Use Q promises instead of mpromise in mongoose
|
||||
mongoose.Promise = Bluebird;
|
||||
mongoose.connect(nconf.get('TEST_DB_URI'));
|
||||
} else { // When running tests and the server in the same process
|
||||
setupNconf('./config.json.example');
|
||||
nconf.set('NODE_DB_URI', nconf.get('TEST_DB_URI'));
|
||||
nconf.set('NODE_ENV', 'test');
|
||||
nconf.set('IS_TEST', true);
|
||||
// We require src/server and npt src/index because
|
||||
// 1. nconf is already setup
|
||||
// 2. we don't need clustering
|
||||
require('../../website/server/server'); // eslint-disable-line global-require
|
||||
}
|
||||
@@ -7,8 +7,8 @@ const STRING_DOES_NOT_EXIST_MSG = /^String '.*' not found.$/;
|
||||
// Use this to verify error messages returned by the server
|
||||
// That way, if the translated string changes, the test
|
||||
// will not break. NOTE: it checks against errors with string as well.
|
||||
export function translate (key, variables) {
|
||||
let translatedString = i18n.t(key, variables);
|
||||
export function translate (key, variables, language) {
|
||||
let translatedString = i18n.t(key, variables, language);
|
||||
|
||||
expect(translatedString).to.not.be.empty;
|
||||
expect(translatedString).to.not.eql(STRING_ERROR_MSG);
|
||||
|
||||
@@ -2,6 +2,7 @@ var path = require('path');
|
||||
var config = require('./config');
|
||||
var utils = require('./utils');
|
||||
var projectRoot = path.resolve(__dirname, '../');
|
||||
var webpack = require('webpack');
|
||||
|
||||
var IS_PROD = process.env.NODE_ENV === 'production';
|
||||
var baseConfig = {
|
||||
@@ -17,6 +18,7 @@ var baseConfig = {
|
||||
extensions: ['', '.js', '.vue'],
|
||||
fallback: [path.join(__dirname, '../node_modules')],
|
||||
alias: {
|
||||
jquery: 'jquery/src/jquery',
|
||||
client: path.resolve(__dirname, '../website/client'),
|
||||
assets: path.resolve(__dirname, '../website/client/assets'),
|
||||
components: path.resolve(__dirname, '../website/client/components'),
|
||||
@@ -25,6 +27,12 @@ var baseConfig = {
|
||||
resolveLoader: {
|
||||
fallback: [path.join(__dirname, '../node_modules')],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
$: 'jquery',
|
||||
jQuery: 'jquery',
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
preLoaders: !IS_PROD ? [
|
||||
{
|
||||
@@ -79,6 +87,9 @@ var baseConfig = {
|
||||
require('autoprefixer')({
|
||||
browsers: ['last 2 versions'],
|
||||
}),
|
||||
require('postcss-easy-import')({
|
||||
glob: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user