mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-29 20:24:53 +01:00
Compare commits
501 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f666f3cd04 | ||
|
|
236bd6cec4 | ||
|
|
e124c36274 | ||
|
|
d5511a0047 | ||
|
|
87f003f392 | ||
|
|
930a869365 | ||
|
|
809da8add0 | ||
|
|
1dad176320 | ||
|
|
1596c6218f | ||
|
|
0d3aba950a | ||
|
|
f1110e0f89 | ||
|
|
7273f8f6d9 | ||
|
|
a0ae200a54 | ||
|
|
ca448f081d | ||
|
|
cd27afa9f0 | ||
|
|
8bc8183895 | ||
|
|
c26c52f1fe | ||
|
|
8d5becc9ce | ||
|
|
cf7f6e2a67 | ||
|
|
acad3b8873 | ||
|
|
04f4eb8490 | ||
|
|
8c8af83dfc | ||
|
|
86d65956d9 | ||
|
|
42c5e6c22b | ||
|
|
79b51a40ce | ||
|
|
79c3efaf9c | ||
|
|
74c6a891fc | ||
|
|
9a5d17f538 | ||
|
|
070c4a8fbd | ||
|
|
2bbc4f4f4d | ||
|
|
39c00ea433 | ||
|
|
dd6c1c764a | ||
|
|
9b456d1760 | ||
|
|
acf1031317 | ||
|
|
5d45204d8b | ||
|
|
37a71924fe | ||
|
|
9cf2408988 | ||
|
|
638525f8d8 | ||
|
|
2c37ba3cee | ||
|
|
ad5b2fe540 | ||
|
|
bfb6daad51 | ||
|
|
281b8e2b7c | ||
|
|
3f88ea2378 | ||
|
|
9c6275d4ab | ||
|
|
fd3c8ddc8b | ||
|
|
72f47ad4e6 | ||
|
|
74c9a1b02d | ||
|
|
ffa561473c | ||
|
|
080ffae4e1 | ||
|
|
e395182c95 | ||
|
|
68f4275c44 | ||
|
|
4bf4c3a6c2 | ||
|
|
f00bb29192 | ||
|
|
016447ec77 | ||
|
|
fa024e071b | ||
|
|
28fec237fe | ||
|
|
e4bb82768c | ||
|
|
65eca22407 | ||
|
|
cea1597ee1 | ||
|
|
3906952154 | ||
|
|
6169b9d0ae | ||
|
|
69cac7e504 | ||
|
|
febf3f0024 | ||
|
|
563f40e4b7 | ||
|
|
e2b06161e1 | ||
|
|
e7de8b8e2f | ||
|
|
a0624d9507 | ||
|
|
cddd0df4f2 | ||
|
|
220bfb3517 | ||
|
|
2106a5ebd3 | ||
|
|
bbffa9830b | ||
|
|
caa546eb62 | ||
|
|
4e83059652 | ||
|
|
903cdb36ef | ||
|
|
d8128cc3db | ||
|
|
f888e80b01 | ||
|
|
fd7aedbff2 | ||
|
|
6c5234313d | ||
|
|
08aa5758b4 | ||
|
|
1415e344c0 | ||
|
|
2ce7915f06 | ||
|
|
838c8b4e08 | ||
|
|
1590d955cd | ||
|
|
2690caed35 | ||
|
|
dc2d4fa10b | ||
|
|
1540ec89ee | ||
|
|
f304d4fe52 | ||
|
|
023e433a5c | ||
|
|
ef4aeb29ab | ||
|
|
2b80931202 | ||
|
|
2950713712 | ||
|
|
118f3bd1bb | ||
|
|
69f1343ea8 | ||
|
|
918ee02d64 | ||
|
|
0cac34dd26 | ||
|
|
1c859fc91f | ||
|
|
857aa5827b | ||
|
|
28e8ec2d2c | ||
|
|
856f13d213 | ||
|
|
121fd38bd1 | ||
|
|
36d72f5f7a | ||
|
|
f1b8bd80e7 | ||
|
|
84d2ce6a3f | ||
|
|
76010e6c8f | ||
|
|
c707b6c99b | ||
|
|
e4bd466cc7 | ||
|
|
001b8eb894 | ||
|
|
9abcfe8614 | ||
|
|
bc6102551d | ||
|
|
959a3ff85b | ||
|
|
518b874f64 | ||
|
|
6cc359a935 | ||
|
|
514d35c0be | ||
|
|
13da92ea68 | ||
|
|
03c4d82b7d | ||
|
|
d905ab7f86 | ||
|
|
c6560b6b1b | ||
|
|
c61f660255 | ||
|
|
2f1b683ec9 | ||
|
|
47bb217068 | ||
|
|
f49fd05da1 | ||
|
|
b0341aa06f | ||
|
|
b07ec18e33 | ||
|
|
12930a2bac | ||
|
|
91f5c47d9d | ||
|
|
fe7850d10f | ||
|
|
c5c2da75bf | ||
|
|
969607cd3b | ||
|
|
2a1f52a359 | ||
|
|
47d9594679 | ||
|
|
97e40c81f3 | ||
|
|
c8b61a2f7d | ||
|
|
e9543f0d28 | ||
|
|
77b88490e4 | ||
|
|
7fc2500bfd | ||
|
|
fb229acb58 | ||
|
|
6ce83d1fa4 | ||
|
|
2be4815aea | ||
|
|
1dbc42f48a | ||
|
|
89279c8aed | ||
|
|
faedb13598 | ||
|
|
c0c74659c5 | ||
|
|
bf5ad2db1f | ||
|
|
7d99873960 | ||
|
|
e02ef00397 | ||
|
|
23c5c4211c | ||
|
|
69cc134fff | ||
|
|
ffd9400cb7 | ||
|
|
5be91ef842 | ||
|
|
3123183e46 | ||
|
|
49cca7a601 | ||
|
|
7fbd38d18c | ||
|
|
1f95376d39 | ||
|
|
2da0a1e88c | ||
|
|
afacd3e1cf | ||
|
|
a69b9e6705 | ||
|
|
e4e5d10316 | ||
|
|
27c38bdf45 | ||
|
|
ea24eeb019 | ||
|
|
55a8eef3e1 | ||
|
|
92cbb4a07d | ||
|
|
3f96d05365 | ||
|
|
0b72f6a613 | ||
|
|
5e1e6be518 | ||
|
|
472ec99291 | ||
|
|
0284e9a4e3 | ||
|
|
1a0721c078 | ||
|
|
6b6b548ac5 | ||
|
|
30f3d786bb | ||
|
|
07bbba6789 | ||
|
|
6afb2bd0d4 | ||
|
|
f1a3bd5001 | ||
|
|
3f6a13d209 | ||
|
|
3658e41fec | ||
|
|
c69d5c7ae6 | ||
|
|
747f9e6a99 | ||
|
|
7755ab090b | ||
|
|
9ed17df1e3 | ||
|
|
faeb040a83 | ||
|
|
0a1ae1375e | ||
|
|
9756030fa2 | ||
|
|
c66172b74b | ||
|
|
281f6d1806 | ||
|
|
237095d109 | ||
|
|
fa788f49fc | ||
|
|
0817cf96e1 | ||
|
|
97e1d75dce | ||
|
|
52bf20c27d | ||
|
|
5dbaf39aba | ||
|
|
66d402c985 | ||
|
|
8048146223 | ||
|
|
e2c07e458d | ||
|
|
90a9e8e192 | ||
|
|
f8039f48a6 | ||
|
|
04337f8e83 | ||
|
|
45297f8bf9 | ||
|
|
6f112c29f2 | ||
|
|
4d1edb363c | ||
|
|
4e303cc592 | ||
|
|
798a975185 | ||
|
|
eb2b46fc5d | ||
|
|
29854d3bdb | ||
|
|
f8751b002c | ||
|
|
cd545e08d5 | ||
|
|
f69bb4f023 | ||
|
|
847081d2b2 | ||
|
|
8112d46ea4 | ||
|
|
d13bded647 | ||
|
|
1de4ab3612 | ||
|
|
f9f22f313f | ||
|
|
f57eed85a8 | ||
|
|
10dd3318ab | ||
|
|
cbef83c14a | ||
|
|
59709a8590 | ||
|
|
f85f2a0c6d | ||
|
|
605a5a1d5c | ||
|
|
2d5d786c8e | ||
|
|
5efe5b7b10 | ||
|
|
3e92bb22fa | ||
|
|
1249b9d410 | ||
|
|
197aafe092 | ||
|
|
79829ca128 | ||
|
|
adaa1d9a3e | ||
|
|
3e6691dbbb | ||
|
|
046761b9aa | ||
|
|
0b0466b960 | ||
|
|
f8d4a2bd6b | ||
|
|
1af59a3770 | ||
|
|
bbcb13c91b | ||
|
|
d27dc46c50 | ||
|
|
679459b83b | ||
|
|
5a619773d5 | ||
|
|
ad76ab1315 | ||
|
|
15eb8db925 | ||
|
|
a0e92c5605 | ||
|
|
eac3e36c07 | ||
|
|
0b8def555b | ||
|
|
5f5fa5c2eb | ||
|
|
1eac8bbbbe | ||
|
|
49c7580cd4 | ||
|
|
dca958f2e2 | ||
|
|
eae5f0d605 | ||
|
|
6ab091645c | ||
|
|
d66041c280 | ||
|
|
de070a450a | ||
|
|
eaaab35f31 | ||
|
|
6a63f080ad | ||
|
|
c42f81b629 | ||
|
|
9a78a7b896 | ||
|
|
8b70721137 | ||
|
|
44ffbd716d | ||
|
|
5bfc3a5ff4 | ||
|
|
0ba5df4164 | ||
|
|
52a59c8192 | ||
|
|
c1a860494d | ||
|
|
395dafa127 | ||
|
|
bab41647f5 | ||
|
|
8582a67308 | ||
|
|
0d58fb0fd3 | ||
|
|
1d2482f8bc | ||
|
|
f4cf906127 | ||
|
|
559f9b1825 | ||
|
|
c7039bc9ea | ||
|
|
f929d36e1a | ||
|
|
254d1a3465 | ||
|
|
442aae8a35 | ||
|
|
bcb0ed0a5c | ||
|
|
a48b8f0e34 | ||
|
|
7eeeda2aae | ||
|
|
a5ad9c30f0 | ||
|
|
ac732b2c85 | ||
|
|
a56b2d68fb | ||
|
|
25b0ff38c4 | ||
|
|
dcc06931cc | ||
|
|
bc3ebbd095 | ||
|
|
e5b9581743 | ||
|
|
4b9fe49e3a | ||
|
|
ab4c8b0a46 | ||
|
|
f6c26fe869 | ||
|
|
80e9735b28 | ||
|
|
aa6f188bd9 | ||
|
|
e8b7660376 | ||
|
|
7d76622410 | ||
|
|
928e5f66c4 | ||
|
|
6a343535c0 | ||
|
|
f58f6acb44 | ||
|
|
64754777ed | ||
|
|
3b5e4b6d84 | ||
|
|
9383578cb8 | ||
|
|
474672ec64 | ||
|
|
25c6691793 | ||
|
|
3ea7b72024 | ||
|
|
2d6f05a9a4 | ||
|
|
28637286d6 | ||
|
|
874887b790 | ||
|
|
c977e5ebb5 | ||
|
|
f040e668f3 | ||
|
|
55a15f938c | ||
|
|
8c4f35daf4 | ||
|
|
8f38ce3424 | ||
|
|
b8f57a74d0 | ||
|
|
7ed26c0dbe | ||
|
|
e8f5b26d4d | ||
|
|
0273648b6b | ||
|
|
b6fdac8885 | ||
|
|
00e6389672 | ||
|
|
e02c669b61 | ||
|
|
f0cb7c6bf3 | ||
|
|
571ef0b309 | ||
|
|
74328d1bcc | ||
|
|
d34a9d828c | ||
|
|
2fd35b3a40 | ||
|
|
e27512f626 | ||
|
|
dbf9cb3b4e | ||
|
|
34c1245519 | ||
|
|
f602bfe438 | ||
|
|
9aa4b8aa64 | ||
|
|
5a150ebc5b | ||
|
|
cbe1892b50 | ||
|
|
13df60e0dd | ||
|
|
3ff7692528 | ||
|
|
111bba84dc | ||
|
|
b0d2b72b88 | ||
|
|
696317ea8a | ||
|
|
593178a46a | ||
|
|
f8fe16482d | ||
|
|
5108480ec5 | ||
|
|
95968b1b1c | ||
|
|
566569af98 | ||
|
|
6693e9fca9 | ||
|
|
431bde56d2 | ||
|
|
7cf17c0e63 | ||
|
|
49561bfc8c | ||
|
|
8cbbb58e78 | ||
|
|
905549e379 | ||
|
|
5d45c7209a | ||
|
|
371cddfe17 | ||
|
|
fcfac30caa | ||
|
|
b094fb1e52 | ||
|
|
a2dd82b6db | ||
|
|
e6071610e4 | ||
|
|
bdd0e2bb79 | ||
|
|
054a9a6f2b | ||
|
|
35b9ed6273 | ||
|
|
e65277baa5 | ||
|
|
421bd8624c | ||
|
|
4562c6422a | ||
|
|
a5cd9f2473 | ||
|
|
18bbdfa84b | ||
|
|
d8c37f6e2d | ||
|
|
7f38c61c70 | ||
|
|
1c018cedb1 | ||
|
|
80892bd6a8 | ||
|
|
6801dae75d | ||
|
|
59e1de6771 | ||
|
|
5b240a1950 | ||
|
|
3ec3722038 | ||
|
|
d798ebadfe | ||
|
|
6cbddef627 | ||
|
|
016de411c9 | ||
|
|
2173f53883 | ||
|
|
f2e5bc52e5 | ||
|
|
393a9290e9 | ||
|
|
ad5045bc09 | ||
|
|
9b515ebdd1 | ||
|
|
97bf9ee8e8 | ||
|
|
f5ba636579 | ||
|
|
4dd7e49552 | ||
|
|
d2f673ef1e | ||
|
|
e198dd551a | ||
|
|
0bfc9d9516 | ||
|
|
d4e20ee4aa | ||
|
|
a751a367fc | ||
|
|
d323be19c6 | ||
|
|
be3f61a94b | ||
|
|
f1bb2db73b | ||
|
|
a622344d44 | ||
|
|
e279a3550b | ||
|
|
70aab3059c | ||
|
|
c264e37182 | ||
|
|
b31bc15493 | ||
|
|
ba19c00617 | ||
|
|
93aa92de7c | ||
|
|
d021680945 | ||
|
|
f9595af8a5 | ||
|
|
d2756278c3 | ||
|
|
2e2dc179c4 | ||
|
|
acf7b811ab | ||
|
|
d5170251c0 | ||
|
|
c9ba9054e3 | ||
|
|
d4aac1ee4b | ||
|
|
9615a332a5 | ||
|
|
417455e5ef | ||
|
|
136502a110 | ||
|
|
425887c1e4 | ||
|
|
cfa8a5190f | ||
|
|
df5be81706 | ||
|
|
08b3491047 | ||
|
|
e73c3147c1 | ||
|
|
a43254000e | ||
|
|
4e3c984baf | ||
|
|
c112e923f1 | ||
|
|
540353f024 | ||
|
|
2b9b5e369e | ||
|
|
cb38475765 | ||
|
|
8bb92577b0 | ||
|
|
fb26cbd26d | ||
|
|
a0de5cd8f8 | ||
|
|
9fe10b1818 | ||
|
|
d8dd39422a | ||
|
|
3f9b710773 | ||
|
|
8a8bab4be1 | ||
|
|
2a0747ed72 | ||
|
|
a5196e94f6 | ||
|
|
009ab26711 | ||
|
|
3fabf3391f | ||
|
|
8020990264 | ||
|
|
a2cfeafc02 | ||
|
|
d04a4fb1ed | ||
|
|
aeb86db306 | ||
|
|
49960c0e32 | ||
|
|
932cb5cf6a | ||
|
|
74d6e77504 | ||
|
|
8400f1786b | ||
|
|
d7bd5dd9f8 | ||
|
|
3288b0de33 | ||
|
|
c025ffbd10 | ||
|
|
afb5b473a3 | ||
|
|
aeee29f5fa | ||
|
|
0cca2a07a2 | ||
|
|
55d94c129a | ||
|
|
358e1aed22 | ||
|
|
36241f061f | ||
|
|
b6201a3b75 | ||
|
|
005f74d918 | ||
|
|
926e188017 | ||
|
|
94da808279 | ||
|
|
7568dd52e9 | ||
|
|
c6e2b78982 | ||
|
|
b6104c3ef3 | ||
|
|
56b5c960f0 | ||
|
|
528abf77af | ||
|
|
8db6b7c6cb | ||
|
|
578dee59bd | ||
|
|
d40c923e6e | ||
|
|
3c4c64b023 | ||
|
|
c84d6ba141 | ||
|
|
5f3b147d2a | ||
|
|
ff08e8b586 | ||
|
|
cb2acbfefd | ||
|
|
b16da35585 | ||
|
|
826d7b85d7 | ||
|
|
6bcc6a15e2 | ||
|
|
b600eceb49 | ||
|
|
b83ef872c9 | ||
|
|
4ebc2e2175 | ||
|
|
2f4b8c569a | ||
|
|
85b5b5a62d | ||
|
|
e271e57f63 | ||
|
|
558fb145b5 | ||
|
|
fc30456b53 | ||
|
|
68b2d19b04 | ||
|
|
6d33acccf4 | ||
|
|
acee4bad80 | ||
|
|
30fe5088b8 | ||
|
|
69602f93e9 | ||
|
|
0109aa4250 | ||
|
|
2dc0958678 | ||
|
|
52f4e5f37d | ||
|
|
c014da297c | ||
|
|
285041cdee | ||
|
|
6a82206f81 | ||
|
|
8b6052a3ca | ||
|
|
04fd907a45 | ||
|
|
70343079f1 | ||
|
|
df952eece5 | ||
|
|
e3a619c7ff | ||
|
|
23f531372b | ||
|
|
97b15006fd | ||
|
|
35b92f13a3 | ||
|
|
556a7e5229 | ||
|
|
378625b4af | ||
|
|
ee15e29ba4 | ||
|
|
ed880a665a | ||
|
|
3c7f71d214 | ||
|
|
edac06b0d1 | ||
|
|
24562f8d60 | ||
|
|
97840ed732 | ||
|
|
76499412ed | ||
|
|
9b10f348cc | ||
|
|
17b0329c43 | ||
|
|
cda84a6d68 | ||
|
|
306505ebab | ||
|
|
2476cdd873 | ||
|
|
8465dd69be | ||
|
|
461e7445c2 | ||
|
|
24df8d8f2f | ||
|
|
2bca92b4d5 | ||
|
|
c3843cae80 | ||
|
|
816e4a2f19 | ||
|
|
d0d4927e59 |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.git
|
||||
website
|
||||
@@ -14,7 +14,7 @@ files:
|
||||
owner: root
|
||||
group: users
|
||||
content: |
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@3
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@4
|
||||
container_commands:
|
||||
01_makeBabel:
|
||||
command: "touch /tmp/.babel.json"
|
||||
|
||||
@@ -10,7 +10,6 @@ dist-client/
|
||||
# Not linted
|
||||
migrations/*
|
||||
website/client-old/
|
||||
debug-scripts/*
|
||||
scripts/*
|
||||
test/server_side/**/*
|
||||
test/client-old/spec/**/*
|
||||
@@ -21,8 +20,4 @@ website/common/browserify.js
|
||||
test/content/**/*
|
||||
Gruntfile.js
|
||||
gulpfile.js
|
||||
gulp
|
||||
webpack
|
||||
test/client/e2e
|
||||
test/client/unit/index.js
|
||||
test/client/unit/karma.conf.js
|
||||
gulp
|
||||
@@ -4,7 +4,7 @@
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg/es6",
|
||||
"habitrpg"
|
||||
"habitrpg",
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
}
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -9,5 +9,6 @@ Fixes put_issue_url_here
|
||||
|
||||
|
||||
[//]: # (Put User ID in here - found in Settings -> API)
|
||||
|
||||
----
|
||||
UUID:
|
||||
|
||||
38
.travis.yml
38
.travis.yml
@@ -1,17 +1,33 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '4.3.1'
|
||||
- '6'
|
||||
sudo: required
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- "npm install -g npm@3"
|
||||
- "npm install -g gulp"
|
||||
- "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10"
|
||||
- "echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list"
|
||||
- "sudo apt-get update"
|
||||
- "sudo apt-get install mongodb-org-server"
|
||||
- $CXX --version
|
||||
- npm install -g npm@4
|
||||
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
|
||||
before_script:
|
||||
- 'npm install -g grunt-cli mocha'
|
||||
- npm run test:build
|
||||
- cp config.json.example config.json
|
||||
- "until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done"
|
||||
- "export DISPLAY=:99"
|
||||
- if [ $REQUIRES_SERVER ]; then until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done; export DISPLAY=:99; fi
|
||||
after_script:
|
||||
- "./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js"
|
||||
- ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js
|
||||
script: npm run $TEST
|
||||
env:
|
||||
global:
|
||||
- CXX=g++-4.8
|
||||
- DISABLE_REQUEST_LOGGING=true
|
||||
matrix:
|
||||
- TEST="lint"
|
||||
- TEST="test:api-v3" REQUIRES_SERVER=true
|
||||
- TEST="test:sanity"
|
||||
- TEST="test:content"
|
||||
- TEST="test:common"
|
||||
- TEST="test:karma"
|
||||
- TEST="client:unit"
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -17,20 +17,22 @@ RUN apt-get install -y \
|
||||
python
|
||||
|
||||
# Install NodeJS
|
||||
RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
|
||||
RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Install npm@latest
|
||||
RUN curl -sL https://www.npmjs.org/install.sh | sh
|
||||
|
||||
# Clean up package management
|
||||
RUN apt-get clean
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g npm@3
|
||||
RUN npm install -g gulp grunt-cli bower
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
WORKDIR /habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitrpg.git /habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
|
||||
13
Gruntfile.js
13
Gruntfile.js
@@ -57,7 +57,7 @@ module.exports = function(grunt) {
|
||||
files: [
|
||||
{expand: true, cwd: 'website/client-old/', src: 'favicon.ico', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/client-old/', src: 'favicon_192x192.png', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/static/sprites'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'backer-only/*.gif', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'npc_ian.gif', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'quest_*.gif', dest: 'website/build/'},
|
||||
@@ -78,6 +78,7 @@ module.exports = function(grunt) {
|
||||
'website/build/favicon.ico',
|
||||
'website/build/favicon_192x192.png',
|
||||
'website/build/*.png',
|
||||
'website/build/static/sprites/*.png',
|
||||
'website/build/*.gif',
|
||||
'website/build/bower_components/bootstrap/dist/fonts/*'
|
||||
],
|
||||
@@ -126,15 +127,7 @@ module.exports = function(grunt) {
|
||||
// Register tasks.
|
||||
grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']);
|
||||
grunt.registerTask('build:dev', ['cssmin', 'stylus']);
|
||||
grunt.registerTask('build:test', ['test:prepare:translations', 'build:dev']);
|
||||
|
||||
grunt.registerTask('test:prepare:translations', function() {
|
||||
var i18n = require('./website/server/libs/i18n'),
|
||||
fs = require('fs');
|
||||
fs.writeFileSync('test/client-old/spec/mocks/translations.js',
|
||||
"if(!window.env) window.env = {};\n" +
|
||||
"window.env.translations = " + JSON.stringify(i18n.translations['en']) + ';');
|
||||
});
|
||||
grunt.registerTask('build:test', ['build:dev']);
|
||||
|
||||
// Load tasks
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
|
||||
20
README.md
20
README.md
@@ -1,4 +1,4 @@
|
||||
Habitica [](https://travis-ci.org/HabitRPG/habitrpg) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/r/HabitRPG/habitrpg?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/github/HabitRPG/habitica?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||
===============
|
||||
|
||||
[Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
||||
@@ -10,21 +10,3 @@ For an introduction to the technologies used and how the software is organized,
|
||||
To set up a local install of Habitica for development and testing, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally), which contains instructions for Windows, *nix / Mac OS, and Vagrant.
|
||||
|
||||
Then read [Guidance for Blacksmiths](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths) for additional instructions and useful tips.
|
||||
|
||||
## Debug Scripts
|
||||
|
||||
In the `./debug-scripts/` folder, there are a few files. Here's a sample:
|
||||
|
||||
```bash
|
||||
grant-all-equipment.js
|
||||
grant-all-mounts.js
|
||||
grant-all-pets.js
|
||||
```
|
||||
|
||||
You can run them by doing:
|
||||
|
||||
```bash
|
||||
node debug-scripts/name-of-script.js
|
||||
```
|
||||
|
||||
If there are more arguments required to make the script work, it will print out the usage and an explanation of what the script does.
|
||||
|
||||
@@ -36,14 +36,15 @@
|
||||
"jquery-ui": "1.10.3",
|
||||
"jquery.cookie": "1.4.0",
|
||||
"js-emoji": "snicker/js-emoji#f25d8a303f",
|
||||
"ngInfiniteScroll": "1.0.0",
|
||||
"ngInfiniteScroll": "1.1.0",
|
||||
"pnotify": "1.3.1",
|
||||
"sticky": "1.0.3",
|
||||
"swagger-ui": "wordnik/swagger-ui#v2.0.24",
|
||||
"smart-app-banner": "78ef9c0679723b25be1a0ae04f7b4aef7cbced4f",
|
||||
"habitica-markdown": "1.2.2",
|
||||
"pusher-js-auth": "^2.0.0",
|
||||
"pusher-websocket-iso": "pusher#^3.2.0"
|
||||
"pusher-websocket-iso": "pusher#^3.2.0",
|
||||
"taggle": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "1.3.9"
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
"PORT":3000,
|
||||
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
||||
"IP":"0.0.0.0",
|
||||
"CORES":1,
|
||||
"WEB_CONCURRENCY":1,
|
||||
"BASE_URL":"http://localhost:3000",
|
||||
"FACEBOOK_ANALYTICS":"1234567890123456",
|
||||
"FACEBOOK_KEY":"123456789012345",
|
||||
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"GOOGLE_CLIENT_ID":"123456789012345",
|
||||
@@ -79,6 +78,7 @@
|
||||
},
|
||||
"SLACK": {
|
||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/"
|
||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||
"SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { MongoClient as mongo } from 'mongodb';
|
||||
import config from '../config';
|
||||
|
||||
module.exports.updateUser = (_id, path, value) => {
|
||||
mongo.connect(config.NODE_DB_URI, (err, db) => {
|
||||
if (err) throw err;
|
||||
|
||||
let collection = db.collection('users');
|
||||
collection.updateOne(
|
||||
{ _id },
|
||||
{ $set: { [`${path}`]: value } },
|
||||
(updateErr, result) => {
|
||||
if (updateErr) throw updateErr;
|
||||
console.log('done updating', _id);
|
||||
db.close();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require('babel-register');
|
||||
|
||||
let _ = require('lodash');
|
||||
let updateUser = require('./_helper').updateUser;
|
||||
|
||||
let userId = process.argv[2];
|
||||
|
||||
if (!userId) {
|
||||
console.error('USAGE: node debug-scripts/grant-all-equipment.js <user_id>');
|
||||
console.error('EFFECT: Adds all gear to specified user');
|
||||
return;
|
||||
}
|
||||
|
||||
let gearFlat = require('../common').content.gear.flat;
|
||||
|
||||
let userGear = {};
|
||||
|
||||
_.each(gearFlat, (piece, key) => {
|
||||
userGear[key] = true;
|
||||
});
|
||||
|
||||
updateUser(userId, 'items.gear.owned', userGear);
|
||||
@@ -1,28 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require('babel-register');
|
||||
|
||||
let _ = require('lodash');
|
||||
let updateUser = require('./_helper').updateUser;
|
||||
let userId = process.argv[2];
|
||||
|
||||
if (!userId) {
|
||||
console.error('USAGE: node debug-scripts/grant-all-mounts.js <user_id>');
|
||||
console.error('EFFECT: Adds all mounts to specified user');
|
||||
return;
|
||||
}
|
||||
|
||||
let dropMounts = require('../common').content.mounts;
|
||||
let questMounts = require('../common').content.questMounts;
|
||||
let specialMounts = require('../common').content.specialMounts;
|
||||
let premiumMounts = require('../common').content.premiumPets; // premium mounts isn't exposed on the content object
|
||||
|
||||
let userMounts = {};
|
||||
|
||||
_.each([ dropMounts, questMounts, specialMounts, premiumMounts ], (set) => {
|
||||
_.each(set, (pet, key) => {
|
||||
userMounts[key] = true;
|
||||
});
|
||||
})
|
||||
|
||||
updateUser(userId, 'items.mounts', userMounts);
|
||||
@@ -1,28 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require('babel-register');
|
||||
|
||||
let _ = require('lodash');
|
||||
let updateUser = require('./_helper').updateUser;
|
||||
let userId = process.argv[2];
|
||||
|
||||
if (!userId) {
|
||||
console.error('USAGE: node debug-scripts/grant-all-pets.js <user_id>');
|
||||
console.error('EFFECT: Adds all pets to specified user');
|
||||
return;
|
||||
}
|
||||
|
||||
let dropPets = require('../common').content.pets;
|
||||
let questPets = require('../common').content.questPets;
|
||||
let specialPets = require('../common').content.specialPets;
|
||||
let premiumPets = require('../common').content.premiumPets;
|
||||
|
||||
let userPets = {};
|
||||
|
||||
_.each([ dropPets, questPets, specialPets, premiumPets ], (set) => {
|
||||
_.each(set, (pet, key) => {
|
||||
userPets[key] = 95;
|
||||
});
|
||||
})
|
||||
|
||||
updateUser(userId, 'items.pets', userPets);
|
||||
@@ -1,3 +1,3 @@
|
||||
web:
|
||||
volumes:
|
||||
- '.:/habitrpg'
|
||||
- '.:/habitrpg'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "3000:3000"
|
||||
links:
|
||||
- mongo
|
||||
- mongo
|
||||
environment:
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
|
||||
mongo:
|
||||
image: mongo
|
||||
ports:
|
||||
- "27017:27017"
|
||||
- "27017:27017"
|
||||
|
||||
@@ -20,3 +20,7 @@ gulp.task('apidoc', ['apidoc:clean'], (done) => {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('apidoc:watch', ['apidoc'], () => {
|
||||
return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']);
|
||||
});
|
||||
|
||||
@@ -12,6 +12,9 @@ import {each} from 'lodash';
|
||||
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
|
||||
const DIST_PATH = 'website/assets/sprites/dist/';
|
||||
|
||||
const IMG_DIST_PATH_NEW_CLIENT = 'website/static/sprites/';
|
||||
const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/';
|
||||
|
||||
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
@@ -25,7 +28,7 @@ gulp.task('sprites:largeSprites', () => {
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', (done) => {
|
||||
clean(`${DIST_PATH}spritesmith*`, done);
|
||||
clean(`{${DIST_PATH}spritesmith*,${IMG_DIST_PATH_NEW_CLIENT}spritesmith*,${CSS_DIST_PATH_NEW_CLIENT}spritesmith*}`, done);
|
||||
});
|
||||
|
||||
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
|
||||
@@ -66,14 +69,16 @@ function createSpritesStream (name, src) {
|
||||
algorithm: 'binary-tree',
|
||||
padding: 1,
|
||||
cssTemplate: 'website/assets/sprites/css/css.template.handlebars',
|
||||
cssVarMap: cssVarMap
|
||||
cssVarMap: cssVarMap,
|
||||
}));
|
||||
|
||||
let imgStream = spriteData.img
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
let cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
stream.add(imgStream);
|
||||
@@ -148,4 +153,9 @@ function cssVarMap (sprite) {
|
||||
}
|
||||
if (~sprite.name.indexOf('shirt'))
|
||||
sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts
|
||||
if (~sprite.name.indexOf('hair_base')) {
|
||||
let styleArray = sprite.name.split('_').slice(2,3);
|
||||
if (Number(styleArray[0]) > 14)
|
||||
sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import Bluebird from 'bluebird';
|
||||
import runSequence from 'run-sequence';
|
||||
import os from 'os';
|
||||
import nconf from 'nconf';
|
||||
import fs from 'fs';
|
||||
|
||||
const i18n = require('../website/server/libs/i18n');
|
||||
|
||||
// TODO rewrite
|
||||
|
||||
@@ -72,10 +75,17 @@ gulp.task('test:prepare:server', ['test:prepare:mongo'], () => {
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:build', ['build'], (cb) => {
|
||||
exec(testBin('grunt build:test'), cb);
|
||||
gulp.task('test:prepare:translations', (cb) => {
|
||||
fs.writeFile(
|
||||
'test/client-old/spec/mocks/translations.js',
|
||||
`if(!window.env) window.env = {};
|
||||
window.env.translations = ${JSON.stringify(i18n.translations['en'])};`, cb);
|
||||
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:build', ['build', 'test:prepare:translations']);
|
||||
// exec(testBin('grunt build:test'), cb);
|
||||
|
||||
gulp.task('test:prepare:webdriver', (cb) => {
|
||||
exec('npm run test:prepare:webdriver', cb);
|
||||
});
|
||||
@@ -175,32 +185,6 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:server_side', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(SERVER_SIDE_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
cb(err);
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:server_side:safe', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(SERVER_SIDE_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
testResults.push({
|
||||
suite: 'Server Side Specs',
|
||||
pass: testCount(stdout, /(\d+) passing/),
|
||||
fail: testCount(stdout, /(\d+) failing/),
|
||||
pend: testCount(stdout, /(\d+) pending/),
|
||||
});
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:karma', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(KARMA_TEST_COMMAND),
|
||||
@@ -296,7 +280,7 @@ gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => {
|
||||
|
||||
gulp.task('test:api-v3:unit', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/unit --recursive'),
|
||||
testBin('mocha test/api/v3/unit --recursive --require ./test/helpers/start-server'),
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
@@ -314,7 +298,7 @@ gulp.task('test:api-v3:unit:watch', () => {
|
||||
|
||||
gulp.task('test:api-v3:integration', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive'),
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
@@ -334,7 +318,7 @@ gulp.task('test:api-v3:integration:watch', () => {
|
||||
|
||||
gulp.task('test:api-v3:integration:separate-server', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive', 'LOAD_SERVER=0'),
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => done(err)
|
||||
);
|
||||
|
||||
@@ -84,8 +84,8 @@ gulp.task('transifex:malformedStrings', () => {
|
||||
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithMalformedInterpolations.push(malformedString);
|
||||
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
||||
let missingInterploationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterploationString);
|
||||
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
116
migrations/20161002_add_missing_webhook_type.js
Normal file
116
migrations/20161002_add_missing_webhook_type.js
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
|
||||
/****************************************
|
||||
* Author: Blade Barringer @crookedneighbor
|
||||
*
|
||||
* Reason: Webhooks have been moved from
|
||||
* being an object on preferences.webhooks
|
||||
* to being an array on webhooks. In addition
|
||||
* they support a type and options and label
|
||||
* ***************************************/
|
||||
|
||||
global.Promise = require('bluebird');
|
||||
const TaskQueue = require('cwait').TaskQueue;
|
||||
const logger = require('./utils/logger');
|
||||
const Timer = require('./utils/timer');
|
||||
const connectToDb = require('./utils/connect').connectToDb;
|
||||
const closeDb = require('./utils/connect').closeDb;
|
||||
const validator = require('validator');
|
||||
|
||||
const timer = new Timer();
|
||||
const MIGRATION_NAME = '20161002_add_missing_webhook_type.js';
|
||||
|
||||
// const DB_URI = 'mongodb://username:password@dsXXXXXX-a0.mlab.com:XXXXX,dsXXXXXX-a1.mlab.com:XXXXX/habitica?replicaSet=rs-dsXXXXXX';
|
||||
const DB_URI = 'mongodb://localhost/prod-copy-1';
|
||||
|
||||
const LOGGEDIN_DATE_RANGE = {
|
||||
$gte: new Date("2016-09-30T00:00:00.000Z"),
|
||||
// $lte: new Date("2016-09-25T00:00:00.000Z"),
|
||||
};
|
||||
|
||||
let Users;
|
||||
|
||||
connectToDb(DB_URI).then((db) => {
|
||||
Users = db.collection('users');
|
||||
})
|
||||
.then(findUsersWithWebhooks)
|
||||
.then(correctWebhooks)
|
||||
.then(() => {
|
||||
timer.stop();
|
||||
closeDb();
|
||||
}).catch(reportError);
|
||||
|
||||
function reportError (err) {
|
||||
logger.error('Uh oh, an error occurred');
|
||||
logger.error(err);
|
||||
closeDb();
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
// Cached ids of users that need updating
|
||||
const USER_IDS = require('../../ids_of_webhooks_to_update.json');
|
||||
|
||||
function findUsersWithWebhooks () {
|
||||
logger.warn('Fetching users with webhooks...');
|
||||
|
||||
return Users.find({'_id': {$in: USER_IDS}}, ['preferences.webhooks']).toArray().then((docs) => {
|
||||
// return Users.find({'preferences.webhooks': {$ne: {} }}, ['preferences.webhooks']).toArray().then((docs) => {
|
||||
// TODO: Run this after the initial migration to catch any webhooks that may have been aded since the prod backup download
|
||||
// return Users.find({'preferences.webhooks': {$ne: {} }, 'auth.timestamps.loggedin': LOGGEDIN_DATE_RANGE}, ['preferences.webhooks']).toArray().then((docs) => {
|
||||
let updates = docs.map((user) => {
|
||||
let oldWebhooks = user.preferences.webhooks;
|
||||
let webhooks = Object.keys(oldWebhooks).map((id) => {
|
||||
let webhook = oldWebhooks[id]
|
||||
|
||||
webhook.type = 'taskActivity';
|
||||
webhook.label = '';
|
||||
webhook.options = {
|
||||
created: false,
|
||||
updated: false,
|
||||
deleted: false,
|
||||
scored: true,
|
||||
};
|
||||
|
||||
return webhook;
|
||||
}).sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
|
||||
return {
|
||||
webhooks,
|
||||
id: user._id,
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve(updates);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUserById (user) {
|
||||
let userId = user.id;
|
||||
let webhooks = user.webhooks;
|
||||
|
||||
return Users.findOneAndUpdate({
|
||||
_id: userId},
|
||||
{$set: {webhooks: webhooks, migration: MIGRATION_NAME}
|
||||
}, {returnOriginal: false})
|
||||
}
|
||||
|
||||
function correctWebhooks (users) {
|
||||
let queue = new TaskQueue(Promise, 300);
|
||||
|
||||
logger.warn('About to update', users.length, 'users...');
|
||||
|
||||
return Promise.map(users, queue.wrap(updateUserById)).then((result) => {
|
||||
let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting)
|
||||
let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting));
|
||||
|
||||
logger.warn(updates.length, 'users have been fixed');
|
||||
|
||||
if (failures.length > 0) {
|
||||
logger.error(failures.length, 'users could not be found');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
73
migrations/20161002_takeThis.js
Normal file
73
migrations/20161002_takeThis.js
Normal file
@@ -0,0 +1,73 @@
|
||||
var migrationName = '20161002_takeThis.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['4bbf63b5-10bc-49f9-8e95-5bd2ac99cd1c']}
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
86
migrations/20161030-jackolanterns.js
Normal file
86
migrations/20161030-jackolanterns.js
Normal file
@@ -0,0 +1,86 @@
|
||||
var migrationName = '20161030-jackolanterns.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* set the newStuff flag in all user accounts so they see a Bailey message
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-10-01')} // remove when running migration a second time
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'migration': 1,
|
||||
'items.pets.JackOLantern-Base': 1,
|
||||
'items.mounts.JackOLantern-Base': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
var inc = {};
|
||||
if (user.migration !== migrationName) {
|
||||
if (user.items.mounts['JackOLantern-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost':5};
|
||||
} else if (user.items.pets['JackOLantern-Base']) {
|
||||
set = {'migration':migrationName, 'items.mounts.JackOLantern-Base':true};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.pets.JackOLantern-Base':5};
|
||||
}
|
||||
inc = {
|
||||
'items.food.Candy_Base': 1,
|
||||
'items.food.Candy_CottonCandyBlue': 1,
|
||||
'items.food.Candy_CottonCandyPink': 1,
|
||||
'items.food.Candy_Desert': 1,
|
||||
'items.food.Candy_Golden': 1,
|
||||
'items.food.Candy_Red': 1,
|
||||
'items.food.Candy_Shade': 1,
|
||||
'items.food.Candy_Skeleton': 1,
|
||||
'items.food.Candy_White': 1,
|
||||
'items.food.Candy_Zombie': 1,
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set, $inc:inc});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
75
migrations/20161102_takeThis.js
Normal file
75
migrations/20161102_takeThis.js
Normal file
@@ -0,0 +1,75 @@
|
||||
var migrationName = '20161102_takeThis.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['d1be0965-e909-4d30-82fa-9a0011f885b2']}
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
74
migrations/20161122_turkey_ladder.js
Normal file
74
migrations/20161122_turkey_ladder.js
Normal file
@@ -0,0 +1,74 @@
|
||||
var migrationName = '20161122_turkey_ladder.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Yearly Turkey Day award. Turkey pet, Turkey mount, Gilded Turkey pet, Gilded Turkey mount
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-10-31')} // Extend timeframe each run of migration
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'migration': 1,
|
||||
'items.mounts': 1,
|
||||
'items.pets': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (user.items.pets['Turkey-Gilded']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true};
|
||||
} else if (user.items.mounts['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5};
|
||||
} else if (user.items.pets['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Base':true};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Base':5};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
73
migrations/20161230_nye_hats.js
Normal file
73
migrations/20161230_nye_hats.js
Normal file
@@ -0,0 +1,73 @@
|
||||
var migrationName = '20161230_nye_hats.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Yearly New Year's party hat award
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-11-30')} // Remove after first run
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2016':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
113
migrations/20170120_missing_incentive.js
Normal file
113
migrations/20170120_missing_incentive.js
Normal file
@@ -0,0 +1,113 @@
|
||||
var migrationName = '20170120_missing_incentive.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award missing Royal Purple Hatching Potion to users with 55+ check-ins
|
||||
* Reduce users with impossible check-in counts to a reasonable number
|
||||
*/
|
||||
|
||||
import monk from 'monk';
|
||||
import common from '../website/common';
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'loginIncentives': {$gt:54},
|
||||
'migration': {$ne: migrationName},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var language = user.preferences.language || 'en';
|
||||
var set = {'migration': migrationName};
|
||||
var inc = {'items.hatchingPotions.RoyalPurple': 1};
|
||||
if (user.loginIncentives > 58) {
|
||||
set = {'migration': migrationName, 'loginIncentives': 58};
|
||||
}
|
||||
var push = {
|
||||
'notifications': {
|
||||
'type': 'LOGIN_INCENTIVE',
|
||||
'data': {
|
||||
'nextRewardAt': 60,
|
||||
'rewardKey': [
|
||||
'Pet_HatchingPotion_Purple',
|
||||
],
|
||||
'rewardText': common.i18n.t('potion', {potionType: common.i18n.t('hatchingPotionRoyalPurple', language)}, language),
|
||||
'reward': [
|
||||
{
|
||||
'premium': true,
|
||||
'key': 'RoyalPurple',
|
||||
'limited': true,
|
||||
'value': 2,
|
||||
}
|
||||
],
|
||||
'message': common.i18n.t('unlockedCheckInReward', language),
|
||||
},
|
||||
'id': common.uuid(),
|
||||
}
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set, $push:push, $inc:inc});
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
109
migrations/20170131_habit_birthday.js
Normal file
109
migrations/20170131_habit_birthday.js
Normal file
@@ -0,0 +1,109 @@
|
||||
var migrationName = '20170131_habit_birthday.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award 2017 party robes if user has 2016 robes, 2016 robes if they have the 2015 robes,
|
||||
* 2015 robes if they have the 2014 robes, and 2014 robes otherwise. Also cake!
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2017-01-24')}, // remove after first run to cover remaining users
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data)
|
||||
'items.gear.owned'
|
||||
],
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {'migration':migrationName};
|
||||
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) {
|
||||
set['items.gear.owned.armor_special_birthday2017'] = false;
|
||||
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) {
|
||||
set['items.gear.owned.armor_special_birthday2016'] = false;
|
||||
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) {
|
||||
set['items.gear.owned.armor_special_birthday2015'] = false;
|
||||
} else {
|
||||
set['items.gear.owned.armor_special_birthday'] = false;
|
||||
}
|
||||
|
||||
var inc = {
|
||||
'items.food.Cake_Skeleton':1,
|
||||
'items.food.Cake_Base':1,
|
||||
'items.food.Cake_CottonCandyBlue':1,
|
||||
'items.food.Cake_CottonCandyPink':1,
|
||||
'items.food.Cake_Shade':1,
|
||||
'items.food.Cake_White':1,
|
||||
'items.food.Cake_Golden':1,
|
||||
'items.food.Cake_Zombie':1,
|
||||
'items.food.Cake_Desert':1,
|
||||
'items.food.Cake_Red':1,
|
||||
'achievements.habitBirthdays':1
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set, $inc:inc});
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
21
migrations/migration-runner.js
Normal file
21
migrations/migration-runner.js
Normal file
@@ -0,0 +1,21 @@
|
||||
require("babel-register");
|
||||
require("babel-polyfill");
|
||||
|
||||
// This file must use ES5, everything required can be in ES6
|
||||
|
||||
function setUpServer () {
|
||||
var nconf = require('nconf');
|
||||
var mongoose = require('mongoose');
|
||||
var Bluebird = require('bluebird');
|
||||
var setupNconf = require('../website/server/libs/setupNconf');
|
||||
setupNconf();
|
||||
// We require src/server and npt src/index because
|
||||
// 1. nconf is already setup
|
||||
// 2. we don't need clustering
|
||||
require('../website/server/server'); // eslint-disable-line global-require
|
||||
}
|
||||
setUpServer();
|
||||
|
||||
// Replace this with your migration
|
||||
var processUsers = require('./new_stuff');
|
||||
processUsers();
|
||||
@@ -2,7 +2,7 @@ var _id = '';
|
||||
var update = {
|
||||
$addToSet: {
|
||||
'purchased.plan.mysteryItems':{
|
||||
$each:['head_mystery_201609','armor_mystery_201609']
|
||||
$each:['shield_mystery_201701','eyewear_mystery_201701']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,49 +6,70 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
* set the newStuff flag in all user accounts so they see a Bailey message
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'flags.newStuff':{$ne:true}
|
||||
};
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'flags.newStuff': {$ne:true},
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
};
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPaymentPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPaymentPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {'flags.newStuff':true};
|
||||
var set = {'flags.newStuff': true};
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
@@ -58,3 +79,5 @@ function exiting(code, msg) {
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
|
||||
@@ -6,14 +6,11 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
* Remove flag stating that the Enchanted Armoire is empty, for when new equipment is added
|
||||
*/
|
||||
|
||||
var dbserver = 'localhost:27017'; // FOR TEST DATABASE
|
||||
// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE
|
||||
var dbname = 'habitrpg';
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
@@ -22,7 +19,6 @@ var query = {
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'flags.armoireEmpty':1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
@@ -32,7 +28,8 @@ dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
return displayData();
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
|
||||
115
migrations/restore-profile-data.js
Normal file
115
migrations/restore-profile-data.js
Normal file
@@ -0,0 +1,115 @@
|
||||
var migrationName = 'restore_profile_data.js';
|
||||
var authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
|
||||
var authorUuid = ''; //... own data is done
|
||||
|
||||
/*
|
||||
* Check if users have empty profile data in new database and update it with old database info
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = ''; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
var monk2 = require('monk');
|
||||
var oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId)
|
||||
{
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
// 'profile.name': 'profile name not found',
|
||||
'profile.blurb': null,
|
||||
// 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: ['_id', 'profile', 'auth.timestamps.loggedin'] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
|
||||
var userPaymentPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPaymentPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
if (!user.profile.name || user.profile.name === 'profile name not found' || !user.profile.imageUrl || !user.profile.blurb) {
|
||||
return olDbUsers.findOne({_id: user._id}, '_id profile')
|
||||
.then((oldUserData) => {
|
||||
if (!oldUserData) return;
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (oldUserData.profile.name === 'profile name not found') return;
|
||||
|
||||
var userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
|
||||
if (userNeedsProfileName && oldUserData.profile.name) {
|
||||
set['profile.name'] = oldUserData.profile.name;
|
||||
}
|
||||
|
||||
if (!user.profile.imageUrl && oldUserData.profile.imageUrl) {
|
||||
set['profile.imageUrl'] = oldUserData.profile.imageUrl;
|
||||
}
|
||||
|
||||
if (!user.profile.blurb && oldUserData.profile.blurb) {
|
||||
set['profile.blurb'] = oldUserData.profile.blurb;
|
||||
}
|
||||
|
||||
if (Object.keys(set).length !== 0 && set.constructor === Object) {
|
||||
console.log(set)
|
||||
return dbUsers.update({_id: user._id}, {$set:set});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
|
||||
processUsers()
|
||||
80
migrations/takeThis.js
Normal file
80
migrations/takeThis.js
Normal file
@@ -0,0 +1,80 @@
|
||||
var migrationName = '20170103_takeThis.js'; // Update per month
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['ff674aba-a114-4a6f-8ebc-1de27ffb646e']}
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName};
|
||||
{ else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
});
|
||||
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ function connectToDb (dbUri) {
|
||||
function closeDb () {
|
||||
if (db) db.close();
|
||||
|
||||
logger.success('CLosed connection to the database');
|
||||
logger.success('Closed connection to the database');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
6298
npm-shrinkwrap.json
generated
6298
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "3.44.2",
|
||||
"version": "3.73.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "3.6.0",
|
||||
@@ -13,15 +13,19 @@
|
||||
"async": "^1.5.0",
|
||||
"autoprefixer": "^6.4.0",
|
||||
"aws-sdk": "^2.0.25",
|
||||
"axios": "^0.15.3",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-loader": "^6.0.0",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-async-to-module-method": "^6.8.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.16.0",
|
||||
"babel-plugin-transform-regenerator": "^6.16.1",
|
||||
"babel-polyfill": "^6.6.1",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"babel-register": "^6.6.0",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"babelify": "^7.2.0",
|
||||
"bcrypt": "^1.0.2",
|
||||
"bluebird": "^3.3.5",
|
||||
"body-parser": "^1.15.0",
|
||||
"bower": "~1.3.12",
|
||||
@@ -29,13 +33,13 @@
|
||||
"compression": "^1.6.1",
|
||||
"connect-ratelimit": "0.0.7",
|
||||
"cookie-session": "^1.2.0",
|
||||
"coupon-code": "^0.4.3",
|
||||
"coupon-code": "^0.4.5",
|
||||
"css-loader": "^0.23.1",
|
||||
"csv-stringify": "^1.0.2",
|
||||
"cwait": "^1.0.0",
|
||||
"domain-middleware": "~0.1.0",
|
||||
"estraverse": "^4.1.1",
|
||||
"express": "~4.13.3",
|
||||
"express": "~4.14.0",
|
||||
"express-csv": "~0.6.0",
|
||||
"express-validator": "^2.18.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
@@ -64,16 +68,18 @@
|
||||
"image-size": "~0.3.2",
|
||||
"in-app-purchase": "^1.1.6",
|
||||
"jade": "~1.11.0",
|
||||
"jquery": "https://registry.npmjs.org/jquery/-/jquery-3.1.1.tgz",
|
||||
"js2xmlparser": "~1.0.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"lodash": "^3.10.1",
|
||||
"lodash.pickby": "^4.2.0",
|
||||
"lodash.setwith": "^4.2.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"method-override": "^2.3.5",
|
||||
"moment": "^2.13.0",
|
||||
"mongoose": "^4.4.16",
|
||||
"mongoose": "^4.7.1",
|
||||
"mongoose-id-autoinc": "~2013.7.14-4",
|
||||
"morgan": "^1.7.0",
|
||||
"nconf": "~0.8.2",
|
||||
@@ -89,12 +95,13 @@
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
"paypal-ipn": "3.0.0",
|
||||
"paypal-rest-sdk": "^1.2.1",
|
||||
"postcss-easy-import": "^1.0.1",
|
||||
"pretty-data": "^0.40.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.0-beta6",
|
||||
"push-notify": "habitrpg/push-notify#v1.2.0",
|
||||
"pusher": "^1.3.0",
|
||||
"request": "~2.72.0",
|
||||
"request": "~2.74.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"run-sequence": "^1.1.4",
|
||||
"s3-upload-stream": "^1.0.6",
|
||||
@@ -110,11 +117,11 @@
|
||||
"validator": "^4.9.0",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"vue": "^2.0.0-rc.6",
|
||||
"vue": "^2.1.0",
|
||||
"vue-hot-reload-api": "^1.2.0",
|
||||
"vue-loader": "^9.4.0",
|
||||
"vue-resource": "^1.0.2",
|
||||
"vue-loader": "^10.0.0",
|
||||
"vue-router": "^2.0.0-rc.5",
|
||||
"vue-template-compiler": "^2.1.0",
|
||||
"webpack": "^1.12.2",
|
||||
"webpack-merge": "^0.8.3",
|
||||
"winston": "^2.1.0",
|
||||
@@ -122,12 +129,13 @@
|
||||
},
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "^4.3.1",
|
||||
"npm": "^3.8.9"
|
||||
"node": "^6.9.1",
|
||||
"npm": "^4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"test": "npm run lint && gulp test && npm run client:unit",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
"test:api-v3:unit": "gulp test:api-v3:unit",
|
||||
"test:api-v3:integration": "gulp test:api-v3:integration",
|
||||
@@ -153,7 +161,6 @@
|
||||
"postinstall": "bower --config.interactive=false install -f; gulp build; npm run client:build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^6.0.0",
|
||||
"chai": "^3.4.0",
|
||||
"chai-as-promised": "^5.1.0",
|
||||
"chalk": "^1.1.3",
|
||||
@@ -163,13 +170,12 @@
|
||||
"cross-spawn": "^2.1.5",
|
||||
"csv": "~0.3.6",
|
||||
"deep-diff": "~0.1.4",
|
||||
"eslint": "~2.12.0",
|
||||
"eslint-config-habitrpg": "^1.0.0",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-config-habitrpg": "^2.0.0",
|
||||
"eslint-friendly-formatter": "^2.0.5",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"eslint-plugin-babel": "^3.0.0",
|
||||
"eslint-plugin-html": "^1.3.0",
|
||||
"eslint-plugin-mocha": "^2.1.0",
|
||||
"eslint-plugin-mocha": "^4.7.0",
|
||||
"event-stream": "^3.2.2",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"expect.js": "~0.2.0",
|
||||
@@ -194,6 +200,7 @@
|
||||
"mocha": "^2.3.3",
|
||||
"mongodb": "^2.0.46",
|
||||
"mongoskin": "~2.1.0",
|
||||
"monk": "^3.1.3",
|
||||
"nightwatch": "^0.8.18",
|
||||
"phantomjs-prebuilt": "^2.1.12",
|
||||
"protractor": "^3.1.1",
|
||||
@@ -202,8 +209,8 @@
|
||||
"selenium-server": "2.53.0",
|
||||
"sinon": "^1.17.2",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"sinon-stub-promise": "^4.0.0",
|
||||
"superagent-defaults": "^0.1.13",
|
||||
"vinyl-source-stream": "^1.0.0",
|
||||
"vinyl-transform": "^1.0.0",
|
||||
"webpack-dev-middleware": "^1.4.0",
|
||||
"webpack-hot-middleware": "^2.6.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": [
|
||||
"habitrpg/mocha",
|
||||
"habitrpg/babel"
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
describe('GET challenges/group/:groupId', () => {
|
||||
describe('GET challenges/groups/:groupId', () => {
|
||||
context('Public Guild', () => {
|
||||
let publicGuild, user, nonMember, challenge, challenge2;
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
sleep,
|
||||
server,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /chat', () => {
|
||||
let user, groupWithChat, userWithChatRevoked, member;
|
||||
@@ -32,6 +35,24 @@ describe('POST /chat', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when an empty message is provided', async () => {
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: ' '}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when an message containing only newlines is provided', async () => {
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: '\n\n'}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when group is not found', async () => {
|
||||
await expect(user.post('/groups/invalidID/chat', { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
@@ -40,7 +61,7 @@ describe('POST /chat', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when chat privileges are revoked', async () => {
|
||||
it('returns an error when chat privileges are revoked when sending a message to a public guild', async () => {
|
||||
await expect(userWithChatRevoked.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
@@ -48,12 +69,86 @@ describe('POST /chat', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not error when sending a message to a private guild with a user with revoked chat', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Private Guild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let privateGuildMemberWithChatsRevoked = members[0];
|
||||
await privateGuildMemberWithChatsRevoked.update({'flags.chatRevoked': true});
|
||||
|
||||
let message = await privateGuildMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('does not error when sending a message to a party with a user with revoked chat', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Party',
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let privatePartyMemberWithChatsRevoked = members[0];
|
||||
await privatePartyMemberWithChatsRevoked.update({'flags.chatRevoked': true});
|
||||
|
||||
let message = await privatePartyMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('creates a chat', async () => {
|
||||
let message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('sends group chat received webhooks', async () => {
|
||||
let userUuid = generateUUID();
|
||||
let memberUuid = generateUUID();
|
||||
await server.start();
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${userUuid}`,
|
||||
type: 'groupChatReceived',
|
||||
enabled: true,
|
||||
options: {
|
||||
groupId: groupWithChat.id,
|
||||
},
|
||||
});
|
||||
await member.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${memberUuid}`,
|
||||
type: 'groupChatReceived',
|
||||
enabled: true,
|
||||
options: {
|
||||
groupId: groupWithChat.id,
|
||||
},
|
||||
});
|
||||
|
||||
let message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
|
||||
|
||||
await sleep();
|
||||
|
||||
await server.close();
|
||||
|
||||
let userBody = server.getWebhookData(userUuid);
|
||||
let memberBody = server.getWebhookData(memberUuid);
|
||||
|
||||
[userBody, memberBody].forEach((body) => {
|
||||
expect(body.group.id).to.eql(groupWithChat._id);
|
||||
expect(body.group.name).to.eql(groupWithChat.name);
|
||||
expect(body.chat).to.eql(message.message);
|
||||
});
|
||||
});
|
||||
|
||||
it('notifies other users of new messages for a guild', async () => {
|
||||
let message = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
|
||||
let memberWithNotification = await member.get('/user');
|
||||
|
||||
@@ -29,14 +29,6 @@ describe('POST /coupons/generate/:event', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if event is missing', async () => {
|
||||
await expect(user.post('/coupons/generate')).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Not found.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if event is invalid', async () => {
|
||||
await expect(user.post('/coupons/generate/notValid?count=1')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
|
||||
@@ -7,15 +7,21 @@ import {
|
||||
import moment from 'moment';
|
||||
|
||||
describe('GET /export/history.csv', () => {
|
||||
it('should return a valid CSV file with tasks history data', async () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid CSV file with tasks history data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'daily', text: 'daily 1'},
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'habit', text: 'habit 2'},
|
||||
{type: 'todo', text: 'todo 1'},
|
||||
]);
|
||||
|
||||
// to handle occasional inconsistency in task creation order
|
||||
tasks.sort(function (a, b) {
|
||||
return a.text.localeCompare(b.text);
|
||||
});
|
||||
|
||||
// score all the tasks twice
|
||||
await user.post(`/tasks/${tasks[0]._id}/score/up`);
|
||||
await user.post(`/tasks/${tasks[1]._id}/score/up`);
|
||||
@@ -28,7 +34,7 @@ describe('GET /export/history.csv', () => {
|
||||
await user.post(`/tasks/${tasks[3]._id}/score/up`);
|
||||
|
||||
// adding an history entry to daily 1 manually because cron didn't run yet
|
||||
await updateDocument('tasks', tasks[1], {
|
||||
await updateDocument('tasks', tasks[0], {
|
||||
history: [{value: 3.2, date: Number(new Date())}],
|
||||
});
|
||||
|
||||
@@ -41,11 +47,11 @@ describe('GET /export/history.csv', () => {
|
||||
let splitRes = res.split('\n');
|
||||
|
||||
expect(splitRes[0]).to.equal('Task Name,Task ID,Task Type,Date,Value');
|
||||
expect(splitRes[1]).to.equal(`habit 1,${tasks[0]._id},habit,${moment(tasks[0].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[0].value}`);
|
||||
expect(splitRes[2]).to.equal(`habit 1,${tasks[0]._id},habit,${moment(tasks[0].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[1].value}`);
|
||||
expect(splitRes[3]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[0].value}`);
|
||||
expect(splitRes[4]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[1].value}`);
|
||||
expect(splitRes[5]).to.equal(`daily 1,${tasks[1]._id},daily,${moment(tasks[1].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[0].value}`);
|
||||
expect(splitRes[1]).to.equal(`daily 1,${tasks[0]._id},daily,${moment(tasks[0].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[0].value}`);
|
||||
expect(splitRes[2]).to.equal(`habit 1,${tasks[1]._id},habit,${moment(tasks[1].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[0].value}`);
|
||||
expect(splitRes[3]).to.equal(`habit 1,${tasks[1]._id},habit,${moment(tasks[1].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[1].value}`);
|
||||
expect(splitRes[4]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[0].value}`);
|
||||
expect(splitRes[5]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[1].value}`);
|
||||
expect(splitRes[6]).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,8 @@ import Bluebird from 'bluebird';
|
||||
let parseStringAsync = Bluebird.promisify(xml2js.parseString, {context: xml2js});
|
||||
|
||||
describe('GET /export/userdata.xml', () => {
|
||||
it('should return a valid XML file with user data', async () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid XML file with user data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
|
||||
@@ -82,8 +82,10 @@ describe('GET /groups/:groupId/members', () => {
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql(['size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background'].sort());
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
|
||||
@@ -134,6 +134,22 @@ describe('POST /group/:groupId/join', () => {
|
||||
|
||||
await expect(user.get('/user')).to.eventually.have.deep.property('items.quests.basilist', 1);
|
||||
});
|
||||
|
||||
it('notifies inviting user that their invitation was accepted', async () => {
|
||||
await invitedUser.post(`/groups/${guild._id}/join`);
|
||||
|
||||
let inviter = await user.get('/user');
|
||||
let expectedData = {
|
||||
headerText: t('invitationAcceptedHeader'),
|
||||
bodyText: t('invitationAcceptedBody', {
|
||||
username: invitedUser.auth.local.username,
|
||||
groupName: guild.name,
|
||||
}),
|
||||
};
|
||||
|
||||
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
|
||||
expect(inviter.notifications[0].data).to.eql(expectedData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,6 +188,23 @@ describe('POST /group/:groupId/join', () => {
|
||||
await expect(invitedUser.get('/user')).to.eventually.have.deep.property('party._id', party._id);
|
||||
});
|
||||
|
||||
it('notifies inviting user that their invitation was accepted', async () => {
|
||||
await invitedUser.post(`/groups/${party._id}/join`);
|
||||
|
||||
let inviter = await user.get('/user');
|
||||
|
||||
let expectedData = {
|
||||
headerText: t('invitationAcceptedHeader'),
|
||||
bodyText: t('invitationAcceptedBody', {
|
||||
username: invitedUser.auth.local.username,
|
||||
groupName: party.name,
|
||||
}),
|
||||
};
|
||||
|
||||
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
|
||||
expect(inviter.notifications[0].data).to.eql(expectedData);
|
||||
});
|
||||
|
||||
it('clears invitation from user when joining party', async () => {
|
||||
await invitedUser.post(`/groups/${party._id}/join`);
|
||||
|
||||
|
||||
@@ -65,6 +65,19 @@ describe('POST /groups/:groupId/leave', () => {
|
||||
expect(groupToLeave.leader).to.equal(member._id);
|
||||
});
|
||||
|
||||
it('removes new messages for that group from user', async () => {
|
||||
await member.post(`/groups/${groupToLeave._id}/chat`, { message: 'Some message' });
|
||||
|
||||
await leader.sync();
|
||||
|
||||
expect(leader.newMessages[groupToLeave._id]).to.not.be.empty;
|
||||
|
||||
await leader.post(`/groups/${groupToLeave._id}/leave`);
|
||||
await leader.sync();
|
||||
|
||||
expect(leader.newMessages[groupToLeave._id]).to.be.empty;
|
||||
});
|
||||
|
||||
context('With challenges', () => {
|
||||
let challenge;
|
||||
|
||||
@@ -122,6 +135,8 @@ describe('POST /groups/:groupId/leave', () => {
|
||||
privateGuild = group;
|
||||
leader = groupLeader;
|
||||
invitedUser = invitees[0];
|
||||
|
||||
await leader.post(`/groups/${group._id}/chat`, { message: 'Some message' });
|
||||
});
|
||||
|
||||
it('removes a group when the last member leaves', async () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
|
||||
describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
let leader;
|
||||
@@ -60,6 +61,14 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
});
|
||||
|
||||
context('Guilds', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('can remove other members', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${member._id}`);
|
||||
let memberRemoved = await member.get('/user');
|
||||
@@ -80,6 +89,22 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
|
||||
expect(_.findIndex(invitedUserWithoutInvite.invitations.guilds, {id: guild._id})).eql(-1);
|
||||
});
|
||||
|
||||
it('sends email to user with rescinded invite', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${invitedUser._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(invitedUser._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('guild-invite-rescinded');
|
||||
});
|
||||
|
||||
it('sends email to removed user', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${member._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(member._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('kicked-from-guild');
|
||||
});
|
||||
});
|
||||
|
||||
context('Party', () => {
|
||||
@@ -87,6 +112,7 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
let partyLeader;
|
||||
let partyInvitedUser;
|
||||
let partyMember;
|
||||
let removedMember;
|
||||
|
||||
beforeEach(async () => {
|
||||
let { group, groupLeader, invitees, members } = await createAndPopulateGroup({
|
||||
@@ -96,13 +122,19 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
privacy: 'private',
|
||||
},
|
||||
invites: 1,
|
||||
members: 1,
|
||||
members: 2,
|
||||
});
|
||||
|
||||
party = group;
|
||||
partyLeader = groupLeader;
|
||||
partyInvitedUser = invitees[0];
|
||||
partyMember = members[0];
|
||||
removedMember = members[1];
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('can remove other members', async () => {
|
||||
@@ -129,6 +161,18 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
expect(invitedUserWithoutInvite.invitations.party).to.be.empty;
|
||||
});
|
||||
|
||||
it('removes new messages from a member who is removed', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/chat`, { message: 'Some message' });
|
||||
await removedMember.sync();
|
||||
|
||||
expect(removedMember.newMessages[party._id]).to.not.be.empty;
|
||||
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${removedMember._id}`);
|
||||
await removedMember.sync();
|
||||
|
||||
expect(removedMember.newMessages[party._id]).to.be.empty;
|
||||
});
|
||||
|
||||
it('removes user from quest when removing user from party after quest starts', async () => {
|
||||
let petQuest = 'whale';
|
||||
await partyLeader.update({
|
||||
@@ -173,5 +217,21 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
expect(party.quest.members[partyLeader._id]).to.be.true;
|
||||
expect(party.quest.members[partyMember._id]).to.not.exist;
|
||||
});
|
||||
|
||||
it('sends email to user with rescinded invite', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${partyInvitedUser._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(partyInvitedUser._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('party-invite-rescinded');
|
||||
});
|
||||
|
||||
it('sends email to removed user', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${partyMember._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(partyMember._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('kicked-from-party');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,11 +57,27 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns empty when uuids is empty', async () => {
|
||||
it('returns an error when uuids and emails are empty', async () => {
|
||||
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||
emails: [],
|
||||
uuids: [],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('inviteMustNotBeEmpty'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when uuids is empty and emails is not passed', async () => {
|
||||
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [],
|
||||
}))
|
||||
.to.eventually.be.empty;
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('inviteMissingUuid'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when there are more than INVITES_LIMIT uuids', async () => {
|
||||
@@ -159,11 +175,15 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns empty when emails is an empty array', async () => {
|
||||
it('returns an error when emails is empty and uuids is not passed', async () => {
|
||||
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||
emails: [],
|
||||
}))
|
||||
.to.eventually.be.empty;
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('inviteMissingEmail'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when there are more than INVITES_LIMIT emails', async () => {
|
||||
@@ -280,6 +300,26 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
message: t('userAlreadyInGroup'),
|
||||
});
|
||||
});
|
||||
|
||||
// @TODO: Add this after we are able to mock the group plan route
|
||||
xit('returns an error when a non-leader invites to a group plan', async () => {
|
||||
let userToInvite = await generateUser();
|
||||
|
||||
let nonGroupLeader = await generateUser();
|
||||
await inviter.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [nonGroupLeader._id],
|
||||
});
|
||||
await nonGroupLeader.post(`/groups/${group._id}/join`);
|
||||
|
||||
await expect(nonGroupLeader.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [userToInvite._id],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanInviteToGroupPlan'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('party invites', () => {
|
||||
|
||||
44
test/api/v3/integration/members/GET-achievements.test.js
Normal file
44
test/api/v3/integration/members/GET-achievements.test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('GET /members/:memberId/achievements', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('validates req.params.memberId', async () => {
|
||||
await expect(user.get('/members/invalidUUID/achievements')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns achievements based on given user', async () => {
|
||||
let member = await generateUser({
|
||||
contributor: {level: 1},
|
||||
backer: {tier: 3},
|
||||
});
|
||||
let achievementsRes = await user.get(`/members/${member._id}/achievements`);
|
||||
|
||||
expect(achievementsRes.special.achievements.contributor.earned).to.equal(true);
|
||||
expect(achievementsRes.special.achievements.contributor.value).to.equal(1);
|
||||
|
||||
expect(achievementsRes.special.achievements.kickstarter.earned).to.equal(true);
|
||||
expect(achievementsRes.special.achievements.kickstarter.value).to.equal(3);
|
||||
});
|
||||
|
||||
it('handles non-existing members', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.get(`/members/${dummyId}/achievements`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('userWithIDNotFound', {userId: dummyId}),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -35,8 +35,10 @@ describe('GET /members/:memberId', () => {
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql(['size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background'].sort());
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
|
||||
@@ -4,6 +4,14 @@ import {
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
function findMessage (messages, receiverId) {
|
||||
let message = _.find(messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiverId;
|
||||
});
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
describe('POST /members/transfer-gems', () => {
|
||||
let userToSendMessage;
|
||||
let receiver;
|
||||
@@ -116,19 +124,14 @@ describe('POST /members/transfer-gems', () => {
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === userToSendMessage._id;
|
||||
});
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiver._id;
|
||||
});
|
||||
|
||||
let messageSentContent = t('privateMessageGiftIntro', {
|
||||
let messageSentContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
});
|
||||
messageSentContent += t('privateMessageGiftGemsMessage', {gemAmount});
|
||||
messageSentContent = `\`${messageSentContent}\` `;
|
||||
messageSentContent += message;
|
||||
|
||||
@@ -150,19 +153,14 @@ describe('POST /members/transfer-gems', () => {
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === userToSendMessage._id;
|
||||
});
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiver._id;
|
||||
});
|
||||
|
||||
let messageSentContent = t('privateMessageGiftIntro', {
|
||||
let messageSentContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
});
|
||||
messageSentContent += t('privateMessageGiftGemsMessage', {gemAmount});
|
||||
messageSentContent = `\`${messageSentContent}\` `;
|
||||
|
||||
expect(sendersMessageInReceiversInbox).to.exist;
|
||||
@@ -173,4 +171,40 @@ describe('POST /members/transfer-gems', () => {
|
||||
expect(sendersMessageInSendersInbox.text).to.equal(messageSentContent);
|
||||
expect(updatedSender.balance).to.equal(0);
|
||||
});
|
||||
|
||||
it('sends transfer gems message in each participant\'s language', async () => {
|
||||
await receiver.update({
|
||||
'preferences.language': 'es',
|
||||
});
|
||||
await userToSendMessage.update({
|
||||
'preferences.language': 'cs',
|
||||
});
|
||||
await userToSendMessage.post('/members/transfer-gems', {
|
||||
gemAmount,
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let [receieversMessageContent, sendersMessageContent] = ['es', 'cs'].map((lang) => {
|
||||
let messageContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
}, lang);
|
||||
|
||||
return `\`${messageContent}\` `;
|
||||
});
|
||||
|
||||
expect(sendersMessageInReceiversInbox).to.exist;
|
||||
expect(sendersMessageInReceiversInbox.text).to.equal(receieversMessageContent);
|
||||
|
||||
expect(sendersMessageInSendersInbox).to.exist;
|
||||
expect(sendersMessageInSendersInbox.text).to.equal(sendersMessageContent);
|
||||
expect(updatedSender.balance).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('GET /models/:model/paths', () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('returns an error when model is not accessible or doesn\'t exists', async () => {
|
||||
it('returns an error when model is not accessible or doesn\'t exist', async () => {
|
||||
await expect(user.get('/models/1234/paths')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /notifications/:notificationId/read', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('errors when notification is not found', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.post(`/notifications/${dummyId}/read`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('messageNotificationNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
xit('removes a notification', async () => {
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : amazon #subscribeCancel', () => {
|
||||
let endpoint = '/amazon/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #checkout', () => {
|
||||
let endpoint = '/paypal/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #checkoutSuccess', () => {
|
||||
let endpoint = '/paypal/checkout/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #subscribe', () => {
|
||||
let endpoint = '/paypal/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #subscribeCancel', () => {
|
||||
let endpoint = '/paypal/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #subscribeSuccess', () => {
|
||||
let endpoint = '/paypal/subscribe/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #subscribeCancel', () => {
|
||||
let endpoint = '/stripe/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #checkout', () => {
|
||||
let endpoint = '/amazon/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.orderReferenceId',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #createOrderReferenceId', () => {
|
||||
let endpoint = '/amazon/createOrderReferenceId';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies billingAgreementId', async (done) => {
|
||||
try {
|
||||
await user.post(endpoint);
|
||||
} catch (e) {
|
||||
// Parameter AWSAccessKeyId cannot be empty.
|
||||
expect(e.error).to.eql('BadRequest');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #subscribe', () => {
|
||||
let endpoint = '/amazon/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription code', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscriptionCode'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - paypal - #ipn', () => {
|
||||
let endpoint = '/paypal/ipn';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
let result = await user.post(endpoint);
|
||||
expect(result).to.eql('OK');
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #checkout', () => {
|
||||
let endpoint = '/stripe/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint, {id: 123})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'Error',
|
||||
message: 'Invalid API Key provided: ****************************1111',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #subscribeEdit', () => {
|
||||
let endpoint = '/stripe/subscribe/edit';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments : amazon #subscribeCancel', () => {
|
||||
let endpoint = '/amazon/subscribe/cancel';
|
||||
let user, group, amazonSubscribeCancelStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('throws error when there users has no subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(async () => {
|
||||
amazonSubscribeCancelStub = sinon.stub(amzLib, 'cancelSubscription').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
amzLib.cancelSubscription.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(amazonSubscribeCancelStub).to.be.calledOnce;
|
||||
expect(amazonSubscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].groupId).to.eql(undefined);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?groupId=${group._id}`);
|
||||
|
||||
expect(amazonSubscribeCancelStub).to.be.calledOnce;
|
||||
expect(amazonSubscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].groupId).to.eql(group._id);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments - amazon - #checkout', () => {
|
||||
let endpoint = '/amazon/checkout';
|
||||
let user, amazonCheckoutStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.orderReferenceId',
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(async () => {
|
||||
amazonCheckoutStub = sinon.stub(amzLib, 'checkout').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
amzLib.checkout.restore();
|
||||
});
|
||||
|
||||
it('makes a purcahse with amazon checkout', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
let gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 16,
|
||||
uuid: user._id,
|
||||
},
|
||||
};
|
||||
|
||||
let orderReferenceId = 'orderReferenceId-example';
|
||||
|
||||
await user.post(endpoint, {
|
||||
gift,
|
||||
orderReferenceId,
|
||||
});
|
||||
|
||||
expect(amazonCheckoutStub).to.be.calledOnce;
|
||||
expect(amazonCheckoutStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(amazonCheckoutStub.args[0][0].gift).to.eql(gift);
|
||||
expect(amazonCheckoutStub.args[0][0].orderReferenceId).to.eql(orderReferenceId);
|
||||
expect(amazonCheckoutStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(amazonCheckoutStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #createOrderReferenceId', () => {
|
||||
let endpoint = '/amazon/createOrderReferenceId';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies billingAgreementId', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.billingAgreementId',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments - amazon - #subscribe', () => {
|
||||
let endpoint = '/amazon/subscribe';
|
||||
let user, group, subscribeWithAmazonStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription code', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscriptionCode'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let billingAgreementId = 'billingAgreementId-example';
|
||||
let subscription = 'basic_3mo';
|
||||
let coupon;
|
||||
|
||||
beforeEach(() => {
|
||||
subscribeWithAmazonStub = sinon.stub(amzLib, 'subscribe').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
amzLib.subscribe.restore();
|
||||
});
|
||||
|
||||
it('creates a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
billingAgreementId,
|
||||
subscription,
|
||||
coupon,
|
||||
});
|
||||
|
||||
expect(subscribeWithAmazonStub).to.be.calledOnce;
|
||||
expect(subscribeWithAmazonStub.args[0][0].billingAgreementId).to.eql(billingAgreementId);
|
||||
expect(subscribeWithAmazonStub.args[0][0].sub).to.exist;
|
||||
expect(subscribeWithAmazonStub.args[0][0].coupon).to.eql(coupon);
|
||||
expect(subscribeWithAmazonStub.args[0][0].groupId).not.exist;
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
|
||||
it('creates a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
billingAgreementId,
|
||||
subscription,
|
||||
coupon,
|
||||
groupId: group._id,
|
||||
});
|
||||
|
||||
expect(subscribeWithAmazonStub).to.be.calledOnce;
|
||||
expect(subscribeWithAmazonStub.args[0][0].billingAgreementId).to.eql(billingAgreementId);
|
||||
expect(subscribeWithAmazonStub.args[0][0].sub).to.exist;
|
||||
expect(subscribeWithAmazonStub.args[0][0].coupon).to.eql(coupon);
|
||||
expect(subscribeWithAmazonStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(subscribeWithAmazonStub.args[0][0].groupId).to.eql(group._id);
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : amazon', () => {
|
||||
let endpoint = '/amazon/verifyAccessToken';
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #checkout', () => {
|
||||
let endpoint = '/paypal/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let checkoutStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
checkoutStub = sinon.stub(paypalPayments, 'checkout').returnsPromise().resolves('/');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.checkout.restore();
|
||||
});
|
||||
|
||||
it('creates a purchase link', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(checkoutStub).to.be.calledOnce;
|
||||
expect(checkoutStub.args[0][0].gift).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #checkoutSuccess', () => {
|
||||
let endpoint = '/paypal/checkout/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies paymentId', async () => {
|
||||
await expect(user.get(endpoint))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingPaymentId'),
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies customerId', async () => {
|
||||
await expect(user.get(`${endpoint}?paymentId=test-paymentid`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingCustomerId'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let checkoutSuccessStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
checkoutSuccessStub = sinon.stub(paypalPayments, 'checkoutSuccess').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.checkoutSuccess.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
let paymentId = 'test-paymentid';
|
||||
let customerId = 'test-customerId';
|
||||
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?PayerID=${customerId}&paymentId=${paymentId}`);
|
||||
|
||||
expect(checkoutSuccessStub).to.be.calledOnce;
|
||||
|
||||
expect(checkoutSuccessStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(checkoutSuccessStub.args[0][0].gift).to.eql(undefined);
|
||||
expect(checkoutSuccessStub.args[0][0].paymentId).to.eql(paymentId);
|
||||
expect(checkoutSuccessStub.args[0][0].customerId).to.eql(customerId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import shared from '../../../../../../website/common';
|
||||
|
||||
describe('payments : paypal #subscribe', () => {
|
||||
let endpoint = '/paypal/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies sub key', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubKey'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let subscribeStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeStub = sinon.stub(paypalPayments, 'subscribe').returnsPromise().resolves('/');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.subscribe.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
let subKey = 'basic_3mo';
|
||||
let sub = shared.content.subscriptionBlocks[subKey];
|
||||
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?sub=${subKey}`);
|
||||
|
||||
expect(subscribeStub).to.be.calledOnce;
|
||||
|
||||
expect(subscribeStub.args[0][0].sub).to.eql(sub);
|
||||
expect(subscribeStub.args[0][0].coupon).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #subscribeCancel', () => {
|
||||
let endpoint = '/paypal/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let subscribeCancelStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeCancelStub = sinon.stub(paypalPayments, 'subscribeCancel').returnsPromise().resolves('/');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.subscribeCancel.restore();
|
||||
});
|
||||
|
||||
it('cancels a subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(subscribeCancelStub).to.be.calledOnce;
|
||||
|
||||
expect(subscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(subscribeCancelStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #subscribeSuccess', () => {
|
||||
let endpoint = '/paypal/subscribe/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies Paypal Block', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingPaypalBlock'),
|
||||
});
|
||||
});
|
||||
|
||||
xdescribe('success', () => {
|
||||
let subscribeSuccessStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeSuccessStub = sinon.stub(paypalPayments, 'subscribeSuccess').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.subscribeSuccess.restore();
|
||||
});
|
||||
|
||||
it('creates a subscription', async () => {
|
||||
let token = 'test-token';
|
||||
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?token=${token}`);
|
||||
|
||||
expect(subscribeSuccessStub).to.be.calledOnce;
|
||||
|
||||
expect(subscribeSuccessStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(subscribeSuccessStub.args[0][0].block).to.eql(undefined);
|
||||
expect(subscribeSuccessStub.args[0][0].groupId).to.eql(undefined);
|
||||
expect(subscribeSuccessStub.args[0][0].token).to.eql(token);
|
||||
expect(subscribeSuccessStub.args[0][0].headers).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments - paypal - #ipn', () => {
|
||||
let endpoint = '/paypal/ipn';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
let result = await user.post(endpoint);
|
||||
expect(result).to.eql('OK');
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let ipnStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
ipnStub = sinon.stub(paypalPayments, 'ipn').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.ipn.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint);
|
||||
|
||||
expect(ipnStub).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
|
||||
describe('payments - stripe - #subscribeCancel', () => {
|
||||
let endpoint = '/stripe/subscribe/cancel';
|
||||
let user, group, stripeCancelSubscriptionStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(async () => {
|
||||
stripeCancelSubscriptionStub = sinon.stub(stripePayments, 'cancelSubscription').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stripePayments.cancelSubscription.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?redirect=none`);
|
||||
|
||||
expect(stripeCancelSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?groupId=${group._id}&redirect=none`);
|
||||
|
||||
expect(stripeCancelSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
|
||||
describe('payments - stripe - #checkout', () => {
|
||||
let endpoint = '/stripe/checkout';
|
||||
let user, group;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint, {id: 123})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'Error',
|
||||
message: 'Invalid API Key provided: ****************************1111',
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let stripeCheckoutSubscriptionStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
stripeCheckoutSubscriptionStub = sinon.stub(stripePayments, 'checkout').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stripePayments.checkout.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint);
|
||||
|
||||
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.post(`${endpoint}?groupId=${group._id}`);
|
||||
|
||||
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
|
||||
describe('payments - stripe - #subscribeEdit', () => {
|
||||
let endpoint = '/stripe/subscribe/edit';
|
||||
let user, group;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let stripeEditSubscriptionStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
stripeEditSubscriptionStub = sinon.stub(stripePayments, 'editSubscription').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stripePayments.editSubscription.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint);
|
||||
|
||||
expect(stripeEditSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
groupId: group._id,
|
||||
});
|
||||
|
||||
expect(stripeEditSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -148,5 +148,20 @@ describe('POST /groups/:groupId/quests/accept', () => {
|
||||
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||
});
|
||||
|
||||
it('begins the quest if accepting the last pending invite and verifies chat', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
// quest will start after everyone has accepted
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
await questingGroup.sync();
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -231,5 +231,22 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
||||
expect(questingGroup.quest.members[partyMembers[0]._id]).to.exist;
|
||||
expect(questingGroup.quest.members[leader._id]).to.exist;
|
||||
});
|
||||
|
||||
it('allows group leader to force start quest and verifies chat', async () => {
|
||||
let questLeader = partyMembers[0];
|
||||
await questLeader.update({[`items.quests.${PET_QUEST}`]: 1});
|
||||
await questLeader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
|
||||
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,5 +188,25 @@ describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
||||
|
||||
expect(group.quest.active).to.eql(true);
|
||||
});
|
||||
|
||||
it('starts quest automatically if user is in a solo party and verifies chat', async () => {
|
||||
let leaderDetails = { balance: 10 };
|
||||
leaderDetails[`items.quests.${PET_QUEST}`] = 1;
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: { type: 'party', privacy: 'private' },
|
||||
leaderDetails,
|
||||
});
|
||||
|
||||
await groupLeader.post(`/groups/${group._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await group.sync();
|
||||
|
||||
expect(group.chat[0].text).to.exist;
|
||||
expect(group.chat[0]._meta).to.exist;
|
||||
expect(group.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await groupLeader.get(`/groups/${group._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
|
||||
describe('POST /groups/:groupId/quests/abort', () => {
|
||||
let questingGroup;
|
||||
@@ -89,6 +90,8 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
let stub = sandbox.stub(Group.prototype, 'sendChat');
|
||||
|
||||
let res = await leader.post(`/groups/${questingGroup._id}/quests/abort`);
|
||||
await Promise.all([
|
||||
leader.sync(),
|
||||
@@ -123,5 +126,9 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
},
|
||||
members: {},
|
||||
});
|
||||
expect(Group.prototype.sendChat).to.be.calledOnce;
|
||||
expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/);
|
||||
|
||||
stub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -180,5 +180,19 @@ describe('POST /groups/:groupId/quests/reject', () => {
|
||||
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||
});
|
||||
|
||||
it('starts the quest when the last user reject and verifies chat', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`);
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('GET /shops/seasonal', () => {
|
||||
|
||||
expect(shop.identifier).to.equal('seasonalShop');
|
||||
expect(shop.text).to.eql(t('seasonalShop'));
|
||||
expect(shop.notes).to.eql(t('seasonalShopFallText'));
|
||||
expect(shop.notes).to.be.a('string');
|
||||
expect(shop.imageName).to.be.a('string');
|
||||
expect(shop.categories).to.be.an('array');
|
||||
});
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
generateGroup,
|
||||
sleep,
|
||||
generateChallenge,
|
||||
server,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('DELETE /tasks/:id', () => {
|
||||
let user;
|
||||
@@ -42,6 +47,77 @@ describe('DELETE /tasks/:id', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('sending task activity webhooks', () => {
|
||||
before(async () => {
|
||||
await server.start();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('sends task activity webhooks if task is user owned', async () => {
|
||||
let uuid = generateUUID();
|
||||
|
||||
let task = await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: false,
|
||||
deleted: true,
|
||||
},
|
||||
});
|
||||
|
||||
await user.del(`/tasks/${task.id}`);
|
||||
|
||||
await sleep();
|
||||
|
||||
let body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body.type).to.eql('deleted');
|
||||
expect(body.task).to.eql(task);
|
||||
});
|
||||
|
||||
it('does not send task activity webhooks if task is not user owned', async () => {
|
||||
let uuid = generateUUID();
|
||||
|
||||
await user.update({
|
||||
balance: 10,
|
||||
});
|
||||
let guild = await generateGroup(user);
|
||||
let challenge = await generateChallenge(user, guild);
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: false,
|
||||
deleted: true,
|
||||
},
|
||||
});
|
||||
|
||||
let challengeTask = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
await user.del(`/tasks/${challengeTask.id}`);
|
||||
|
||||
await sleep();
|
||||
|
||||
let body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
context('task cannot be deleted', () => {
|
||||
it('cannot delete a non-existant task', async () => {
|
||||
await expect(user.del('/tasks/550e8400-e29b-41d4-a716-446655440000')).to.eventually.be.rejected.and.eql({
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('GET /tasks/user', () => {
|
||||
for (let i = 0; i < numberOfTodos; i++) {
|
||||
let id = todos[i]._id;
|
||||
|
||||
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line babel/no-await-in-loop
|
||||
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
await user.sync();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /tasks/clearCompletedTodos', () => {
|
||||
it('deletes all completed todos except the ones from a challenge', async () => {
|
||||
it('deletes all completed todos except the ones from a challenge and group', async () => {
|
||||
let user = await generateUser({balance: 1});
|
||||
let guild = await generateGroup(user);
|
||||
let challenge = await generateChallenge(user, guild);
|
||||
@@ -24,12 +24,18 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
||||
type: 'todo',
|
||||
});
|
||||
|
||||
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'todo 7',
|
||||
type: 'todo',
|
||||
});
|
||||
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||
|
||||
let tasks = await user.get('/tasks/user?type=todos');
|
||||
expect(tasks.length).to.equal(initialTodoCount + 6);
|
||||
expect(tasks.length).to.equal(initialTodoCount + 7);
|
||||
|
||||
for (let task of tasks) {
|
||||
if (['todo 2', 'todo 3', 'todo 6'].indexOf(task.text) !== -1) {
|
||||
await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line babel/no-await-in-loop
|
||||
await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +43,7 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
||||
let completedTodos = await user.get('/tasks/user?type=completedTodos');
|
||||
let todos = await user.get('/tasks/user?type=todos');
|
||||
let allTodos = todos.concat(completedTodos);
|
||||
expect(allTodos.length).to.equal(initialTodoCount + 4); // + 6 - 3 completed (but one is from challenge)
|
||||
expect(allTodos[allTodos.length - 1].text).to.equal('todo 6');
|
||||
expect(allTodos.length).to.equal(initialTodoCount + 5); // + 7 - 3 completed (but one is from challenge)
|
||||
expect(allTodos[allTodos.length - 1].text).to.equal('todo 6'); // last completed todo
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
generateUser,
|
||||
sleep,
|
||||
translate as t,
|
||||
server,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
@@ -45,6 +47,40 @@ describe('POST /tasks/:id/score/:direction', () => {
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('sends task scored webhooks', async () => {
|
||||
let uuid = generateUUID();
|
||||
await server.start();
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: false,
|
||||
scored: true,
|
||||
},
|
||||
});
|
||||
|
||||
let task = await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task.id}/score/up`);
|
||||
|
||||
await sleep();
|
||||
|
||||
await server.close();
|
||||
|
||||
let body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body.user).to.have.all.keys('_id', '_tmp', 'stats');
|
||||
expect(body.user.stats).to.have.all.keys('hp', 'mp', 'exp', 'gp', 'lvl', 'class', 'points', 'str', 'con', 'int', 'per', 'buffs', 'training', 'maxHealth', 'maxMP', 'toNextLevel');
|
||||
expect(body.task.id).to.eql(task.id);
|
||||
expect(body.direction).to.eql('up');
|
||||
expect(body.delta).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
context('todos', () => {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import {
|
||||
generateUser,
|
||||
sleep,
|
||||
translate as t,
|
||||
server,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /tasks/user', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
@@ -205,6 +207,71 @@ describe('POST /tasks/user', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('sending task activity webhooks', () => {
|
||||
before(async () => {
|
||||
await server.start();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('sends task activity webhooks', async () => {
|
||||
let uuid = generateUUID();
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: true,
|
||||
},
|
||||
});
|
||||
|
||||
let task = await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
await sleep();
|
||||
|
||||
let body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body.task).to.eql(task);
|
||||
});
|
||||
|
||||
it('sends a task activity webhook for each task', async () => {
|
||||
let uuid = generateUUID();
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: true,
|
||||
},
|
||||
});
|
||||
|
||||
let tasks = await user.post('/tasks/user', [{
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
}, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
}]);
|
||||
|
||||
await sleep();
|
||||
|
||||
let taskBodies = [
|
||||
server.getWebhookData(uuid),
|
||||
server.getWebhookData(uuid),
|
||||
];
|
||||
|
||||
expect(taskBodies.find(body => body.task.id === tasks[0].id)).to.exist;
|
||||
expect(taskBodies.find(body => body.task.id === tasks[1].id)).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
context('all types', () => {
|
||||
it('can create reminders', async () => {
|
||||
let id1 = generateUUID();
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
generateGroup,
|
||||
sleep,
|
||||
generateChallenge,
|
||||
server,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
@@ -73,6 +74,7 @@ describe('PUT /tasks/:id', () => {
|
||||
checklist: [
|
||||
{text: 123, completed: false},
|
||||
],
|
||||
collapseChecklist: false,
|
||||
});
|
||||
await sleep(2);
|
||||
|
||||
@@ -110,6 +112,7 @@ describe('PUT /tasks/:id', () => {
|
||||
{text: 123, completed: false},
|
||||
{text: 456, completed: true},
|
||||
],
|
||||
collapseChecklist: true,
|
||||
notes: 'new notes',
|
||||
attribute: 'per',
|
||||
tags: [challengeUserTaskId],
|
||||
@@ -142,6 +145,83 @@ describe('PUT /tasks/:id', () => {
|
||||
expect(savedChallengeUserTask.streak).to.equal(25);
|
||||
expect(savedChallengeUserTask.reminders.length).to.equal(2);
|
||||
expect(savedChallengeUserTask.checklist.length).to.equal(2);
|
||||
expect(savedChallengeUserTask.alias).to.equal('a-short-task-name');
|
||||
expect(savedChallengeUserTask.collapseChecklist).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
context('sending task activity webhooks', () => {
|
||||
before(async () => {
|
||||
await server.start();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('sends task activity webhooks if task is user owned', async () => {
|
||||
let uuid = generateUUID();
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: false,
|
||||
updated: true,
|
||||
},
|
||||
});
|
||||
|
||||
let task = await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
let updatedTask = await user.put(`/tasks/${task.id}`, {
|
||||
text: 'updated text',
|
||||
});
|
||||
|
||||
await sleep();
|
||||
|
||||
let body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body.type).to.eql('updated');
|
||||
expect(body.task).to.eql(updatedTask);
|
||||
});
|
||||
|
||||
it('does not send task activity webhooks if task is not user owned', async () => {
|
||||
let uuid = generateUUID();
|
||||
|
||||
await user.update({
|
||||
balance: 10,
|
||||
});
|
||||
let guild = await generateGroup(user);
|
||||
let challenge = await generateChallenge(user, guild);
|
||||
|
||||
await user.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'taskActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
created: false,
|
||||
updated: true,
|
||||
},
|
||||
});
|
||||
|
||||
let task = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
await user.put(`/tasks/${task.id}`, {
|
||||
text: 'updated text',
|
||||
});
|
||||
|
||||
await sleep();
|
||||
|
||||
let body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,12 +5,17 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/challenge/:challengeId', () => {
|
||||
let user;
|
||||
let guild;
|
||||
let challenge;
|
||||
|
||||
function findUserChallengeTask (memberTask) {
|
||||
return memberTask.challenge.id === challenge._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({balance: 1});
|
||||
guild = await generateGroup(user);
|
||||
@@ -88,6 +93,9 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.habits.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test habit');
|
||||
@@ -95,6 +103,8 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
expect(task.type).to.eql('habit');
|
||||
expect(task.up).to.eql(false);
|
||||
expect(task.down).to.eql(true);
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('creates a todo', async () => {
|
||||
@@ -105,11 +115,16 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.todos.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test todo');
|
||||
expect(task.notes).to.eql('1976');
|
||||
expect(task.type).to.eql('todo');
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('creates a daily', async () => {
|
||||
@@ -124,6 +139,9 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.dailys.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test daily');
|
||||
@@ -132,5 +150,7 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
expect(task.frequency).to.eql('daily');
|
||||
expect(task.everyX).to.eql(5);
|
||||
expect(new Date(task.startDate)).to.eql(now);
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,4 +69,48 @@ describe('DELETE /tasks/:id', () => {
|
||||
expect(syncedTask.group.broken).to.equal('TASK_DELETED');
|
||||
expect(member2SyncedTask.group.broken).to.equal('TASK_DELETED');
|
||||
});
|
||||
|
||||
it('prevents a user from deleting a task they are assigned to', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.del(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cantDeleteAssignedGroupTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to delete a broken task', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await user.del(`/tasks/${task._id}`);
|
||||
|
||||
await member.del(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await expect(member.get(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Task not found.',
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to delete a task after leaving a group', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await member.post(`/groups/${guild._id}/leave`);
|
||||
|
||||
await member.del(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await expect(member.get(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Task not found.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('GET /approvals/group/:groupId', () => {
|
||||
let user, guild, member, task, syncedTask;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
try {
|
||||
await member.post(`/tasks/${syncedTask._id}/score/up`);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-empty
|
||||
}
|
||||
});
|
||||
|
||||
it('errors when user is not the group leader', async () => {
|
||||
await expect(member.get(`/approvals/group/${guild._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanEditTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a list of task that need approval', async () => {
|
||||
let approvals = await user.get(`/approvals/group/${guild._id}`);
|
||||
expect(approvals[0]._id).to.equal(syncedTask._id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/:id/approve/:userId', () => {
|
||||
let user, guild, member, task;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when user is not assigned', async () => {
|
||||
await expect(user.post(`/tasks/${task._id}/approve/${member._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('taskNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when user is not the group leader', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
await expect(member.post(`/tasks/${task._id}/approve/${member._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanEditTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('approves an assigned user', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
await user.post(`/tasks/${task._id}/approve/${member._id}`);
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await member.sync();
|
||||
|
||||
expect(member.notifications.length).to.equal(2);
|
||||
expect(member.notifications[0].type).to.equal('GROUP_TASK_APPROVED');
|
||||
expect(member.notifications[0].data.message).to.equal(t('yourTaskHasBeenApproved', {taskText: task.text}));
|
||||
expect(member.notifications[1].type).to.equal('SCORED_TASK');
|
||||
expect(member.notifications[1].data.message).to.equal(t('yourTaskHasBeenApproved', {taskText: task.text}));
|
||||
|
||||
expect(syncedTask.group.approval.approved).to.be.true;
|
||||
expect(syncedTask.group.approval.approvingUser).to.equal(user._id);
|
||||
expect(syncedTask.group.approval.dateApproved).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/:id/score/:direction', () => {
|
||||
let user, guild, member, task;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
});
|
||||
|
||||
it('prevents user from scoring a task that needs to be approved', async () => {
|
||||
await user.update({
|
||||
'preferences.language': 'cs',
|
||||
});
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
let updatedTask = await member.get(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].type).to.equal('GROUP_TASK_APPROVAL');
|
||||
expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', {
|
||||
user: member.auth.local.username,
|
||||
taskName: updatedTask.text,
|
||||
}, 'cs')); // This test only works if we have the notification translated
|
||||
expect(user.notifications[0].data.groupId).to.equal(guild._id);
|
||||
|
||||
expect(updatedTask.group.approval.requested).to.equal(true);
|
||||
expect(updatedTask.group.approval.requestedDate).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
|
||||
it('errors when approval has already been requested', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskRequiresApproval'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to score an apporoved task', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await user.post(`/tasks/${task._id}/approve/${member._id}`);
|
||||
|
||||
await member.post(`/tasks/${syncedTask._id}/score/up`);
|
||||
let updatedTask = await member.get(`/tasks/${syncedTask._id}`);
|
||||
|
||||
expect(updatedTask.completed).to.equal(true);
|
||||
expect(updatedTask.dateCompleted).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
});
|
||||
@@ -74,7 +74,7 @@ describe('POST /tasks/:taskId', () => {
|
||||
});
|
||||
|
||||
it('returns error when non leader tries to create a task', async () => {
|
||||
await expect(member.post(`/tasks/${task._id}/assign/${member._id}`))
|
||||
await expect(member2.post(`/tasks/${task._id}/assign/${member._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
@@ -82,6 +82,26 @@ describe('POST /tasks/:taskId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('allows user to assign themselves (claim)', async () => {
|
||||
await member.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let groupTask = await user.get(`/tasks/group/${guild._id}`);
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
expect(groupTask[0].group.assignedUsers).to.contain(member._id);
|
||||
expect(syncedTask).to.exist;
|
||||
});
|
||||
|
||||
it('sends a message to the group when a user claims a task', async () => {
|
||||
await member.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let updateGroup = await user.get(`/groups/${guild._id}`);
|
||||
|
||||
expect(updateGroup.chat[0].text).to.equal(t('userIsClamingTask', {username: member.profile.name, task: task.text}));
|
||||
expect(updateGroup.chat[0].uuid).to.equal('system');
|
||||
});
|
||||
|
||||
it('assigns a task to a user', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user