Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79c1a5d9c1 | |||
| 8b955e2c5e | |||
| 67c607216f | |||
| 672fd43ad0 | |||
| 69281f80ea | |||
| ec0e5024a7 | |||
| 8e6ce39d64 | |||
| 617832f02d | |||
| 43efe2d6d6 | |||
| 9474a44df3 | |||
| 6a27feb3f9 | |||
| e04d4e8bea | |||
| 2832226539 | |||
| dcbc5da2ba | |||
| 1d44bfe0cd | |||
| 6553118636 | |||
| a238b264e5 | |||
| 4696237b21 | |||
| b7956a82ee | |||
| a5babc493f | |||
| 708bd4a292 | |||
| d9e774dd77 | |||
| 97ef3b1d4b | |||
| 8e3ac280b0 | |||
| 7fedbdde03 | |||
| 6ad808f717 | |||
| c6541399bb | |||
| 81028893b2 | |||
| 943862c0ea | |||
| 43511aacb9 | |||
| ce68e2f855 | |||
| ec06faef32 | |||
| cfac54d44c | |||
| f4b41bd958 | |||
| 7d7d71e95f | |||
| 2b21c2a28e | |||
| dfa8725c55 | |||
| 1d8880c04d | |||
| 07fd3cef4c | |||
| b58443140a | |||
| a0b93d57e4 | |||
| 7cac574c31 | |||
| 86619a2ac9 | |||
| 058c1464d3 | |||
| 70e747c131 | |||
| 8869d3ebf0 | |||
| d82f4f5c91 | |||
| d9a6120cfe | |||
| 5c7ad58ce4 | |||
| 284510d0a3 | |||
| 0791848fa3 | |||
| 466f109edb | |||
| 87fe4aa28e | |||
| baa86bab88 | |||
| 17cbe16773 | |||
| 660f3635aa | |||
| 10a28a9161 | |||
| 2964eff298 | |||
| 7119763c1e | |||
| 12bd10a095 | |||
| 221c95eb14 | |||
| 7815d501fa | |||
| 9dec717e10 | |||
| d23368a826 | |||
| 33913743e9 | |||
| d92b344e91 | |||
| def065d86a | |||
| 5b50761d9f | |||
| 30a52e2bb8 | |||
| b361e3fc82 | |||
| 7419e8a926 | |||
| 0d04509a97 | |||
| 4950ceb814 | |||
| 218d186969 | |||
| 4f6306e748 | |||
| 334478f3d4 | |||
| fd4c50083c | |||
| 435471a48c | |||
| be96e2c3a4 | |||
| d018401f96 | |||
| 63bb9b8e68 | |||
| 682f559762 | |||
| aa39af5ab4 | |||
| d9503ef35a | |||
| 322a826576 | |||
| 362dea6c5c | |||
| f74e908bd5 | |||
| d4c11ff798 | |||
| 24bc3822b6 | |||
| 4fc796b177 | |||
| c099242cb7 | |||
| f7142b6f55 | |||
| c0be28072b | |||
| a218f9ef25 | |||
| 65e881fa5a | |||
| 2c34972629 | |||
| 9bd07034a5 | |||
| 285507d68a | |||
| 5c7227fc02 | |||
| 4a99fc5a74 | |||
| 0a2e50ce76 | |||
| 49d8c739c0 | |||
| 2c01e6347d | |||
| d050ef4779 | |||
| 21d008aaee | |||
| 0d4ed102a0 | |||
| 83bcfcde06 | |||
| 32ac00b417 | |||
| 2a00aefdd6 | |||
| 679d615224 | |||
| e4e980a6e3 | |||
| 73c5764d63 | |||
| 205d2758de | |||
| efe7ea52bc | |||
| 938152edea | |||
| 3e1e9b6d56 | |||
| 8b01d1a88c | |||
| 9910fb7b99 | |||
| a91d639606 | |||
| eaeb942ad8 | |||
| c509d0edbd | |||
| 59566d9d65 | |||
| e457b8ddc0 | |||
| a43f7734dc | |||
| 2618825ce2 | |||
| af35cbb743 | |||
| 989361bb46 | |||
| b18225561e | |||
| a500119879 | |||
| 4d2a738748 | |||
| 38047b9d5c | |||
| 035a6be8b7 | |||
| e3bc4a5fc7 | |||
| d8d825c6b0 | |||
| f47c243cb0 | |||
| 8c31f944fa | |||
| 931403ef0b | |||
| 17ff5f4640 | |||
| ea590307c9 | |||
| e12597b330 | |||
| dd92b20ef5 | |||
| fd0365eec9 | |||
| cdeefcf4e9 | |||
| 5d799546ff | |||
| 931bd33a9f | |||
| 0b07aae1bb | |||
| 4ff769db0c | |||
| 5456cc2348 | |||
| 97baa69a8d | |||
| 66644b7369 | |||
| 4dcac53d6d | |||
| ead1c1b851 | |||
| f3a1ac425f | |||
| e450e7d975 | |||
| 4984f99de0 | |||
| 3ae6bfcb57 | |||
| 5c6f727c4b | |||
| 4bbb75a6a3 | |||
| a2dee15289 | |||
| db59a4b925 | |||
| 96aaa6f0e3 | |||
| 9eb7654a3c | |||
| d2902910bd | |||
| 3a7c9b1c1f | |||
| 6c214df289 | |||
| c2fe33650c | |||
| 53748d62f6 | |||
| c0364c0e00 | |||
| 2ccaa02e7e | |||
| 943bf1442d | |||
| 76f13abee1 | |||
| 06a0147a97 | |||
| 03984df562 | |||
| f95ad1201d | |||
| 91e311284a | |||
| ab554320f3 | |||
| e5afb89706 | |||
| 0c1d78acdb | |||
| 3361ed1f2d | |||
| 4d7d8e84c5 | |||
| 28d3611c4a | |||
| fbb758fa89 | |||
| 40f9651378 | |||
| e90041976b | |||
| 6d737330f2 | |||
| 910c49df4f | |||
| 1a46fac7bf | |||
| 6bbdd74927 | |||
| dd4c9c2536 | |||
| 0a6b22fdd9 | |||
| d940066ea2 | |||
| 7be3143e55 | |||
| b42875e2e1 | |||
| 6c6e8f0001 | |||
| 8219dfd9e6 | |||
| 025b994224 | |||
| b056763f09 | |||
| 6e91326648 | |||
| 8b9c76a2b7 | |||
| c4fc2d825d | |||
| 411ac94986 | |||
| 8b952a6caf | |||
| b21bf0d428 | |||
| 1b5134c0bc | |||
| 54ffe1d3b6 | |||
| ff77455b00 | |||
| 22eaefb3b2 | |||
| b9710c4180 | |||
| 919b30c237 | |||
| 9a51d200f7 | |||
| 170df77c3d | |||
| 043f34d619 | |||
| 0b20f8a980 | |||
| 7f208eb141 | |||
| 75a196dd3b | |||
| 701a6170a3 | |||
| ee70ed6146 | |||
| d45a6f260d | |||
| de09e56f67 | |||
| 17b520de12 | |||
| 0655e2e0a0 | |||
| bcf489f847 | |||
| adc2207249 | |||
| 1c205a96fb | |||
| 8e2bbbd570 | |||
| 13d1f1b48b | |||
| 7c4faf8b7a | |||
| 82be621850 | |||
| 265fb7c5f6 | |||
| cfa4d03e45 | |||
| f28c5a563a | |||
| 0455f08ec5 | |||
| 48193dce59 | |||
| ffe8d34acb | |||
| 29a278d0bc | |||
| 2fb9dfe909 | |||
| 8ca369f937 | |||
| 12c7fb7b27 | |||
| 2428347836 | |||
| 5aab13b28b | |||
| caa02141b8 | |||
| 74428d0727 | |||
| 0a15c0f1d8 | |||
| 3aa58398b7 | |||
| 9450f9ee1d | |||
| a1cea3b584 | |||
| 1b260aee52 | |||
| f2182428e5 | |||
| 996b8f67c5 | |||
| b297b2a48a | |||
| 74eab5bba9 | |||
| 87504f4d76 | |||
| 58a73dd62a | |||
| 83aca20ce5 | |||
| cd11d4c179 | |||
| e821fab4a3 | |||
| 82e6d544c8 | |||
| 70661da94d | |||
| 069a133546 | |||
| 6e49831ab3 | |||
| 81cd09d0f5 | |||
| 1aca81d15d | |||
| dc558df0b1 | |||
| da6f275adc | |||
| 633961e8a7 | |||
| 284d67b385 | |||
| 78fe87055c | |||
| d47bd198ca | |||
| e82d532f58 | |||
| 34e87c4b3f | |||
| a3193c56c1 | |||
| bf014ad781 | |||
| b1fc88d330 | |||
| 1e54a474b1 | |||
| bff7d9bab3 | |||
| 99d6ce5bdb | |||
| b0817d7f53 | |||
| 09622df94a | |||
| 7d0c97ae09 | |||
| ac64ad63bb | |||
| 166ed6e237 | |||
| 59d0ce1dfb | |||
| 1d44e0e6fa | |||
| 780c2857b1 | |||
| ca995648f4 | |||
| 0c03093173 | |||
| 4183cfb8a0 | |||
| 0837d17616 | |||
| 52667661c6 | |||
| 9adf5160fe | |||
| da796c305d | |||
| 953c84260f | |||
| c133445ff6 | |||
| f311fe201b | |||
| 5716b4eb72 | |||
| c89f66e4b8 | |||
| 503d79b320 | |||
| be58c8193e | |||
| 60ce16bf08 | |||
| 83ec53c614 | |||
| d7b251c5eb | |||
| 5fa140376e | |||
| 79b218ecf0 | |||
| e2442fe56d | |||
| d9e85b329f | |||
| 05f22ababc | |||
| 405ef7a3ba | |||
| b61162e475 | |||
| d7cc317208 | |||
| 9f988acb55 | |||
| eec57cb9b8 | |||
| f5eb868763 | |||
| 7f67d5a8a6 | |||
| 731b241b46 | |||
| 912fef88a0 | |||
| 96d22e54f8 | |||
| 03bf7b7d59 | |||
| 78016c0aeb | |||
| b0786647ed | |||
| 1b25d30ac6 | |||
| 362677acb8 | |||
| d37c156fa0 | |||
| e5ccb634e0 | |||
| cea6cbad50 | |||
| 68584e25c1 | |||
| fbab46eab0 | |||
| 2740c02f35 | |||
| ffb51fc18b | |||
| d19fb4f489 | |||
| 49bfe386a6 | |||
| 93ee579bbf | |||
| f9ed36a9f2 | |||
| e965f6a28f | |||
| d98932e183 | |||
| a91c2f7c7c | |||
| 5122d137b0 | |||
| 70d057e66c | |||
| b680b6026b | |||
| 0c3b16ca74 | |||
| 7be039a35f | |||
| 01ae56f944 | |||
| f1469b52f6 | |||
| ef118f23c2 | |||
| 964861bd6c | |||
| 32e9dbe1ed | |||
| 466fb4e42e | |||
| 8f923f7753 | |||
| 3bb8db45fd | |||
| 181317dbff | |||
| 73a7ef8b2c | |||
| 206e3468f4 | |||
| 77fde9d73f | |||
| c1d4bcbac3 | |||
| e98d0d88d9 | |||
| fc3a1dd93d | |||
| 897155e4c8 | |||
| 689f5ad634 | |||
| aa1ea74daa | |||
| 1b301e9c68 | |||
| c00b2247d4 | |||
| 88de5552a0 |
@@ -3,9 +3,7 @@ import { exec } from 'child_process';
|
||||
import gulp from 'gulp';
|
||||
import os from 'os';
|
||||
import nconf from 'nconf';
|
||||
import {
|
||||
pipe,
|
||||
} from './taskHelper';
|
||||
import { pipe } from './taskHelper';
|
||||
import {
|
||||
getDevelopmentConnectionUrl,
|
||||
getDefaultConnectionOptions,
|
||||
@@ -21,15 +19,16 @@ const TEST_DB_URI = nconf.get('TEST_DB_URI');
|
||||
const SANITY_TEST_COMMAND = 'npm run test:sanity';
|
||||
const COMMON_TEST_COMMAND = 'npm run test:common';
|
||||
const CONTENT_TEST_COMMAND = 'npm run test:content';
|
||||
const CONTENT_OPTIONS = { maxBuffer: 1024 * 500 };
|
||||
const LIMIT_MAX_BUFFER_OPTIONS = { maxBuffer: 1024 * 500 };
|
||||
|
||||
/* Helper methods for reporting test summary */
|
||||
/* Helper method for reporting test summary */
|
||||
const testResults = [];
|
||||
const testCount = (stdout, regexp) => {
|
||||
const match = stdout.match(regexp);
|
||||
return parseInt(match && (match[1] || 0), 10);
|
||||
};
|
||||
|
||||
/* Helper methods to correctly run child test processes */
|
||||
const testBin = (string, additionalEnvVariables = '') => {
|
||||
if (os.platform() === 'win32') {
|
||||
if (additionalEnvVariables !== '') {
|
||||
@@ -41,6 +40,15 @@ const testBin = (string, additionalEnvVariables = '') => {
|
||||
return `NODE_ENV=test ${additionalEnvVariables} ${string}`;
|
||||
};
|
||||
|
||||
function runInChildProcess (command, options = {}, envVariables = '') {
|
||||
return done => pipe(exec(testBin(command, envVariables), options, done));
|
||||
}
|
||||
|
||||
function integrationTestCommand (testDir, coverageDir) {
|
||||
return `istanbul cover --dir coverage/${coverageDir} --report lcovonly node_modules/mocha/bin/_mocha -- ${testDir} --recursive --require ./test/helpers/start-server`;
|
||||
}
|
||||
|
||||
/* Test task definitions */
|
||||
gulp.task('test:nodemon', gulp.series(done => {
|
||||
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
|
||||
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
|
||||
@@ -82,31 +90,9 @@ gulp.task('test:prepare', gulp.series(
|
||||
done => done(),
|
||||
));
|
||||
|
||||
gulp.task('test:sanity', cb => {
|
||||
const runner = exec(
|
||||
testBin(SANITY_TEST_COMMAND),
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
gulp.task('test:sanity', runInChildProcess(SANITY_TEST_COMMAND));
|
||||
|
||||
gulp.task('test:common', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(COMMON_TEST_COMMAND),
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
gulp.task('test:common', gulp.series('test:prepare:build', runInChildProcess(COMMON_TEST_COMMAND)));
|
||||
|
||||
gulp.task('test:common:clean', cb => {
|
||||
pipe(exec(testBin(COMMON_TEST_COMMAND), () => cb()));
|
||||
@@ -130,22 +116,11 @@ gulp.task('test:common:safe', gulp.series('test:prepare:build', cb => {
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:content', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
gulp.task('test:content', gulp.series('test:prepare:build',
|
||||
runInChildProcess(CONTENT_TEST_COMMAND, LIMIT_MAX_BUFFER_OPTIONS)));
|
||||
|
||||
gulp.task('test:content:clean', cb => {
|
||||
pipe(exec(testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, () => cb()));
|
||||
pipe(exec(testBin(CONTENT_TEST_COMMAND), LIMIT_MAX_BUFFER_OPTIONS, () => cb()));
|
||||
});
|
||||
|
||||
gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done()))));
|
||||
@@ -153,7 +128,7 @@ gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.wat
|
||||
gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
testResults.push({
|
||||
suite: 'Content Specs\t',
|
||||
@@ -167,76 +142,39 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:api:unit:run', done => {
|
||||
const runner = exec(
|
||||
testBin('istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
},
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
gulp.task('test:api:unit:run',
|
||||
runInChildProcess(integrationTestCommand('test/api/unit', 'coverage/api-unit')));
|
||||
|
||||
gulp.task('test:api:unit:watch', () => gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit:run', done => done())));
|
||||
|
||||
gulp.task('test:api-v3:integration', gulp.series('test:prepare:mongo', done => {
|
||||
const runner = exec(
|
||||
testBin('istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
},
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
}));
|
||||
gulp.task('test:api-v3:integration', gulp.series('test:prepare:mongo',
|
||||
runInChildProcess(
|
||||
integrationTestCommand('test/api/v3/integration', 'coverage/api-v3-integration'),
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
)));
|
||||
|
||||
gulp.task('test:api-v3:integration:watch', () => gulp.watch([
|
||||
'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js',
|
||||
'test/api/v3/integration/**/*',
|
||||
], gulp.series('test:api-v3:integration', done => done())));
|
||||
|
||||
gulp.task('test:api-v3:integration:separate-server', done => {
|
||||
const runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => done(err),
|
||||
);
|
||||
gulp.task('test:api-v3:integration:separate-server', runInChildProcess(
|
||||
'mocha test/api/v3/integration --recursive --require ./test/helpers/start-server',
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
'LOAD_SERVER=0',
|
||||
));
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
gulp.task('test:api-v4:integration', gulp.series('test:prepare:mongo',
|
||||
runInChildProcess(
|
||||
integrationTestCommand('test/api/v4', 'api-v4-integration'),
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
)));
|
||||
|
||||
gulp.task('test:api-v4:integration', gulp.series('test:prepare:mongo', done => {
|
||||
const runner = exec(
|
||||
testBin('istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
},
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:api-v4:integration:separate-server', done => {
|
||||
const runner = exec(
|
||||
testBin('mocha test/api/v4 --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => done(err),
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
gulp.task('test:api-v4:integration:separate-server', runInChildProcess(
|
||||
'mocha test/api/v4 --recursive --require ./test/helpers/start-server',
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
'LOAD_SERVER=0',
|
||||
));
|
||||
|
||||
gulp.task('test:api:unit', gulp.series(
|
||||
'test:prepare:mongo',
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20201020_pet_color_achievements';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (user && user.items && user.items.pets) {
|
||||
const pets = user.items.pets;
|
||||
if (pets['Wolf-Golden'] > 0
|
||||
&& pets['TigerCub-Skeleton'] > 0
|
||||
&& pets['PandaCub-Skeleton'] > 0
|
||||
&& pets['LionCub-Skeleton'] > 0
|
||||
&& pets['Fox-Skeleton'] > 0
|
||||
&& pets['FlyingPig-Skeleton'] > 0
|
||||
&& pets['Dragon-Skeleton'] > 0
|
||||
&& pets['Cactus-Skeleton'] > 0
|
||||
&& pets['BearCub-Skeleton'] > 0) {
|
||||
set['achievements.boneCollector'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (user && user.items && user.items.mounts) {
|
||||
const mounts = user.items.mounts;
|
||||
if (mounts['Wolf-Skeleton']
|
||||
&& mounts['TigerCub-Skeleton']
|
||||
&& mounts['PandaCub-Skeleton']
|
||||
&& mounts['LionCub-Skeleton']
|
||||
&& mounts['Fox-Skeleton']
|
||||
&& mounts['FlyingPig-Skeleton']
|
||||
&& mounts['Dragon-Skeleton']
|
||||
&& mounts['Cactus-Skeleton']
|
||||
&& mounts['BearCub-Skeleton'] ) {
|
||||
set['achievements.skeletonCrew'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2020-10-01') },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
items: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1]._id,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
const MIGRATION_NAME = 'tag-challenge-field-string2bool';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
export default async function processUsers () {
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
tags: {
|
||||
$elemMatch: {
|
||||
challenge: {
|
||||
$exists: true,
|
||||
$type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const users = await User.find(query)
|
||||
.sort({ _id: 1 })
|
||||
.limit(250)
|
||||
.select({ _id: 1, tags: 1 })
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
|
||||
async function updateUser (user) {
|
||||
count += 1;
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
let requiresUpdate = false;
|
||||
|
||||
if (user && user.tags) {
|
||||
user.tags.forEach(tag => {
|
||||
if (tag && typeof tag.challenge === 'string') {
|
||||
requiresUpdate = true;
|
||||
if (tag.challenge === 'true') {
|
||||
tag.challenge = true;
|
||||
} else if (tag.challenge === 'false') {
|
||||
tag.challenge = false;
|
||||
} else {
|
||||
tag.challenge = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (requiresUpdate) {
|
||||
const set = {
|
||||
migration: MIGRATION_NAME,
|
||||
tags: user.tags,
|
||||
};
|
||||
return User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.155.0",
|
||||
"version": "4.166.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -23,18 +23,18 @@
|
||||
}
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz",
|
||||
"integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==",
|
||||
"version": "7.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz",
|
||||
"integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.4",
|
||||
"@babel/generator": "^7.11.6",
|
||||
"@babel/helper-module-transforms": "^7.11.0",
|
||||
"@babel/helpers": "^7.10.4",
|
||||
"@babel/parser": "^7.11.4",
|
||||
"@babel/parser": "^7.11.5",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/traverse": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/traverse": "^7.11.5",
|
||||
"@babel/types": "^7.11.5",
|
||||
"convert-source-map": "^1.7.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.1",
|
||||
@@ -46,29 +46,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz",
|
||||
"integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==",
|
||||
"version": "7.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
|
||||
"integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/types": "^7.11.5",
|
||||
"jsesc": "^2.5.1",
|
||||
"source-map": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-transforms": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz",
|
||||
"integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.10.4",
|
||||
"@babel/helper-replace-supers": "^7.10.4",
|
||||
"@babel/helper-simple-access": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.11.0",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/types": "^7.11.0",
|
||||
"lodash": "^4.17.19"
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
|
||||
@@ -78,30 +64,30 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz",
|
||||
"integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA=="
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
|
||||
"integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q=="
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
|
||||
"integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
|
||||
"integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.0",
|
||||
"@babel/generator": "^7.11.5",
|
||||
"@babel/helper-function-name": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.11.0",
|
||||
"@babel/parser": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/parser": "^7.11.5",
|
||||
"@babel/types": "^7.11.5",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0",
|
||||
"lodash": "^4.17.19"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
|
||||
"integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
@@ -183,11 +169,10 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-explode-assignable-expression": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz",
|
||||
"integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==",
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz",
|
||||
"integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==",
|
||||
"requires": {
|
||||
"@babel/traverse": "^7.10.4",
|
||||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
},
|
||||
@@ -234,17 +219,37 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-transforms": {
|
||||
"version": "7.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz",
|
||||
"integrity": "sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA==",
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz",
|
||||
"integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.10.4",
|
||||
"@babel/helper-replace-supers": "^7.10.4",
|
||||
"@babel/helper-simple-access": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.11.0",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/types": "^7.10.5",
|
||||
"@babel/types": "^7.11.0",
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
|
||||
"integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.11.0"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
|
||||
"integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-optimise-call-expression": {
|
||||
@@ -269,14 +274,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-remap-async-to-generator": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz",
|
||||
"integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==",
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz",
|
||||
"integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.10.4",
|
||||
"@babel/helper-wrap-function": "^7.10.4",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/traverse": "^7.10.4",
|
||||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
},
|
||||
@@ -309,9 +313,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
|
||||
"integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
@@ -624,9 +628,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-block-scoping": {
|
||||
"version": "7.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz",
|
||||
"integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==",
|
||||
"version": "7.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz",
|
||||
"integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==",
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.10.4"
|
||||
}
|
||||
@@ -881,9 +885,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-env": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz",
|
||||
"integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz",
|
||||
"integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==",
|
||||
"requires": {
|
||||
"@babel/compat-data": "^7.11.0",
|
||||
"@babel/helper-compilation-targets": "^7.10.4",
|
||||
@@ -947,7 +951,7 @@
|
||||
"@babel/plugin-transform-unicode-escapes": "^7.10.4",
|
||||
"@babel/plugin-transform-unicode-regex": "^7.10.4",
|
||||
"@babel/preset-modules": "^0.1.3",
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/types": "^7.11.5",
|
||||
"browserslist": "^4.12.0",
|
||||
"core-js-compat": "^3.6.2",
|
||||
"invariant": "^2.2.2",
|
||||
@@ -956,9 +960,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
|
||||
"integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
@@ -968,9 +972,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-modules": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz",
|
||||
"integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==",
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz",
|
||||
"integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==",
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
|
||||
@@ -980,9 +984,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/register": {
|
||||
"version": "7.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.10.5.tgz",
|
||||
"integrity": "sha512-eYHdLv43nyvmPn9bfNfrcC4+iYNwdQ8Pxk1MFJuU/U5LpSYl/PH4dFMazCYZDFVi8ueG3shvO+AQfLrxpYulQw==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.11.5.tgz",
|
||||
"integrity": "sha512-CAml0ioKX+kOAvBQDHa/+t1fgOt3qkTIz0TrRtRAT6XY0m5qYZXR85k6/sLCNPMGhYDlCFHCYuU0ybTJbvlC6w==",
|
||||
"requires": {
|
||||
"find-cache-dir": "^2.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
@@ -992,9 +996,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.0.tgz",
|
||||
"integrity": "sha512-qArkXsjJq7H+T86WrIFV0Fnu/tNOkZ4cgXmjkzAu3b/58D5mFIO8JH/y77t7C9q0OdDRdh9s7Ue5GasYssxtXw==",
|
||||
"version": "7.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
|
||||
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
@@ -1046,9 +1050,9 @@
|
||||
}
|
||||
},
|
||||
"@google-cloud/common": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.3.2.tgz",
|
||||
"integrity": "sha512-W7JRLBEJWYtZQQuGQX06U6GBOSLrSrlvZxv6kGNwJtFrusu6AVgZltQ9Pajuz9Dh9aSXy9aTnBcyxn2/O0EGUw==",
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.4.0.tgz",
|
||||
"integrity": "sha512-bVMQlK4aZEeopo2oJwDUJiBhPVjRRQHfFCCv9JowmKS3L//PBHNDJzC/LxJixGZEU3fh3YXkUwm67JZ5TBCCNQ==",
|
||||
"requires": {
|
||||
"@google-cloud/projectify": "^2.0.0",
|
||||
"@google-cloud/promisify": "^2.0.0",
|
||||
@@ -1067,14 +1071,14 @@
|
||||
"integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ=="
|
||||
},
|
||||
"@google-cloud/promisify": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.2.tgz",
|
||||
"integrity": "sha512-EvuabjzzZ9E2+OaYf+7P9OAiiwbTxKYL0oGLnREQd+Su2NTQBpomkdlkBowFvyWsaV0d1sSGxrKpSNcrhPqbxg=="
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
|
||||
"integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw=="
|
||||
},
|
||||
"@google-cloud/trace-agent": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-5.1.0.tgz",
|
||||
"integrity": "sha512-OPtIXET63QI1ved7Dyxmrx6o1tNJ++aX+WPm0OfNtYFCn8BPzVMBw/ZCsRmwL6nL+jY1jJ0N9teUO7abCvwpfg==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-5.1.1.tgz",
|
||||
"integrity": "sha512-YTcK0RLN90pLCprg0XC8uV4oAVd79vsXhkcxmEVwiOOYjUDvSrAhb7y/0SY606zgfhJHmUTNb/fZSWEtZP/slQ==",
|
||||
"requires": {
|
||||
"@google-cloud/common": "^3.0.0",
|
||||
"@opencensus/propagation-stackdriver": "0.0.22",
|
||||
@@ -1154,9 +1158,9 @@
|
||||
}
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.0.tgz",
|
||||
"integrity": "sha512-n4J+zu52VdY43kdi/XdI9DzuMr1Mur8zFL5ZRG2opCans9aiFwkPxHYFEb5Xgy7n1Z4K6WfI4FpqUqsh3E8BPQ=="
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz",
|
||||
"integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ=="
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.8.1",
|
||||
@@ -1187,9 +1191,9 @@
|
||||
}
|
||||
},
|
||||
"@sinonjs/samsam": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.1.0.tgz",
|
||||
"integrity": "sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.2.0.tgz",
|
||||
"integrity": "sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "^1.6.0",
|
||||
@@ -1308,9 +1312,9 @@
|
||||
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
|
||||
"integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
|
||||
"version": "4.17.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz",
|
||||
"integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==",
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
@@ -1328,9 +1332,9 @@
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz",
|
||||
"integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==",
|
||||
"version": "4.17.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz",
|
||||
"integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
@@ -1399,9 +1403,9 @@
|
||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
|
||||
},
|
||||
"@types/mongodb": {
|
||||
"version": "3.5.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.25.tgz",
|
||||
"integrity": "sha512-2H/Owt+pHCl9YmBOYnXc3VdnxejJEjVdH+QCWL5ZAfPehEn3evygKBX3/vKRv7aTwfNbUd0E5vjJdQklH/9a6w==",
|
||||
"version": "3.5.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.27.tgz",
|
||||
"integrity": "sha512-1jxKDgdfJEOO9zp+lv43p8jOqRs02xPrdUTzAZIVK9tVEySfCEmktL2jEu9A3wOBEOs18yKzpVIKUh8b8ALk3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/bson": "*",
|
||||
@@ -1441,9 +1445,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz",
|
||||
"integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ=="
|
||||
"version": "6.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
|
||||
"integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ=="
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
@@ -1816,6 +1820,16 @@
|
||||
"axios": "^0.19.0",
|
||||
"express": "^4.17.1",
|
||||
"jsonwebtoken": "^8.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
|
||||
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aproba": {
|
||||
@@ -2814,9 +2828,9 @@
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
|
||||
"integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==",
|
||||
"requires": {
|
||||
"readable-stream": "^2.3.5",
|
||||
"safe-buffer": "^5.1.1"
|
||||
@@ -2992,14 +3006,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz",
|
||||
"integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==",
|
||||
"version": "4.14.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.1.tgz",
|
||||
"integrity": "sha512-zyBTIHydW37pnb63c7fHFXUG6EcqWOqoMdDx6cdyaDFriZ20EoVxcE95S54N+heRqY8m8IUgB5zYta/gCwSaaA==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001093",
|
||||
"electron-to-chromium": "^1.3.488",
|
||||
"escalade": "^3.0.1",
|
||||
"node-releases": "^1.1.58"
|
||||
"caniuse-lite": "^1.0.30001124",
|
||||
"electron-to-chromium": "^1.3.562",
|
||||
"escalade": "^3.0.2",
|
||||
"node-releases": "^1.1.60"
|
||||
}
|
||||
},
|
||||
"bson": {
|
||||
@@ -3152,9 +3166,9 @@
|
||||
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001109",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001109.tgz",
|
||||
"integrity": "sha512-4JIXRodHzdS3HdK8nSgIqXYLExOvG+D2/EenSvcub2Kp3QEADjo2v2oUn5g0n0D+UNwG9BtwKOyGcSq2qvQXvQ=="
|
||||
"version": "1.0.30001124",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001124.tgz",
|
||||
"integrity": "sha512-zQW8V3CdND7GHRH6rxm6s59Ww4g/qGWTheoboW9nfeMg7sUoopIfKCcNZUjwYRCOrvereh3kwDpZj4VLQ7zGtA=="
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
@@ -4613,9 +4627,9 @@
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.516",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.516.tgz",
|
||||
"integrity": "sha512-WDM5AAQdOrvLqSX8g3Zd5AujBXfMxf96oeZkff0U2HF5op3tjShE+on2yay3r1UD4M9I3p0iHpAS4+yV8U8A9A=="
|
||||
"version": "1.3.562",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.562.tgz",
|
||||
"integrity": "sha512-WhRe6liQ2q/w1MZc8mD8INkenHivuHdrr4r5EQHNomy3NJux+incP6M6lDMd0paShP3MD0WGe5R1TWmEClf+Bg=="
|
||||
},
|
||||
"emitter-listener": {
|
||||
"version": "1.1.2",
|
||||
@@ -6136,9 +6150,9 @@
|
||||
}
|
||||
},
|
||||
"gaxios": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.0.4.tgz",
|
||||
"integrity": "sha512-97NmFuMETFQh6gqPUxkqjxRMjmY8aRKRMphIkgO/b90AbCt5wAVuXsp8oWjIXlLN2pIK/fsXD8edcM7ULkFMLg==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz",
|
||||
"integrity": "sha512-+6WPeVzPvOshftpxJwRi2Ozez80tn/hdtOUag7+gajDHRJvAblKxTFSSMPtr2hmnLy7p0mvYz0rMXLBl8pSO7Q==",
|
||||
"requires": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"extend": "^3.0.2",
|
||||
@@ -6148,9 +6162,9 @@
|
||||
}
|
||||
},
|
||||
"gcp-metadata": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.1.4.tgz",
|
||||
"integrity": "sha512-5J/GIH0yWt/56R3dNaNWPGQ/zXsZOddYECfJaqxFWgrZ9HC2Kvc5vl9upOgUUHKzURjAVf2N+f6tEJiojqXUuA==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.0.tgz",
|
||||
"integrity": "sha512-vQZD57cQkqIA6YPGXM/zc+PIZfNRFdukWGsGZ5+LcJzesi5xp6Gn7a02wRJi4eXPyArNMIYpPET4QMxGqtlk6Q==",
|
||||
"requires": {
|
||||
"gaxios": "^3.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
@@ -6661,9 +6675,9 @@
|
||||
}
|
||||
},
|
||||
"google-auth-library": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.5.tgz",
|
||||
"integrity": "sha512-Wj31lfTm2yR4g3WfOOB1Am1tt478Xq9OvzTPQJi17tn/I9R5IcsxjANBsE93nYmxYxtwDedhOdIb8l3vSPG49Q==",
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.6.tgz",
|
||||
"integrity": "sha512-fWYdRdg55HSJoRq9k568jJA1lrhg9i2xgfhVIMJbskUmbDpJGHsbv9l41DGhCDXM21F9Kn4kUwdysgxSYBYJUw==",
|
||||
"requires": {
|
||||
"arrify": "^2.0.0",
|
||||
"base64-js": "^1.3.0",
|
||||
@@ -6677,26 +6691,26 @@
|
||||
}
|
||||
},
|
||||
"google-p12-pem": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.2.tgz",
|
||||
"integrity": "sha512-tbjzndQvSIHGBLzHnhDs3cL4RBjLbLXc2pYvGH+imGVu5b4RMAttUTdnmW2UH0t11QeBTXZ7wlXPS7hrypO/tg==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
|
||||
"integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
|
||||
"requires": {
|
||||
"node-forge": "^0.9.0"
|
||||
"node-forge": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "11.5.2",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.5.2.tgz",
|
||||
"integrity": "sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww==",
|
||||
"version": "11.7.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.7.0.tgz",
|
||||
"integrity": "sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg==",
|
||||
"requires": {
|
||||
"@sindresorhus/is": "^3.0.0",
|
||||
"@sindresorhus/is": "^3.1.1",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.1",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"responselike": "^2.0.0"
|
||||
@@ -6751,9 +6765,9 @@
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
|
||||
},
|
||||
"keyv": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz",
|
||||
"integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
|
||||
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
|
||||
"requires": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
@@ -6801,9 +6815,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"gtoken": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.2.tgz",
|
||||
"integrity": "sha512-lull70rHCTvRTmAt+R/6W5bTtx4MjHku7AwJwK5fGqhOmygcZud0nrZcX+QUNfBJwCzqy7S5i1Bc4NYnr5PMMA==",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.3.tgz",
|
||||
"integrity": "sha512-Nyd1wZCMRc2dj/mAD0LlfQLcAO06uKdpKJXvK85SGrF5+5+Bpfil9u/2aw35ltvEHjvl0h5FMKN5knEU+9JrOg==",
|
||||
"requires": {
|
||||
"gaxios": "^3.0.0",
|
||||
"google-p12-pem": "^3.0.0",
|
||||
@@ -7045,9 +7059,9 @@
|
||||
}
|
||||
},
|
||||
"habitica-markdown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-2.0.2.tgz",
|
||||
"integrity": "sha512-GGzWuDKbFBMI+GqmQnSPUD5Zi/WPZDDuC2dC2La6uvu1zVNW7uHyQG0lxwCO43t5EdYXtOoOPin1rhhLdTuZfA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-3.0.0.tgz",
|
||||
"integrity": "sha512-rw1LJ5Vsjx8sfjNa4e2wFuZf5eqqyb5/kfZXPxqfMMgJCCgIhWStDqY3nIclnpGWpemlKd+qbdh2rLiLgm9kng==",
|
||||
"requires": {
|
||||
"habitica-markdown-emoji": "1.2.4",
|
||||
"markdown-it": "10.0.0",
|
||||
@@ -7411,9 +7425,9 @@
|
||||
}
|
||||
},
|
||||
"image-size": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz",
|
||||
"integrity": "sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==",
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.9.1.tgz",
|
||||
"integrity": "sha512-yBo6xGGjiWtApYroCGR9wTvaIgande5vmAfTYIld5ss5kN4tyDG5lrW1qGomOXgB05ss7GLXLpDYXEiFqSqkzg==",
|
||||
"requires": {
|
||||
"queue": "6.0.1"
|
||||
}
|
||||
@@ -8348,9 +8362,9 @@
|
||||
"integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo="
|
||||
},
|
||||
"just-extend": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz",
|
||||
"integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz",
|
||||
"integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==",
|
||||
"dev": true
|
||||
},
|
||||
"jwa": {
|
||||
@@ -8364,13 +8378,15 @@
|
||||
}
|
||||
},
|
||||
"jwks-rsa": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.9.0.tgz",
|
||||
"integrity": "sha512-UPCfQQg0s2kF2Ju6UFJrQH73f7MaVN/hKBnYBYOp+X9KN4y6TLChhLtaXS5nRKbZqshwVdrZ9OY63m/Q9CLqcg==",
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.10.1.tgz",
|
||||
"integrity": "sha512-UmjOsATVu7eQr17wbBCS+BSoz5LFtl57PtNXHbHFeT1WKomHykCHtn7c8inWVI7tpnsy6CZ1KOMJTgipFwXPig==",
|
||||
"requires": {
|
||||
"@types/express-jwt": "0.0.42",
|
||||
"axios": "^0.19.2",
|
||||
"debug": "^4.1.0",
|
||||
"http-proxy-agent": "^4.0.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"limiter": "^1.1.5",
|
||||
"lru-memoizer": "^2.1.2",
|
||||
@@ -9330,9 +9346,9 @@
|
||||
"integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is="
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.27.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||
},
|
||||
"moment-recur": {
|
||||
"version": "1.0.7",
|
||||
@@ -9343,9 +9359,9 @@
|
||||
}
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "3.5.9",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.9.tgz",
|
||||
"integrity": "sha512-vXHBY1CsGYcEPoVWhwgxIBeWqP3dSu9RuRDsoLRPTITrcrgm1f0Ubu1xqF9ozMwv53agmEiZm0YGo+7WL3Nbug==",
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.1.tgz",
|
||||
"integrity": "sha512-uH76Zzr5wPptnjEKJRQnwTsomtFOU/kQEU8a9hKHr2M7y9qVk7Q4Pkv0EQVp88742z9+RwvsdTw6dRjDZCNu1g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^2.2.0",
|
||||
@@ -9357,9 +9373,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
|
||||
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "^2.3.5",
|
||||
@@ -9430,13 +9446,13 @@
|
||||
}
|
||||
},
|
||||
"mongoose": {
|
||||
"version": "5.9.29",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.29.tgz",
|
||||
"integrity": "sha512-lNkWoGdXkI7no3cTXHnHYcAhjB0j/kM5cmdLO07axcr8DVtTWz0ZOHsWMikUkWWbMmw4aOGejwR4WT3WvpJk1A==",
|
||||
"version": "5.10.9",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.9.tgz",
|
||||
"integrity": "sha512-7dkr1d6Uyk87hELzoc6B7Zo7kkPTx8rKummk51Y0je2V2Ttsw0KFPwTp1G8JIbBta7Wpw8j15PJi0d33Ode2nw==",
|
||||
"requires": {
|
||||
"bson": "^1.1.4",
|
||||
"kareem": "2.3.1",
|
||||
"mongodb": "3.5.10",
|
||||
"mongodb": "3.6.2",
|
||||
"mongoose-legacy-pluralize": "1.0.2",
|
||||
"mpath": "0.7.0",
|
||||
"mquery": "3.2.2",
|
||||
@@ -9448,20 +9464,20 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
|
||||
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||
"requires": {
|
||||
"readable-stream": "^2.3.5",
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "3.5.10",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.10.tgz",
|
||||
"integrity": "sha512-p/C48UvTU/dr/PQEDKfb9DsCVDJWXGmdJNFC+u5FPmTQVtog69X6D8vrWHz+sJx1zJnd96sjdh9ueo7bx2ILTw==",
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz",
|
||||
"integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==",
|
||||
"requires": {
|
||||
"bl": "^2.2.0",
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
"denque": "^1.4.1",
|
||||
"require_optional": "^1.0.1",
|
||||
@@ -9518,9 +9534,9 @@
|
||||
"integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ=="
|
||||
},
|
||||
"monk": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/monk/-/monk-7.3.1.tgz",
|
||||
"integrity": "sha512-09+K52A7vfdH7ZxyZyXS6E5C3LUMxXvd8ZLAYLJclXVbqxb6dsmA1ArIWGblhGFEgYOaTI9MOfWO3uE4b5IQkA==",
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/monk/-/monk-7.3.2.tgz",
|
||||
"integrity": "sha512-rf44p/l4HRDnrQXWci9SOz5uFAEQsHERVmE0tgya8OjOwqzeztE1sIcZ7JdxqpVk0Y1QyzApZWyBFWfvFB6sfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/mongodb": "^3.5.25",
|
||||
@@ -9885,9 +9901,9 @@
|
||||
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz",
|
||||
"integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==",
|
||||
"version": "2.19.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.1.tgz",
|
||||
"integrity": "sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
@@ -9904,14 +9920,14 @@
|
||||
"integrity": "sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
|
||||
"integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ=="
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
|
||||
},
|
||||
"node-gcm": {
|
||||
"version": "1.0.3",
|
||||
@@ -11712,12 +11728,11 @@
|
||||
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
|
||||
},
|
||||
"retry-request": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz",
|
||||
"integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz",
|
||||
"integrity": "sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"through2": "^3.0.1"
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"reusify": {
|
||||
@@ -12058,9 +12073,9 @@
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"simple-get": {
|
||||
@@ -12100,15 +12115,15 @@
|
||||
}
|
||||
},
|
||||
"sinon": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.3.tgz",
|
||||
"integrity": "sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==",
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.0.tgz",
|
||||
"integrity": "sha512-eSNXz1XMcGEMHw08NJXSyTHIu6qTCOiN8x9ODACmZpNQpr0aXTBXBnI4xTzQzR+TEpOmLiKowGf9flCuKIzsbw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "^1.7.2",
|
||||
"@sinonjs/commons": "^1.8.1",
|
||||
"@sinonjs/fake-timers": "^6.0.1",
|
||||
"@sinonjs/formatio": "^5.0.1",
|
||||
"@sinonjs/samsam": "^5.1.0",
|
||||
"@sinonjs/samsam": "^5.2.0",
|
||||
"diff": "^4.0.2",
|
||||
"nise": "^4.0.4",
|
||||
"supports-color": "^7.1.0"
|
||||
@@ -12127,9 +12142,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
@@ -12813,9 +12828,9 @@
|
||||
"integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls="
|
||||
},
|
||||
"superagent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.0.0.tgz",
|
||||
"integrity": "sha512-gBiyDSUR3zbYO8za+MudSNxMFSOhKcZfQ1Anya1DWzk9R32zl++cYUFHXzP3VnyvQO8h/1/uPPA9FUDDtE+qdA==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz",
|
||||
"integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==",
|
||||
"requires": {
|
||||
"component-emitter": "^1.3.0",
|
||||
"cookiejar": "^2.1.2",
|
||||
@@ -12966,9 +12981,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
|
||||
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
@@ -12977,12 +12992,12 @@
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz",
|
||||
"integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
|
||||
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^4.0.1",
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
@@ -13088,15 +13103,6 @@
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"through2": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
|
||||
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "2 || 3"
|
||||
}
|
||||
},
|
||||
"through2-concurrent": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/through2-concurrent/-/through2-concurrent-2.0.0.tgz",
|
||||
@@ -13834,9 +13840,9 @@
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
|
||||
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.1",
|
||||
@@ -13861,9 +13867,9 @@
|
||||
}
|
||||
},
|
||||
"validator": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz",
|
||||
"integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw=="
|
||||
"version": "13.1.17",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.17.tgz",
|
||||
"integrity": "sha512-zL5QBoemJ3jYFb2/j38y7ljhwYGXVLUp8H6W1nVxadnAOvUOytec+L7BHh1oBQ82/TzWXHd+GSaxUWp4lROkLg=="
|
||||
},
|
||||
"value-or-function": {
|
||||
"version": "3.0.0",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.155.0",
|
||||
"version": "4.166.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.4",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"@babel/register": "^7.10.3",
|
||||
"@google-cloud/trace-agent": "^5.1.0",
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@babel/register": "^7.11.5",
|
||||
"@google-cloud/trace-agent": "^5.1.1",
|
||||
"@slack/client": "^4.12.0",
|
||||
"accepts": "^1.3.5",
|
||||
"amazon-payments": "^0.2.8",
|
||||
@@ -30,25 +30,25 @@
|
||||
"express-basic-auth": "^1.1.5",
|
||||
"express-validator": "^5.2.0",
|
||||
"glob": "^7.1.6",
|
||||
"got": "^11.5.2",
|
||||
"got": "^11.7.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-imagemin": "^7.1.0",
|
||||
"gulp-nodemon": "^2.5.0",
|
||||
"gulp.spritesmith": "^6.9.0",
|
||||
"habitica-markdown": "^2.0.2",
|
||||
"habitica-markdown": "^3.0.0",
|
||||
"helmet": "^3.23.3",
|
||||
"image-size": "^0.8.3",
|
||||
"image-size": "^0.9.1",
|
||||
"in-app-purchase": "^1.11.3",
|
||||
"js2xmlparser": "^4.0.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwks-rsa": "^1.9.0",
|
||||
"jwks-rsa": "^1.10.1",
|
||||
"lodash": "^4.17.20",
|
||||
"merge-stream": "^2.0.0",
|
||||
"method-override": "^3.0.0",
|
||||
"moment": "^2.27.0",
|
||||
"moment": "^2.29.1",
|
||||
"moment-recur": "^1.0.7",
|
||||
"mongoose": "^5.9.29",
|
||||
"mongoose": "^5.10.9",
|
||||
"morgan": "^1.10.0",
|
||||
"nconf": "^0.10.0",
|
||||
"node-gcm": "^1.0.3",
|
||||
@@ -67,11 +67,11 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"short-uuid": "^3.0.0",
|
||||
"stripe": "^7.15.0",
|
||||
"superagent": "^6.0.0",
|
||||
"superagent": "^6.1.0",
|
||||
"universal-analytics": "^0.4.23",
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^8.3.0",
|
||||
"validator": "^13.1.1",
|
||||
"uuid": "^8.3.1",
|
||||
"validator": "^13.1.17",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"winston": "^3.3.3",
|
||||
"winston-loggly-bulk": "^3.1.1",
|
||||
@@ -118,10 +118,10 @@
|
||||
"expect.js": "^0.3.1",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
"mocha": "^5.1.1",
|
||||
"monk": "^7.3.1",
|
||||
"monk": "^7.3.2",
|
||||
"require-again": "^2.0.0",
|
||||
"run-rs": "^0.6.2",
|
||||
"sinon": "^9.0.3",
|
||||
"sinon": "^9.2.0",
|
||||
"sinon-chai": "^3.5.0",
|
||||
"sinon-stub-promise": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -171,28 +171,32 @@ describe('highlightMentions', () => {
|
||||
it('github issue 12118, method crashes when square brackets are used', async () => {
|
||||
const text = '[test]';
|
||||
|
||||
let err;
|
||||
const result = await highlightMentions(text);
|
||||
|
||||
try {
|
||||
await highlightMentions(text);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
expect(err).to.be.undefined;
|
||||
expect(result[0]).to.equal(text);
|
||||
});
|
||||
|
||||
it('github issue 12138, method crashes when regex chars are used in code block', async () => {
|
||||
const text = '`[test]`';
|
||||
|
||||
let err;
|
||||
const result = await highlightMentions(text);
|
||||
|
||||
try {
|
||||
await highlightMentions(text);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
expect(result[0]).to.equal(text);
|
||||
});
|
||||
|
||||
expect(err).to.be.undefined;
|
||||
it('github issue 12586, method crashes when empty link is used', async () => {
|
||||
const text = '[]()';
|
||||
|
||||
const result = await highlightMentions(text);
|
||||
|
||||
expect(result[0]).to.equal(text);
|
||||
});
|
||||
|
||||
it('github issue 12586, method crashes when link without title is used', async () => {
|
||||
const text = '[](www.google.com)';
|
||||
|
||||
const result = await highlightMentions(text);
|
||||
|
||||
expect(result[0]).to.equal(text);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,13 +2,14 @@ import { model as User } from '../../../../../../website/server/models/user';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../website/common';
|
||||
import apiError from '../../../../../../website/server/libs/apiError';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('Amazon Payments - Checkout', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user; let orderReferenceId; let
|
||||
headers;
|
||||
headers; const gemsBlockKey = '21gems'; const gemsBlock = common.content.gems[gemsBlockKey];
|
||||
let setOrderReferenceDetailsSpy;
|
||||
let confirmOrderReferenceSpy;
|
||||
let authorizeSpy;
|
||||
@@ -16,7 +17,7 @@ describe('Amazon Payments - Checkout', () => {
|
||||
|
||||
let paymentBuyGemsStub;
|
||||
let paymentCreateSubscritionStub;
|
||||
let amount = 5;
|
||||
let amount = gemsBlock.price / 100;
|
||||
|
||||
function expectOrderReferenceSpy () {
|
||||
expect(setOrderReferenceDetailsSpy).to.be.calledOnce;
|
||||
@@ -107,13 +108,20 @@ describe('Amazon Payments - Checkout', () => {
|
||||
paymentMethod,
|
||||
headers,
|
||||
};
|
||||
if (gift) expectedArgs.gift = gift;
|
||||
if (gift) {
|
||||
expectedArgs.gift = gift;
|
||||
expectedArgs.gemsBlock = undefined;
|
||||
} else {
|
||||
expectedArgs.gemsBlock = gemsBlock;
|
||||
}
|
||||
expect(paymentBuyGemsStub).to.be.calledWith(expectedArgs);
|
||||
}
|
||||
|
||||
it('should purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
await amzLib.checkout({ user, orderReferenceId, headers });
|
||||
await amzLib.checkout({
|
||||
user, orderReferenceId, headers, gemsBlock: gemsBlockKey,
|
||||
});
|
||||
|
||||
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD);
|
||||
expectAmazonStubs();
|
||||
@@ -144,7 +152,9 @@ describe('Amazon Payments - Checkout', () => {
|
||||
|
||||
it('should error if user cannot get gems gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(false);
|
||||
await expect(amzLib.checkout({ user, orderReferenceId, headers }))
|
||||
await expect(amzLib.checkout({
|
||||
user, orderReferenceId, headers, gemsBlock: gemsBlockKey,
|
||||
}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
@@ -153,6 +163,17 @@ describe('Amazon Payments - Checkout', () => {
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('should error if gems block is not valid', async () => {
|
||||
await expect(amzLib.checkout({
|
||||
user, orderReferenceId, headers, gemsBlock: 'invalid',
|
||||
}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: apiError('invalidGemsBlock'),
|
||||
name: 'BadRequest',
|
||||
});
|
||||
});
|
||||
|
||||
it('should gift gems', async () => {
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
@@ -195,6 +216,7 @@ describe('Amazon Payments - Checkout', () => {
|
||||
paymentMethod: amzLib.constants.PAYMENT_METHOD_GIFT,
|
||||
headers,
|
||||
gift,
|
||||
gemsBlock: undefined,
|
||||
});
|
||||
expectAmazonStubs();
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ import applePayments from '../../../../../website/server/libs/payments/apple';
|
||||
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import common from '../../../../../website/common';
|
||||
import { mockFindById, restoreFindById } from '../../../../helpers/mongoose.helper';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
@@ -84,7 +83,7 @@ describe('Apple Payments', () => {
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('errors if amount does not exist', async () => {
|
||||
it('errors if gemsBlock does not exist', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
@@ -106,23 +105,23 @@ describe('Apple Payments', () => {
|
||||
const gemsCanPurchase = [
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.4gems',
|
||||
amount: 1,
|
||||
gemsBlock: '4gems',
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.20gems',
|
||||
amount: 5.25,
|
||||
gemsBlock: '21gems',
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.21gems',
|
||||
amount: 5.25,
|
||||
gemsBlock: '21gems',
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.42gems',
|
||||
amount: 10.5,
|
||||
gemsBlock: '42gems',
|
||||
},
|
||||
{
|
||||
productId: 'com.habitrpg.ios.Habitica.84gems',
|
||||
amount: 21,
|
||||
gemsBlock: '84gems',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -149,8 +148,9 @@ describe('Apple Payments', () => {
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
amount: gemTest.amount,
|
||||
gemsBlock: common.content.gems[gemTest.gemsBlock],
|
||||
headers,
|
||||
gift: undefined,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
@@ -161,8 +161,6 @@ describe('Apple Payments', () => {
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
|
||||
mockFindById(receivingUser);
|
||||
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
@@ -184,12 +182,17 @@ describe('Apple Payments', () => {
|
||||
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user: receivingUser,
|
||||
user,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
amount: gemsCanPurchase[0].amount,
|
||||
headers,
|
||||
gift: {
|
||||
type: 'gems',
|
||||
gems: { amount: 4 },
|
||||
member: sinon.match({ _id: receivingUser._id }),
|
||||
uuid: receivingUser._id,
|
||||
},
|
||||
gemsBlock: common.content.gems['4gems'],
|
||||
});
|
||||
restoreFindById();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import common from '../../../../../website/common';
|
||||
import { getGemsBlock } from '../../../../../website/server/libs/payments/gems';
|
||||
|
||||
describe('payments/gems', () => {
|
||||
describe('#getGemsBlock', () => {
|
||||
it('throws an error if the gem block key is invalid', () => {
|
||||
expect(() => getGemsBlock('invalid')).to.throw;
|
||||
});
|
||||
|
||||
it('returns the gem block for the given key', () => {
|
||||
expect(getGemsBlock('21gems')).to.equal(common.content.gems['21gems']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,6 @@ import googlePayments from '../../../../../website/server/libs/payments/google';
|
||||
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import common from '../../../../../website/common';
|
||||
import { mockFindById, restoreFindById } from '../../../../helpers/mongoose.helper';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
@@ -14,7 +13,7 @@ describe('Google Payments', () => {
|
||||
|
||||
describe('verifyGemPurchase', () => {
|
||||
let sku; let user; let token; let receipt; let signature; let
|
||||
headers;
|
||||
headers; const gemsBlock = common.content.gems['21gems'];
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub; let
|
||||
paymentBuyGemsStub;
|
||||
|
||||
@@ -103,8 +102,9 @@ describe('Google Payments', () => {
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user,
|
||||
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||
amount: 5.25,
|
||||
gemsBlock,
|
||||
headers,
|
||||
gift: undefined,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
@@ -114,8 +114,6 @@ describe('Google Payments', () => {
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
|
||||
mockFindById(receivingUser);
|
||||
|
||||
const gift = { uuid: receivingUser._id };
|
||||
await googlePayments.verifyGemPurchase({
|
||||
user, gift, receipt, signature, headers,
|
||||
@@ -132,12 +130,17 @@ describe('Google Payments', () => {
|
||||
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user: receivingUser,
|
||||
user,
|
||||
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||
amount: 5.25,
|
||||
gemsBlock,
|
||||
headers,
|
||||
gift: {
|
||||
type: 'gems',
|
||||
gems: { amount: 21 },
|
||||
member: sinon.match({ _id: receivingUser._id }),
|
||||
uuid: receivingUser._id,
|
||||
},
|
||||
});
|
||||
restoreFindById();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import moment from 'moment';
|
||||
|
||||
import * as sender from '../../../../../website/server/libs/email';
|
||||
import common from '../../../../../website/common';
|
||||
import api from '../../../../../website/server/libs/payments/payments';
|
||||
import * as analytics from '../../../../../website/server/libs/analyticsService';
|
||||
import * as notifications from '../../../../../website/server/libs/pushNotifications';
|
||||
@@ -9,6 +10,7 @@ import { translate as t } from '../../../../helpers/api-integration/v3';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../helpers/api-unit.helper';
|
||||
import * as worldState from '../../../../../website/server/libs/worldState';
|
||||
|
||||
describe('payments/index', () => {
|
||||
let user; let group; let data; let
|
||||
@@ -246,6 +248,7 @@ describe('payments/index', () => {
|
||||
quantity: 1,
|
||||
gift: true,
|
||||
purchaseValue: 15,
|
||||
firstPurchase: true,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
@@ -343,6 +346,7 @@ describe('payments/index', () => {
|
||||
quantity: 1,
|
||||
gift: false,
|
||||
purchaseValue: 15,
|
||||
firstPurchase: true,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
@@ -419,10 +423,22 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
context('Mystery Items', () => {
|
||||
it('awards mystery items when within the timeframe for a mystery item', async () => {
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
let clock;
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
|
||||
beforeEach(() => {
|
||||
const mayMysteryItemTimeframe = new Date(2016, 4, 31); // May 31st 2016
|
||||
clock = sinon.useFakeTimers({
|
||||
now: mayMysteryItemTimeframe,
|
||||
toFake: ['Date'],
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (clock) clock.restore();
|
||||
});
|
||||
|
||||
it('awards mystery items when within the timeframe for a mystery item', async () => {
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
|
||||
const oldNotificationsCount = user.notifications.length;
|
||||
@@ -435,14 +451,9 @@ describe('payments/index', () => {
|
||||
expect(user.purchased.plan.mysteryItems).to.include('head_mystery_201605');
|
||||
expect(user.notifications.length).to.equal(oldNotificationsCount + 1);
|
||||
expect(user.notifications[0].type).to.equal('NEW_MYSTERY_ITEMS');
|
||||
|
||||
fakeClock.restore();
|
||||
});
|
||||
|
||||
it('does not award mystery item when user already owns the item', async () => {
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
user.items.gear.owned[mayMysteryItem] = true;
|
||||
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
@@ -451,14 +462,9 @@ describe('payments/index', () => {
|
||||
|
||||
expect(user.purchased.plan.mysteryItems).to.have.a.lengthOf(1);
|
||||
expect(user.purchased.plan.mysteryItems).to.include('head_mystery_201605');
|
||||
|
||||
fakeClock.restore();
|
||||
});
|
||||
|
||||
it('does not award mystery item when user already has the item in the mystery box', async () => {
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
user.purchased.plan.mysteryItems = [mayMysteryItem];
|
||||
|
||||
sandbox.spy(user.purchased.plan.mysteryItems, 'push');
|
||||
@@ -468,8 +474,6 @@ describe('payments/index', () => {
|
||||
|
||||
expect(user.purchased.plan.mysteryItems.push).to.be.calledOnce;
|
||||
expect(user.purchased.plan.mysteryItems.push).to.be.calledWith('head_mystery_201605');
|
||||
|
||||
fakeClock.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -555,6 +559,7 @@ describe('payments/index', () => {
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
user,
|
||||
gemsBlock: common.content.gems['21gems'],
|
||||
paymentMethod: 'payment',
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
@@ -564,22 +569,6 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
context('Self Purchase', () => {
|
||||
it('amount property defaults to 5', async () => {
|
||||
expect(user.balance).to.eql(0);
|
||||
|
||||
await api.buyGems(data);
|
||||
|
||||
expect(user.balance).to.eql(5);
|
||||
});
|
||||
|
||||
it('can set amount that is purchased', async () => {
|
||||
data.amount = 13;
|
||||
|
||||
await api.buyGems(data);
|
||||
|
||||
expect(user.balance).to.eql(13);
|
||||
});
|
||||
|
||||
it('sends a donation email', async () => {
|
||||
await api.buyGems(data);
|
||||
|
||||
@@ -588,6 +577,51 @@ describe('payments/index', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('No Active Promotion', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(worldState, 'getCurrentEvent').returns(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
worldState.getCurrentEvent.restore();
|
||||
});
|
||||
|
||||
it('does not apply a discount', async () => {
|
||||
const balanceBefore = user.balance;
|
||||
|
||||
await api.buyGems(data);
|
||||
|
||||
const balanceAfter = user.balance;
|
||||
const balanceDiff = balanceAfter - balanceBefore;
|
||||
|
||||
expect(balanceDiff * 4).to.eql(21);
|
||||
});
|
||||
});
|
||||
|
||||
context('Active Promotion', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(worldState, 'getCurrentEvent').returns({
|
||||
...common.content.events.fall2020,
|
||||
event: 'fall2020',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
worldState.getCurrentEvent.restore();
|
||||
});
|
||||
|
||||
it('applies a discount', async () => {
|
||||
const balanceBefore = user.balance;
|
||||
|
||||
await api.buyGems(data);
|
||||
|
||||
const balanceAfter = user.balance;
|
||||
const balanceDiff = balanceAfter - balanceBefore;
|
||||
|
||||
expect(balanceDiff * 4).to.eql(30);
|
||||
});
|
||||
});
|
||||
|
||||
context('Gift', () => {
|
||||
let recipient;
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../website/common';
|
||||
|
||||
describe('checkout success', () => {
|
||||
describe('paypal - checkout success', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user; let gift; let customerId; let
|
||||
paymentId;
|
||||
const gemsBlockKey = '21gems'; const gemsBlock = common.content.gems[gemsBlockKey];
|
||||
let paypalPaymentExecuteStub; let paymentBuyGemsStub; let
|
||||
paymentsCreateSubscritionStub;
|
||||
|
||||
@@ -28,7 +30,7 @@ describe('checkout success', () => {
|
||||
|
||||
it('purchases gems', async () => {
|
||||
await paypalPayments.checkoutSuccess({
|
||||
user, gift, paymentId, customerId,
|
||||
user, gift, paymentId, customerId, gemsBlock: gemsBlockKey,
|
||||
});
|
||||
|
||||
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||
@@ -38,6 +40,7 @@ describe('checkout success', () => {
|
||||
user,
|
||||
customerId,
|
||||
paymentMethod: 'Paypal',
|
||||
gemsBlock,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@ import nconf from 'nconf';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../website/common';
|
||||
import apiError from '../../../../../../website/server/libs/apiError';
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
const { i18n } = common;
|
||||
|
||||
describe('checkout', () => {
|
||||
describe('paypal - checkout', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
const gemsBlockKey = '21gems';
|
||||
let paypalPaymentCreateStub;
|
||||
let approvalHerf;
|
||||
|
||||
@@ -53,10 +55,10 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('creates a link for gem purchases', async () => {
|
||||
const link = await paypalPayments.checkout({ user: new User() });
|
||||
const link = await paypalPayments.checkout({ user: new User(), gemsBlock: gemsBlockKey });
|
||||
|
||||
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems', 5.00));
|
||||
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems', 4.99));
|
||||
expect(link).to.eql(approvalHerf);
|
||||
});
|
||||
|
||||
@@ -83,11 +85,23 @@ describe('checkout', () => {
|
||||
const user = new User();
|
||||
sinon.stub(user, 'canGetGems').resolves(false);
|
||||
|
||||
await expect(paypalPayments.checkout({ user })).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
await expect(paypalPayments.checkout({ user, gemsBlock: gemsBlockKey }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if the gems block is not valid', async () => {
|
||||
const user = new User();
|
||||
|
||||
await expect(paypalPayments.checkout({ user, gemsBlock: 'invalid' }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: apiError('invalidGemsBlock'),
|
||||
name: 'BadRequest',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a link for gifting gems', async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
|
||||
describe('ipn', () => {
|
||||
describe('paypal - ipn', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user; let group; let txn_type; let userPaymentId; let
|
||||
groupPaymentId;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { createNonLeaderGroupMember } from '../paymentHelpers';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('subscribeCancel', () => {
|
||||
describe('paypal - subscribeCancel', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user; let group; let groupId; let customerId; let groupCustomerId; let
|
||||
nextBillingDate;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../website/common';
|
||||
|
||||
describe('subscribeSuccess', () => {
|
||||
describe('paypal - subscribeSuccess', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user; let group; let block; let groupId; let token; let headers; let
|
||||
customerId;
|
||||
|
||||
@@ -8,7 +8,7 @@ import common from '../../../../../../website/common';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('subscribe', () => {
|
||||
describe('paypal - subscribe', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let coupon; let sub; let
|
||||
approvalHerf;
|
||||
|
||||
@@ -4,7 +4,7 @@ import api from '../../../../../../website/server/libs/payments/payments';
|
||||
|
||||
const groupPlanId = api.constants.GROUP_PLAN_CUSTOMER_ID;
|
||||
|
||||
describe('#calculateSubscriptionTerminationDate', () => {
|
||||
describe('stripe - #calculateSubscriptionTerminationDate', () => {
|
||||
let plan;
|
||||
let nextBill;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import common from '../../../../../../website/common';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('cancel subscription', () => {
|
||||
describe('stripe - cancel subscription', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
const stripe = stripeModule('test');
|
||||
let user; let groupId; let
|
||||
|
||||
@@ -12,7 +12,7 @@ import common from '../../../../../../website/common';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('checkout with subscription', () => {
|
||||
describe('stripe - checkout with subscription', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
const stripe = stripeModule('test');
|
||||
let user; let group; let data; let gift; let sub;
|
||||
|
||||
@@ -4,16 +4,17 @@ import { model as User } from '../../../../../../website/server/models/user';
|
||||
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../website/common';
|
||||
import apiError from '../../../../../../website/server/libs/apiError';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('checkout', () => {
|
||||
describe('stripe - checkout', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
const stripe = stripeModule('test');
|
||||
let stripeChargeStub; let paymentBuyGemsStub; let
|
||||
paymentCreateSubscritionStub;
|
||||
let user; let gift; let groupId; let email; let headers; let coupon; let customerIdResponse; let
|
||||
token;
|
||||
token; const gemsBlockKey = '21gems'; const gemsBlock = common.content.gems[gemsBlockKey];
|
||||
|
||||
beforeEach(() => {
|
||||
user = new User();
|
||||
@@ -89,6 +90,7 @@ describe('checkout', () => {
|
||||
await expect(stripePayments.checkout({
|
||||
token,
|
||||
user,
|
||||
gemsBlock: gemsBlockKey,
|
||||
gift,
|
||||
groupId,
|
||||
email,
|
||||
@@ -101,6 +103,25 @@ describe('checkout', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if the gems block is invalid', async () => {
|
||||
gift = undefined;
|
||||
|
||||
await expect(stripePayments.checkout({
|
||||
token,
|
||||
user,
|
||||
gemsBlock: 'invalid',
|
||||
gift,
|
||||
groupId,
|
||||
email,
|
||||
headers,
|
||||
coupon,
|
||||
}, stripe)).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: apiError('invalidGemsBlock'),
|
||||
name: 'BadRequest',
|
||||
});
|
||||
});
|
||||
|
||||
it('should purchase gems', async () => {
|
||||
gift = undefined;
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
@@ -108,6 +129,7 @@ describe('checkout', () => {
|
||||
await stripePayments.checkout({
|
||||
token,
|
||||
user,
|
||||
gemsBlock: gemsBlockKey,
|
||||
gift,
|
||||
groupId,
|
||||
email,
|
||||
@@ -117,7 +139,7 @@ describe('checkout', () => {
|
||||
|
||||
expect(stripeChargeStub).to.be.calledOnce;
|
||||
expect(stripeChargeStub).to.be.calledWith({
|
||||
amount: 500,
|
||||
amount: 499,
|
||||
currency: 'usd',
|
||||
card: token,
|
||||
});
|
||||
@@ -128,6 +150,7 @@ describe('checkout', () => {
|
||||
customerId: customerIdResponse,
|
||||
paymentMethod: 'Stripe',
|
||||
gift,
|
||||
gemsBlock,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
@@ -167,6 +190,7 @@ describe('checkout', () => {
|
||||
customerId: customerIdResponse,
|
||||
paymentMethod: 'Gift',
|
||||
gift,
|
||||
gemsBlock: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -205,6 +229,7 @@ describe('checkout', () => {
|
||||
customerId: customerIdResponse,
|
||||
paymentMethod: 'Gift',
|
||||
gift,
|
||||
gemsBlock: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import common from '../../../../../../website/common';
|
||||
|
||||
const { i18n } = common;
|
||||
|
||||
describe('edit subscription', () => {
|
||||
describe('stripe - edit subscription', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
const stripe = stripeModule('test');
|
||||
let user; let groupId; let group; let
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as xmlMarshaller from '../../../../website/server/libs/xmlMarshaller';
|
||||
|
||||
describe('xml marshaller marshalls user data', () => {
|
||||
const minimumUser = {
|
||||
pinnedItems: [],
|
||||
unpinnedItems: [],
|
||||
inbox: {},
|
||||
};
|
||||
|
||||
function userDataWith (fields) {
|
||||
return { ...minimumUser, ...fields };
|
||||
}
|
||||
|
||||
it('maps the newMessages field to have id as a value in a list.', () => {
|
||||
const userData = userDataWith({
|
||||
newMessages: {
|
||||
'283171a5-422c-4991-bc78-95b1b5b51629': {
|
||||
name: 'The Language Hackers',
|
||||
value: true,
|
||||
},
|
||||
'283171a6-422c-4991-bc78-95b1b5b51629': {
|
||||
name: 'The Bug Hackers',
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const xml = xmlMarshaller.marshallUserData(userData);
|
||||
|
||||
expect(xml).to.equal(`<user>
|
||||
<inbox/>
|
||||
<newMessages>
|
||||
<id>283171a5-422c-4991-bc78-95b1b5b51629</id>
|
||||
<name>The Language Hackers</name>
|
||||
<value>true</value>
|
||||
</newMessages>
|
||||
<newMessages>
|
||||
<id>283171a6-422c-4991-bc78-95b1b5b51629</id>
|
||||
<name>The Bug Hackers</name>
|
||||
<value>false</value>
|
||||
</newMessages>
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
@@ -293,4 +293,107 @@ describe('cron middleware', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Drop Cap A/B Test', async () => {
|
||||
it('enrolls web users', async () => {
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.a.string;
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not enroll 50% of users', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.6);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-not-enrolled');
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('enables the new notification for 25% of users', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.25);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-enabled');
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('disables the new notification for 25% of users', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.5);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-disabled');
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not affect subscribers', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.2);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
sandbox.stub(User.prototype, 'isSubscribed').returns(true);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.not.exist;
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not affect mobile users', async () => {
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-ios';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.not.exist;
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
generateNext,
|
||||
} from '../../../helpers/api-unit.helper';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import { ensureAdmin, ensureSudo } from '../../../../website/server/middlewares/ensureAccessRight';
|
||||
import { ensureAdmin, ensureSudo, ensureNewsPoster } from '../../../../website/server/middlewares/ensureAccessRight';
|
||||
import { NotAuthorized } from '../../../../website/server/libs/errors';
|
||||
import apiError from '../../../../website/server/libs/apiError';
|
||||
|
||||
@@ -40,6 +40,27 @@ describe('ensure access middlewares', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('ensure newsPoster', () => {
|
||||
it('returns not authorized when user is not a newsPoster', () => {
|
||||
res.locals = { user: { contributor: { newsPoster: false } } };
|
||||
|
||||
ensureNewsPoster(req, res, next);
|
||||
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(calledWith[0].message).to.equal(apiError('noNewsPosterAccess'));
|
||||
expect(calledWith[0] instanceof NotAuthorized).to.equal(true);
|
||||
});
|
||||
|
||||
it('passes when user is a newsPoster', () => {
|
||||
res.locals = { user: { contributor: { newsPoster: true } } };
|
||||
|
||||
ensureNewsPoster(req, res, next);
|
||||
|
||||
expect(next).to.be.calledOnce;
|
||||
expect(next.args[0]).to.be.empty;
|
||||
});
|
||||
});
|
||||
|
||||
context('ensure sudo', () => {
|
||||
it('returns not authorized when user is not a sudo user', () => {
|
||||
res.locals = { user: { contributor: { sudo: false } } };
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { v4 } from 'uuid';
|
||||
import { model as NewsPost, refreshNewsPost } from '../../../../website/server/models/newsPost';
|
||||
import { sleep } from '../../../helpers/api-unit.helper';
|
||||
|
||||
describe('NewsPost Model', () => {
|
||||
const publishDate = Number(new Date());
|
||||
|
||||
// NOTE publishDate is manually increased by +500 for each test
|
||||
// to make sure it's always in the future from the previous one
|
||||
// bevause NewsPost.lastNewsPost() is not reset between tests.
|
||||
// And without a more recent publishDate it wouldn't update
|
||||
|
||||
it('#lastNewsPost', () => {
|
||||
const lastPost = { _id: v4(), publishDate, published: true };
|
||||
NewsPost.updateLastNewsPost(lastPost);
|
||||
expect(NewsPost.lastNewsPost()).to.equal(lastPost);
|
||||
});
|
||||
|
||||
it('#getLastPostFromDatabase', async () => {
|
||||
const expectedId = v4();
|
||||
|
||||
await NewsPost.create([
|
||||
// more recent but not published
|
||||
{
|
||||
_id: v4(),
|
||||
publishDate: new Date(publishDate + 50),
|
||||
author: v4(),
|
||||
published: false,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
},
|
||||
// expected
|
||||
{
|
||||
_id: expectedId,
|
||||
publishDate,
|
||||
author: v4(),
|
||||
published: true,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
},
|
||||
// published but less recent
|
||||
{
|
||||
_id: v4(),
|
||||
publishDate: new Date(Number(publishDate) - 50),
|
||||
author: v4(),
|
||||
published: true,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
},
|
||||
]);
|
||||
|
||||
const fetched = await NewsPost.getLastPostFromDatabase();
|
||||
expect(fetched._id).to.equal(expectedId);
|
||||
});
|
||||
|
||||
context('#updateLastNewsPost', () => {
|
||||
it('updates the post if new one is more recent and published', () => {
|
||||
const previousPost = {
|
||||
_id: v4(),
|
||||
publishDate: new Date(publishDate + 100),
|
||||
published: true,
|
||||
};
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
const newPost = {
|
||||
_id: v4(),
|
||||
publishDate: new Date(publishDate + 150),
|
||||
published: true,
|
||||
};
|
||||
NewsPost.updateLastNewsPost(newPost);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('does not update the post if new one is from the past', () => {
|
||||
const previousPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 200), published: true,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
const newPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 175), published: true,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(newPost);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(previousPost._id);
|
||||
});
|
||||
|
||||
it('does not update the post if new one is not published', () => {
|
||||
const previousPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 250), published: true,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
const newPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 300), published: false,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(newPost);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(previousPost._id);
|
||||
});
|
||||
});
|
||||
|
||||
context('refreshes NewsPost', () => {
|
||||
let intervalId;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Delete all existing posts from the database
|
||||
await NewsPost.remove();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (intervalId) clearInterval(intervalId);
|
||||
});
|
||||
|
||||
it('refreshes the last post at a specific interval', async () => {
|
||||
await sleep(0.1); // wait 100ms to make sure all previous posts are in the past
|
||||
const previousPost = {
|
||||
_id: v4(), publishDate: new Date(), published: true,
|
||||
};
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
intervalId = refreshNewsPost(50); // refreshes every 50ms
|
||||
|
||||
await sleep(0.1); // wait 100ms to make sure the new post has a more recent publishDate
|
||||
const newPost = await NewsPost.create({
|
||||
_id: v4(),
|
||||
publishDate: new Date(),
|
||||
author: v4(),
|
||||
published: true,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
});
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(previousPost._id);
|
||||
await sleep(0.15); // wait 150ms
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newPost._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,87 +1,90 @@
|
||||
import moment from 'moment';
|
||||
import { model as User } from '../../../../website/server/models/user';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
import { model as Group } from '../../../../website/server/models/group';
|
||||
import common from '../../../../website/common';
|
||||
|
||||
describe('User Model', () => {
|
||||
it('keeps user._tmp when calling .toJSON', () => {
|
||||
const user = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username',
|
||||
lowerCaseUsername: 'username',
|
||||
email: 'email@email.email',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
describe('.toJSON()', () => {
|
||||
it('keeps user._tmp when calling .toJSON', () => {
|
||||
const user = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username',
|
||||
lowerCaseUsername: 'username',
|
||||
email: 'email@email.email',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
user._tmp = { ok: true };
|
||||
user._nonTmp = { ok: true };
|
||||
|
||||
expect(user._tmp).to.eql({ ok: true });
|
||||
expect(user._nonTmp).to.eql({ ok: true });
|
||||
|
||||
const toObject = user.toObject();
|
||||
const toJSON = user.toJSON();
|
||||
|
||||
expect(toObject).to.not.have.keys('_tmp');
|
||||
expect(toObject).to.not.have.keys('_nonTmp');
|
||||
|
||||
expect(toJSON).to.have.any.key('_tmp');
|
||||
expect(toJSON._tmp).to.eql({ ok: true });
|
||||
expect(toJSON).to.not.have.keys('_nonTmp');
|
||||
});
|
||||
|
||||
user._tmp = { ok: true };
|
||||
user._nonTmp = { ok: true };
|
||||
it('can add computed stats to a JSONified user object', () => {
|
||||
const user = new User();
|
||||
const userToJSON = user.toJSON();
|
||||
|
||||
expect(user._tmp).to.eql({ ok: true });
|
||||
expect(user._nonTmp).to.eql({ ok: true });
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
|
||||
const toObject = user.toObject();
|
||||
const toJSON = user.toJSON();
|
||||
User.addComputedStatsToJSONObj(userToJSON.stats, userToJSON);
|
||||
|
||||
expect(toObject).to.not.have.keys('_tmp');
|
||||
expect(toObject).to.not.have.keys('_nonTmp');
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
|
||||
expect(toJSON).to.have.any.key('_tmp');
|
||||
expect(toJSON._tmp).to.eql({ ok: true });
|
||||
expect(toJSON).to.not.have.keys('_nonTmp');
|
||||
});
|
||||
it('can transform user object without mongoose helpers', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
|
||||
it('can add computed stats to a JSONified user object', () => {
|
||||
const user = new User();
|
||||
const userToJSON = user.toJSON();
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
expect(userToJSON.id).to.not.exist;
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
User.transformJSONUser(userToJSON);
|
||||
|
||||
User.addComputedStatsToJSONObj(userToJSON.stats, userToJSON);
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
});
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
it('can transform user object without mongoose helpers (including computed stats)', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
|
||||
it('can transform user object without mongoose helpers', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
expect(userToJSON.id).to.not.exist;
|
||||
User.transformJSONUser(userToJSON, true);
|
||||
|
||||
User.transformJSONUser(userToJSON);
|
||||
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
});
|
||||
|
||||
it('can transform user object without mongoose helpers (including computed stats)', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
|
||||
User.transformJSONUser(userToJSON, true);
|
||||
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
});
|
||||
|
||||
context('achievements', () => {
|
||||
@@ -589,6 +592,50 @@ describe('User Model', () => {
|
||||
});
|
||||
|
||||
context('pre-save hook', () => {
|
||||
it('enrolls users that signup through web in the Drop Cap AB test', async () => {
|
||||
let user = new User();
|
||||
user.registeredThrough = 'habitica-web';
|
||||
user = await user.save();
|
||||
expect(user._ABtests.dropCapNotif).to.exist;
|
||||
});
|
||||
|
||||
it('does not enroll users that signup through modal in the Drop Cap AB test', async () => {
|
||||
let user = new User();
|
||||
user.registeredThrough = 'habitica-ios';
|
||||
user = await user.save();
|
||||
expect(user._ABtests.dropCapNotif).to.not.exist;
|
||||
});
|
||||
|
||||
it('marks the last news post as read for new users', async () => {
|
||||
const lastNewsPost = { _id: '1' };
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns(lastNewsPost);
|
||||
|
||||
let user = new User();
|
||||
expect(user.isNew).to.equal(true);
|
||||
user = await user.save();
|
||||
|
||||
expect(user.checkNewStuff()).to.equal(false);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(false);
|
||||
expect(user.flags.lastNewStuffRead).to.equal(lastNewsPost._id);
|
||||
});
|
||||
|
||||
it('does not mark the last news post as read for existing users', async () => {
|
||||
const lastNewsPost = { _id: '1' };
|
||||
const lastNewsPostStub = sandbox.stub(NewsPost, 'lastNewsPost');
|
||||
lastNewsPostStub.returns(lastNewsPost);
|
||||
|
||||
let user = new User();
|
||||
user = await user.save();
|
||||
|
||||
expect(user.isNew).to.equal(false);
|
||||
user.profile.name = 'new name';
|
||||
|
||||
lastNewsPostStub.returns({ _id: '2' });
|
||||
user = await user.save();
|
||||
|
||||
expect(user.flags.lastNewStuffRead).to.equal(lastNewsPost._id); // not _id: 2
|
||||
});
|
||||
|
||||
it('does not try to award achievements when achievements or items not selected in query', async () => {
|
||||
let user = new User();
|
||||
user = await user.save(); // necessary for user.isSelected to work correctly
|
||||
@@ -827,4 +874,46 @@ describe('User Model', () => {
|
||||
expect(daysMissed).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('isNewsPoster', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
|
||||
expect(user.isNewsPoster()).to.equal(false);
|
||||
|
||||
user.contributor.newsPoster = true;
|
||||
expect(user.isNewsPoster()).to.equal(true);
|
||||
});
|
||||
|
||||
describe('checkNewStuff', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = new User();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('no last news post', () => {
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns(null);
|
||||
expect(user.checkNewStuff()).to.equal(false);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(false);
|
||||
});
|
||||
|
||||
it('last news post read', () => {
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns({ _id: '123' });
|
||||
user.flags.lastNewStuffRead = '123';
|
||||
expect(user.checkNewStuff()).to.equal(false);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(false);
|
||||
});
|
||||
|
||||
it('last news post not read', () => {
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns({ _id: '123' });
|
||||
user.flags.lastNewStuffRead = '124';
|
||||
expect(user.checkNewStuff()).to.equal(true);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
|
||||
describe('GET challenges/user', () => {
|
||||
context('no official challenges', () => {
|
||||
let user; let member; let nonMember; let challenge; let challenge2; let
|
||||
publicGuild;
|
||||
let user; let member; let nonMember; let challenge; let challenge2;
|
||||
let publicGuild; let userData; let groupData;
|
||||
|
||||
before(async () => {
|
||||
const { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
@@ -19,225 +19,197 @@ describe('GET challenges/user', () => {
|
||||
members: 1,
|
||||
});
|
||||
|
||||
user = groupLeader;
|
||||
publicGuild = group;
|
||||
groupData = {
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
};
|
||||
|
||||
user = groupLeader;
|
||||
userData = {
|
||||
_id: publicGuild.leader._id,
|
||||
id: publicGuild.leader._id,
|
||||
profile: { name: user.profile.name },
|
||||
auth: {
|
||||
local: {
|
||||
username: user.auth.local.username,
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
verifiedUsername: true,
|
||||
},
|
||||
};
|
||||
|
||||
member = members[0]; // eslint-disable-line prefer-destructuring
|
||||
nonMember = await generateUser();
|
||||
|
||||
challenge = await generateChallenge(user, group);
|
||||
await user.post(`/challenges/${challenge._id}/join`);
|
||||
challenge2 = await generateChallenge(user, group);
|
||||
await user.post(`/challenges/${challenge2._id}/join`);
|
||||
});
|
||||
|
||||
it('should return challenges user has joined', async () => {
|
||||
await nonMember.post(`/challenges/${challenge._id}/join`);
|
||||
});
|
||||
context('all challenges', () => {
|
||||
it('should return challenges user has joined', async () => {
|
||||
const challenges = await nonMember.get('/challenges/user');
|
||||
|
||||
const challenges = await nonMember.get('/challenges/user');
|
||||
const foundChallenge = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge).to.exist;
|
||||
expect(foundChallenge.leader).to.eql(userData);
|
||||
expect(foundChallenge.group).to.eql(groupData);
|
||||
});
|
||||
|
||||
const foundChallenge = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge).to.exist;
|
||||
expect(foundChallenge.leader).to.eql({
|
||||
_id: publicGuild.leader._id,
|
||||
id: publicGuild.leader._id,
|
||||
profile: { name: user.profile.name },
|
||||
auth: {
|
||||
local: {
|
||||
username: user.auth.local.username,
|
||||
it('should not return challenges a non-member has not joined', async () => {
|
||||
const challenges = await nonMember.get('/challenges/user');
|
||||
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.not.exist;
|
||||
});
|
||||
|
||||
it('should return challenges user has created', async () => {
|
||||
const challenges = await user.get('/challenges/user');
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql(userData);
|
||||
expect(foundChallenge1.group).to.eql(groupData);
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql(userData);
|
||||
expect(foundChallenge2.group).to.eql(groupData);
|
||||
});
|
||||
|
||||
it('should return challenges in user\'s group', async () => {
|
||||
const challenges = await member.get('/challenges/user');
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql(userData);
|
||||
expect(foundChallenge1.group).to.eql(groupData);
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql(userData);
|
||||
expect(foundChallenge2.group).to.eql(groupData);
|
||||
});
|
||||
|
||||
it('should return newest challenges first', async () => {
|
||||
let challenges = await user.get('/challenges/user');
|
||||
|
||||
let foundChallengeIndex = _.findIndex(challenges, { _id: challenge2._id });
|
||||
expect(foundChallengeIndex).to.eql(0);
|
||||
|
||||
const newChallenge = await generateChallenge(user, publicGuild);
|
||||
await user.post(`/challenges/${newChallenge._id}/join`);
|
||||
|
||||
challenges = await user.get('/challenges/user');
|
||||
|
||||
foundChallengeIndex = _.findIndex(challenges, { _id: newChallenge._id });
|
||||
expect(foundChallengeIndex).to.eql(0);
|
||||
});
|
||||
|
||||
it('should not return challenges user doesn\'t have access to', async () => {
|
||||
const { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestPrivateGuild',
|
||||
summary: 'summary for TestPrivateGuild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
verifiedUsername: true,
|
||||
},
|
||||
});
|
||||
expect(foundChallenge.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return challenges user has created', async () => {
|
||||
const challenges = await user.get('/challenges/user');
|
||||
const privateChallenge = await generateChallenge(groupLeader, group);
|
||||
await groupLeader.post(`/challenges/${privateChallenge._id}/join`);
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql({
|
||||
_id: publicGuild.leader._id,
|
||||
id: publicGuild.leader._id,
|
||||
profile: { name: user.profile.name },
|
||||
auth: {
|
||||
local: {
|
||||
username: user.auth.local.username,
|
||||
const challenges = await nonMember.get('/challenges/user');
|
||||
|
||||
const foundChallenge = _.find(challenges, { _id: privateChallenge._id });
|
||||
expect(foundChallenge).to.not.exist;
|
||||
});
|
||||
|
||||
it('should not return challenges user doesn\'t have access to, even with query parameters', async () => {
|
||||
const { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestPrivateGuild',
|
||||
summary: 'summary for TestPrivateGuild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
verifiedUsername: true,
|
||||
},
|
||||
});
|
||||
expect(foundChallenge1.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql({
|
||||
_id: publicGuild.leader._id,
|
||||
id: publicGuild.leader._id,
|
||||
profile: { name: user.profile.name },
|
||||
auth: {
|
||||
local: {
|
||||
username: user.auth.local.username,
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
verifiedUsername: true,
|
||||
},
|
||||
});
|
||||
expect(foundChallenge2.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
|
||||
const privateChallenge = await generateChallenge(groupLeader, group, {
|
||||
categories: [{
|
||||
name: 'academics',
|
||||
slug: 'academics',
|
||||
}],
|
||||
});
|
||||
await groupLeader.post(`/challenges/${privateChallenge._id}/join`);
|
||||
|
||||
const challenges = await nonMember.get('/challenges/user?categories=academics&owned=not_owned');
|
||||
|
||||
const foundChallenge = _.find(challenges, { _id: privateChallenge._id });
|
||||
expect(foundChallenge).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return challenges in user\'s group', async () => {
|
||||
const challenges = await member.get('/challenges/user');
|
||||
context('my challenges', () => {
|
||||
it('should return challenges user has joined', async () => {
|
||||
const challenges = await nonMember.get(`/challenges/user?member=${true}`);
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql({
|
||||
_id: publicGuild.leader._id,
|
||||
id: publicGuild.leader._id,
|
||||
profile: { name: user.profile.name },
|
||||
auth: {
|
||||
local: {
|
||||
username: user.auth.local.username,
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
verifiedUsername: true,
|
||||
},
|
||||
});
|
||||
expect(foundChallenge1.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql({
|
||||
_id: publicGuild.leader._id,
|
||||
id: publicGuild.leader._id,
|
||||
profile: { name: user.profile.name },
|
||||
auth: {
|
||||
local: {
|
||||
username: user.auth.local.username,
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
verifiedUsername: true,
|
||||
},
|
||||
});
|
||||
expect(foundChallenge2.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return challenges in user groups if we send member true param', async () => {
|
||||
const challenges = await member.get(`/challenges/user?member=${true}`);
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.not.exist;
|
||||
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.not.exist;
|
||||
});
|
||||
|
||||
it('should return newest challenges first', async () => {
|
||||
let challenges = await user.get('/challenges/user');
|
||||
|
||||
let foundChallengeIndex = _.findIndex(challenges, { _id: challenge2._id });
|
||||
expect(foundChallengeIndex).to.eql(0);
|
||||
|
||||
const newChallenge = await generateChallenge(user, publicGuild);
|
||||
await user.post(`/challenges/${newChallenge._id}/join`);
|
||||
|
||||
challenges = await user.get('/challenges/user');
|
||||
|
||||
foundChallengeIndex = _.findIndex(challenges, { _id: newChallenge._id });
|
||||
expect(foundChallengeIndex).to.eql(0);
|
||||
});
|
||||
|
||||
it('should not return challenges user doesn\'t have access to', async () => {
|
||||
const { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestPrivateGuild',
|
||||
summary: 'summary for TestPrivateGuild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
const foundChallenge = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge).to.exist;
|
||||
expect(foundChallenge.leader).to.eql(userData);
|
||||
expect(foundChallenge.group).to.eql(groupData);
|
||||
});
|
||||
|
||||
const privateChallenge = await generateChallenge(groupLeader, group);
|
||||
await groupLeader.post(`/challenges/${privateChallenge._id}/join`);
|
||||
it('should return challenges user has created', async () => {
|
||||
const challenges = await user.get(`/challenges/user?member=${true}`);
|
||||
|
||||
const challenges = await nonMember.get('/challenges/user');
|
||||
|
||||
const foundChallenge = _.find(challenges, { _id: privateChallenge._id });
|
||||
expect(foundChallenge).to.not.exist;
|
||||
});
|
||||
|
||||
it('should not return challenges user doesn\'t have access to, even with query parameters', async () => {
|
||||
const { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestPrivateGuild',
|
||||
summary: 'summary for TestPrivateGuild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql(userData);
|
||||
expect(foundChallenge1.group).to.eql(groupData);
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql(userData);
|
||||
expect(foundChallenge2.group).to.eql(groupData);
|
||||
});
|
||||
|
||||
const privateChallenge = await generateChallenge(groupLeader, group, {
|
||||
categories: [{
|
||||
name: 'academics',
|
||||
slug: 'academics',
|
||||
}],
|
||||
it('should return challenges user has created if filter by owned', async () => {
|
||||
const challenges = await user.get(`/challenges/user?member=${true}&owned=owned`);
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql(userData);
|
||||
expect(foundChallenge1.group).to.eql(groupData);
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql(userData);
|
||||
expect(foundChallenge2.group).to.eql(groupData);
|
||||
});
|
||||
await groupLeader.post(`/challenges/${privateChallenge._id}/join`);
|
||||
|
||||
const challenges = await nonMember.get('/challenges/user?categories=academics&owned=not_owned');
|
||||
it('should not return challenges user has created if filter by not owned', async () => {
|
||||
const challenges = await user.get(`/challenges/user?owned=not_owned&member=${true}`);
|
||||
|
||||
const foundChallenge = _.find(challenges, { _id: privateChallenge._id });
|
||||
expect(foundChallenge).to.not.exist;
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.not.exist;
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.not.exist;
|
||||
});
|
||||
|
||||
it('should not return challenges in user groups', async () => {
|
||||
const challenges = await member.get(`/challenges/user?member=${true}`);
|
||||
|
||||
const foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.not.exist;
|
||||
|
||||
const foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.not.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => {
|
||||
|
||||
expect(testTask.challenge.broken).to.eql('CHALLENGE_CLOSED');
|
||||
expect(testTask.challenge.winner).to.eql(winningUser.profile.name);
|
||||
expect(challengeTag.challenge).to.eql('false');
|
||||
expect(challengeTag.challenge).to.eql(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
TAVERN_ID,
|
||||
} from '../../../../../website/server/models/group';
|
||||
import { CHAT_FLAG_FROM_SHADOW_MUTE, MAX_MESSAGE_LENGTH } from '../../../../../website/common/script/constants';
|
||||
import guildsAllowingBannedWords from '../../../../../website/server/libs/guildsAllowingBannedWords';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
@@ -329,7 +328,8 @@ describe('POST /chat', () => {
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guildsAllowingBannedWords[group._id] = true;
|
||||
// Update the bannedWordsAllowed property for the group
|
||||
group.update({ bannedWordsAllowed: true });
|
||||
|
||||
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage });
|
||||
|
||||
@@ -503,8 +503,8 @@ describe('POST /chat', () => {
|
||||
const memberUsername = 'memberUsername';
|
||||
await member.update({ 'auth.local.username': memberUsername });
|
||||
|
||||
const messageWithMentions = `hi @${memberUsername} 123456789
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 89 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345678 END.`;
|
||||
const messageWithMentions = `hi @${memberUsername} 123456789
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 89 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345678 END.`;
|
||||
expect(messageWithMentions.length).to.equal(MAX_MESSAGE_LENGTH);
|
||||
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: messageWithMentions });
|
||||
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
|
||||
|
||||
@@ -79,4 +79,56 @@ describe('PUT /group', () => {
|
||||
expect(updatedGroup.leader.profile.name).to.eql(nonLeader.profile.name);
|
||||
expect(updatedGroup.name).to.equal(groupUpdatedName);
|
||||
});
|
||||
|
||||
it('allows for an admin to update the bannedWordsAllow property for an existing guild', async () => {
|
||||
const { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'public guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
});
|
||||
|
||||
const updateGroupDetails = {
|
||||
id: group._id,
|
||||
name: 'public guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
bannedWordsAllowed: true,
|
||||
};
|
||||
|
||||
// Make guild leader into admin
|
||||
await groupLeader.post('/debug/make-admin');
|
||||
await groupLeader.sync();
|
||||
|
||||
// Update the bannedWordsAllowed property for the group
|
||||
const response = await groupLeader.put(`/groups/${group._id}`, updateGroupDetails);
|
||||
|
||||
expect(groupLeader.contributor.admin).to.eql(true);
|
||||
expect(response.bannedWordsAllowed).to.eql(true);
|
||||
});
|
||||
|
||||
it('does not allow for a non-admin to update the bannedWordsAllow property for an existing guild', async () => {
|
||||
const { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'public guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
});
|
||||
|
||||
const updateGroupDetails = {
|
||||
id: group._id,
|
||||
name: 'public guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
bannedWordsAllowed: true,
|
||||
};
|
||||
|
||||
// Update the bannedWordsAllowed property for the group
|
||||
const response = await groupLeader.put(`/groups/${group._id}`, updateGroupDetails);
|
||||
|
||||
expect(groupLeader.contributor.admin).to.eql(undefined);
|
||||
expect(response.bannedWordsAllowed).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
|
||||
describe('GET /news', () => {
|
||||
let api;
|
||||
|
||||
beforeEach(async () => {
|
||||
api = requester();
|
||||
});
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as NewsPost } from '../../../../../website/server/models/newsPost';
|
||||
|
||||
describe('POST /news/tell-me-later', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'flags.newStuff': true,
|
||||
NewsPost.updateLastNewsPost({
|
||||
_id: '1234', publishDate: new Date(), title: 'Title', published: true,
|
||||
});
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('marks new stuff as read and adds notification', async () => {
|
||||
expect(user.flags.newStuff).to.equal(true);
|
||||
const initialNotifications = user.notifications.length;
|
||||
|
||||
await user.post('/news/tell-me-later');
|
||||
await user.sync();
|
||||
|
||||
expect(user.flags.newStuff).to.equal(false);
|
||||
expect(user.flags.lastNewStuffRead).to.equal('1234');
|
||||
// fetching the user because newStuff is a computed property
|
||||
expect((await user.get('/user')).flags.newStuff).to.equal(false);
|
||||
expect(user.notifications.length).to.equal(initialNotifications + 1);
|
||||
|
||||
const notification = user.notifications[user.notifications.length - 1];
|
||||
|
||||
@@ -14,7 +14,10 @@ describe('payments - stripe - #checkout', () => {
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint, { id: 123 })).to.eventually.be.rejected.and.include({
|
||||
await expect(user.post(
|
||||
`${endpoint}?gemsBlock=4gems`,
|
||||
{ id: 123 },
|
||||
)).to.eventually.be.rejected.and.include({
|
||||
code: 401,
|
||||
error: 'Error',
|
||||
// message: 'Invalid API Key provided: aaaabbbb********************1111',
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as NewsPost } from '../../../../../website/server/models/newsPost';
|
||||
|
||||
describe('PUT /user', () => {
|
||||
let user;
|
||||
@@ -101,6 +102,24 @@ describe('PUT /user', () => {
|
||||
message: t('displaynameIssueNewline'),
|
||||
});
|
||||
});
|
||||
|
||||
it('can set flags.newStuff to false', async () => {
|
||||
NewsPost.updateLastNewsPost({
|
||||
_id: '1234', publishDate: new Date(), title: 'Title', published: true,
|
||||
});
|
||||
|
||||
await user.update({
|
||||
'flags.lastNewStuffRead': '123',
|
||||
});
|
||||
|
||||
await user.put('/user', {
|
||||
'flags.newStuff': false,
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.flags.lastNewStuffRead).to.eql('1234');
|
||||
});
|
||||
});
|
||||
|
||||
context('Top Level Protected Operations', () => {
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
requester,
|
||||
resetHabiticaDB,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import * as worldState from '../../../../../website/server/libs/worldState';
|
||||
import common from '../../../../../website/common';
|
||||
|
||||
describe('GET /world-state', () => {
|
||||
before(async () => {
|
||||
@@ -39,4 +41,41 @@ describe('GET /world-state', () => {
|
||||
expect(res).to.have.nested.property('npcImageSuffix');
|
||||
expect(res.npcImageSuffix).to.be.a('string');
|
||||
});
|
||||
|
||||
context('no current event', () => {
|
||||
beforeEach(async () => {
|
||||
sinon.stub(worldState, 'getCurrentEvent').returns(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
worldState.getCurrentEvent.restore();
|
||||
});
|
||||
|
||||
it('returns null for the current event when there is none active', async () => {
|
||||
const res = await requester().get('/world-state');
|
||||
|
||||
expect(res.currentEvent).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
context('no current event', () => {
|
||||
const evt = {
|
||||
...common.content.events.fall2020,
|
||||
event: 'fall2020',
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
sinon.stub(worldState, 'getCurrentEvent').returns(evt);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
worldState.getCurrentEvent.restore();
|
||||
});
|
||||
|
||||
it('returns the current event when there is an active one', async () => {
|
||||
const res = await requester().get('/world-state');
|
||||
|
||||
expect(res.currentEvent).to.eql(evt);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
describe('DELETE /news/:newsID', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('disallows access to non-newsPosters', async () => {
|
||||
const nonAdminUser = await generateUser({ 'contributor.newsPoster': false });
|
||||
await expect(nonAdminUser.del(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: 'You don\'t have news poster access.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the post does not exist', async () => {
|
||||
await expect(user.del(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('newsPostNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes news posts', async () => {
|
||||
const existingPost = await user.post('/news', newsPost);
|
||||
await user.del(`/news/${existingPost._id}`);
|
||||
|
||||
const returnedPosts = await user.get('/news');
|
||||
const deletedPost = returnedPosts.find(returnedPost => returnedPost._id === existingPost._id);
|
||||
|
||||
expect(returnedPosts).is.an('array');
|
||||
expect(deletedPost).to.not.exist;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
requester, generateUser,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
describe('GET /news', () => {
|
||||
let api;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
api = requester();
|
||||
const user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns the latest news in json format, does not require authentication, 10 per page', async () => {
|
||||
const res = await api.get('/news');
|
||||
expect(res.length).to.be.equal(10);
|
||||
expect(res[0].title).to.be.not.empty;
|
||||
expect(res[0].text).to.be.not.empty;
|
||||
});
|
||||
|
||||
it('supports pagination', async () => {
|
||||
const res = await api.get('/news?page=1');
|
||||
expect(res.length).to.be.equal(2);
|
||||
expect(res[0].title).to.be.not.empty;
|
||||
expect(res[0].text).to.be.not.empty;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
describe('GET /news/:newsID', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the post does not exist', async () => {
|
||||
await expect(user.get(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('newsPostNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches an existing post', async () => {
|
||||
const existingPost = await user.post('/news', newsPost);
|
||||
const fetchedPost = await user.get(`/news/${existingPost._id}`);
|
||||
|
||||
expect(fetchedPost._id).to.equal(existingPost._id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import moment from 'moment';
|
||||
import {
|
||||
generateUser,
|
||||
sleep,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
|
||||
describe('POST /news', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('disallows access to non-admins', async () => {
|
||||
const nonAdminUser = await generateUser({ 'contributor.newsPoster': false });
|
||||
await expect(nonAdminUser.post('/news')).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: 'You don\'t have news poster access.',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates news posts', async () => {
|
||||
const response = await user.post('/news', newsPost);
|
||||
|
||||
expect(response.title).to.equal(newsPost.title);
|
||||
expect(response.credits).to.equal(newsPost.credits);
|
||||
expect(response.text).to.equal(newsPost.text);
|
||||
expect(response._id).to.exist;
|
||||
|
||||
const res = await user.get('/news');
|
||||
expect(res[0]._id).to.equal(response._id);
|
||||
expect(res[0].title).to.equal(newsPost.title);
|
||||
expect(res[0].text).to.equal(newsPost.text);
|
||||
});
|
||||
|
||||
context('calls updateLastNewsPost', () => {
|
||||
beforeEach(async () => {
|
||||
await NewsPost.remove({ });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
newsPost.publishDate = new Date();
|
||||
newsPost.published = true;
|
||||
});
|
||||
|
||||
it('new post is published and the most recent one', async () => {
|
||||
newsPost.publishDate = new Date();
|
||||
const newPost = await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('new post is not published', async () => {
|
||||
newsPost.published = false;
|
||||
const newPost = await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.not.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('new post is published but in the future', async () => {
|
||||
newsPost.publishDate = moment().add({ days: 1 }).toDate();
|
||||
const newPost = await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.not.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('new post is published but not the most recent one', async () => {
|
||||
const oldPost = await user.post('/news', newsPost);
|
||||
newsPost.publishDate = moment().subtract({ days: 1 }).toDate();
|
||||
await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(oldPost._id);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets default fields', async () => {
|
||||
const response = await user.post('/news', {
|
||||
title: 'A post',
|
||||
credits: 'Credits',
|
||||
text: 'Text',
|
||||
});
|
||||
|
||||
expect(response.published).to.equal(false);
|
||||
expect(response.publishDate).to.exist;
|
||||
expect(response.author).to.equal(user._id);
|
||||
expect(response.createdAt).to.exist;
|
||||
expect(response.updatedAt).to.exist;
|
||||
});
|
||||
|
||||
context('required fields', () => {
|
||||
it('title', async () => {
|
||||
await expect(user.post('/news', {
|
||||
text: 'Text',
|
||||
credits: 'Credits',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'NewsPost validation failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('credits', async () => {
|
||||
await expect(user.post('/news', {
|
||||
text: 'Text',
|
||||
title: 'Title',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'NewsPost validation failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('text', async () => {
|
||||
await expect(user.post('/news', {
|
||||
credits: 'credits',
|
||||
title: 'Title',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'NewsPost validation failed',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
|
||||
describe('POST /news/read', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('marks new stuff as read', async () => {
|
||||
NewsPost.updateLastNewsPost({ _id: '1234', publishDate: new Date(), published: true });
|
||||
await user.post('/news/read');
|
||||
await user.sync();
|
||||
|
||||
expect(user.flags.lastNewStuffRead).to.equal('1234');
|
||||
// fetching the user because newStuff is a computed property
|
||||
expect((await user.get('/user')).flags.newStuff).to.equal(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
sleep,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
|
||||
describe('PUT /news/:newsID', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('disallows access to non-admins', async () => {
|
||||
const nonAdminUser = await generateUser({ 'contributor.newsPoster': false });
|
||||
await expect(nonAdminUser.put('/news/1234')).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: 'You don\'t have news poster access.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the post does not exist', async () => {
|
||||
await expect(user.put(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('newsPostNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('updates existing news posts', async () => {
|
||||
const existingPost = await user.post('/news', newsPost);
|
||||
const updatedPost = await user.put(`/news/${existingPost._id}`, {
|
||||
title: 'Changed Title',
|
||||
});
|
||||
|
||||
expect(updatedPost.title).to.equal('Changed Title');
|
||||
expect(updatedPost.credits).to.equal(existingPost.credits);
|
||||
expect(updatedPost.text).to.equal(existingPost.text);
|
||||
expect(updatedPost.published).to.equal(existingPost.published);
|
||||
expect(updatedPost._id).to.equal(existingPost._id);
|
||||
});
|
||||
|
||||
context('calls updateLastNewsPost', () => {
|
||||
beforeEach(async () => {
|
||||
await NewsPost.remove({ });
|
||||
});
|
||||
|
||||
it('updates post data', async () => {
|
||||
const existingPost = await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const updatedPost = await user.put(`/news/${existingPost._id}`, {
|
||||
title: 'Changed Title',
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost().title).to.equal(updatedPost.title);
|
||||
});
|
||||
|
||||
it('updated post is not published', async () => {
|
||||
const oldPost = await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const newUnpublished = await user.post('/news', { ...newsPost, published: false });
|
||||
await user.put(`/news/${newUnpublished._id}`, {
|
||||
title: 'Changed Title',
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(oldPost._id);
|
||||
});
|
||||
|
||||
it('updated post is published', async () => {
|
||||
await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const newUnpublished = await user.post('/news', { ...newsPost, published: false, publishDate: new Date() });
|
||||
await user.put(`/news/${newUnpublished._id}`, {
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newUnpublished._id);
|
||||
});
|
||||
|
||||
it('updated post publishDate is in future', async () => {
|
||||
const oldPost = await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const newUnpublished = await user.post('/news', newsPost);
|
||||
await user.put(`/news/${newUnpublished._id}`, {
|
||||
publishDate: Date.now() + 50000,
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(oldPost._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import randomDrop from '../../../website/common/script/fns/randomDrop';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
generateTodo,
|
||||
@@ -144,5 +145,148 @@ describe('common.fns.randomDrop', () => {
|
||||
expect(acceptableDrops).to.contain(user._tmp.drop.key); // always Desert
|
||||
});
|
||||
});
|
||||
|
||||
context('drop cap notification', () => {
|
||||
let analytics;
|
||||
const req = {};
|
||||
let isSubscribedStub;
|
||||
|
||||
beforeEach(() => {
|
||||
user.addNotification = () => {};
|
||||
sandbox.stub(user, 'addNotification');
|
||||
user.isSubscribed = () => {};
|
||||
isSubscribedStub = sandbox.stub(user, 'isSubscribed');
|
||||
isSubscribedStub.returns(false);
|
||||
analytics = { track () {} };
|
||||
sandbox.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
it('sends a notification if A/B test is enabled when drop cap is reached', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.be.calledOnce;
|
||||
expect(user.addNotification).to.be.calledWith('DROP_CAP_REACHED', {
|
||||
message: i18n.t('dropCapReached'),
|
||||
items: 5,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send a notification if user is enrolled in disabled A/B test group', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-disabled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not send a notification if user is enrolled in disabled A/B test group', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-not-enrolled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not send a notification if drop cap is not reached', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(4);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not send a notification if user is subscribed', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('tracks drop cap reached event for enrolled users (notification enabled)', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(analytics.track).to.be.calledWith('drop cap reached');
|
||||
});
|
||||
|
||||
it('tracks drop cap reached event for enrolled users (notification disabled)', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-disabled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(analytics.track).to.be.calledWith('drop cap reached');
|
||||
});
|
||||
|
||||
it('does not track drop cap reached event for users not enrolled in A/B test', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-not-enrolled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(analytics.track).to.not.be.calledWith('drop cap reached');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ import BootstrapVue from 'bootstrap-vue';
|
||||
import StoreModule from '@/libs/store';
|
||||
|
||||
// couldn't inject the languages easily,
|
||||
// so just a "$t()" string to show that this will be translated
|
||||
// so just a "$t()" string to show that this will be translated
|
||||
Vue.prototype.$t = function translateString (...args) {
|
||||
return `$t(${JSON.stringify(args)})`;
|
||||
};
|
||||
|
||||
@@ -16,50 +16,50 @@
|
||||
"@storybook/addon-actions": "^5.3.19",
|
||||
"@storybook/addon-knobs": "^5.3.19",
|
||||
"@storybook/addon-links": "^5.3.19",
|
||||
"@storybook/addon-notes": "^5.3.19",
|
||||
"@storybook/addon-notes": "^5.3.21",
|
||||
"@storybook/vue": "^5.3.19",
|
||||
"@vue/cli-plugin-babel": "^4.5.3",
|
||||
"@vue/cli-plugin-eslint": "^4.5.3",
|
||||
"@vue/cli-plugin-router": "^4.5.3",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.5.3",
|
||||
"@vue/cli-service": "^4.5.3",
|
||||
"@vue/cli-plugin-babel": "^4.5.7",
|
||||
"@vue/cli-plugin-eslint": "^4.5.7",
|
||||
"@vue/cli-plugin-router": "^4.5.7",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.5.7",
|
||||
"@vue/cli-service": "^4.5.7",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"amplitude-js": "^7.1.0",
|
||||
"amplitude-js": "^7.2.2",
|
||||
"axios": "^0.19.2",
|
||||
"axios-progress-bar": "^1.2.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"bootstrap": "^4.5.2",
|
||||
"bootstrap-vue": "^2.16.0",
|
||||
"bootstrap-vue": "^2.17.3",
|
||||
"chai": "^4.1.2",
|
||||
"core-js": "^3.6.5",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-habitrpg": "^6.2.0",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"habitica-markdown": "^2.0.2",
|
||||
"hellojs": "^1.18.4",
|
||||
"habitica-markdown": "^3.0.0",
|
||||
"hellojs": "^1.18.6",
|
||||
"inspectpack": "^4.5.2",
|
||||
"intro.js": "^2.9.3",
|
||||
"jquery": "^3.5.1",
|
||||
"lodash": "^4.17.20",
|
||||
"moment": "^2.27.0",
|
||||
"moment": "^2.29.1",
|
||||
"nconf": "^0.10.0",
|
||||
"sass": "^1.26.10",
|
||||
"sass": "^1.27.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"smartbanner.js": "^1.16.0",
|
||||
"svg-inline-loader": "^0.8.2",
|
||||
"svg-url-loader": "^6.0.0",
|
||||
"svgo": "^1.3.2",
|
||||
"svgo-loader": "^2.2.1",
|
||||
"uuid": "^8.3.0",
|
||||
"validator": "^13.1.1",
|
||||
"vue": "^2.6.11",
|
||||
"uuid": "^8.3.1",
|
||||
"validator": "^13.1.17",
|
||||
"vue": "^2.6.12",
|
||||
"vue-cli-plugin-storybook": "^0.6.1",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-router": "^3.4.3",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuedraggable": "^2.24.0",
|
||||
"vue-router": "^3.4.6",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"vuedraggable": "^2.24.1",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0",
|
||||
"webpack": "^4.44.1"
|
||||
"webpack": "^4.44.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
id="app"
|
||||
:class="{
|
||||
'casting-spell': castingSpell,
|
||||
'resting': showRestingBanner
|
||||
}"
|
||||
>
|
||||
<!-- <banned-account-modal /> -->
|
||||
@@ -38,31 +37,8 @@
|
||||
<router-view v-if="!isUserLoggedIn || isStaticPage" />
|
||||
<template v-else>
|
||||
<template v-if="isUserLoaded">
|
||||
<div
|
||||
v-show="showRestingBanner"
|
||||
ref="restingBanner"
|
||||
class="resting-banner"
|
||||
>
|
||||
<span class="content">
|
||||
<span class="label d-inline d-sm-none">{{ $t('innCheckOutBannerShort') }}</span>
|
||||
<span class="label d-none d-sm-inline">{{ $t('innCheckOutBanner') }}</span>
|
||||
<span class="separator">|</span>
|
||||
<span
|
||||
class="resume"
|
||||
@click="resumeDamage()"
|
||||
>{{ $t('resumeDamage') }}</span>
|
||||
</span>
|
||||
<div
|
||||
class="closepadding"
|
||||
@click="hideBanner()"
|
||||
>
|
||||
<span
|
||||
class="svg-icon inline icon-10"
|
||||
aria-hidden="true"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<damage-paused-banner />
|
||||
<gems-promo-banner />
|
||||
<notifications-display />
|
||||
<app-menu />
|
||||
<div
|
||||
@@ -99,27 +75,17 @@
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/variables.scss';
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.resting {
|
||||
--banner-resting-height: #{$restingToolbarHeight};
|
||||
}
|
||||
|
||||
&.giftingBanner {
|
||||
--banner-gifting-height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
#loading-screen-inapp {
|
||||
#melior {
|
||||
margin: 0 auto;
|
||||
width: 70.9px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.row {
|
||||
@@ -144,15 +110,6 @@
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.closepadding {
|
||||
margin: 11px 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
@@ -173,51 +130,11 @@
|
||||
margin-top: .5em;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.resting-banner {
|
||||
width: 100%;
|
||||
height: $restingToolbarHeight;
|
||||
background-color: $blue-10;
|
||||
top: 0;
|
||||
z-index: 1300;
|
||||
display: flex;
|
||||
|
||||
.content {
|
||||
line-height: 1.71;
|
||||
text-align: center;
|
||||
color: $white;
|
||||
padding: 8px 38px 8px 8px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.content {
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
color: $blue-100;
|
||||
margin: 0px 15px;
|
||||
}
|
||||
|
||||
.resume {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
white-space:nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang='scss'>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.closepadding span svg path {
|
||||
stroke: #FFF;
|
||||
opacity: 0.48;
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
opacity: .9 !important;
|
||||
background-color: $purple-100 !important;
|
||||
@@ -235,6 +152,8 @@ import { loadProgressBar } from 'axios-progress-bar';
|
||||
|
||||
import AppMenu from './components/header/menu';
|
||||
import AppHeader from './components/header/index';
|
||||
import DamagePausedBanner from './components/header/banners/damagePaused';
|
||||
import GemsPromoBanner from './components/header/banners/gemsPromo';
|
||||
import AppFooter from './components/appFooter';
|
||||
import notificationsDisplay from './components/notifications';
|
||||
import snackbars from './components/snackbars/notifications';
|
||||
@@ -256,8 +175,6 @@ import {
|
||||
removeLocalSetting,
|
||||
} from '@/libs/userlocalManager';
|
||||
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
|
||||
const COMMUNITY_MANAGER_EMAIL = process.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
|
||||
|
||||
export default {
|
||||
@@ -266,6 +183,8 @@ export default {
|
||||
AppMenu,
|
||||
AppHeader,
|
||||
AppFooter,
|
||||
DamagePausedBanner,
|
||||
GemsPromoBanner,
|
||||
notificationsDisplay,
|
||||
snackbars,
|
||||
BuyModal,
|
||||
@@ -278,9 +197,6 @@ export default {
|
||||
mixins: [notifications, spellsMixin],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
}),
|
||||
selectedItemToBuy: null,
|
||||
selectedSpellToBuy: null,
|
||||
|
||||
@@ -300,9 +216,6 @@ export default {
|
||||
castingSpell () {
|
||||
return this.$store.state.spellOptions.castingSpell;
|
||||
},
|
||||
showRestingBanner () {
|
||||
return !this.bannerHidden && this.user && this.user.preferences.sleep;
|
||||
},
|
||||
noMargin () {
|
||||
return ['privateMessages'].includes(this.$route.name);
|
||||
},
|
||||
@@ -590,12 +503,6 @@ export default {
|
||||
hideLoadingScreen () {
|
||||
this.loading = false;
|
||||
},
|
||||
hideBanner () {
|
||||
this.bannerHidden = true;
|
||||
},
|
||||
resumeDamage () {
|
||||
this.$store.dispatch('user:sleep');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,72 +1,84 @@
|
||||
.promo_armoire_backgrounds_202007 {
|
||||
.promo_armoire_backgrounds_202010 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -376px 0px;
|
||||
background-position: 0px -639px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_armoire_backgrounds_202008 {
|
||||
.promo_fall_customizations {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -376px -148px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
background-position: -532px 0px;
|
||||
width: 336px;
|
||||
height: 207px;
|
||||
}
|
||||
.promo_golden_achievements {
|
||||
.promo_fall_festival_2019 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -419px -540px;
|
||||
width: 246px;
|
||||
height: 112px;
|
||||
background-position: 0px -449px;
|
||||
width: 360px;
|
||||
height: 189px;
|
||||
}
|
||||
.promo_mystery_202008 {
|
||||
.promo_fall_festival_2020 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -383px;
|
||||
width: 294px;
|
||||
height: 156px;
|
||||
background-position: -361px -449px;
|
||||
width: 360px;
|
||||
height: 174px;
|
||||
}
|
||||
.promo_mystery_202009 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -295px -383px;
|
||||
background-position: -869px -296px;
|
||||
width: 282px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_mystery_202010 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -869px -444px;
|
||||
width: 282px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_sandy_sidekicks_bundle {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -869px -148px;
|
||||
width: 420px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_skeleton_achievements {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -244px;
|
||||
width: 366px;
|
||||
height: 204px;
|
||||
}
|
||||
.promo_spooky_sparkles {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -639px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_take_this {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -800px -168px;
|
||||
background-position: -1152px -296px;
|
||||
width: 96px;
|
||||
height: 69px;
|
||||
}
|
||||
.promo_time_travelers {
|
||||
.promo_vampire_potions {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -869px 0px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.scene_positivity {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px 0px;
|
||||
width: 375px;
|
||||
height: 186px;
|
||||
width: 531px;
|
||||
height: 243px;
|
||||
}
|
||||
.scene_CernyPie {
|
||||
.scene_squall {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -800px 0px;
|
||||
background-position: -532px -208px;
|
||||
width: 141px;
|
||||
height: 167px;
|
||||
height: 169px;
|
||||
}
|
||||
.scene_achievement {
|
||||
.scene_strength {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -540px;
|
||||
width: 210px;
|
||||
height: 210px;
|
||||
}
|
||||
.scene_public_space {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -187px;
|
||||
width: 345px;
|
||||
height: 195px;
|
||||
}
|
||||
.scene_reading {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -578px -383px;
|
||||
width: 171px;
|
||||
height: 144px;
|
||||
}
|
||||
.scene_rewards {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -211px -540px;
|
||||
width: 207px;
|
||||
height: 180px;
|
||||
background-position: -869px -592px;
|
||||
width: 192px;
|
||||
height: 129px;
|
||||
}
|
||||
|
||||
@@ -1,234 +1,714 @@
|
||||
.shop_weapon_wizard_5 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1547px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_weapon_wizard_6 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -220px -324px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.weapon_healer_0 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1313px -1095px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_healer_1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1313px -1186px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_healer_2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1513px -1315px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_healer_3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -702px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_healer_4 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -793px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_healer_5 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -884px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_healer_6 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -975px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_0 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1066px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1157px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1248px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1339px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_4 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1430px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_5 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1521px -1466px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_rogue_6 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px 0px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1410px -1315px;
|
||||
width: 102px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -91px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -182px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_aetherCrystals {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1295px -1315px;
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_bardInstrument {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -273px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_fencingFoil {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -364px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_lunarScythe {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -455px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_mammothRiderSpear {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -546px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_nomadsScimitar {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -637px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_pageBanner {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -728px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_roguishRainbowMessage {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -819px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_skeletonKey {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -910px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_tachi {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1001px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_taskwoodsLantern {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1092px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_special_tridentOfCrashingTides {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1183px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_0 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1274px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1365px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1624px -1456px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: 0px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_4 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -91px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_5 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -182px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_warrior_6 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -273px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_0 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -364px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -455px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -546px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -637px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_4 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -728px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_5 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -819px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_wizard_6 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -910px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.Pet_Currency_Gem {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1913px -902px;
|
||||
background-position: -1715px -828px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.Pet_Currency_Gem1x {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1992px -592px;
|
||||
background-position: -1767px -1232px;
|
||||
width: 15px;
|
||||
height: 13px;
|
||||
}
|
||||
.Pet_Currency_Gem2x {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1902px -1211px;
|
||||
background-position: -1715px -1644px;
|
||||
width: 30px;
|
||||
height: 26px;
|
||||
}
|
||||
.PixelPaw-Gold {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1929px -711px;
|
||||
background-position: -1715px -1232px;
|
||||
width: 51px;
|
||||
height: 51px;
|
||||
}
|
||||
.PixelPaw {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1913px -1040px;
|
||||
background-position: -1715px -1284px;
|
||||
width: 51px;
|
||||
height: 51px;
|
||||
}
|
||||
.PixelPaw002 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1905px -1109px;
|
||||
background-position: -1715px -1336px;
|
||||
width: 51px;
|
||||
height: 51px;
|
||||
}
|
||||
.avatar_floral_healer {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -302px -1466px;
|
||||
width: 99px;
|
||||
height: 99px;
|
||||
}
|
||||
.avatar_floral_rogue {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -402px -1466px;
|
||||
width: 99px;
|
||||
height: 99px;
|
||||
}
|
||||
.avatar_floral_warrior {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -502px -1466px;
|
||||
width: 99px;
|
||||
height: 99px;
|
||||
}
|
||||
.avatar_floral_wizard {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -602px -1466px;
|
||||
width: 99px;
|
||||
height: 99px;
|
||||
}
|
||||
.avatar_snowball_healer {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -811px -1315px;
|
||||
width: 120px;
|
||||
height: 105px;
|
||||
}
|
||||
.avatar_snowball_rogue {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -932px -1315px;
|
||||
width: 120px;
|
||||
height: 105px;
|
||||
}
|
||||
.avatar_snowball_warrior {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1053px -1315px;
|
||||
width: 120px;
|
||||
height: 105px;
|
||||
}
|
||||
.avatar_snowball_wizard {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1174px -1315px;
|
||||
width: 120px;
|
||||
height: 105px;
|
||||
}
|
||||
.empty_bottles {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -1177px;
|
||||
width: 64px;
|
||||
height: 54px;
|
||||
}
|
||||
.ghost {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1001px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.inventory_present {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1390px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_01 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -660px -435px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_02 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -660px -504px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_03 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -660px -573px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_04 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -880px -655px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_05 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -880px -724px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_06 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -880px -793px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_07 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1100px -875px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_08 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1100px -944px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_09 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1100px -1013px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_10 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1183px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_11 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1252px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_present_12 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1321px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_birthday {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1459px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_congrats {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1528px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_fortify {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1597px -1617px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_getwell {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px 0px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_goodluck {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -69px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_greeting {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -138px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_nye {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -207px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_opaquePotion {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -276px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_seafoam {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -345px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_shinySeed {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -414px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_snowball {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -483px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_spookySparkles {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1715px -552px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_thankyou {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1929px -642px;
|
||||
background-position: -1715px -621px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_trinket {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1935px -763px;
|
||||
background-position: -1715px -690px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.inventory_special_valentine {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -902px;
|
||||
background-position: -1715px -759px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.knockout {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -854px;
|
||||
background-position: -1407px -1237px;
|
||||
width: 120px;
|
||||
height: 47px;
|
||||
}
|
||||
.pet_key {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -971px;
|
||||
background-position: -1715px -897px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.rebirth_orb {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1913px -971px;
|
||||
background-position: -1715px -966px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.seafoam_star {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -763px;
|
||||
background-position: -1092px -1617px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.shop_armoire {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -1040px;
|
||||
background-position: -1715px -1035px;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.zzz {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1963px -551px;
|
||||
background-position: -1715px -1429px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.zzz_light {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1963px -510px;
|
||||
background-position: -1715px -1388px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.notif_inventory_present_01 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1963px -592px;
|
||||
background-position: -1715px -1470px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_02 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1981px -711px;
|
||||
background-position: -1744px -1470px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_03 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1965px -854px;
|
||||
background-position: -1715px -1499px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_04 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1965px -1040px;
|
||||
background-position: -1744px -1499px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_05 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1957px -1109px;
|
||||
background-position: -1715px -1528px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_06 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -1182px;
|
||||
background-position: -1744px -1528px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_07 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1873px -1182px;
|
||||
background-position: -1715px -1557px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_08 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1902px -1182px;
|
||||
background-position: -1744px -1557px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_09 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1931px -1182px;
|
||||
background-position: -1715px -1586px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_10 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1960px -1182px;
|
||||
background-position: -1744px -1586px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_11 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -1211px;
|
||||
background-position: -1715px -1615px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_present_12 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1873px -1211px;
|
||||
background-position: -1744px -1615px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.notif_inventory_special_birthday {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1982px -971px;
|
||||
background-position: -1746px -1644px;
|
||||
width: 20px;
|
||||
height: 24px;
|
||||
}
|
||||
.notif_inventory_special_congrats {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1981px -740px;
|
||||
background-position: -1757px -1671px;
|
||||
width: 20px;
|
||||
height: 22px;
|
||||
}
|
||||
.notif_inventory_special_getwell {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1965px -1069px;
|
||||
background-position: -1693px -1547px;
|
||||
width: 20px;
|
||||
height: 22px;
|
||||
}
|
||||
.notif_inventory_special_goodluck {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1982px -929px;
|
||||
background-position: -1756px -1429px;
|
||||
width: 20px;
|
||||
height: 26px;
|
||||
}
|
||||
.notif_inventory_special_greeting {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1986px -1069px;
|
||||
background-position: -1693px -1570px;
|
||||
width: 20px;
|
||||
height: 22px;
|
||||
}
|
||||
.notif_inventory_special_nye {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1982px -902px;
|
||||
background-position: -1756px -1388px;
|
||||
width: 24px;
|
||||
height: 26px;
|
||||
}
|
||||
.notif_inventory_special_thankyou {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1982px -996px;
|
||||
background-position: -1715px -1671px;
|
||||
width: 20px;
|
||||
height: 24px;
|
||||
}
|
||||
.notif_inventory_special_valentine {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1986px -1109px;
|
||||
background-position: -1736px -1671px;
|
||||
width: 20px;
|
||||
height: 24px;
|
||||
}
|
||||
.npc_bailey {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -1109px;
|
||||
background-position: -1715px -1104px;
|
||||
width: 60px;
|
||||
height: 72px;
|
||||
}
|
||||
.npc_justin {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -642px;
|
||||
background-position: -220px -203px;
|
||||
width: 84px;
|
||||
height: 120px;
|
||||
}
|
||||
.npc_matt {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -191px -1535px;
|
||||
background-position: -1407px -1098px;
|
||||
width: 195px;
|
||||
height: 138px;
|
||||
}
|
||||
@@ -240,19 +720,19 @@
|
||||
}
|
||||
.banner_flair_dysheartener {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1935px -832px;
|
||||
background-position: -1528px -1237px;
|
||||
width: 69px;
|
||||
height: 18px;
|
||||
}
|
||||
.phobia_dysheartener {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px -1278px;
|
||||
background-position: -307px -220px;
|
||||
width: 201px;
|
||||
height: 195px;
|
||||
}
|
||||
.quest_alligator {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px -862px;
|
||||
background-position: -967px -660px;
|
||||
width: 201px;
|
||||
height: 213px;
|
||||
}
|
||||
@@ -270,19 +750,19 @@
|
||||
}
|
||||
.quest_atom1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -885px -1315px;
|
||||
background-position: 0px -1315px;
|
||||
width: 250px;
|
||||
height: 150px;
|
||||
}
|
||||
.quest_atom2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1387px -1315px;
|
||||
background-position: -1407px -959px;
|
||||
width: 207px;
|
||||
height: 138px;
|
||||
}
|
||||
.quest_atom3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -967px -660px;
|
||||
background-position: -1407px 0px;
|
||||
width: 216px;
|
||||
height: 180px;
|
||||
}
|
||||
@@ -300,13 +780,13 @@
|
||||
}
|
||||
.quest_basilist {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: 0px -1719px;
|
||||
background-position: -502px -1315px;
|
||||
width: 189px;
|
||||
height: 141px;
|
||||
}
|
||||
.quest_beetle {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px -1076px;
|
||||
background-position: -747px -440px;
|
||||
width: 204px;
|
||||
height: 201px;
|
||||
}
|
||||
@@ -318,7 +798,7 @@
|
||||
}
|
||||
.quest_bunny {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1187px -880px;
|
||||
background-position: -1102px -1095px;
|
||||
width: 210px;
|
||||
height: 186px;
|
||||
}
|
||||
@@ -336,7 +816,7 @@
|
||||
}
|
||||
.quest_cow {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -307px -220px;
|
||||
background-position: -1407px -537px;
|
||||
width: 174px;
|
||||
height: 213px;
|
||||
}
|
||||
@@ -348,13 +828,13 @@
|
||||
}
|
||||
.quest_dilatoryDistress1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px -651px;
|
||||
background-position: -1187px -880px;
|
||||
width: 210px;
|
||||
height: 210px;
|
||||
}
|
||||
.quest_dilatoryDistress2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -208px;
|
||||
background-position: 0px -1466px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
@@ -384,13 +864,13 @@
|
||||
}
|
||||
.quest_egg {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px 0px;
|
||||
background-position: -1407px -751px;
|
||||
width: 165px;
|
||||
height: 207px;
|
||||
}
|
||||
.quest_evilsanta {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -510px;
|
||||
background-position: -692px -1315px;
|
||||
width: 118px;
|
||||
height: 131px;
|
||||
}
|
||||
@@ -420,7 +900,7 @@
|
||||
}
|
||||
.quest_frog {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -440px -1315px;
|
||||
background-position: -880px -1095px;
|
||||
width: 221px;
|
||||
height: 213px;
|
||||
}
|
||||
@@ -438,7 +918,7 @@
|
||||
}
|
||||
.quest_goldenknight2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1136px -1315px;
|
||||
background-position: -251px -1315px;
|
||||
width: 250px;
|
||||
height: 150px;
|
||||
}
|
||||
@@ -450,7 +930,7 @@
|
||||
}
|
||||
.quest_gryphon {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -747px -440px;
|
||||
background-position: -1407px -181px;
|
||||
width: 216px;
|
||||
height: 177px;
|
||||
}
|
||||
@@ -468,7 +948,7 @@
|
||||
}
|
||||
.quest_hedgehog {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1407px -1100px;
|
||||
background-position: -527px -220px;
|
||||
width: 219px;
|
||||
height: 186px;
|
||||
}
|
||||
@@ -492,7 +972,7 @@
|
||||
}
|
||||
.quest_kraken {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -527px -220px;
|
||||
background-position: -1407px -359px;
|
||||
width: 216px;
|
||||
height: 177px;
|
||||
}
|
||||
@@ -516,7 +996,7 @@
|
||||
}
|
||||
.quest_mayhemMistiflying1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1844px -359px;
|
||||
background-position: -151px -1466px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
@@ -526,87 +1006,3 @@
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_mayhemMistiflying3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -880px -1095px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_monkey {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1100px -1095px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_moon1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px 0px;
|
||||
width: 216px;
|
||||
height: 216px;
|
||||
}
|
||||
.quest_moon2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1407px 0px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_moon3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1407px -220px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_moonstone1 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1407px -440px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_moonstone2 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1407px -660px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_moonstone3 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1407px -880px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_nudibranch {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px -217px;
|
||||
width: 216px;
|
||||
height: 216px;
|
||||
}
|
||||
.quest_octopus {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -662px -1315px;
|
||||
width: 222px;
|
||||
height: 177px;
|
||||
}
|
||||
.quest_owl {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: 0px -1315px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
.quest_peacock {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -1627px -434px;
|
||||
width: 216px;
|
||||
height: 216px;
|
||||
}
|
||||
.quest_penguin {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: 0px -1535px;
|
||||
width: 190px;
|
||||
height: 183px;
|
||||
}
|
||||
.quest_pterodactyl {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-main-13.png');
|
||||
background-position: -220px -1315px;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 11 KiB |