mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-27 11:12:28 +01:00
Compare commits
507 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
791c19b5f1 | ||
|
|
7193cc6bae | ||
|
|
1845bd1e35 | ||
|
|
5f468d16b7 | ||
|
|
20a99e526d | ||
|
|
1eb0f5baa5 | ||
|
|
b28189fff5 | ||
|
|
82497e4041 | ||
|
|
2a5e9c0780 | ||
|
|
c8ca67aa64 | ||
|
|
dc2269a307 | ||
|
|
0c713ab368 | ||
|
|
be5d776fb4 | ||
|
|
b0051c45b4 | ||
|
|
b82044e07b | ||
|
|
b4c4769208 | ||
|
|
42a05446c0 | ||
|
|
9f7c0b4861 | ||
|
|
4814b0c52b | ||
|
|
67b16d91a3 | ||
|
|
8d444980de | ||
|
|
08ccd595f2 | ||
|
|
6b625a60ab | ||
|
|
6bdb695616 | ||
|
|
b999d46142 | ||
|
|
ce0f5af08d | ||
|
|
c5296d4cb0 | ||
|
|
22b683b1d9 | ||
|
|
229fd06ee3 | ||
|
|
eb8f84aae0 | ||
|
|
69b69e9d27 | ||
|
|
8033e7c0a0 | ||
|
|
e39b80bb9a | ||
|
|
2038d5e7c8 | ||
|
|
46a8ee52d4 | ||
|
|
52064f6b2a | ||
|
|
f15a27a7f1 | ||
|
|
fcf0dd87f9 | ||
|
|
ab974675b9 | ||
|
|
cb612d99d7 | ||
|
|
cb5a47ec7b | ||
|
|
838b9a5822 | ||
|
|
6c65056e2b | ||
|
|
2bf0fdf4a2 | ||
|
|
cd5ff04ee4 | ||
|
|
dbc5b9f850 | ||
|
|
ca3437d676 | ||
|
|
c43ca62bc4 | ||
|
|
eaa91b2a09 | ||
|
|
6259b68b4f | ||
|
|
08073acf11 | ||
|
|
bddafd4392 | ||
|
|
3ab14e4e5a | ||
|
|
d9830950aa | ||
|
|
45696a6273 | ||
|
|
3f92317b9e | ||
|
|
848883736d | ||
|
|
69e0ab11c0 | ||
|
|
d8d7a81edf | ||
|
|
03a09b7546 | ||
|
|
0d2737572d | ||
|
|
28149202db | ||
|
|
4b23cd9f23 | ||
|
|
8aaabdc086 | ||
|
|
6e6ca05352 | ||
|
|
74d8ecc732 | ||
|
|
63c8a09e22 | ||
|
|
865f623c99 | ||
|
|
061968dd1a | ||
|
|
21bc91c3ae | ||
|
|
0bfc6608c1 | ||
|
|
4108a22d78 | ||
|
|
34f6b63968 | ||
|
|
8d1ebff7e9 | ||
|
|
993df72708 | ||
|
|
50d3226a86 | ||
|
|
17ce2febf9 | ||
|
|
0caa195c6f | ||
|
|
f964e3c0a5 | ||
|
|
9d69d4b863 | ||
|
|
19500600bc | ||
|
|
f25fe9e263 | ||
|
|
5f37487c23 | ||
|
|
12fd79059b | ||
|
|
232061a629 | ||
|
|
3fcc1c522d | ||
|
|
8302c50302 | ||
|
|
6eb06fb054 | ||
|
|
286c8c7530 | ||
|
|
47ab8f2073 | ||
|
|
83353f6481 | ||
|
|
9cbd7ad62d | ||
|
|
3485a1d0bc | ||
|
|
2e5106fda1 | ||
|
|
2e5f5714e4 | ||
|
|
3cf7b2c96c | ||
|
|
286db39478 | ||
|
|
4d4c1cfaf3 | ||
|
|
d7ad3efabf | ||
|
|
f8876fe055 | ||
|
|
b973335d69 | ||
|
|
3b6fce0708 | ||
|
|
ff6bd6de71 | ||
|
|
042afe1df3 | ||
|
|
a208ba4aba | ||
|
|
0e958fd306 | ||
|
|
d98fe79e9c | ||
|
|
0e5a811b98 | ||
|
|
a28aea65f8 | ||
|
|
0f92349902 | ||
|
|
d4881cb73a | ||
|
|
b3216fdb85 | ||
|
|
3e37941e0a | ||
|
|
32088767ac | ||
|
|
f4d021ab8c | ||
|
|
8532203717 | ||
|
|
365daba6fc | ||
|
|
70692752c7 | ||
|
|
95d4016678 | ||
|
|
061457b268 | ||
|
|
e7ec9a6d65 | ||
|
|
d1396e7bc6 | ||
|
|
d5cedaa925 | ||
|
|
bea813b318 | ||
|
|
b31268fbc2 | ||
|
|
35727228f0 | ||
|
|
feb7ab8345 | ||
|
|
f4422b8d6c | ||
|
|
2d4dc9e23c | ||
|
|
bba2e71af3 | ||
|
|
26123ac6ae | ||
|
|
addee73e4d | ||
|
|
c39505d41c | ||
|
|
9736ef0d25 | ||
|
|
638259b885 | ||
|
|
c24545cae5 | ||
|
|
6bc70ca471 | ||
|
|
d2f0d7b20b | ||
|
|
b445bc8261 | ||
|
|
31281b43d3 | ||
|
|
a8a915ea8e | ||
|
|
dc3ee25e65 | ||
|
|
3c9f7ff9d8 | ||
|
|
54f57445da | ||
|
|
95ef2b1789 | ||
|
|
4d32977e5c | ||
|
|
7fe2504906 | ||
|
|
b74cee3d21 | ||
|
|
5af7733150 | ||
|
|
824bf62e0a | ||
|
|
ac24a5dddd | ||
|
|
9111f59da4 | ||
|
|
6a550b34df | ||
|
|
0c973b1cf0 | ||
|
|
4170ef5e79 | ||
|
|
506275c30e | ||
|
|
6838b7d0a6 | ||
|
|
e987cd52a6 | ||
|
|
1df8668d38 | ||
|
|
1af42aa7fe | ||
|
|
9dc9faa70d | ||
|
|
7fbcc0a263 | ||
|
|
0450e9c3ae | ||
|
|
15626b8ae1 | ||
|
|
444f393f3a | ||
|
|
d711bf4085 | ||
|
|
7b93e326fc | ||
|
|
c62ea522c0 | ||
|
|
810362a404 | ||
|
|
2e429513da | ||
|
|
c8625cb23f | ||
|
|
ad50f90ba0 | ||
|
|
d5305f74e3 | ||
|
|
5ea20e4c8c | ||
|
|
9e4e2d0b34 | ||
|
|
6456e6b8e3 | ||
|
|
af961ff16c | ||
|
|
eb2a6095c2 | ||
|
|
ee82f5a774 | ||
|
|
f1c8c4c54b | ||
|
|
79b15d7ddf | ||
|
|
f2fed7ea39 | ||
|
|
ca81ff5af6 | ||
|
|
e28c214696 | ||
|
|
792f45e9bd | ||
|
|
a69c0999d3 | ||
|
|
37ff35306c | ||
|
|
fd430e95b2 | ||
|
|
0ea472b3af | ||
|
|
6d0496fbd0 | ||
|
|
fac1889776 | ||
|
|
9ca6da0f75 | ||
|
|
99f50f825a | ||
|
|
2308f2397e | ||
|
|
f10d5110e5 | ||
|
|
5ced47f590 | ||
|
|
24b2bc9aa1 | ||
|
|
aea08a971a | ||
|
|
e3452b3ba7 | ||
|
|
3db666eb9e | ||
|
|
cf5985e38c | ||
|
|
d88a8247d1 | ||
|
|
7e1ae6a571 | ||
|
|
d117117885 | ||
|
|
5dd168eee4 | ||
|
|
a7c9355dd5 | ||
|
|
81d3e8a68f | ||
|
|
e0fbfffbf2 | ||
|
|
36b968a74a | ||
|
|
fff249fb00 | ||
|
|
ce5c6b9517 | ||
|
|
702013f9ed | ||
|
|
3503b307b2 | ||
|
|
f1ec8bbf2c | ||
|
|
58b033db9e | ||
|
|
b0dcc2f6ef | ||
|
|
3377f8a916 | ||
|
|
22f83d09c4 | ||
|
|
496534ab4b | ||
|
|
7325d2020e | ||
|
|
1a0d39e566 | ||
|
|
dc62ab7577 | ||
|
|
c4e5633e48 | ||
|
|
d977656e96 | ||
|
|
b0a980d56e | ||
|
|
3d75c99f8d | ||
|
|
0aa16d7021 | ||
|
|
4fa3046104 | ||
|
|
fda4be01b8 | ||
|
|
67436fbef1 | ||
|
|
ffb92a5faa | ||
|
|
4a44d60fac | ||
|
|
122cc510d3 | ||
|
|
29a7a07d14 | ||
|
|
18783aefe3 | ||
|
|
a5e242759c | ||
|
|
e01c6cc9a6 | ||
|
|
8a75383c43 | ||
|
|
47ebee9ae8 | ||
|
|
4e97355110 | ||
|
|
7045b5c214 | ||
|
|
e41dccf6d5 | ||
|
|
d0815ea9ee | ||
|
|
d0bd62bf02 | ||
|
|
39d7581c6c | ||
|
|
fdf146dd01 | ||
|
|
023b3ffd88 | ||
|
|
f01e552637 | ||
|
|
9f11820a02 | ||
|
|
ca6c7b8e5f | ||
|
|
02f8ba1638 | ||
|
|
8eb7c67f12 | ||
|
|
1f895fda44 | ||
|
|
e87c180e9b | ||
|
|
dbf9fd54be | ||
|
|
c9b99d1ecf | ||
|
|
fd8120c80d | ||
|
|
053e75562f | ||
|
|
1d50027f51 | ||
|
|
7fe74fd06a | ||
|
|
173a8561b6 | ||
|
|
f21bef707b | ||
|
|
9cf2ccf7c4 | ||
|
|
77d75c4669 | ||
|
|
1c17b95146 | ||
|
|
d89fc209d1 | ||
|
|
844c8bb3e6 | ||
|
|
569fb11db8 | ||
|
|
7671347d3a | ||
|
|
dc3a02bc2e | ||
|
|
1d8c126687 | ||
|
|
7ee49a43f2 | ||
|
|
900bc8dfc1 | ||
|
|
ec260016d3 | ||
|
|
ec770fb29e | ||
|
|
c1079e4eae | ||
|
|
3cb5637fd5 | ||
|
|
cf3a0118c9 | ||
|
|
895a383089 | ||
|
|
f730e7b345 | ||
|
|
d8f3d99d59 | ||
|
|
99fb1f6116 | ||
|
|
7c6dce2124 | ||
|
|
c757a3f52d | ||
|
|
ffb318fe8d | ||
|
|
19cd15ed62 | ||
|
|
3495662196 | ||
|
|
908a1340a4 | ||
|
|
31f4610b20 | ||
|
|
5eac2a6697 | ||
|
|
4495539e8c | ||
|
|
42e6f10b08 | ||
|
|
66750e77d1 | ||
|
|
1eb31a4fec | ||
|
|
fdf2dd1f1a | ||
|
|
f098fbcc80 | ||
|
|
5c429d0328 | ||
|
|
08a84ce13d | ||
|
|
f4bf2df4a9 | ||
|
|
ee6ceecc35 | ||
|
|
76c3e51660 | ||
|
|
d40543f4ca | ||
|
|
e1ad19c216 | ||
|
|
a03c6184b3 | ||
|
|
526d0b1a23 | ||
|
|
f523bd424f | ||
|
|
eda76efd28 | ||
|
|
08323f307c | ||
|
|
1c301f8328 | ||
|
|
a515168766 | ||
|
|
927a08defd | ||
|
|
c8d5eb9689 | ||
|
|
ec298291d4 | ||
|
|
356f2c7b7f | ||
|
|
b12bf773f1 | ||
|
|
d70ff4e5a3 | ||
|
|
a68b02d403 | ||
|
|
2db5ab2352 | ||
|
|
2a43df34c0 | ||
|
|
a317b351be | ||
|
|
4759764e61 | ||
|
|
5aea8def3b | ||
|
|
dde63b619f | ||
|
|
cbdb0bc3e3 | ||
|
|
6edd1a1fa5 | ||
|
|
6fcf739c89 | ||
|
|
f128f3d3cd | ||
|
|
744090e652 | ||
|
|
9c3b367b29 | ||
|
|
90451fbec8 | ||
|
|
64addcf847 | ||
|
|
5263b5e42f | ||
|
|
81fc727d41 | ||
|
|
7a74d4c296 | ||
|
|
b6a5efc524 | ||
|
|
1e1220d0f9 | ||
|
|
0a691ffb4f | ||
|
|
746fb982a7 | ||
|
|
5cd467d5b0 | ||
|
|
6df91e35c8 | ||
|
|
837713f2b7 | ||
|
|
cd0222e208 | ||
|
|
7812e14898 | ||
|
|
c0f159a8a5 | ||
|
|
dd0c95f051 | ||
|
|
6d93bcf4ff | ||
|
|
05e9f9693a | ||
|
|
136947169b | ||
|
|
1379140cf6 | ||
|
|
2cb9228a7c | ||
|
|
b1652ddd97 | ||
|
|
c9f68e2466 | ||
|
|
70aabd8b11 | ||
|
|
0a69c7a08d | ||
|
|
e784ae21ea | ||
|
|
68a438f3d4 | ||
|
|
32fa49191e | ||
|
|
ce5372647a | ||
|
|
36b4190f23 | ||
|
|
ccc862f82a | ||
|
|
ff92f14a5b | ||
|
|
0b0baf2195 | ||
|
|
8ccec0ed9d | ||
|
|
46b42c8441 | ||
|
|
381f652c08 | ||
|
|
5c5e117da0 | ||
|
|
79b3b26ab2 | ||
|
|
89f8f047ae | ||
|
|
a23d44347e | ||
|
|
fb872a5b59 | ||
|
|
04d3f084e2 | ||
|
|
a5dfb499b3 | ||
|
|
5b1530b216 | ||
|
|
c6881c5e30 | ||
|
|
2f913666cd | ||
|
|
5ba3e3ce5b | ||
|
|
cb825106af | ||
|
|
39bc25b0b7 | ||
|
|
9e38cec769 | ||
|
|
099eadeafc | ||
|
|
e29c393629 | ||
|
|
0c6540e6d8 | ||
|
|
3c2cad43d0 | ||
|
|
de67c130fa | ||
|
|
55e62cdc79 | ||
|
|
eee41142b1 | ||
|
|
176a4a0962 | ||
|
|
32ca3d5dfe | ||
|
|
e87a10513d | ||
|
|
d2832b7169 | ||
|
|
daca2c7fff | ||
|
|
eb52f73cac | ||
|
|
5c4d08336c | ||
|
|
861b78ce8a | ||
|
|
1dc1923d7b | ||
|
|
7872336820 | ||
|
|
6e70f27bc6 | ||
|
|
ad5decc285 | ||
|
|
3e1c128600 | ||
|
|
d9817be8f3 | ||
|
|
e54dcd2859 | ||
|
|
5ec7815cfe | ||
|
|
adeee244e3 | ||
|
|
fe8a44b8c4 | ||
|
|
80ae9c3e82 | ||
|
|
88fbcd5f8c | ||
|
|
2b912b354d | ||
|
|
52eb2deb62 | ||
|
|
1812d1ba61 | ||
|
|
5f65be98df | ||
|
|
56c32d9691 | ||
|
|
36a933d0c4 | ||
|
|
d051bdf2c9 | ||
|
|
0424d214c5 | ||
|
|
c2aaa9b592 | ||
|
|
e450e52836 | ||
|
|
e2ecec03e8 | ||
|
|
5a92ced288 | ||
|
|
658b96c5ac | ||
|
|
76b6806db1 | ||
|
|
318482d3ff | ||
|
|
2e4f665fa5 | ||
|
|
3d9738ac2f | ||
|
|
666cc855c1 | ||
|
|
3a059f6aca | ||
|
|
cdd3bc3cd6 | ||
|
|
87d57dab13 | ||
|
|
19789eb7ab | ||
|
|
395385f3e2 | ||
|
|
65aabc8333 | ||
|
|
5c16600b25 | ||
|
|
809960b3c0 | ||
|
|
e8841d275d | ||
|
|
6ba42332f9 | ||
|
|
32fd4e33c8 | ||
|
|
560d247c9b | ||
|
|
40567fc8d0 | ||
|
|
9fc7bae13e | ||
|
|
515fd62dd8 | ||
|
|
d0d319316b | ||
|
|
5ce80c71b9 | ||
|
|
0e2e8616d1 | ||
|
|
170f901d86 | ||
|
|
d1fbe98379 | ||
|
|
fb1808d85a | ||
|
|
4094ca99dd | ||
|
|
fd05286e1a | ||
|
|
9436c83919 | ||
|
|
4f636d3d2c | ||
|
|
1a056be637 | ||
|
|
f38e184434 | ||
|
|
d24eb67fa2 | ||
|
|
c35e4f5750 | ||
|
|
0233f7b486 | ||
|
|
c129c38631 | ||
|
|
748ce8a23f | ||
|
|
a86166742f | ||
|
|
82c912237b | ||
|
|
5c89451985 | ||
|
|
2624b06729 | ||
|
|
7b7f5c09fd | ||
|
|
ad621e7208 | ||
|
|
034f18c419 | ||
|
|
7a6bf8b870 | ||
|
|
f529a5c64c | ||
|
|
69662f84df | ||
|
|
7d0ab1ba25 | ||
|
|
bd46e3e195 | ||
|
|
e5a92f64c0 | ||
|
|
798d0ab82b | ||
|
|
acaed1ef0e | ||
|
|
78f94e365c | ||
|
|
11379f150c | ||
|
|
41da55921c | ||
|
|
e9141ff5c9 | ||
|
|
2760de7951 | ||
|
|
203d6d3ac2 | ||
|
|
2a2192e196 | ||
|
|
876552b922 | ||
|
|
2b922508c5 | ||
|
|
cbee0542ad | ||
|
|
bc499bcfbf | ||
|
|
861c85f099 | ||
|
|
de63622cdd | ||
|
|
f5cf27a79e | ||
|
|
88e6cf7d8a | ||
|
|
0d28e663e4 | ||
|
|
716695e11e | ||
|
|
2770650340 | ||
|
|
0bff37b600 | ||
|
|
8614f11a31 | ||
|
|
b27319313d | ||
|
|
1f1c7826a4 | ||
|
|
d8736c17e6 | ||
|
|
00d72fe555 | ||
|
|
9943a94108 | ||
|
|
7a1b7b3291 | ||
|
|
832106dc86 | ||
|
|
24b9bd6ccc | ||
|
|
bd19b83db4 | ||
|
|
eb43f83c71 | ||
|
|
6e89197b3f | ||
|
|
51739a4dfe | ||
|
|
87f39b4273 | ||
|
|
fcea1ecbc2 | ||
|
|
7b9ebc3465 | ||
|
|
40ebaefac9 |
@@ -14,17 +14,13 @@ files:
|
||||
owner: root
|
||||
group: users
|
||||
content: |
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@4
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@5
|
||||
container_commands:
|
||||
01_makeBabel:
|
||||
command: "touch /tmp/.babel.json"
|
||||
02_ownBabel:
|
||||
command: "chmod a+rw /tmp/.babel.json"
|
||||
03_installBower:
|
||||
command: "$NODE_HOME/bin/npm install -g bower"
|
||||
04_installGulp:
|
||||
03_installGulp:
|
||||
command: "$NODE_HOME/bin/npm install -g gulp"
|
||||
05_runBower:
|
||||
command: "$NODE_HOME/lib/node_modules/bower/bin/bower --config.interactive=false --allow-root install -f"
|
||||
06_runGulp:
|
||||
04_runGulp:
|
||||
command: "$NODE_HOME/lib/node_modules/gulp/bin/gulp.js build"
|
||||
|
||||
@@ -6,6 +6,9 @@ website/transpiled-babel/
|
||||
website/common/transpiled-babel/
|
||||
dist/
|
||||
dist-client/
|
||||
apidoc_build/
|
||||
content_cache/
|
||||
node_modules/
|
||||
|
||||
# Not linted
|
||||
website/client-old/
|
||||
@@ -16,5 +19,3 @@ migrations/*
|
||||
scripts/*
|
||||
website/common/browserify.js
|
||||
Gruntfile.js
|
||||
gulpfile.js
|
||||
gulp
|
||||
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -4,7 +4,7 @@
|
||||
|
||||
# Pull Request
|
||||
|
||||
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request)
|
||||
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
|
||||
|
||||
# Requesting a feature
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -6,7 +6,7 @@
|
||||
|
||||
[//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760)
|
||||
|
||||
[//]: # (Fill out relevant information - UUID is found in Settings -> API)
|
||||
[//]: # (Fill out relevant information - UUID is found from the Habitia website at User Icon > Settings > API)
|
||||
### General Info
|
||||
* UUID:
|
||||
* Browser:
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request for more info)
|
||||
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
|
||||
|
||||
[//]: # (Put Issue # or URL here, if applicable. This will automatically close the issue if your PR is merged in)
|
||||
Fixes put_issue_url_here
|
||||
@@ -8,7 +8,7 @@ Fixes put_issue_url_here
|
||||
|
||||
|
||||
|
||||
[//]: # (Put User ID in here - found in Settings -> API)
|
||||
[//]: # (Put User ID in here - found on the Habitica website at User Icon > Settings > API)
|
||||
|
||||
----
|
||||
UUID:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,11 +2,14 @@
|
||||
website/client-old/gen
|
||||
website/client-old/common
|
||||
website/client-old/apidoc
|
||||
website/build
|
||||
website/client-old/js/habitrpg-shared.js*
|
||||
website/client-old/css/habitrpg-shared.css
|
||||
website/transpiled-babel/
|
||||
website/common/transpiled-babel/
|
||||
node_modules
|
||||
content_cache
|
||||
apidoc_build
|
||||
*.swp
|
||||
.idea*
|
||||
config.json
|
||||
|
||||
@@ -13,7 +13,7 @@ addons:
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- $CXX --version
|
||||
- npm install -g npm@4
|
||||
- npm install -g npm@5
|
||||
- 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
|
||||
install:
|
||||
- npm install &> npm.install.log || (cat npm.install.log; false)
|
||||
@@ -34,6 +34,4 @@ env:
|
||||
- TEST="test:sanity"
|
||||
- TEST="test:content" COVERAGE=true
|
||||
- TEST="test:common" COVERAGE=true
|
||||
- TEST="test:karma" COVERAGE=true
|
||||
- TEST="client:unit" COVERAGE=true
|
||||
- TEST="apidoc"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
FROM node:boron
|
||||
|
||||
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
|
||||
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
|
||||
RUN yarn global add npm@5
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
RUN npm install -g gulp mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
@@ -9,7 +12,6 @@ WORKDIR /usr/src/habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN cp config.json.example config.json
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
FROM node:boron
|
||||
|
||||
ENV ADMIN_EMAIL admin@habitica.com
|
||||
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
||||
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
|
||||
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
|
||||
ENV BASE_URL https://habitica.com
|
||||
ENV FACEBOOK_KEY 128307497299777
|
||||
ENV GA_ID UA-33510635-1
|
||||
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
||||
ENV NODE_ENV production
|
||||
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
||||
|
||||
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
|
||||
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
|
||||
RUN yarn global add npm@5
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
RUN npm install -g gulp mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch v3.111.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN git clone --branch v4.11.0 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
|
||||
142
Gruntfile.js
142
Gruntfile.js
@@ -1,142 +0,0 @@
|
||||
/*global module:false*/
|
||||
require('babel-register');
|
||||
var _ = require('lodash');
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
karma: {
|
||||
unit: {
|
||||
configFile: 'test/client-old/spec/karma.conf.js'
|
||||
},
|
||||
continuous: {
|
||||
configFile: 'test/client-old/spec/karma.conf.js',
|
||||
singleRun: true,
|
||||
autoWatch: false
|
||||
}
|
||||
},
|
||||
|
||||
clean: {
|
||||
build: ['website/build']
|
||||
},
|
||||
|
||||
cssmin: {
|
||||
dist: {
|
||||
options: {
|
||||
report: 'gzip'
|
||||
},
|
||||
files:{
|
||||
"website/client-old/css/habitrpg-shared.css": [
|
||||
"website/assets/sprites/dist/spritesmith*.css",
|
||||
"website/assets/sprites/css/backer.css",
|
||||
"website/assets/sprites/css/Mounts.css",
|
||||
"website/assets/sprites/css/index.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stylus: {
|
||||
build: {
|
||||
options: {
|
||||
compress: false, // AFTER
|
||||
'include css': true,
|
||||
paths: ['website/client-old']
|
||||
},
|
||||
files: {
|
||||
'website/build/app.css': ['website/client-old/css/index.styl'],
|
||||
'website/build/static.css': ['website/client-old/css/static.styl']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
copy: {
|
||||
build: {
|
||||
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/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/'},
|
||||
{expand: true, cwd: 'website/client-old/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'website/build/'}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// UPDATE IT WHEN YOU ADD SOME FILES NOT ALREADY MATCHED!
|
||||
hashres: {
|
||||
build: {
|
||||
options: {
|
||||
fileNameFormat: '${name}-${hash}.${ext}'
|
||||
},
|
||||
src: [
|
||||
'website/build/*.js',
|
||||
'website/build/*.css',
|
||||
'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/*'
|
||||
],
|
||||
dest: 'website/build/*.css'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Load build files from client-old/manifest.json
|
||||
grunt.registerTask('loadManifestFiles', 'Load all build files from client-old/manifest.json', function(){
|
||||
var files = grunt.file.readJSON('./website/client-old/manifest.json');
|
||||
var uglify = {};
|
||||
var cssmin = {};
|
||||
|
||||
_.each(files, function(val, key){
|
||||
|
||||
var js = uglify['website/build/' + key + '.js'] = [];
|
||||
|
||||
_.each(files[key].js, function(val){
|
||||
var path = "./";
|
||||
if( val.indexOf('common/') == -1)
|
||||
path = './website/client-old/';
|
||||
js.push(path + val);
|
||||
});
|
||||
|
||||
var css = cssmin['website/build/' + key + '.css'] = [];
|
||||
|
||||
_.each(files[key].css, function(val){
|
||||
var path = "./";
|
||||
if( val.indexOf('common/') == -1) {
|
||||
path = (val == 'app.css' || val == 'static.css') ? './website/build/' : './website/client-old/';
|
||||
}
|
||||
css.push(path + val)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
grunt.config.set('uglify.build.files', uglify);
|
||||
grunt.config.set('uglify.build.options', {compress: false});
|
||||
|
||||
grunt.config.set('cssmin.build.files', cssmin);
|
||||
// Rewrite urls to relative path
|
||||
grunt.config.set('cssmin.build.options', {'target': 'website/client-old/css/whatever-css.css'});
|
||||
});
|
||||
|
||||
// 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', ['build:dev']);
|
||||
|
||||
// Load tasks
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-stylus');
|
||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-hashres');
|
||||
if (process.env.NODE_ENV !== 'production') grunt.loadNpmTasks('grunt-karma');
|
||||
|
||||
};
|
||||
56
bower.json
56
bower.json
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"name": "HabitRPG",
|
||||
"version": "0.1.1",
|
||||
"homepage": "https://github.com/lefnire/habitrpg",
|
||||
"authors": [
|
||||
"Tyler Renelle <tylerrenelle@gmail.com>"
|
||||
],
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"website/client-old/bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"Angular-At-Directive": "snicker/Angular-At-Directive#c27bae207aa06d1e",
|
||||
"angular": "1.3.9",
|
||||
"angular-bootstrap": "0.13.0",
|
||||
"angular-filter": "0.5.1",
|
||||
"angular-loading-bar": "0.6.0",
|
||||
"angular-resource": "1.3.9",
|
||||
"angular-sanitize": "1.3.9",
|
||||
"angular-ui": "0.4.0",
|
||||
"angular-ui-router": "0.2.13",
|
||||
"angular-ui-select2": "angular-ui/ui-select2#afa6589a54cb72815f",
|
||||
"angular-ui-utils": "0.1.0",
|
||||
"bootstrap": "3.1.0",
|
||||
"bootstrap-growl": "ifightcrime/bootstrap-growl#162daa41cd1155f",
|
||||
"bootstrap-tour": "0.10.1",
|
||||
"css-social-buttons": "samcollins/css-social-buttons#v1.1.1 ",
|
||||
"github-buttons": "mdo/github-buttons#v3.0.0",
|
||||
"hello": "1.14.1",
|
||||
"jquery": "2.1.0",
|
||||
"jquery-colorbox": "1.4.36",
|
||||
"jquery-ui": "1.10.3",
|
||||
"jquery.cookie": "1.4.0",
|
||||
"js-emoji": "snicker/js-emoji#f25d8a303f",
|
||||
"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",
|
||||
"taggle": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "1.3.9"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.3.9",
|
||||
"jquery": ">=1.9.0"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg/server",
|
||||
"habitrpg/babel"
|
||||
],
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import gulp from 'gulp';
|
||||
import clean from 'rimraf';
|
||||
import apidoc from 'apidoc';
|
||||
|
||||
const APIDOC_DEST_PATH = './website/build/apidoc';
|
||||
const APIDOC_DEST_PATH = './apidoc_build';
|
||||
const APIDOC_SRC_PATH = './website/server';
|
||||
gulp.task('apidoc:clean', (done) => {
|
||||
clean(APIDOC_DEST_PATH, done);
|
||||
@@ -22,5 +22,5 @@ gulp.task('apidoc', ['apidoc:clean'], (done) => {
|
||||
});
|
||||
|
||||
gulp.task('apidoc:watch', ['apidoc'], () => {
|
||||
return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']);
|
||||
return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, ['apidoc']);
|
||||
});
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import gulp from 'gulp';
|
||||
import browserify from 'browserify';
|
||||
import source from 'vinyl-source-stream';
|
||||
import buffer from 'vinyl-buffer';
|
||||
import uglify from 'gulp-uglify';
|
||||
import sourcemaps from 'gulp-sourcemaps';
|
||||
import babel from 'babelify';
|
||||
|
||||
gulp.task('browserify', function () {
|
||||
let bundler = browserify({
|
||||
entries: './website/common/browserify.js',
|
||||
debug: true,
|
||||
transform: [[babel, { compact: false }]],
|
||||
});
|
||||
|
||||
return bundler.bundle()
|
||||
.pipe(source('habitrpg-shared.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(sourcemaps.init({loadMaps: true}))
|
||||
.pipe(uglify())
|
||||
.on('error', function (err) {
|
||||
console.error(err);
|
||||
this.emit('end');
|
||||
})
|
||||
.pipe(sourcemaps.write('./'))
|
||||
.pipe(gulp.dest('./website/client-old/js/'));
|
||||
});
|
||||
|
||||
gulp.task('browserify:watch', () => {
|
||||
gulp.watch('./website/common/script/**/*.js', ['browserify']);
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
import gulp from 'gulp';
|
||||
import fs from 'fs';
|
||||
|
||||
// Copy Bootstrap 4 config variables from /website /node_modules so we can check
|
||||
// them into Git
|
||||
|
||||
const BOOSTRAP_NEW_CONFIG_PATH = 'website/client/assets/scss/bootstrap_config.scss';
|
||||
const BOOTSTRAP_ORIGINAL_CONFIG_PATH = 'node_modules/bootstrap/scss/_custom.scss';
|
||||
|
||||
// https://stackoverflow.com/a/14387791/969528
|
||||
function copyFile(source, target, cb) {
|
||||
let cbCalled = false;
|
||||
|
||||
function done(err) {
|
||||
if (!cbCalled) {
|
||||
cb(err);
|
||||
cbCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
let rd = fs.createReadStream(source);
|
||||
rd.on('error', done);
|
||||
let wr = fs.createWriteStream(target);
|
||||
wr.on('error', done);
|
||||
wr.on('close', () => done());
|
||||
rd.pipe(wr);
|
||||
}
|
||||
|
||||
gulp.task('bootstrap', (done) => {
|
||||
// use new config
|
||||
copyFile(
|
||||
BOOSTRAP_NEW_CONFIG_PATH,
|
||||
BOOTSTRAP_ORIGINAL_CONFIG_PATH,
|
||||
done,
|
||||
);
|
||||
});
|
||||
@@ -1,13 +1,10 @@
|
||||
import gulp from 'gulp';
|
||||
import runSequence from 'run-sequence';
|
||||
import babel from 'gulp-babel';
|
||||
require('gulp-grunt')(gulp);
|
||||
import webpackProductionBuild from '../webpack/build';
|
||||
|
||||
gulp.task('build', () => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
|
||||
gulp.start('build:prod');
|
||||
} else {
|
||||
gulp.start('build:dev');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,18 +22,16 @@ gulp.task('build:common', () => {
|
||||
|
||||
gulp.task('build:server', ['build:src', 'build:common']);
|
||||
|
||||
gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff'], (done) => {
|
||||
gulp.start('grunt-build:dev', done);
|
||||
// Client Production Build
|
||||
gulp.task('build:client', (done) => {
|
||||
webpackProductionBuild((err, output) => {
|
||||
if (err) return done(err);
|
||||
console.log(output); // eslint-disable-line no-console
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('build:dev:watch', ['build:dev'], () => {
|
||||
gulp.watch(['website/client-old/**/*.styl', 'website/common/script/*']);
|
||||
});
|
||||
|
||||
gulp.task('build:prod', ['browserify', 'build:server', 'prepare:staticNewStuff'], (done) => {
|
||||
runSequence(
|
||||
'grunt-build:prod',
|
||||
'apidoc',
|
||||
done
|
||||
);
|
||||
});
|
||||
gulp.task('build:prod', [
|
||||
'build:server',
|
||||
'build:client',
|
||||
'apidoc',
|
||||
]);
|
||||
|
||||
@@ -7,10 +7,11 @@ import gulp from 'gulp';
|
||||
|
||||
// Add additional properties to the repl's context
|
||||
let improveRepl = (context) => {
|
||||
|
||||
// Let "exit" and "quit" terminate the console
|
||||
['exit', 'quit'].forEach((term) => {
|
||||
Object.defineProperty(context, term, { get () { process.exit(); }});
|
||||
Object.defineProperty(context, term, { get () {
|
||||
process.exit();
|
||||
}});
|
||||
});
|
||||
|
||||
// "clear" clears the screen
|
||||
@@ -18,12 +19,12 @@ let improveRepl = (context) => {
|
||||
process.stdout.write('\u001B[2J\u001B[0;0f');
|
||||
}});
|
||||
|
||||
context.Challenge = require('../website/server/models/challenge').model;
|
||||
context.Group = require('../website/server/models/group').model;
|
||||
context.User = require('../website/server/models/user').model;
|
||||
context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require
|
||||
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
|
||||
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
|
||||
|
||||
var isProd = nconf.get('NODE_ENV') === 'production';
|
||||
var mongooseOptions = !isProd ? {} : {
|
||||
const isProd = nconf.get('NODE_ENV') === 'production';
|
||||
const mongooseOptions = !isProd ? {} : {
|
||||
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
|
||||
server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
|
||||
};
|
||||
@@ -31,16 +32,15 @@ let improveRepl = (context) => {
|
||||
mongoose.connect(
|
||||
nconf.get('NODE_DB_URI'),
|
||||
mongooseOptions,
|
||||
function (err) {
|
||||
(err) => {
|
||||
if (err) throw err;
|
||||
logger.info('Connected with Mongoose');
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
gulp.task('console', (cb) => {
|
||||
gulp.task('console', () => {
|
||||
improveRepl(repl.start({
|
||||
prompt: 'Habitica > ',
|
||||
}).context);
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import gulp from 'gulp';
|
||||
import jade from 'jade';
|
||||
import {writeFileSync} from 'fs';
|
||||
|
||||
gulp.task('prepare:staticNewStuff', () => {
|
||||
writeFileSync(
|
||||
'./website/client-old/new-stuff.html',
|
||||
jade.compileFile('./website/views/shared/new-stuff.jade')()
|
||||
);
|
||||
});
|
||||
@@ -10,82 +10,39 @@ import {each} from 'lodash';
|
||||
|
||||
// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
|
||||
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/';
|
||||
const IMG_DIST_PATH = 'website/client/assets/images/sprites/';
|
||||
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
|
||||
|
||||
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
|
||||
function checkForSpecialTreatment (name) {
|
||||
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
|
||||
return name.match(regex) || name === 'head_0';
|
||||
}
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
let mainSrc = sync('website/assets/sprites/spritesmith/**/*.png');
|
||||
return createSpritesStream('main', mainSrc);
|
||||
});
|
||||
function calculateImgDimensions (img, addPadding) {
|
||||
let dims = sizeOf(img);
|
||||
|
||||
gulp.task('sprites:largeSprites', () => {
|
||||
let largeSrc = sync('website/assets/sprites/spritesmith_large/**/*.png');
|
||||
return createSpritesStream('largeSprites', largeSrc);
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', (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'], () => {
|
||||
console.log('Verifiying that images do not exceed max dimensions');
|
||||
|
||||
let numberOfSheetsThatAreTooBig = 0;
|
||||
|
||||
let distSpritesheets = sync(`${DIST_PATH}*.png`);
|
||||
|
||||
each(distSpritesheets, (img, index) => {
|
||||
let spriteSize = calculateImgDimensions(img);
|
||||
|
||||
if (spriteSize > MAX_SPRITESHEET_SIZE) {
|
||||
numberOfSheetsThatAreTooBig++;
|
||||
let name = basename(img, '.png');
|
||||
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (numberOfSheetsThatAreTooBig > 0) {
|
||||
console.error(`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle them, but there is a margin of error in these calculations so it is probably okay. Mention this to an admin so they can test a staging site on mobile Safari after your PR is merged.`); // https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
|
||||
} else {
|
||||
console.log('All images are within the correct dimensions');
|
||||
let requiresSpecialTreatment = checkForSpecialTreatment(img);
|
||||
if (requiresSpecialTreatment) {
|
||||
let newWidth = dims.width < 90 ? 90 : dims.width;
|
||||
let newHeight = dims.height < 90 ? 90 : dims.height;
|
||||
dims = {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function createSpritesStream (name, src) {
|
||||
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
|
||||
let stream = mergeStream();
|
||||
let padding = 0;
|
||||
|
||||
each(spritesheetSliceIndicies, (start, index) => {
|
||||
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
|
||||
if (addPadding) {
|
||||
padding = dims.width * 8 + dims.height * 8;
|
||||
}
|
||||
|
||||
let spriteData = gulp.src(slicedSrc)
|
||||
.pipe(spritesmith({
|
||||
imgName: `spritesmith-${name}-${index}.png`,
|
||||
cssName: `spritesmith-${name}-${index}.css`,
|
||||
algorithm: 'binary-tree',
|
||||
padding: 1,
|
||||
cssTemplate: 'website/assets/sprites/css/css.template.handlebars',
|
||||
cssVarMap: cssVarMap,
|
||||
}));
|
||||
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
|
||||
|
||||
let imgStream = spriteData.img
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
let totalPixelSize = dims.width * dims.height + padding;
|
||||
|
||||
let cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
stream.add(imgStream);
|
||||
stream.add(cssStream);
|
||||
});
|
||||
|
||||
return stream;
|
||||
return totalPixelSize;
|
||||
}
|
||||
|
||||
function calculateSpritesheetsSrcIndicies (src) {
|
||||
@@ -105,37 +62,6 @@ function calculateSpritesheetsSrcIndicies (src) {
|
||||
return slices;
|
||||
}
|
||||
|
||||
function calculateImgDimensions (img, addPadding) {
|
||||
let dims = sizeOf(img);
|
||||
|
||||
let requiresSpecialTreatment = checkForSpecialTreatment(img);
|
||||
if (requiresSpecialTreatment) {
|
||||
let newWidth = dims.width < 90 ? 90 : dims.width;
|
||||
let newHeight = dims.height < 90 ? 90 : dims.height;
|
||||
dims = {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
};
|
||||
}
|
||||
|
||||
let padding = 0;
|
||||
|
||||
if (addPadding) {
|
||||
padding = (dims.width * 8) + (dims.height * 8);
|
||||
}
|
||||
|
||||
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims);
|
||||
|
||||
let totalPixelSize = (dims.width * dims.height) + padding;
|
||||
|
||||
return totalPixelSize;
|
||||
}
|
||||
|
||||
function checkForSpecialTreatment (name) {
|
||||
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
|
||||
return name.match(regex) || name === 'head_0';
|
||||
}
|
||||
|
||||
function cssVarMap (sprite) {
|
||||
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
|
||||
// 60x60 image pointing at the proper part of the 90x90 sprite.
|
||||
@@ -144,18 +70,93 @@ function cssVarMap (sprite) {
|
||||
if (requiresSpecialTreatment) {
|
||||
sprite.custom = {
|
||||
px: {
|
||||
offset_x: `-${ sprite.x + 25 }px`,
|
||||
offset_y: `-${ sprite.y + 15 }px`,
|
||||
offsetX: `-${ sprite.x + 25 }px`,
|
||||
offsetY: `-${ sprite.y + 15 }px`,
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
},
|
||||
};
|
||||
}
|
||||
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 (sprite.name.indexOf('shirt') !== -1)
|
||||
sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts
|
||||
if (sprite.name.indexOf('hair_base') !== -1) {
|
||||
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
|
||||
sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos
|
||||
}
|
||||
}
|
||||
|
||||
function createSpritesStream (name, src) {
|
||||
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
|
||||
let stream = mergeStream();
|
||||
|
||||
each(spritesheetSliceIndicies, (start, index) => {
|
||||
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
|
||||
|
||||
let spriteData = gulp.src(slicedSrc)
|
||||
.pipe(spritesmith({
|
||||
imgName: `spritesmith-${name}-${index}.png`,
|
||||
cssName: `spritesmith-${name}-${index}.css`,
|
||||
algorithm: 'binary-tree',
|
||||
padding: 1,
|
||||
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
|
||||
cssVarMap,
|
||||
}));
|
||||
|
||||
let imgStream = spriteData.img
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH));
|
||||
|
||||
let cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH));
|
||||
|
||||
stream.add(imgStream);
|
||||
stream.add(cssStream);
|
||||
});
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
|
||||
return createSpritesStream('main', mainSrc);
|
||||
});
|
||||
|
||||
gulp.task('sprites:largeSprites', () => {
|
||||
let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
|
||||
return createSpritesStream('largeSprites', largeSrc);
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', (done) => {
|
||||
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
|
||||
});
|
||||
|
||||
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
|
||||
console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console
|
||||
|
||||
let numberOfSheetsThatAreTooBig = 0;
|
||||
|
||||
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
|
||||
|
||||
each(distSpritesheets, (img) => {
|
||||
let spriteSize = calculateImgDimensions(img);
|
||||
|
||||
if (spriteSize > MAX_SPRITESHEET_SIZE) {
|
||||
numberOfSheetsThatAreTooBig++;
|
||||
let name = basename(img, '.png');
|
||||
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
|
||||
if (numberOfSheetsThatAreTooBig > 0) {
|
||||
// https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
|
||||
console.error( // eslint-disable-line no-console
|
||||
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
|
||||
them, but there is a margin of error in these calculations so it is probably okay. Mention
|
||||
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`);
|
||||
} else {
|
||||
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,8 +3,6 @@ import nodemon from 'gulp-nodemon';
|
||||
|
||||
let pkg = require('../package.json');
|
||||
|
||||
gulp.task('run:dev', ['nodemon', 'build:dev:watch']);
|
||||
|
||||
gulp.task('nodemon', () => {
|
||||
nodemon({
|
||||
script: pkg.main,
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import {
|
||||
pipe,
|
||||
awaitPort,
|
||||
kill,
|
||||
runMochaTests,
|
||||
} from './taskHelper';
|
||||
import { server as karma } from 'karma';
|
||||
import mongoose from 'mongoose';
|
||||
import { exec } from 'child_process';
|
||||
import psTree from 'ps-tree';
|
||||
import gulp from 'gulp';
|
||||
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
|
||||
|
||||
@@ -24,25 +15,23 @@ let server;
|
||||
|
||||
const TEST_DB_URI = nconf.get('TEST_DB_URI');
|
||||
|
||||
const API_V3_TEST_COMMAND = 'npm run test:api-v3';
|
||||
const SANITY_TEST_COMMAND = 'npm run test:sanity';
|
||||
const COMMON_TEST_COMMAND = 'npm run test:common';
|
||||
const CONTENT_TEST_COMMAND = 'npm run test:content';
|
||||
const CONTENT_OPTIONS = {maxBuffer: 1024 * 500};
|
||||
const KARMA_TEST_COMMAND = 'npm run test:karma';
|
||||
|
||||
/* Helper methods for reporting test summary */
|
||||
let testResults = [];
|
||||
let testCount = (stdout, regexp) => {
|
||||
let match = stdout.match(regexp);
|
||||
return parseInt(match && match[1] || 0);
|
||||
return parseInt(match && match[1] || 0, 10);
|
||||
};
|
||||
|
||||
let testBin = (string, additionalEnvVariables = '') => {
|
||||
if (os.platform() === 'win32') {
|
||||
if (additionalEnvVariables != '') {
|
||||
if (additionalEnvVariables !== '') {
|
||||
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set ');
|
||||
additionalEnvVariables = 'set ' + additionalEnvVariables + '&&';
|
||||
additionalEnvVariables = `set ${additionalEnvVariables}&&`;
|
||||
}
|
||||
return `set NODE_ENV=test&&${additionalEnvVariables}${string}`;
|
||||
} else {
|
||||
@@ -50,9 +39,9 @@ let testBin = (string, additionalEnvVariables = '') => {
|
||||
}
|
||||
};
|
||||
|
||||
gulp.task('test:nodemon', (done) => {
|
||||
process.env.PORT = TEST_SERVER_PORT;
|
||||
process.env.NODE_DB_URI = TEST_DB_URI;
|
||||
gulp.task('test:nodemon', () => {
|
||||
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
|
||||
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
|
||||
|
||||
runSequence('nodemon');
|
||||
});
|
||||
@@ -69,37 +58,27 @@ gulp.task('test:prepare:mongo', (cb) => {
|
||||
gulp.task('test:prepare:server', ['test:prepare:mongo'], () => {
|
||||
if (!server) {
|
||||
server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => {
|
||||
if (error) { throw `Problem with the server: ${error}`; }
|
||||
if (stderr) { console.error(stderr); }
|
||||
if (error) {
|
||||
throw new Error(`Problem with the server: ${error}`);
|
||||
}
|
||||
if (stderr) {
|
||||
console.error(stderr); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
gulp.task('test:prepare:build', ['build']);
|
||||
|
||||
gulp.task('test:prepare', [
|
||||
'test:prepare:build',
|
||||
'test:prepare:mongo',
|
||||
'test:prepare:webdriver',
|
||||
]);
|
||||
|
||||
gulp.task('test:sanity', (cb) => {
|
||||
let runner = exec(
|
||||
testBin(SANITY_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
(err) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -112,7 +91,7 @@ gulp.task('test:sanity', (cb) => {
|
||||
gulp.task('test:common', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(COMMON_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
(err) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -133,7 +112,7 @@ gulp.task('test:common:watch', ['test:common:clean'], () => {
|
||||
gulp.task('test:common:safe', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(COMMON_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
testResults.push({
|
||||
suite: 'Common Specs\t',
|
||||
pass: testCount(stdout, /(\d+) passing/),
|
||||
@@ -150,7 +129,7 @@ gulp.task('test:content', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
(err, stdout, stderr) => {
|
||||
(err) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -172,7 +151,7 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
(err, stdout, stderr) => {
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
testResults.push({
|
||||
suite: 'Content Specs\t',
|
||||
pass: testCount(stdout, /(\d+) passing/),
|
||||
@@ -185,103 +164,10 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:karma', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(KARMA_TEST_COMMAND),
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:karma:watch', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(`${KARMA_TEST_COMMAND}:watch`),
|
||||
(err, stdout) => {
|
||||
cb(err);
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:karma:safe', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(KARMA_TEST_COMMAND),
|
||||
(err, stdout) => {
|
||||
testResults.push({
|
||||
suite: 'Karma Specs\t',
|
||||
pass: testCount(stdout, /(\d+) tests? completed/),
|
||||
fail: testCount(stdout, /(\d+) tests? failed/),
|
||||
pend: testCount(stdout, /(\d+) tests? skipped/),
|
||||
});
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:e2e', ['test:prepare', 'test:prepare:server'], (cb) => {
|
||||
let support = [
|
||||
'Xvfb :99 -screen 0 1024x768x24 -extension RANDR',
|
||||
testBin('npm run test:e2e:webdriver', 'DISPLAY=:99'),
|
||||
].map(exec);
|
||||
support.push(server);
|
||||
|
||||
Bluebird.all([
|
||||
awaitPort(TEST_SERVER_PORT),
|
||||
awaitPort(4444),
|
||||
]).then(() => {
|
||||
let runner = exec(
|
||||
'npm run test:e2e',
|
||||
(err, stdout, stderr) => {
|
||||
support.forEach(kill);
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => {
|
||||
let support = [
|
||||
'Xvfb :99 -screen 0 1024x768x24 -extension RANDR',
|
||||
'npm run test:e2e:webdriver',
|
||||
].map(exec);
|
||||
|
||||
Bluebird.all([
|
||||
awaitPort(TEST_SERVER_PORT),
|
||||
awaitPort(4444),
|
||||
]).then(() => {
|
||||
let runner = exec(
|
||||
'npm run test:e2e',
|
||||
(err, stdout, stderr) => {
|
||||
let match = stdout.match(/(\d+) tests?.*(\d) failures?/);
|
||||
|
||||
testResults.push({
|
||||
suite: 'End-to-End Specs\t',
|
||||
pass: testCount(stdout, /(\d+) passing/),
|
||||
fail: testCount(stdout, /(\d+) failing/),
|
||||
pend: testCount(stdout, /(\d+) pending/),
|
||||
});
|
||||
support.forEach(kill);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3:unit', (done) => {
|
||||
let runner = exec(
|
||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-unit --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/unit --recursive --require ./test/helpers/start-server'),
|
||||
(err, stdout, stderr) => {
|
||||
(err) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -300,7 +186,7 @@ gulp.task('test:api-v3:integration', (done) => {
|
||||
let runner = exec(
|
||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => {
|
||||
(err) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -320,7 +206,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => done(err)
|
||||
(err) => done(err)
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
@@ -331,7 +217,6 @@ gulp.task('test', (done) => {
|
||||
'test:sanity',
|
||||
'test:content',
|
||||
'test:common',
|
||||
'test:karma',
|
||||
'test:api-v3:unit',
|
||||
'test:api-v3:integration',
|
||||
done
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import nconf from 'nconf';
|
||||
import gulp from 'gulp';
|
||||
import { postToSlack, conf } from './taskHelper';
|
||||
|
||||
@@ -12,8 +11,82 @@ const SLACK_CONFIG = {
|
||||
|
||||
const LOCALES = './website/common/locales/';
|
||||
const ENGLISH_LOCALE = `${LOCALES}en/`;
|
||||
|
||||
|
||||
function getArrayOfLanguages () {
|
||||
let languages = fs.readdirSync(LOCALES);
|
||||
languages.shift(); // Remove README.md from array of languages
|
||||
|
||||
return languages;
|
||||
}
|
||||
|
||||
const ALL_LANGUAGES = getArrayOfLanguages();
|
||||
|
||||
function stripOutNonJsonFiles (collection) {
|
||||
let onlyJson = _.filter(collection, (file) => {
|
||||
return file.match(/[a-zA-Z]*\.json/);
|
||||
});
|
||||
|
||||
return onlyJson;
|
||||
}
|
||||
|
||||
function eachTranslationFile (languages, cb) {
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
|
||||
_.each(languages, (lang) => {
|
||||
_.each(jsonFiles, (filename) => {
|
||||
let parsedTranslationFile;
|
||||
try {
|
||||
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
parsedTranslationFile = JSON.parse(translationFile);
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
|
||||
let parsedEnglishFile = JSON.parse(englishFile);
|
||||
|
||||
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function eachTranslationString (languages, cb) {
|
||||
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
|
||||
if (error) return;
|
||||
_.each(englishJSON, (string, key) => {
|
||||
const translationString = translationJSON[key];
|
||||
cb(language, filename, key, string, translationString);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function formatMessageForPosting (msg, items) {
|
||||
let body = `*Warning:* ${msg}`;
|
||||
body += '\n\n```\n';
|
||||
body += items.join('\n');
|
||||
body += '\n```';
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
function getStringsWith (json, interpolationRegex) {
|
||||
let strings = {};
|
||||
|
||||
_.each(json, (fileName) => {
|
||||
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
|
||||
const parsedJson = JSON.parse(rawFile);
|
||||
|
||||
strings[fileName] = {};
|
||||
_.each(parsedJson, (value, key) => {
|
||||
const match = value.match(interpolationRegex);
|
||||
if (match) strings[fileName][key] = match;
|
||||
});
|
||||
});
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
const malformedStringExceptions = {
|
||||
messageDropFood: true,
|
||||
armoireFood: true,
|
||||
@@ -23,7 +96,6 @@ const malformedStringExceptions = {
|
||||
gulp.task('transifex', ['transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings']);
|
||||
|
||||
gulp.task('transifex:missingFiles', () => {
|
||||
|
||||
let missingStrings = [];
|
||||
|
||||
eachTranslationFile(ALL_LANGUAGES, (error) => {
|
||||
@@ -40,7 +112,6 @@ gulp.task('transifex:missingFiles', () => {
|
||||
});
|
||||
|
||||
gulp.task('transifex:missingStrings', () => {
|
||||
|
||||
let missingStrings = [];
|
||||
|
||||
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
|
||||
@@ -58,7 +129,6 @@ gulp.task('transifex:missingStrings', () => {
|
||||
});
|
||||
|
||||
gulp.task('transifex:malformedStrings', () => {
|
||||
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
|
||||
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
|
||||
@@ -66,25 +136,23 @@ gulp.task('transifex:malformedStrings', () => {
|
||||
let stringsWithMalformedInterpolations = [];
|
||||
let stringsWithIncorrectNumberOfInterpolations = [];
|
||||
|
||||
let count = 0;
|
||||
_.each(ALL_LANGUAGES, function (lang) {
|
||||
|
||||
_.each(stringsToLookFor, function (strings, file) {
|
||||
let translationFile = fs.readFileSync(LOCALES + lang + '/' + file);
|
||||
_.each(ALL_LANGUAGES, (lang) => {
|
||||
_.each(stringsToLookFor, (strings, filename) => {
|
||||
let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
let parsedTranslationFile = JSON.parse(translationFile);
|
||||
|
||||
_.each(strings, function (value, key) {
|
||||
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
|
||||
let translationString = parsedTranslationFile[key];
|
||||
if (!translationString) return;
|
||||
|
||||
let englishOccurences = stringsToLookFor[file][key];
|
||||
let englishOccurences = stringsToLookFor[filename][key];
|
||||
let translationOccurences = translationString.match(interpolationRegex);
|
||||
|
||||
if (!translationOccurences) {
|
||||
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
stringsWithMalformedInterpolations.push(malformedString);
|
||||
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
||||
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||
}
|
||||
});
|
||||
@@ -103,74 +171,3 @@ gulp.task('transifex:malformedStrings', () => {
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
function getArrayOfLanguages () {
|
||||
let languages = fs.readdirSync(LOCALES);
|
||||
languages.shift(); // Remove README.md from array of languages
|
||||
|
||||
return languages;
|
||||
}
|
||||
|
||||
function eachTranslationFile (languages, cb) {
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
|
||||
_.each(languages, (lang) => {
|
||||
_.each(jsonFiles, (filename) => {
|
||||
try {
|
||||
var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename);
|
||||
var parsedTranslationFile = JSON.parse(translationFile);
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
|
||||
let parsedEnglishFile = JSON.parse(englishFile);
|
||||
|
||||
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function eachTranslationString (languages, cb) {
|
||||
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
|
||||
if (error) return;
|
||||
_.each(englishJSON, (string, key) => {
|
||||
var translationString = translationJSON[key];
|
||||
cb(language, filename, key, string, translationString);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function formatMessageForPosting (msg, items) {
|
||||
let body = `*Warning:* ${msg}`;
|
||||
body += '\n\n```\n';
|
||||
body += items.join('\n');
|
||||
body += '\n```';
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
function getStringsWith (json, interpolationRegex) {
|
||||
var strings = {};
|
||||
|
||||
_.each(json, function (file_name) {
|
||||
var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name);
|
||||
var parsed_json = JSON.parse(raw_file);
|
||||
|
||||
strings[file_name] = {};
|
||||
_.each(parsed_json, function (value, key) {
|
||||
var match = value.match(interpolationRegex);
|
||||
if (match) strings[file_name][key] = match;
|
||||
});
|
||||
});
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
function stripOutNonJsonFiles (collection) {
|
||||
let onlyJson = _.filter(collection, (file) => {
|
||||
return file.match(/[a-zA-Z]*\.json/);
|
||||
});
|
||||
|
||||
return onlyJson;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { resolve } from 'path';
|
||||
* Get access to configruable values
|
||||
*/
|
||||
nconf.argv().env().file({ file: 'config.json' });
|
||||
export var conf = nconf;
|
||||
export const conf = nconf;
|
||||
|
||||
/*
|
||||
* Kill a child process and any sub-children that process may have spawned.
|
||||
@@ -26,11 +26,12 @@ export function kill (proc) {
|
||||
pids.forEach(kill); return;
|
||||
}
|
||||
try {
|
||||
exec(/^win/.test(process.platform)
|
||||
? `taskkill /PID ${pid} /T /F`
|
||||
: `kill -9 ${pid}`);
|
||||
exec(/^win/.test(process.platform) ?
|
||||
`taskkill /PID ${pid} /T /F` :
|
||||
`kill -9 ${pid}`);
|
||||
} catch (e) {
|
||||
console.log(e); // eslint-disable-line no-console
|
||||
}
|
||||
catch (e) { console.log(e); }
|
||||
});
|
||||
};
|
||||
|
||||
@@ -44,21 +45,25 @@ export function kill (proc) {
|
||||
* before failing.
|
||||
*/
|
||||
export function awaitPort (port, max = 60) {
|
||||
return new Bluebird((reject, resolve) => {
|
||||
let socket, timeout, interval;
|
||||
return new Bluebird((rej, res) => {
|
||||
let socket;
|
||||
let timeout;
|
||||
let interval;
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
reject(`Timed out after ${max} seconds`);
|
||||
rej(`Timed out after ${max} seconds`);
|
||||
}, max * 1000);
|
||||
|
||||
interval = setInterval(() => {
|
||||
socket = net.connect({port: port}, () => {
|
||||
socket = net.connect({port}, () => {
|
||||
clearInterval(interval);
|
||||
clearTimeout(timeout);
|
||||
socket.destroy();
|
||||
resolve();
|
||||
}).on('error', () => { socket.destroy; });
|
||||
res();
|
||||
}).on('error', () => {
|
||||
socket.destroy();
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
@@ -67,8 +72,12 @@ export function awaitPort (port, max = 60) {
|
||||
* Pipe the child's stdin and stderr to the parent process.
|
||||
*/
|
||||
export function pipe (child) {
|
||||
child.stdout.on('data', (data) => { process.stdout.write(data); });
|
||||
child.stderr.on('data', (data) => { process.stderr.write(data); });
|
||||
child.stdout.on('data', (data) => {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
child.stderr.on('data', (data) => {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -78,8 +87,8 @@ export function postToSlack (msg, config = {}) {
|
||||
let slackUrl = nconf.get('SLACK_URL');
|
||||
|
||||
if (!slackUrl) {
|
||||
console.error('No slack post url specified. Your message was:');
|
||||
console.log(msg);
|
||||
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
|
||||
console.log(msg); // eslint-disable-line no-console
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -89,15 +98,15 @@ export function postToSlack (msg, config = {}) {
|
||||
channel: `#${config.channel || '#general'}`,
|
||||
username: config.username || 'gulp task',
|
||||
text: msg,
|
||||
icon_emoji: `:${config.emoji || 'gulp'}:`,
|
||||
icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase
|
||||
})
|
||||
.end((err, res) => {
|
||||
if (err) console.error('Unable to post to slack', err);
|
||||
.end((err) => {
|
||||
if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console
|
||||
});
|
||||
}
|
||||
|
||||
export function runMochaTests (files, server, cb) {
|
||||
require('../test/helpers/globals.helper');
|
||||
require('../test/helpers/globals.helper'); // eslint-disable-line global-require
|
||||
|
||||
let mocha = new Mocha({reporter: 'spec'});
|
||||
let tests = glob(files);
|
||||
@@ -108,7 +117,7 @@ export function runMochaTests (files, server, cb) {
|
||||
});
|
||||
|
||||
mocha.run((numberOfFailures) => {
|
||||
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) {
|
||||
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
|
||||
if (server) kill(server);
|
||||
process.exit(numberOfFailures);
|
||||
}
|
||||
|
||||
13
gulpfile.js
13
gulpfile.js
@@ -8,13 +8,10 @@
|
||||
|
||||
require('babel-register');
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
require('./gulp/gulp-apidoc');
|
||||
require('./gulp/gulp-newstuff');
|
||||
require('./gulp/gulp-build');
|
||||
require('./gulp/gulp-babelify');
|
||||
require('./gulp/gulp-bootstrap');
|
||||
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
|
||||
require('./gulp/gulp-apidoc'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-build'); // eslint-disable-line global-require
|
||||
} else {
|
||||
require('glob').sync('./gulp/gulp-*').forEach(require);
|
||||
require('gulp').task('default', ['test']);
|
||||
require('glob').sync('./gulp/gulp-*').forEach(require); // eslint-disable-line global-require
|
||||
require('gulp').task('default', ['test']); // eslint-disable-line global-require
|
||||
}
|
||||
|
||||
97
migrations/20170928_redesign_guilds.js
Normal file
97
migrations/20170928_redesign_guilds.js
Normal file
@@ -0,0 +1,97 @@
|
||||
var migrationName = '20170928_redesign_guilds.js';
|
||||
|
||||
/*
|
||||
* Copy Guild Leader messages to end of Guild descriptions
|
||||
* Copy Guild logos to beginning of Guild descriptions
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbGroups = monk(connectionString).get('groups', { castIds: false });
|
||||
|
||||
function processGroups(lastId) {
|
||||
// specify a query to limit the affected groups (empty for all groups):
|
||||
var query = {
|
||||
};
|
||||
|
||||
var fields = {
|
||||
'description': 1,
|
||||
'logo': 1,
|
||||
'leaderMessage': 1,
|
||||
}
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
return dbGroups.find(query, {
|
||||
fields: fields,
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
})
|
||||
.then(updateGroups)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateGroups (groups) {
|
||||
if (!groups || groups.length === 0) {
|
||||
console.warn('All appropriate groups found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var groupPromises = groups.map(updateGroup);
|
||||
var lastGroup = groups[groups.length - 1];
|
||||
|
||||
return Promise.all(groupPromises)
|
||||
.then(function () {
|
||||
processGroups(lastGroup._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateGroup (group) {
|
||||
count++;
|
||||
|
||||
var description = group.description;
|
||||
|
||||
if (group.logo) {
|
||||
description = '\n\n \n\n' + description;
|
||||
}
|
||||
|
||||
if (group.leaderMessage) {
|
||||
description = description + '\n\n \n\n' + group.leaderMessage;
|
||||
}
|
||||
|
||||
var set = {
|
||||
description: description,
|
||||
};
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + group._id);
|
||||
|
||||
return dbGroups.update({_id: group._id}, {$set:set});
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' groups 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 = processGroups;
|
||||
128
migrations/20170928_redesign_launch.js
Normal file
128
migrations/20170928_redesign_launch.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils';
|
||||
|
||||
var getItemInfo = require('../website/common/script/libs/getItemInfo');
|
||||
|
||||
var migrationName = '20170928_redesign_launch.js';
|
||||
var authorName = 'paglias'; // in case script author needs to know when their ...
|
||||
var authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; //... own data is done
|
||||
|
||||
/*
|
||||
* Migrate existing in app rewards lists to pinned items
|
||||
* Award Veteran Pets
|
||||
*/
|
||||
|
||||
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-09-21')},
|
||||
};
|
||||
|
||||
var fields = {
|
||||
'items.pets': 1,
|
||||
'items.gear': 1,
|
||||
'stats.class': 1,
|
||||
}
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
return dbUsers.find(query, {
|
||||
fields: fields,
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
})
|
||||
.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};
|
||||
|
||||
var oldRewardsList = selectGearToPin(user);
|
||||
var newPinnedItems = [
|
||||
{
|
||||
type: 'armoire',
|
||||
path: 'armoire',
|
||||
},
|
||||
{
|
||||
type: 'potion',
|
||||
path: 'potion',
|
||||
},
|
||||
];
|
||||
|
||||
oldRewardsList.forEach(item => {
|
||||
var type = 'marketGear';
|
||||
|
||||
var itemInfo = getItemInfo(user, 'marketGear', item);
|
||||
newPinnedItems.push({
|
||||
type,
|
||||
path: itemInfo.path,
|
||||
})
|
||||
});
|
||||
|
||||
set.pinnedItems = newPinnedItems;
|
||||
|
||||
if (user.items.pets['Lion-Veteran']) {
|
||||
set['items.pets.Bear-Veteran'] = 5;
|
||||
} else if (user.items.pets['Tiger-Veteran']) {
|
||||
set['items.pets.Lion-Veteran'] = 5;
|
||||
} else if (user.items.pets['Wolf-Veteran']) {
|
||||
set['items.pets.Tiger-Veteran'] = 5;
|
||||
} else {
|
||||
set['items.pets.Wolf-Veteran'] = 5;
|
||||
}
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
|
||||
return dbUsers.update({_id: user._id}, {$set:set});
|
||||
}
|
||||
|
||||
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;
|
||||
111
migrations/20171030_jackolanterns.js
Normal file
111
migrations/20171030_jackolanterns.js
Normal file
@@ -0,0 +1,111 @@
|
||||
var migrationName = '20171030_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
|
||||
|
||||
/*
|
||||
* Award the Jack-O'-Lantern ladder:
|
||||
* Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet
|
||||
* Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount
|
||||
* Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet
|
||||
* Jack-O-Lantern Pet to everyone else
|
||||
*/
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
'items.pets',
|
||||
'items.mounts',
|
||||
] // 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 set = {};
|
||||
var inc = {
|
||||
'items.food.Candy_Skeleton': 1,
|
||||
'items.food.Candy_Base': 1,
|
||||
'items.food.Candy_CottonCandyBlue': 1,
|
||||
'items.food.Candy_CottonCandyPink': 1,
|
||||
'items.food.Candy_Shade': 1,
|
||||
'items.food.Candy_White': 1,
|
||||
'items.food.Candy_Golden': 1,
|
||||
'items.food.Candy_Zombie': 1,
|
||||
'items.food.Candy_Desert': 1,
|
||||
'items.food.Candy_Red': 1,
|
||||
};
|
||||
|
||||
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
|
||||
set = {'migration':migrationName, 'items.mounts.JackOLantern-Ghost': true};
|
||||
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost': 5};
|
||||
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
|
||||
set = {'migration':migrationName, 'items.mounts.JackOLantern-Base': true};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.pets.JackOLantern-Base': 5};
|
||||
}
|
||||
|
||||
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;
|
||||
128
migrations/20171117_turkey_ladder.js
Normal file
128
migrations/20171117_turkey_ladder.js
Normal file
@@ -0,0 +1,128 @@
|
||||
var migrationName = '20171117_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
|
||||
|
||||
/*
|
||||
* Award the Turkey Day ladder:
|
||||
* Grant Turkey Costume to those who have the Gilded Turkey mount
|
||||
* Grant Gilded Turkey mount to those who have the Gilded Turkey pet
|
||||
* Grant Gilded Turkey pet to those who have the Base Turkey mount
|
||||
* Grant Base Turkey mount to those who have the Base Turkey pet
|
||||
* Grant Base Turkey pet to those who have none of the above yet
|
||||
*/
|
||||
|
||||
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-11-01')},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
'items.pets',
|
||||
'items.mounts',
|
||||
] // 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 set = {};
|
||||
|
||||
if (user && user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
|
||||
set = {
|
||||
migration: migrationName,
|
||||
'items.gear.owned.head_special_turkeyHelmBase': false,
|
||||
'items.gear.owned.armor_special_turkeyArmorBase': false,
|
||||
'items.gear.owned.back_special_turkeyTailBase': false,
|
||||
};
|
||||
var push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_turkeyHelmBase',
|
||||
_id: monk.id(),
|
||||
},
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.armor_special_turkeyArmorBase',
|
||||
_id: monk.id(),
|
||||
},
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.back_special_turkeyTailBase',
|
||||
_id: monk.id(),
|
||||
},
|
||||
];
|
||||
} else if (user && user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true};
|
||||
} else if (user && user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5};
|
||||
} else if (user && user.items && user.items.pets && 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 (push) {
|
||||
dbUsers.update({_id: user._id}, {$push: {pinnedItems: {$each: push}}});
|
||||
}
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' users processed\n');
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting(code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) { msg = 'ERROR!'; }
|
||||
if (msg) {
|
||||
if (code) { console.error(msg); }
|
||||
else { console.log( msg); }
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
@@ -2,7 +2,7 @@ var _id = '';
|
||||
var update = {
|
||||
$addToSet: {
|
||||
'purchased.plan.mysteryItems':{
|
||||
$each:['shield_mystery_201708','weapon_mystery_201708']
|
||||
$each:['armor_mystery_201711','body_mystery_201711']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
98
migrations/s3-upload.js
Normal file
98
migrations/s3-upload.js
Normal file
@@ -0,0 +1,98 @@
|
||||
let Bluebird = require('bluebird');
|
||||
let request = require('superagent');
|
||||
let last = require('lodash/last');
|
||||
let AWS = require('aws-sdk');
|
||||
|
||||
let config = require('../config');
|
||||
const S3_DIRECTORY = 'mobileApp/images'; //config.S3.SPRITES_DIRECTORY;
|
||||
|
||||
AWS.config.update({
|
||||
accessKeyId: config.S3.accessKeyId,
|
||||
secretAccessKey: config.S3.secretAccessKey,
|
||||
// region: config.get('S3_REGION'),
|
||||
});
|
||||
|
||||
let BUCKET_NAME = config.S3.bucket;
|
||||
let s3 = new AWS.S3();
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/22210077/2601552
|
||||
function uploadFile (buffer, fileName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
s3.putObject({
|
||||
Body: buffer,
|
||||
Key: fileName,
|
||||
Bucket: BUCKET_NAME,
|
||||
}, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
// console.info(`${fileName} uploaded to ${BUCKET_NAME} succesfully.`);
|
||||
resolve(fileName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getFileName (file) {
|
||||
let piecesOfPath = file.split('/');
|
||||
let name = last(piecesOfPath);
|
||||
let fullName = S3_DIRECTORY + name;
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
function getFileFromUrl (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.get(url).end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
let file = res.body;
|
||||
resolve(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let commit = '78f94e365c72cc58f66857d5941105638db7d35c';
|
||||
commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015';
|
||||
let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`
|
||||
|
||||
|
||||
let currentIndex = 0;
|
||||
|
||||
function uploadToS3(start, end, filesUrls) {
|
||||
let urls = filesUrls.slice(start, end);
|
||||
|
||||
if (urls.length === 0) {
|
||||
console.log("done");
|
||||
return;
|
||||
}
|
||||
|
||||
let promises = urls.map(fullUrl => {
|
||||
return getFileFromUrl(fullUrl)
|
||||
.then((buffer) => {
|
||||
return uploadFile(buffer, getFileName(fullUrl));
|
||||
});
|
||||
});
|
||||
console.log(promises.length)
|
||||
|
||||
return Bluebird.all(promises)
|
||||
.then(() => {
|
||||
currentIndex += 50;
|
||||
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
|
||||
request.get(gihuburl)
|
||||
.end((err, res) => {
|
||||
console.log(err);
|
||||
let files = res.body.files;
|
||||
|
||||
let filesUrls = [''];
|
||||
filesUrls = files.map(file => {
|
||||
return file.raw_url;
|
||||
})
|
||||
|
||||
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
|
||||
});
|
||||
@@ -65,19 +65,29 @@ function updateUser (user) {
|
||||
set = {'migration':migrationName};
|
||||
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
|
||||
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', '_id': monk.id()}};
|
||||
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', '_id': monk.id()}};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', '_id': monk.id()}};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', '_id': monk.id()}};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', '_id': monk.id()}};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', '_id': monk.id()}};
|
||||
}
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
if (push) {
|
||||
dbUsers.update({_id: user._id}, {$set: set, $push: push});
|
||||
} else {
|
||||
dbUsers.update({_id: user._id}, {$set: set});
|
||||
}
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
|
||||
13885
npm-shrinkwrap.json
generated
13885
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
19350
package-lock.json
generated
Normal file
19350
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
package.json
57
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "3.111.3",
|
||||
"version": "4.12.1",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
@@ -14,6 +14,7 @@
|
||||
"autoprefixer": "^6.4.0",
|
||||
"aws-sdk": "^2.0.25",
|
||||
"axios": "^0.16.0",
|
||||
"axios-progress-bar": "^0.1.7",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^6.0.0",
|
||||
@@ -30,14 +31,14 @@
|
||||
"bcrypt": "^1.0.2",
|
||||
"bluebird": "^3.3.5",
|
||||
"body-parser": "^1.15.0",
|
||||
"bootstrap": "^4.0.0-alpha.6",
|
||||
"bootstrap-vue": "^0.18.0",
|
||||
"bower": "~1.3.12",
|
||||
"bootstrap": "4.0.0-beta.2",
|
||||
"bootstrap-vue": "^1.0.2",
|
||||
"browserify": "~12.0.1",
|
||||
"compression": "^1.6.1",
|
||||
"connect-ratelimit": "0.0.7",
|
||||
"cookie-session": "^1.2.0",
|
||||
"coupon-code": "^0.4.5",
|
||||
"cross-env": "^4.0.0",
|
||||
"css-loader": "^0.28.0",
|
||||
"csv-stringify": "^1.0.2",
|
||||
"cwait": "~1.0.1",
|
||||
@@ -51,18 +52,8 @@
|
||||
"file-loader": "^0.10.0",
|
||||
"glob": "^4.3.5",
|
||||
"got": "^6.1.1",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-contrib-clean": "~0.6.0",
|
||||
"grunt-contrib-copy": "~0.6.0",
|
||||
"grunt-contrib-cssmin": "~0.10.0",
|
||||
"grunt-contrib-stylus": "~0.20.0",
|
||||
"grunt-contrib-uglify": "~0.6.0",
|
||||
"grunt-contrib-watch": "~0.6.1",
|
||||
"grunt-hashres": "habitrpg/grunt-hashres#v0.4.2",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-babel": "^6.1.2",
|
||||
"gulp-grunt": "^0.5.2",
|
||||
"gulp-imagemin": "^2.4.0",
|
||||
"gulp-nodemon": "^2.0.4",
|
||||
"gulp-sourcemaps": "^1.6.0",
|
||||
@@ -75,13 +66,13 @@
|
||||
"in-app-purchase": "^1.1.6",
|
||||
"intro.js": "^2.6.0",
|
||||
"jade": "~1.11.0",
|
||||
"jquery": "^3.1.1",
|
||||
"jquery": ">=3.0.0",
|
||||
"js2xmlparser": "~1.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"merge-stream": "^1.0.0",
|
||||
"method-override": "^2.3.5",
|
||||
"moment": "^2.13.0",
|
||||
"moment-recur": "habitrpg/moment-recur#v1.0.6",
|
||||
"moment-recur": "git://github.com/habitrpg/moment-recur#f147ef27bbc26ca67638385f3db4a44084c76626",
|
||||
"mongoose": "~4.8.6",
|
||||
"mongoose-id-autoinc": "~2013.7.14-4",
|
||||
"morgan": "^1.7.0",
|
||||
@@ -98,11 +89,12 @@
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
"paypal-ipn": "3.0.0",
|
||||
"paypal-rest-sdk": "^1.2.1",
|
||||
"popper.js": "^1.11.0",
|
||||
"postcss-easy-import": "^2.0.0",
|
||||
"pretty-data": "^0.40.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.0-beta.12",
|
||||
"push-notify": "habitrpg/push-notify#v1.2.0",
|
||||
"push-notify": "git://github.com/habitrpg/push-notify#6bc2b5fdb1bdc9649b9ec1964d79ca50187fc8a9",
|
||||
"pusher": "^1.3.0",
|
||||
"request": "~2.74.0",
|
||||
"rimraf": "^2.4.3",
|
||||
@@ -111,6 +103,7 @@
|
||||
"sass-loader": "^6.0.2",
|
||||
"serve-favicon": "^2.3.0",
|
||||
"shelljs": "^0.7.6",
|
||||
"sortablejs": "^1.6.1",
|
||||
"stripe": "^4.2.0",
|
||||
"superagent": "^3.4.3",
|
||||
"svg-inline-loader": "^0.7.1",
|
||||
@@ -123,14 +116,13 @@
|
||||
"validator": "^4.9.0",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"vue": "^2.1.0",
|
||||
"vue-loader": "^11.0.0",
|
||||
"vue": "^2.5.2",
|
||||
"vue-loader": "^13.3.0",
|
||||
"vue-mugen-scroll": "^0.2.1",
|
||||
"vue-notification": "^1.3.2",
|
||||
"vue-router": "^2.0.0-rc.5",
|
||||
"vue-router": "^3.0.0",
|
||||
"vue-style-loader": "^3.0.0",
|
||||
"vue-template-compiler": "^2.1.10",
|
||||
"vuejs-datepicker": "^0.9.4",
|
||||
"vue-template-compiler": "^2.5.2",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker#825a866b6a9c52dd8c588a3e8b900880875ce914",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-merge": "^4.0.0",
|
||||
"winston": "^2.1.0",
|
||||
@@ -140,11 +132,11 @@
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "^6.9.1",
|
||||
"npm": "^4.0.2"
|
||||
"npm": "^5.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"test": "npm run lint && gulp test && npm run client:unit && gulp apidoc",
|
||||
"test": "npm run lint && gulp test && gulp apidoc",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
"test:api-v3:unit": "gulp test:api-v3:unit",
|
||||
@@ -153,22 +145,17 @@
|
||||
"test:sanity": "istanbul cover --dir coverage/sanity --report lcovonly node_modules/mocha/bin/_mocha -- test/sanity --recursive",
|
||||
"test:common": "istanbul cover --dir coverage/common --report lcovonly node_modules/mocha/bin/_mocha -- test/common --recursive",
|
||||
"test:content": "istanbul cover --dir coverage/content --report lcovonly node_modules/mocha/bin/_mocha -- test/content --recursive",
|
||||
"test:karma": "karma start test/client-old/spec/karma.conf.js --single-run",
|
||||
"test:karma:watch": "karma start test/client-old/spec/karma.conf.js",
|
||||
"test:prepare:webdriver": "webdriver-manager update",
|
||||
"test:e2e:webdriver": "webdriver-manager start",
|
||||
"test:e2e": "protractor test/client-old/e2e/protractor.conf.js",
|
||||
"test:nodemon": "gulp test:nodemon",
|
||||
"coverage": "COVERAGE=true mocha --require register-handlers.js --reporter html-cov > coverage.html; open coverage.html",
|
||||
"sprites": "gulp sprites:compile",
|
||||
"client:dev": "gulp bootstrap && node webpack/dev-server.js",
|
||||
"client:build": "gulp bootstrap && node webpack/build.js",
|
||||
"client:dev": "node webpack/dev-server.js",
|
||||
"client:build": "gulp build:client",
|
||||
"client:unit": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js --single-run",
|
||||
"client:unit:watch": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js",
|
||||
"client:e2e": "node test/client/e2e/runner.js",
|
||||
"client:test": "npm run client:unit && npm run client:e2e",
|
||||
"start": "gulp run:dev",
|
||||
"postinstall": "bower --config.interactive=false install -f && gulp build && npm run client:build",
|
||||
"start": "gulp nodemon",
|
||||
"postinstall": "gulp build",
|
||||
"apidoc": "gulp apidoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -179,7 +166,6 @@
|
||||
"chromedriver": "^2.27.2",
|
||||
"connect-history-api-fallback": "^1.1.0",
|
||||
"coveralls": "^2.11.2",
|
||||
"cross-env": "^4.0.0",
|
||||
"cross-spawn": "^5.0.1",
|
||||
"csv": "~0.3.6",
|
||||
"deep-diff": "~0.1.4",
|
||||
@@ -192,7 +178,6 @@
|
||||
"event-stream": "^3.2.2",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"expect.js": "~0.2.0",
|
||||
"grunt-karma": "~0.12.1",
|
||||
"http-proxy-middleware": "^0.17.0",
|
||||
"inject-loader": "^3.0.0-beta4",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
|
||||
@@ -48,8 +48,10 @@ describe('GET /challenges/:challengeId', () => {
|
||||
});
|
||||
expect(chal.group).to.eql({
|
||||
_id: group._id,
|
||||
categories: [],
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
summary: group.name,
|
||||
type: group.type,
|
||||
privacy: group.privacy,
|
||||
leader: groupLeader.id,
|
||||
@@ -100,8 +102,10 @@ describe('GET /challenges/:challengeId', () => {
|
||||
});
|
||||
expect(chal.group).to.eql({
|
||||
_id: group._id,
|
||||
categories: [],
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
summary: group.name,
|
||||
type: group.type,
|
||||
privacy: group.privacy,
|
||||
leader: groupLeader.id,
|
||||
@@ -153,7 +157,9 @@ describe('GET /challenges/:challengeId', () => {
|
||||
expect(chal.group).to.eql({
|
||||
_id: group._id,
|
||||
id: group.id,
|
||||
categories: [],
|
||||
name: group.name,
|
||||
summary: group.name,
|
||||
type: group.type,
|
||||
privacy: group.privacy,
|
||||
leader: groupLeader.id,
|
||||
|
||||
@@ -142,4 +142,22 @@ describe('GET /challenges/:challengeId/members', () => {
|
||||
let resIds = res.concat(res2).map(member => member._id);
|
||||
expect(resIds).to.eql(expectedIds.sort());
|
||||
});
|
||||
|
||||
it('supports using req.query.search to get search members', async () => {
|
||||
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
|
||||
let challenge = await generateChallenge(user, group);
|
||||
|
||||
let usersToGenerate = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
usersToGenerate.push(generateUser({challenges: [challenge._id]}));
|
||||
}
|
||||
let generatedUsers = await Promise.all(usersToGenerate);
|
||||
let profileNames = generatedUsers.map(generatedUser => generatedUser.profile.name);
|
||||
|
||||
let firstProfileName = profileNames[0];
|
||||
let nameToSearch = firstProfileName.substring(0, 4);
|
||||
|
||||
let response = await user.get(`/challenges/${challenge._id}/members?search=${nameToSearch}`);
|
||||
expect(response[0].profile.name).to.eql(firstProfileName);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { TAVERN_ID } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('GET challenges/groups/:groupId', () => {
|
||||
context('Public Guild', () => {
|
||||
@@ -181,4 +182,123 @@ describe('GET challenges/groups/:groupId', () => {
|
||||
expect(foundChallengeIndex).to.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
context('Party', () => {
|
||||
let party, user, nonMember, challenge, challenge2;
|
||||
|
||||
before(async () => {
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestParty',
|
||||
type: 'party',
|
||||
},
|
||||
});
|
||||
|
||||
party = group;
|
||||
user = groupLeader;
|
||||
|
||||
nonMember = await generateUser();
|
||||
|
||||
challenge = await generateChallenge(user, group);
|
||||
challenge2 = await generateChallenge(user, group);
|
||||
});
|
||||
|
||||
it('should prevent non-member from seeing challenges', async () => {
|
||||
await expect(nonMember.get(`/challenges/groups/${party._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('groupNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should return group challenges for member with populated leader', async () => {
|
||||
let challenges = await user.get(`/challenges/groups/${party._id}`);
|
||||
|
||||
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql({
|
||||
_id: party.leader._id,
|
||||
id: party.leader._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql({
|
||||
_id: party.leader._id,
|
||||
id: party.leader._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return group challenges for member using ID "party"', async () => {
|
||||
let challenges = await user.get('/challenges/groups/party');
|
||||
|
||||
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql({
|
||||
_id: party.leader._id,
|
||||
id: party.leader._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql({
|
||||
_id: party.leader._id,
|
||||
id: party.leader._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Tavern', () => {
|
||||
let tavern, user, challenge, challenge2;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
await user.update({balance: 0.5});
|
||||
tavern = await user.get(`/groups/${TAVERN_ID}`);
|
||||
|
||||
challenge = await generateChallenge(user, tavern, {prize: 1});
|
||||
challenge2 = await generateChallenge(user, tavern, {prize: 1});
|
||||
});
|
||||
|
||||
it('should return tavern challenges with populated leader', async () => {
|
||||
let challenges = await user.get(`/challenges/groups/${TAVERN_ID}`);
|
||||
|
||||
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql({
|
||||
_id: user._id,
|
||||
id: user._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql({
|
||||
_id: user._id,
|
||||
id: user._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return tavern challenges using ID "habitrpg', async () => {
|
||||
let challenges = await user.get('/challenges/groups/habitrpg');
|
||||
|
||||
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.exist;
|
||||
expect(foundChallenge1.leader).to.eql({
|
||||
_id: user._id,
|
||||
id: user._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.exist;
|
||||
expect(foundChallenge2.leader).to.eql({
|
||||
_id: user._id,
|
||||
id: user._id,
|
||||
profile: {name: user.profile.name},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,10 +41,12 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
@@ -61,10 +63,12 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge1.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
@@ -76,10 +80,12 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge2.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
@@ -96,10 +102,12 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge1.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
@@ -111,14 +119,26 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge2.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return not return challenges in user groups if we send member true param', async () => {
|
||||
let challenges = await member.get(`/challenges/user?member=${true}`);
|
||||
|
||||
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.not.exist;
|
||||
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.not.exist;
|
||||
});
|
||||
|
||||
it('should return newest challenges first', async () => {
|
||||
let challenges = await user.get('/challenges/user');
|
||||
|
||||
@@ -137,6 +157,7 @@ describe('GET challenges/user', () => {
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestPrivateGuild',
|
||||
summary: 'summary for TestPrivateGuild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
@@ -158,6 +179,7 @@ describe('GET challenges/user', () => {
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestGuild',
|
||||
summary: 'summary for TestGuild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { getMatchesByWordArray, removePunctuationFromString } from '../../../../../website/server/libs/stringUtils';
|
||||
import bannedWords from '../../../../../website/server/libs/bannedWords';
|
||||
import guildsAllowingBannedWords from '../../../../../website/server/libs/guildsAllowingBannedWords';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
import { IncomingWebhook } from '@slack/client';
|
||||
import nconf from 'nconf';
|
||||
@@ -96,6 +97,24 @@ describe('POST /chat', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when chat message contains a banned word in a public guild', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'public guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
await expect(members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when word is part of a phrase', async () => {
|
||||
let wordInPhrase = `phrase ${testBannedWordMessage} end`;
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase}))
|
||||
@@ -161,7 +180,7 @@ describe('POST /chat', () => {
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('does not error when sending a chat message containing a banned word to a public guild', async () => {
|
||||
it('does not error when sending a chat message containing a banned word to a public guild in which banned words are allowed', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'public guild',
|
||||
@@ -171,6 +190,8 @@ describe('POST /chat', () => {
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guildsAllowingBannedWords[group._id] = true;
|
||||
|
||||
let message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
@@ -224,7 +245,7 @@ describe('POST /chat', () => {
|
||||
color: 'danger',
|
||||
author_name: `${user.profile.name} - ${user.auth.local.email} - ${user._id}`,
|
||||
title: 'Slur in Test Guild',
|
||||
title_link: `${BASE_URL}/#/options/groups/guilds/${groupWithChat.id}`,
|
||||
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
|
||||
text: testSlurMessage,
|
||||
// footer: sandbox.match(/<.*?groupId=group-id&chatId=chat-id\|Flag this message>/),
|
||||
mrkdwn_in: [
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('GET /export/avatar-:memberId.html', () => {
|
||||
xdescribe('GET /export/avatar-:memberId.html', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
|
||||
32
test/api/v3/integration/groups/GET-group-plans.test.js
Normal file
32
test/api/v3/integration/groups/GET-group-plans.test.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
describe('GET /group-plans', () => {
|
||||
let user;
|
||||
let groupPlan;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser({balance: 4});
|
||||
groupPlan = await generateGroup(user,
|
||||
{
|
||||
name: 'public guild - is member',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
{
|
||||
purchased: {
|
||||
plan: {
|
||||
customerId: 'existings',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns group plans for the user', async () => {
|
||||
let groupPlans = await user.get('/group-plans');
|
||||
|
||||
expect(groupPlans[0]._id).to.eql(groupPlan._id);
|
||||
});
|
||||
});
|
||||
@@ -10,6 +10,8 @@ import { v4 as generateUUID } from 'uuid';
|
||||
import {
|
||||
each,
|
||||
} from 'lodash';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import * as payments from '../../../../../website/server/libs/payments';
|
||||
|
||||
describe('POST /groups/:groupId/leave', () => {
|
||||
let typesOfGroups = {
|
||||
@@ -264,4 +266,45 @@ describe('POST /groups/:groupId/leave', () => {
|
||||
expect(userWithNonExistentParty.party).to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
context('Leaving a group plan', () => {
|
||||
it('cancels the free subscription', async () => {
|
||||
// Create group
|
||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Private Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let leader = groupLeader;
|
||||
let member = members[0];
|
||||
let userWithFreePlan = await User.findById(leader._id).exec();
|
||||
|
||||
// Create subscription
|
||||
let paymentData = {
|
||||
user: userWithFreePlan,
|
||||
groupId: group._id,
|
||||
sub: {
|
||||
key: 'basic_3mo',
|
||||
},
|
||||
customerId: 'customer-id',
|
||||
paymentMethod: 'Payment Method',
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
},
|
||||
};
|
||||
await payments.createSubscription(paymentData);
|
||||
await member.sync();
|
||||
expect(member.purchased.plan.planId).to.equal('group_plan_auto');
|
||||
expect(member.purchased.plan.dateTerminated).to.not.exist;
|
||||
|
||||
// Leave
|
||||
await member.post(`/groups/${group._id}/leave`);
|
||||
await member.sync();
|
||||
expect(member.purchased.plan.dateTerminated).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,14 +13,44 @@ describe('POST /notifications/:notificationId/read', () => {
|
||||
|
||||
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'),
|
||||
});
|
||||
|
||||
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 () => {
|
||||
it('removes a notification', async () => {
|
||||
expect(user.notifications.length).to.equal(0);
|
||||
|
||||
const id = generateUUID();
|
||||
const id2 = generateUUID();
|
||||
|
||||
await user.update({
|
||||
notifications: [{
|
||||
id,
|
||||
type: 'DROPS_ENABLED',
|
||||
data: {},
|
||||
}, {
|
||||
id: id2,
|
||||
type: 'LOGIN_INCENTIVE',
|
||||
data: {},
|
||||
}],
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
expect(user.notifications.length).to.equal(2);
|
||||
|
||||
const res = await user.post(`/notifications/${id}/read`);
|
||||
expect(res).to.deep.equal([{
|
||||
id: id2,
|
||||
type: 'LOGIN_INCENTIVE',
|
||||
data: {},
|
||||
}]);
|
||||
|
||||
await user.sync();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].id).to.equal(id2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
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/read', {
|
||||
notificationIds: [dummyId],
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('messageNotificationNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('removes multiple notifications', async () => {
|
||||
expect(user.notifications.length).to.equal(0);
|
||||
|
||||
const id = generateUUID();
|
||||
const id2 = generateUUID();
|
||||
const id3 = generateUUID();
|
||||
|
||||
await user.update({
|
||||
notifications: [{
|
||||
id,
|
||||
type: 'DROPS_ENABLED',
|
||||
data: {},
|
||||
}, {
|
||||
id: id2,
|
||||
type: 'LOGIN_INCENTIVE',
|
||||
data: {},
|
||||
}, {
|
||||
id: id3,
|
||||
type: 'CRON',
|
||||
data: {},
|
||||
}],
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
expect(user.notifications.length).to.equal(3);
|
||||
|
||||
const res = await user.post('/notifications/read', {
|
||||
notificationIds: [id, id3],
|
||||
});
|
||||
|
||||
expect(res).to.deep.equal([{
|
||||
id: id2,
|
||||
type: 'LOGIN_INCENTIVE',
|
||||
data: {},
|
||||
}]);
|
||||
|
||||
await user.sync();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].id).to.equal(id2);
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import superagent from 'superagent';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const API_TEST_SERVER_PORT = nconf.get('PORT');
|
||||
describe('GET /qr-code/user/:memberId', () => {
|
||||
xdescribe('GET /qr-code/user/:memberId', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
|
||||
@@ -141,6 +141,16 @@ describe('DELETE /tasks/:id', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('removes a task from user.tasksOrder'); // TODO
|
||||
it('removes a task from user.tasksOrder', async () => {
|
||||
let task = await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
|
||||
await user.del(`/tasks/${task._id}`);
|
||||
await user.sync();
|
||||
|
||||
expect(user.tasksOrder.habits.indexOf(task._id)).to.eql(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ describe('GET /tasks/user', () => {
|
||||
it('returns all user\'s tasks', async () => {
|
||||
let createdTasks = await user.post('/tasks/user', [{text: 'test habit', type: 'habit'}, {text: 'test todo', type: 'todo'}]);
|
||||
let tasks = await user.get('/tasks/user');
|
||||
expect(tasks.length).to.equal(createdTasks.length + 1); // + 1 because 1 is a default task
|
||||
expect(tasks.length).to.equal(createdTasks.length + 1); // Plus one for generated todo
|
||||
});
|
||||
|
||||
it('returns only a type of user\'s tasks if req.query.type is specified', async () => {
|
||||
|
||||
@@ -40,9 +40,13 @@ describe('POST /tasks/:taskId/move/to/:position', () => {
|
||||
|
||||
let taskToMove = tasks[1];
|
||||
expect(taskToMove.text).to.equal('habit 2');
|
||||
|
||||
let newOrder = await user.post(`/tasks/${tasks[1]._id}/move/to/3`);
|
||||
await user.sync();
|
||||
|
||||
expect(newOrder[3]).to.equal(taskToMove._id);
|
||||
expect(newOrder.length).to.equal(5);
|
||||
expect(user.tasksOrder.habits).to.eql(newOrder);
|
||||
});
|
||||
|
||||
it('can move task to new position using alias', async () => {
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('POST /tasks/unlink-all/:challengeId', () => {
|
||||
// Have the leader delete the challenge and unlink the tasks
|
||||
await user.del(`/challenges/${challenge._id}`);
|
||||
await user.post(`/tasks/unlink-all/${challenge._id}?keep=keep-all`);
|
||||
// Get the second task for the second user
|
||||
// Get the task for the second user
|
||||
const [, anotherUserTask] = await anotherUser.get('/tasks/user');
|
||||
// Expect the second user to still have the task, but unlinked
|
||||
expect(anotherUserTask.challenge).to.eql({
|
||||
@@ -106,4 +106,4 @@ describe('POST /tasks/unlink-all/:challengeId', () => {
|
||||
winner: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('POST /tasks/user', () => {
|
||||
expect(task.updatedAt).not.to.equal('tomorrow');
|
||||
expect(task.challenge).not.to.equal('no');
|
||||
expect(task.completed).to.equal(false);
|
||||
expect(task.streak).not.to.equal('never');
|
||||
expect(task.dateCompleted).not.to.equal('never');
|
||||
expect(task.value).not.to.equal(324);
|
||||
expect(task.yesterDaily).to.equal(true);
|
||||
});
|
||||
|
||||
@@ -86,6 +86,12 @@ describe('DELETE /user', () => {
|
||||
});
|
||||
|
||||
it('deletes the user\'s tasks', async () => {
|
||||
await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
await user.sync();
|
||||
|
||||
// gets the user's tasks ids
|
||||
let ids = [];
|
||||
each(user.tasksOrder, (idsForOrder) => {
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('GET /user/anonymized', () => {
|
||||
});
|
||||
// tasks
|
||||
expect(tasks2).to.exist;
|
||||
expect(tasks2.length).to.eql(5); // +1 because generateUser() assigns one todo
|
||||
expect(tasks2.length).to.eql(5);
|
||||
expect(tasks2[0].checklist).to.exist;
|
||||
_.forEach(tasks2, (task) => {
|
||||
expect(task.text).to.eql('task text');
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../website/common/script';
|
||||
|
||||
let content = shared.content;
|
||||
|
||||
describe('POST /user/buy/:key', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'stats.gp': 400,
|
||||
});
|
||||
});
|
||||
|
||||
// More tests in common code unit tests
|
||||
|
||||
it('returns an error if the item is not found', async () => {
|
||||
await expect(user.post('/user/buy/notExisting'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('itemNotFound', {key: 'notExisting'}),
|
||||
});
|
||||
});
|
||||
|
||||
it('buys a potion', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 400,
|
||||
'stats.hp': 40,
|
||||
});
|
||||
|
||||
let potion = content.potion;
|
||||
let res = await user.post('/user/buy/potion');
|
||||
await user.sync();
|
||||
|
||||
expect(user.stats.hp).to.equal(50);
|
||||
expect(res.data).to.eql(user.stats);
|
||||
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||
});
|
||||
|
||||
it('returns an error if user tries to buy a potion with full health', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 40,
|
||||
'stats.hp': 50,
|
||||
});
|
||||
|
||||
await expect(user.post('/user/buy/potion'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageHealthAlreadyMax'),
|
||||
});
|
||||
});
|
||||
it('buys a piece of gear', async () => {
|
||||
let key = 'armor_warrior_1';
|
||||
|
||||
await user.post(`/user/buy/${key}`);
|
||||
await user.sync();
|
||||
|
||||
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||
});
|
||||
});
|
||||
@@ -98,4 +98,24 @@ describe('POST /user/purchase/:type/:key', () => {
|
||||
await members[0].sync();
|
||||
expect(members[0].balance).to.equal(oldBalance);
|
||||
});
|
||||
|
||||
describe('bulk purchasing', () => {
|
||||
it('purchases a gem item', async () => {
|
||||
await user.post(`/user/purchase/${type}/${key}`, {quantity: 2});
|
||||
await user.sync();
|
||||
|
||||
expect(user.items[type][key]).to.equal(2);
|
||||
});
|
||||
|
||||
it('can convert gold to gems if subscribed', async () => {
|
||||
let oldBalance = user.balance;
|
||||
await user.update({
|
||||
'purchased.plan.customerId': 'group-plan',
|
||||
'stats.gp': 1000,
|
||||
});
|
||||
await user.post('/user/purchase/gems/gem', {quantity: 2});
|
||||
await user.sync();
|
||||
expect(user.balance).to.equal(oldBalance + 0.50);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,43 +10,31 @@ import nconf from 'nconf';
|
||||
|
||||
const API_TEST_SERVER_PORT = nconf.get('PORT');
|
||||
|
||||
describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
// @TODO skipped because on travis the client isn't available and the redirect fails
|
||||
xdescribe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
let endpoint = `http://localhost:${API_TEST_SERVER_PORT}/static/user/auth/local/reset-password-set-new-one`;
|
||||
|
||||
// Tests to validate the validatePasswordResetCodeAndFindUser function
|
||||
|
||||
it('renders an error page if the code is missing', async () => {
|
||||
try {
|
||||
await superagent.get(endpoint);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
const res = await superagent.get(endpoint);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders an error page if the code is invalid json', async () => {
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=invalid`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
const res = await superagent.get(`${endpoint}?code=invalid`);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders an error page if the code cannot be decrypted', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
try {
|
||||
let code = JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
});
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
let code = JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
});
|
||||
const res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders an error page if the code is expired', async () => {
|
||||
@@ -60,12 +48,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
const res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders an error page if the user does not exist', async () => {
|
||||
@@ -74,12 +58,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
const res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders an error page if the user has no local auth', async () => {
|
||||
@@ -93,12 +73,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
auth: 'not an object with valid fields',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
const res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders an error page if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
|
||||
@@ -112,12 +88,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': 'invalid',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
const res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
|
||||
});
|
||||
|
||||
//
|
||||
@@ -134,7 +106,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
});
|
||||
|
||||
let res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.req.path.indexOf('hasError=false') !== -1).to.equal(true);
|
||||
expect(res.req.path.indexOf('code=') !== -1).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -10,49 +10,46 @@ import {
|
||||
import moment from 'moment';
|
||||
import {
|
||||
generateUser,
|
||||
requester,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import superagent from 'superagent';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const API_TEST_SERVER_PORT = nconf.get('PORT');
|
||||
|
||||
describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
let endpoint = `http://localhost:${API_TEST_SERVER_PORT}/static/user/auth/local/reset-password-set-new-one`;
|
||||
describe('POST /user/auth/reset-password-set-new-one', () => {
|
||||
const endpoint = '/user/auth/reset-password-set-new-one';
|
||||
const api = requester();
|
||||
|
||||
// Tests to validate the validatePasswordResetCodeAndFindUser function
|
||||
|
||||
it('renders an error page if the code is missing', async () => {
|
||||
try {
|
||||
await superagent.post(endpoint);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error page if the code is invalid json', async () => {
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=invalid`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}?code=invalid`)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error page if the code cannot be decrypted', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
try {
|
||||
let code = JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
});
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
let code = JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
});
|
||||
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error page if the code is expired', async () => {
|
||||
@@ -66,12 +63,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error page if the user does not exist', async () => {
|
||||
@@ -80,12 +78,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error page if the user has no local auth', async () => {
|
||||
@@ -99,12 +98,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
auth: 'not an object with valid fields',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error page if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
|
||||
@@ -118,12 +118,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': 'invalid',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidPasswordResetCode'),
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
@@ -139,12 +140,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the error page if the password confirmation is missing', async () => {
|
||||
@@ -158,14 +160,14 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({newPassword: 'my new password'});
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
newPassword: 'my new password',
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the error page if the password confirmation does not match', async () => {
|
||||
@@ -179,17 +181,15 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'not matching',
|
||||
});
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'not matching',
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('passwordConfirmationMatch'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the success page and save the user', async () => {
|
||||
@@ -203,14 +203,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'my new password',
|
||||
});
|
||||
let res = await api.post(`${endpoint}`, {
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'my new password',
|
||||
code,
|
||||
});
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.message).to.equal(t('passwordChangeSuccess'));
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordResetCode).to.equal(undefined);
|
||||
@@ -246,14 +245,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'my new password',
|
||||
});
|
||||
let res = await api.post(`${endpoint}`, {
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'my new password',
|
||||
code,
|
||||
});
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.message).to.equal(t('passwordChangeSuccess'));
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordResetCode).to.equal(undefined);
|
||||
|
||||
@@ -59,13 +59,8 @@ describe('POST /user/auth/local/register', () => {
|
||||
let tags = await requests.get('/tags');
|
||||
|
||||
expect(habits).to.have.a.lengthOf(0);
|
||||
|
||||
expect(dailys).to.have.a.lengthOf(0);
|
||||
|
||||
expect(todos).to.have.a.lengthOf(1);
|
||||
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
|
||||
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
|
||||
|
||||
expect(rewards).to.have.a.lengthOf(0);
|
||||
|
||||
expect(tags).to.have.a.lengthOf(7);
|
||||
@@ -78,7 +73,7 @@ describe('POST /user/auth/local/register', () => {
|
||||
expect(tags[6].name).to.eql(t('defaultTag7'));
|
||||
});
|
||||
|
||||
it('for Web', async () => {
|
||||
xit('for Web', async () => {
|
||||
api = requester(
|
||||
null,
|
||||
{'x-client': 'habitica-web'},
|
||||
@@ -129,7 +124,9 @@ describe('POST /user/auth/local/register', () => {
|
||||
expect(tags[5].name).to.eql(t('defaultTag6'));
|
||||
expect(tags[6].name).to.eql(t('defaultTag7'));
|
||||
});
|
||||
});
|
||||
|
||||
context('does not provide default tags and tasks', async () => {
|
||||
it('for Android', async () => {
|
||||
api = requester(
|
||||
null,
|
||||
@@ -154,34 +151,11 @@ describe('POST /user/auth/local/register', () => {
|
||||
let rewards = await requests.get('/tasks/user?type=rewards');
|
||||
let tags = await requests.get('/tags');
|
||||
|
||||
expect(habits).to.have.a.lengthOf(2);
|
||||
expect(habits[0].text).to.eql(t('defaultHabit4Text'));
|
||||
expect(habits[0].notes).to.eql(t('defaultHabit4Notes'));
|
||||
expect(habits[1].text).to.eql(t('defaultHabit5Text'));
|
||||
expect(habits[1].notes).to.eql(t('defaultHabit5Notes'));
|
||||
|
||||
expect(dailys).to.have.a.lengthOf(1);
|
||||
expect(dailys[0].text).to.eql(t('defaultDaily1Text'));
|
||||
expect(dailys[0].notes).to.eql('');
|
||||
|
||||
expect(todos).to.have.a.lengthOf(2);
|
||||
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
|
||||
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
|
||||
expect(todos[1].text).to.eql(t('defaultTodo2Text'));
|
||||
expect(todos[1].notes).to.eql(t('defaultTodo2Notes'));
|
||||
|
||||
expect(rewards).to.have.a.lengthOf(1);
|
||||
expect(rewards[0].text).to.eql(t('defaultReward2Text'));
|
||||
expect(rewards[0].notes).to.eql(t('defaultReward2Notes'));
|
||||
|
||||
expect(tags).to.have.a.lengthOf(7);
|
||||
expect(tags[0].name).to.eql(t('defaultTag1'));
|
||||
expect(tags[1].name).to.eql(t('defaultTag2'));
|
||||
expect(tags[2].name).to.eql(t('defaultTag3'));
|
||||
expect(tags[3].name).to.eql(t('defaultTag4'));
|
||||
expect(tags[4].name).to.eql(t('defaultTag5'));
|
||||
expect(tags[5].name).to.eql(t('defaultTag6'));
|
||||
expect(tags[6].name).to.eql(t('defaultTag7'));
|
||||
expect(habits).to.have.a.lengthOf(0);
|
||||
expect(dailys).to.have.a.lengthOf(0);
|
||||
expect(todos).to.have.a.lengthOf(0);
|
||||
expect(rewards).to.have.a.lengthOf(0);
|
||||
expect(tags).to.have.a.lengthOf(0);
|
||||
});
|
||||
|
||||
it('for iOS', async () => {
|
||||
@@ -208,34 +182,11 @@ describe('POST /user/auth/local/register', () => {
|
||||
let rewards = await requests.get('/tasks/user?type=rewards');
|
||||
let tags = await requests.get('/tags');
|
||||
|
||||
expect(habits).to.have.a.lengthOf(2);
|
||||
expect(habits[0].text).to.eql(t('defaultHabit4Text'));
|
||||
expect(habits[0].notes).to.eql(t('defaultHabit4Notes'));
|
||||
expect(habits[1].text).to.eql(t('defaultHabit5Text'));
|
||||
expect(habits[1].notes).to.eql(t('defaultHabit5Notes'));
|
||||
|
||||
expect(dailys).to.have.a.lengthOf(1);
|
||||
expect(dailys[0].text).to.eql(t('defaultDaily1Text'));
|
||||
expect(dailys[0].notes).to.eql('');
|
||||
|
||||
expect(todos).to.have.a.lengthOf(2);
|
||||
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
|
||||
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
|
||||
expect(todos[1].text).to.eql(t('defaultTodo2Text'));
|
||||
expect(todos[1].notes).to.eql(t('defaultTodo2Notes'));
|
||||
|
||||
expect(rewards).to.have.a.lengthOf(1);
|
||||
expect(rewards[0].text).to.eql(t('defaultReward2Text'));
|
||||
expect(rewards[0].notes).to.eql(t('defaultReward2Notes'));
|
||||
|
||||
expect(tags).to.have.a.lengthOf(7);
|
||||
expect(tags[0].name).to.eql(t('defaultTag1'));
|
||||
expect(tags[1].name).to.eql(t('defaultTag2'));
|
||||
expect(tags[2].name).to.eql(t('defaultTag3'));
|
||||
expect(tags[3].name).to.eql(t('defaultTag4'));
|
||||
expect(tags[4].name).to.eql(t('defaultTag5'));
|
||||
expect(tags[5].name).to.eql(t('defaultTag6'));
|
||||
expect(tags[6].name).to.eql(t('defaultTag7'));
|
||||
expect(habits).to.have.a.lengthOf(0);
|
||||
expect(dailys).to.have.a.lengthOf(0);
|
||||
expect(todos).to.have.a.lengthOf(0);
|
||||
expect(rewards).to.have.a.lengthOf(0);
|
||||
expect(tags).to.have.a.lengthOf(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -667,10 +618,10 @@ describe('POST /user/auth/local/register', () => {
|
||||
confirmPassword: password,
|
||||
});
|
||||
|
||||
expect(user.tasksOrder.todos).to.not.be.empty;
|
||||
expect(user.tasksOrder.todos).to.be.empty;
|
||||
expect(user.tasksOrder.dailys).to.be.empty;
|
||||
expect(user.tasksOrder.habits).to.not.be.empty;
|
||||
expect(user.tasksOrder.rewards).to.not.be.empty;
|
||||
expect(user.tasksOrder.habits).to.be.empty;
|
||||
expect(user.tasksOrder.rewards).to.be.empty;
|
||||
});
|
||||
|
||||
it('populates user with default tags', async () => {
|
||||
@@ -697,23 +648,8 @@ describe('POST /user/auth/local/register', () => {
|
||||
let habits = await requests.get('/tasks/user?type=habits');
|
||||
let todos = await requests.get('/tasks/user?type=todos');
|
||||
|
||||
function findTag (tagName) {
|
||||
let tag = user.tags.find((userTag) => {
|
||||
return userTag.name === t(tagName);
|
||||
});
|
||||
return tag.id;
|
||||
}
|
||||
|
||||
expect(habits[0].tags).to.have.a.lengthOf(3);
|
||||
expect(habits[0].tags).to.include.members(['defaultTag1', 'defaultTag4', 'defaultTag6'].map(findTag));
|
||||
|
||||
expect(habits[1].tags).to.have.a.lengthOf(1);
|
||||
expect(habits[1].tags).to.include.members(['defaultTag3'].map(findTag));
|
||||
|
||||
expect(habits[2].tags).to.have.a.lengthOf(2);
|
||||
expect(habits[2].tags).to.include.members(['defaultTag2', 'defaultTag3'].map(findTag));
|
||||
|
||||
expect(todos[0].tags).to.have.a.lengthOf(0);
|
||||
expect(habits).to.have.a.lengthOf(0);
|
||||
expect(todos).to.have.a.lengthOf(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
100
test/api/v3/integration/user/buy/POST-user_buy.test.js
Normal file
100
test/api/v3/integration/user/buy/POST-user_buy.test.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../../website/common/script';
|
||||
|
||||
let content = shared.content;
|
||||
|
||||
describe('POST /user/buy/:key', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'stats.gp': 400,
|
||||
});
|
||||
});
|
||||
|
||||
// More tests in common code unit tests
|
||||
|
||||
it('returns an error if the item is not found', async () => {
|
||||
await expect(user.post('/user/buy/notExisting'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('itemNotFound', {key: 'notExisting'}),
|
||||
});
|
||||
});
|
||||
|
||||
it('buys a potion', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 400,
|
||||
'stats.hp': 40,
|
||||
});
|
||||
|
||||
let potion = content.potion;
|
||||
let res = await user.post('/user/buy/potion');
|
||||
await user.sync();
|
||||
|
||||
expect(user.stats.hp).to.equal(50);
|
||||
expect(res.data).to.eql(user.stats);
|
||||
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||
});
|
||||
|
||||
it('returns an error if user tries to buy a potion with full health', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 40,
|
||||
'stats.hp': 50,
|
||||
});
|
||||
|
||||
await expect(user.post('/user/buy/potion'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageHealthAlreadyMax'),
|
||||
});
|
||||
});
|
||||
|
||||
it('buys a piece of gear', async () => {
|
||||
let key = 'armor_warrior_1';
|
||||
|
||||
await user.post(`/user/buy/${key}`);
|
||||
await user.sync();
|
||||
|
||||
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||
});
|
||||
|
||||
it('buys a special spell', async () => {
|
||||
let key = 'spookySparkles';
|
||||
let item = content.special[key];
|
||||
|
||||
await user.update({'stats.gp': 250});
|
||||
let res = await user.post(`/user/buy/${key}`);
|
||||
await user.sync();
|
||||
|
||||
expect(res.data).to.eql({
|
||||
items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared
|
||||
stats: user.stats,
|
||||
});
|
||||
expect(res.message).to.equal(t('messageBought', {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
});
|
||||
|
||||
it('allows for bulk purchases', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 400,
|
||||
'stats.hp': 20,
|
||||
});
|
||||
|
||||
let potion = content.potion;
|
||||
let res = await user.post('/user/buy/potion', {quantity: 2});
|
||||
await user.sync();
|
||||
|
||||
expect(user.stats.hp).to.equal(50);
|
||||
expect(res.data).to.eql(user.stats);
|
||||
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/buy-armoire', () => {
|
||||
let user;
|
||||
@@ -3,7 +3,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/buy-gear/:key', () => {
|
||||
let user;
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../website/common/script';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../../website/common/script';
|
||||
|
||||
let content = shared.content;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/buy-mystery-set/:key', () => {
|
||||
let user;
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../website/common/script';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../../website/common/script';
|
||||
|
||||
let content = shared.content;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../website/common/script';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import shared from '../../../../../../website/common/script';
|
||||
|
||||
let content = shared.content;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/allocate', () => {
|
||||
let user;
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/allocate-bulk', () => {
|
||||
let user;
|
||||
const statsUpdate = {
|
||||
stats: {
|
||||
con: 1,
|
||||
str: 2,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
// More tests in common code unit tests
|
||||
|
||||
it('returns an error if user does not have enough points', async () => {
|
||||
await expect(user.post('/user/allocate-bulk', statsUpdate))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('notEnoughAttrPoints'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allocates attribute points', async () => {
|
||||
await user.update({'stats.points': 3});
|
||||
|
||||
await user.post('/user/allocate-bulk', statsUpdate);
|
||||
await user.sync();
|
||||
|
||||
expect(user.stats.con).to.equal(1);
|
||||
expect(user.stats.str).to.equal(2);
|
||||
expect(user.stats.points).to.equal(0);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/allocate-now', () => {
|
||||
// More tests in common code unit tests
|
||||
@@ -1,31 +0,0 @@
|
||||
import {
|
||||
getManifestFiles,
|
||||
} from '../../../../../website/server/libs/buildManifest';
|
||||
|
||||
describe('Build Manifest', () => {
|
||||
describe('getManifestFiles', () => {
|
||||
it('returns an html string', () => {
|
||||
let htmlCode = getManifestFiles('app');
|
||||
|
||||
expect(htmlCode.startsWith('<script') || htmlCode.startsWith('<link')).to.be.true;
|
||||
});
|
||||
|
||||
it('can return only js files', () => {
|
||||
let htmlCode = getManifestFiles('app', 'js');
|
||||
|
||||
expect(htmlCode.indexOf('<link') === -1).to.be.true;
|
||||
});
|
||||
|
||||
it('can return only css files', () => {
|
||||
let htmlCode = getManifestFiles('app', 'css');
|
||||
|
||||
expect(htmlCode.indexOf('<script') === -1).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error in case the page does not exist', () => {
|
||||
expect(() => {
|
||||
getManifestFiles('strange name here');
|
||||
}).to.throw(Error);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -365,6 +365,72 @@ describe('cron', () => {
|
||||
|
||||
expect(user.history.todos).to.be.lengthOf(1);
|
||||
});
|
||||
|
||||
it('should remove completed todos from users taskOrder list', () => {
|
||||
tasksByType.todos = [];
|
||||
user.tasksOrder.todos = [];
|
||||
let todo = {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
value: 0,
|
||||
};
|
||||
|
||||
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
tasksByType.todos.push(task);
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
tasksByType.todos.push(task);
|
||||
tasksByType.todos[0].completed = true;
|
||||
|
||||
user.tasksOrder.todos = tasksByType.todos.map(taskTodo => {
|
||||
return taskTodo._id;
|
||||
});
|
||||
// Since ideally tasksByType should not contain completed todos, fake ids should be filtered too
|
||||
user.tasksOrder.todos.push('00000000-0000-0000-0000-000000000000');
|
||||
|
||||
expect(tasksByType.todos).to.be.lengthOf(2);
|
||||
expect(user.tasksOrder.todos).to.be.lengthOf(3);
|
||||
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
|
||||
// user.tasksOrder.todos should be filtered while tasks by type remains unchanged
|
||||
expect(tasksByType.todos).to.be.lengthOf(2);
|
||||
expect(user.tasksOrder.todos).to.be.lengthOf(1);
|
||||
});
|
||||
|
||||
it('should preserve todos order in task list', () => {
|
||||
tasksByType.todos = [];
|
||||
user.tasksOrder.todos = [];
|
||||
let todo = {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
value: 0,
|
||||
};
|
||||
|
||||
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
tasksByType.todos.push(task);
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
tasksByType.todos.push(task);
|
||||
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
|
||||
tasksByType.todos.push(task);
|
||||
|
||||
// Set up user.tasksOrder list in a specific order
|
||||
user.tasksOrder.todos = tasksByType.todos.map(todoTask => {
|
||||
return todoTask._id;
|
||||
}).reverse();
|
||||
let original = user.tasksOrder.todos; // Preserve the original order
|
||||
|
||||
cron({user, tasksByType, daysMissed, analytics});
|
||||
|
||||
let listsAreEqual = true;
|
||||
user.tasksOrder.todos.forEach((taskId, index) => {
|
||||
if (original[index]._id !== taskId) {
|
||||
listsAreEqual = false;
|
||||
}
|
||||
});
|
||||
|
||||
expect(listsAreEqual);
|
||||
expect(user.tasksOrder.todos).to.be.lengthOf(original.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dailys', () => {
|
||||
|
||||
@@ -142,12 +142,12 @@ describe('emails', () => {
|
||||
describe('getGroupUrl', () => {
|
||||
it('returns correct url if group is the tavern', () => {
|
||||
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
|
||||
expect(getGroupUrl({_id: TAVERN_ID, type: 'guild'})).to.eql('/#/options/groups/tavern');
|
||||
expect(getGroupUrl({_id: TAVERN_ID, type: 'guild'})).to.eql('/groups/tavern');
|
||||
});
|
||||
|
||||
it('returns correct url if group is a guild', () => {
|
||||
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
|
||||
expect(getGroupUrl({_id: 'random _id', type: 'guild'})).to.eql('/#/options/groups/guilds/random _id');
|
||||
expect(getGroupUrl({_id: 'random _id', type: 'guild'})).to.eql('/groups/guild/random _id');
|
||||
});
|
||||
|
||||
it('returns correct url if group is a party', () => {
|
||||
|
||||
@@ -475,7 +475,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(3, 4);
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(3, 5);
|
||||
});
|
||||
|
||||
it('adds months to members with existing recurring subscription (Paypal)', async () => {
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('slack', () => {
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledWithMatch({
|
||||
attachments: [sandbox.match({
|
||||
title: 'Flag in Some group',
|
||||
title_link: sandbox.match(/.*\/#\/options\/groups\/guilds\/group-id/),
|
||||
title_link: sandbox.match(/.*\/groups\/guild\/group-id/),
|
||||
})],
|
||||
});
|
||||
});
|
||||
@@ -86,7 +86,7 @@ describe('slack', () => {
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledWithMatch({
|
||||
attachments: [sandbox.match({
|
||||
title: 'Flag in Tavern',
|
||||
title_link: sandbox.match(/.*\/#\/options\/groups\/tavern/),
|
||||
title_link: sandbox.match(/.*\/groups\/tavern/),
|
||||
})],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,9 @@ import analyticsService from '../../../../../website/server/libs/analyticsServic
|
||||
import * as cronLib from '../../../../../website/server/libs/cron';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
const CRON_TIMEOUT_WAIT = new Date(60 * 60 * 1000).getTime();
|
||||
const CRON_TIMEOUT_UNIT = new Date(60 * 1000).getTime();
|
||||
|
||||
describe('cron middleware', () => {
|
||||
let res, req;
|
||||
let user;
|
||||
@@ -235,7 +238,13 @@ describe('cron middleware', () => {
|
||||
sandbox.spy(cronLib, 'recoverCron');
|
||||
|
||||
sandbox.stub(User, 'update')
|
||||
.withArgs({ _id: user._id, _cronSignature: 'NOT_RUNNING' })
|
||||
.withArgs({
|
||||
_id: user._id,
|
||||
$or: [
|
||||
{_cronSignature: 'NOT_RUNNING'},
|
||||
{_cronSignature: {$lt: sinon.match.number}},
|
||||
],
|
||||
})
|
||||
.returns({
|
||||
exec () {
|
||||
return Promise.resolve(updatedUser);
|
||||
@@ -251,4 +260,48 @@ describe('cron middleware', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('cronSignature less than an hour ago should error', async () => {
|
||||
user.lastCron = moment(new Date()).subtract({days: 2});
|
||||
let now = new Date();
|
||||
await User.update({
|
||||
_id: user._id,
|
||||
}, {
|
||||
$set: {
|
||||
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT + CRON_TIMEOUT_UNIT,
|
||||
},
|
||||
}).exec();
|
||||
await user.save();
|
||||
let expectedErrMessage = `Impossible to recover from cron for user ${user._id}.`;
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, (err) => {
|
||||
if (!err) return reject(new Error('Cron should have failed.'));
|
||||
expect(err.message).to.be.equal(expectedErrMessage);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('cronSignature longer than an hour ago should allow cron', async () => {
|
||||
user.lastCron = moment(new Date()).subtract({days: 2});
|
||||
let now = new Date();
|
||||
await User.update({
|
||||
_id: user._id,
|
||||
}, {
|
||||
$set: {
|
||||
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT - CRON_TIMEOUT_UNIT,
|
||||
},
|
||||
}).exec();
|
||||
await user.save();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, (err) => {
|
||||
if (err) return reject(err);
|
||||
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
|
||||
expect(user._cronSignature).to.be.equal('NOT_RUNNING');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateNext,
|
||||
} from '../../../../helpers/api-unit.helper';
|
||||
import responseMiddleware from '../../../../../website/server/middlewares/response';
|
||||
import packageInfo from '../../../../../package.json';
|
||||
|
||||
describe('response middleware', () => {
|
||||
let res, req, next;
|
||||
@@ -33,6 +34,8 @@ describe('response middleware', () => {
|
||||
success: true,
|
||||
data: {field: 1},
|
||||
notifications: [],
|
||||
userV: res.locals.user._v,
|
||||
appVersion: packageInfo.version,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,6 +52,8 @@ describe('response middleware', () => {
|
||||
data: {field: 1},
|
||||
message: 'hello',
|
||||
notifications: [],
|
||||
userV: res.locals.user._v,
|
||||
appVersion: packageInfo.version,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,12 +69,13 @@ describe('response middleware', () => {
|
||||
success: false,
|
||||
data: {field: 1},
|
||||
notifications: [],
|
||||
userV: res.locals.user._v,
|
||||
appVersion: packageInfo.version,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns userV if a user is authenticated req.query.userV is passed', () => {
|
||||
it('returns userV if a user is authenticated', () => {
|
||||
responseMiddleware(req, res, next);
|
||||
req.query.userV = 3;
|
||||
res.respond(200, {field: 1});
|
||||
|
||||
expect(res.json).to.be.calledOnce;
|
||||
@@ -79,6 +85,7 @@ describe('response middleware', () => {
|
||||
data: {field: 1},
|
||||
notifications: [],
|
||||
userV: 0,
|
||||
appVersion: packageInfo.version,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,6 +108,8 @@ describe('response middleware', () => {
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
userV: res.locals.user._v,
|
||||
appVersion: packageInfo.version,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -282,7 +282,7 @@ describe('Group Model', () => {
|
||||
expect(finishQuest).to.be.calledWith(quest);
|
||||
});
|
||||
|
||||
context('with Rage', () => {
|
||||
context('with healing Rage', () => {
|
||||
beforeEach(async () => {
|
||||
party.quest.active = false;
|
||||
party.quest.key = 'trex_undead';
|
||||
@@ -327,6 +327,46 @@ describe('Group Model', () => {
|
||||
expect(party.quest.progress.hp).to.eql(500);
|
||||
});
|
||||
});
|
||||
|
||||
context('with Mana drain Rage', () => {
|
||||
beforeEach(async () => {
|
||||
party.quest.active = false;
|
||||
party.quest.key = 'lostMasterclasser4';
|
||||
|
||||
await party.startQuest(questLeader);
|
||||
await party.save();
|
||||
});
|
||||
|
||||
it('applies down progress to boss rage', async () => {
|
||||
progress.down = -2;
|
||||
|
||||
await Group.processQuestProgress(participatingMember, progress);
|
||||
|
||||
party = await Group.findOne({_id: party._id});
|
||||
|
||||
expect(party.quest.progress.rage).to.eql(8);
|
||||
|
||||
let drainedUser = await User.findById(participatingMember._id);
|
||||
expect(drainedUser.stats.mp).to.eql(10);
|
||||
});
|
||||
|
||||
it('activates rage when progress.down triggers rage bar', async () => {
|
||||
let quest = questScrolls[party.quest.key];
|
||||
|
||||
progress.down = -999;
|
||||
|
||||
await party.save();
|
||||
await Group.processQuestProgress(participatingMember, progress);
|
||||
|
||||
party = await Group.findOne({_id: party._id});
|
||||
|
||||
expect(Group.prototype.sendChat).to.be.calledWith(quest.boss.rage.effect('en'));
|
||||
expect(party.quest.progress.rage).to.eql(0);
|
||||
|
||||
let drainedUser = await User.findById(participatingMember._id);
|
||||
expect(drainedUser.stats.mp).to.eql(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Collection Quests', () => {
|
||||
@@ -1342,6 +1382,42 @@ describe('Group Model', () => {
|
||||
expect(updatedParticipatingMember.achievements.quests[quest.key]).to.eql(1);
|
||||
});
|
||||
|
||||
it('gives out super awesome Masterclasser achievement to the deserving', async () => {
|
||||
quest = questScrolls.lostMasterclasser4;
|
||||
party.quest.key = quest.key;
|
||||
|
||||
questLeader.achievements.quests = {
|
||||
mayhemMistiflying1: 1,
|
||||
mayhemMistiflying2: 1,
|
||||
mayhemMistiflying3: 1,
|
||||
stoikalmCalamity1: 1,
|
||||
stoikalmCalamity2: 1,
|
||||
stoikalmCalamity3: 1,
|
||||
taskwoodsTerror1: 1,
|
||||
taskwoodsTerror2: 1,
|
||||
taskwoodsTerror3: 1,
|
||||
dilatoryDistress1: 1,
|
||||
dilatoryDistress2: 1,
|
||||
dilatoryDistress3: 1,
|
||||
lostMasterclasser1: 1,
|
||||
lostMasterclasser2: 1,
|
||||
lostMasterclasser3: 1,
|
||||
};
|
||||
await questLeader.save();
|
||||
await party.finishQuest(quest);
|
||||
|
||||
let [
|
||||
updatedLeader,
|
||||
updatedParticipatingMember,
|
||||
] = await Promise.all([
|
||||
User.findById(questLeader._id),
|
||||
User.findById(participatingMember._id),
|
||||
]);
|
||||
|
||||
expect(updatedLeader.achievements.lostMasterclasser).to.eql(true);
|
||||
expect(updatedParticipatingMember.achievements.lostMasterclasser).to.not.eql(true);
|
||||
});
|
||||
|
||||
it('gives xp and gold', async () => {
|
||||
await party.finishQuest(quest);
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"globals": {
|
||||
"browser": true,
|
||||
"by": true,
|
||||
"element": true
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import { resetHabiticaDB } from '../../helpers/mongo';
|
||||
|
||||
before(async () => {
|
||||
await resetHabiticaDB();
|
||||
});
|
||||
|
||||
// based on https://github.com/angular/protractor/issues/114#issuecomment-29046939
|
||||
afterEach(async function () {
|
||||
let lastTest = this.currentTest;
|
||||
|
||||
if (lastTest.state === 'failed') {
|
||||
let filename = `exception_${lastTest.title}.png`;
|
||||
let png = await browser.takeScreenshot();
|
||||
let buffer = new Buffer(png, 'base64');
|
||||
let stream = fs.createWriteStream(filename);
|
||||
|
||||
stream.write(buffer);
|
||||
stream.end();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
let chai = require('chai');
|
||||
let chaiAsPromised = require('chai-as-promised');
|
||||
|
||||
require('babel-register');
|
||||
require('babel-polyfill');
|
||||
|
||||
exports.config = {
|
||||
specs: ['./helper.js', './**/*.test.js'],
|
||||
baseUrl: 'http://localhost:3003/',
|
||||
capabilities: {
|
||||
browserName: 'firefox',
|
||||
},
|
||||
directConnect: true,
|
||||
seleniumAddress: 'http://localhost:4444/wd/hub',
|
||||
framework: 'mocha',
|
||||
mochaOpts: {
|
||||
reporter: 'spec',
|
||||
slow: 6000,
|
||||
timeout: 10000,
|
||||
compilers: 'js:babel-register',
|
||||
},
|
||||
onPrepare: () => {
|
||||
browser.ignoreSynchronization = true;
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
global.expect = chai.expect;
|
||||
},
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
import { v4 as generateUniqueId } from 'uuid';
|
||||
|
||||
describe('Static Front Page', () => {
|
||||
beforeEach(() => {
|
||||
browser.get('/');
|
||||
browser.sleep(1000);
|
||||
});
|
||||
|
||||
it('shows the front page', async () => {
|
||||
let button = element(by.id('play-btn'));
|
||||
|
||||
await expect(button.getText()).to.eventually.eql('Join for free');
|
||||
});
|
||||
|
||||
it('does not login when using wrong credentials', async () => {
|
||||
let button = element(by.id('play-btn'));
|
||||
let randomName = generateUniqueId();
|
||||
|
||||
button.click();
|
||||
browser.sleep(1000);
|
||||
|
||||
element(by.model('loginUsername')).sendKeys(randomName);
|
||||
element(by.model('loginPassword')).sendKeys('pass');
|
||||
|
||||
let login = element(by.css('#loginForm input[value="Login"]'));
|
||||
|
||||
login.click();
|
||||
browser.sleep(1000);
|
||||
|
||||
let alertDialog = browser.switchTo().alert();
|
||||
|
||||
await expect(alertDialog.getText()).to.eventually.match(/username or password is incorrect./);
|
||||
|
||||
alertDialog.accept();
|
||||
});
|
||||
|
||||
it('registers a new user', async function () {
|
||||
this.timeout(30000); // TODO: Speed up registration action. Takes way too long and times out unless you extend the timeout
|
||||
|
||||
let button = element(by.id('play-btn'));
|
||||
let randomName = generateUniqueId();
|
||||
|
||||
button.click();
|
||||
browser.sleep(1000);
|
||||
|
||||
let registerTab = element(by.linkText('Register'));
|
||||
registerTab.click();
|
||||
element(by.model('registerVals.username')).sendKeys(randomName);
|
||||
element(by.model('registerVals.email')).sendKeys(`${randomName}@example.com`);
|
||||
element(by.model('registerVals.password')).sendKeys('pass');
|
||||
element(by.model('registerVals.confirmPassword')).sendKeys('pass');
|
||||
|
||||
let register = element(by.css('#registrationForm input[type="submit"]'));
|
||||
|
||||
register.click();
|
||||
browser.sleep(3000);
|
||||
|
||||
let url = await browser.getCurrentUrl();
|
||||
|
||||
expect(url).to.not.match(/static\/front/);
|
||||
});
|
||||
|
||||
it('logs in an existing user');
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('AppJS', function() {
|
||||
describe('Automatic page refresh', function(){
|
||||
var clock;
|
||||
beforeEach(function () {
|
||||
clock = sandbox.useFakeTimers();
|
||||
sandbox.stub(window, "refresher", function(){return true});
|
||||
});
|
||||
|
||||
it('should not call refresher if idle time is less than 6 hours', function() {
|
||||
window.awaitIdle();
|
||||
clock.tick(21599999);
|
||||
expect(window.refresher).to.not.be.called;
|
||||
});
|
||||
|
||||
it('should not call refresher if awaitIdle is called within 6 hours', function() {
|
||||
window.awaitIdle();
|
||||
clock.tick(21500000);
|
||||
window.awaitIdle();
|
||||
clock.tick(21500000);
|
||||
expect(window.refresher).to.not.be.called;
|
||||
});
|
||||
|
||||
it('should call refresher if idle time is 6 hours or greater', function() {
|
||||
window.awaitIdle();
|
||||
clock.tick(21900000);
|
||||
expect(window.refresher).to.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,125 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Auth Controller', function() {
|
||||
var scope, ctrl, user, $httpBackend, $window, $modal, alert, Auth;
|
||||
|
||||
beforeEach(function(){
|
||||
module(function($provide) {
|
||||
Auth = {
|
||||
runAuth: sandbox.spy(),
|
||||
};
|
||||
$provide.value('Analytics', analyticsMock);
|
||||
$provide.value('Chat', { seenMessage: function() {} });
|
||||
$provide.value('Auth', Auth);
|
||||
});
|
||||
|
||||
inject(function(_$httpBackend_, $rootScope, $controller, _$modal_) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
scope = $rootScope.$new();
|
||||
scope.loginUsername = 'user';
|
||||
scope.loginPassword = 'pass';
|
||||
$window = { location: { href: ""}, alert: sandbox.spy() };
|
||||
$modal = _$modal_;
|
||||
user = { user: {}, authenticate: sandbox.spy() };
|
||||
alert = { authErrorAlert: sandbox.spy() };
|
||||
|
||||
ctrl = $controller('AuthCtrl', {$scope: scope, $window: $window, User: user, Alert: alert});
|
||||
})
|
||||
});
|
||||
|
||||
describe('logging in', function() {
|
||||
it('should log in users with correct uname / pass', function() {
|
||||
$httpBackend.expectPOST('/api/v3/user/auth/local/login').respond({data: {id: 'abc', apiToken: 'abc'}});
|
||||
scope.auth();
|
||||
$httpBackend.flush();
|
||||
expect(Auth.runAuth).to.be.calledOnce;
|
||||
expect(alert.authErrorAlert).to.not.be.called;
|
||||
});
|
||||
|
||||
it('should not log in users with incorrect uname / pass', function() {
|
||||
$httpBackend.expectPOST('/api/v3/user/auth/local/login').respond(404, '');
|
||||
scope.auth();
|
||||
$httpBackend.flush();
|
||||
expect(Auth.runAuth).to.not.be.called;
|
||||
expect(alert.authErrorAlert).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#clearLocalStorage', function () {
|
||||
var timer;
|
||||
|
||||
beforeEach(function () {
|
||||
timer = sandbox.useFakeTimers();
|
||||
sandbox.stub($modal, 'open');
|
||||
});
|
||||
|
||||
it('opens modal with message about clearing local storage and logging out', function () {
|
||||
scope.clearLocalStorage();
|
||||
|
||||
expect($modal.open).to.be.calledOnce;
|
||||
expect($modal.open).to.be.calledWith({
|
||||
templateUrl: 'modals/message-modal.html',
|
||||
scope: scope
|
||||
});
|
||||
|
||||
expect(scope.messageModal.title).to.eql(window.env.t('localStorageClearing'));
|
||||
expect(scope.messageModal.body).to.eql(window.env.t('localStorageClearingExplanation'));
|
||||
});
|
||||
|
||||
it('does not call $scope.logout before 3 seconds', function () {
|
||||
sandbox.stub(scope, 'logout');
|
||||
|
||||
scope.clearLocalStorage();
|
||||
|
||||
timer.tick(2999);
|
||||
|
||||
expect(scope.logout).to.not.be.called;
|
||||
});
|
||||
|
||||
it('calls $scope.logout after 3 seconds', function () {
|
||||
sandbox.stub(scope, 'logout');
|
||||
|
||||
scope.clearLocalStorage();
|
||||
|
||||
timer.tick(3000);
|
||||
|
||||
expect(scope.logout).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('does not clear local storage before 3 seconds', function () {
|
||||
sandbox.stub(localStorage, 'clear');
|
||||
|
||||
scope.clearLocalStorage();
|
||||
|
||||
timer.tick(2999);
|
||||
|
||||
expect(localStorage.clear).to.not.be.called;
|
||||
});
|
||||
|
||||
it('clears local storage after 3 seconds', function () {
|
||||
sandbox.stub(localStorage, 'clear');
|
||||
|
||||
scope.clearLocalStorage();
|
||||
|
||||
timer.tick(3000);
|
||||
|
||||
expect(localStorage.clear).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('does not redirect to /logout route before 3 seconds', function () {
|
||||
scope.clearLocalStorage();
|
||||
|
||||
timer.tick(2999);
|
||||
|
||||
expect($window.location.href).to.eql('');
|
||||
});
|
||||
|
||||
it('redirects to /logout after 3 seconds', function () {
|
||||
scope.clearLocalStorage();
|
||||
|
||||
timer.tick(3000);
|
||||
|
||||
expect($window.location.href).to.eql('/logout');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,120 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe("Autocomplete controller", function() {
|
||||
var scope, ctrl, user, $rootScope, $controller;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(function($rootScope, _$controller_){
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
|
||||
scope = $rootScope.$new();
|
||||
scope.group = {}
|
||||
scope.group.chat = [];
|
||||
|
||||
$controller = _$controller_;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
ctrl = $controller('AutocompleteCtrl', {$scope: scope});
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearUserList", function() {
|
||||
it('calling the function clears the list of usernames and responses', function() {
|
||||
scope.response.push("blah");
|
||||
scope.usernames.push("blub");
|
||||
|
||||
scope.clearUserlist();
|
||||
expect(scope.response).to.be.empty;
|
||||
expect(scope.usernames).to.be.empty;
|
||||
});
|
||||
|
||||
it('the function is called upon initialization of the controller', function() {
|
||||
scope.response.push("blah");
|
||||
scope.response.push("blub");
|
||||
ctrl = $controller('AutocompleteCtrl', {$scope: scope});
|
||||
|
||||
expect(scope.response).to.be.empty;
|
||||
expect(scope.usernames).to.be.empty;
|
||||
});
|
||||
})
|
||||
|
||||
describe("filterUser", function() {
|
||||
it('filters with undefined query (not loaded yet) and returns false (so it will not be rendered)', function() {
|
||||
expect(scope.filterUser({user: "boo"})).to.be.eq(false);
|
||||
});
|
||||
|
||||
it('filters with null query (no typing yet) and returns false (so it will not be rendered)', function() {
|
||||
scope.query = null
|
||||
expect(scope.filterUser({user: "boo"})).to.be.eq(false);
|
||||
});
|
||||
|
||||
it('filters with empty prefix and returns true', function() {
|
||||
scope.query = {text: ""};
|
||||
expect(scope.filterUser({user: "prefix"})).to.be.eq(true);
|
||||
});
|
||||
|
||||
it('filters with prefix element and returns true', function() {
|
||||
scope.query = {text: "pre"}
|
||||
expect(scope.filterUser({user: "prefix"})).to.be.eq(true);
|
||||
});
|
||||
|
||||
it('filters with prefix element of a different case and returns true', function() {
|
||||
scope.query = {text: "pre"}
|
||||
expect(scope.filterUser({user: "Prefix"})).to.be.eq(true);
|
||||
});
|
||||
|
||||
it('filters with nonprefix element and returns false', function() {
|
||||
scope.query = {text: "noprefix"}
|
||||
expect(scope.filterUser({user: "prefix"})).to.be.eq(false);
|
||||
});
|
||||
|
||||
it('filters out system messages (messages without username)', function() {
|
||||
scope.query = {text: "myquery"}
|
||||
expect(scope.filterUser({uuid: "system"})).to.be.eq(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("performCompletion", function() {
|
||||
it('triggers autoComplete', function() {
|
||||
scope.autoComplete = sandbox.spy();
|
||||
|
||||
var msg = {user: "boo"}; // scope.autoComplete only cares about user
|
||||
scope.query = {text: "b"};
|
||||
scope.performCompletion(msg);
|
||||
|
||||
expect(scope.query).to.be.eq(null);
|
||||
expect(scope.autoComplete.callCount).to.be.eq(1);
|
||||
expect(scope.autoComplete).to.have.been.calledWith(msg);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addNewUser", function() {
|
||||
it('a new message from a new user will modify the usernames', function() {
|
||||
expect(scope.response).to.be.empty;
|
||||
expect(scope.usernames).to.be.empty;
|
||||
|
||||
var msg = {user: "boo"};
|
||||
scope.addNewUser(msg);
|
||||
expect(scope.response[0]).to.be.eq(msg);
|
||||
expect(scope.usernames[0]).to.be.eq("boo");
|
||||
});
|
||||
});
|
||||
|
||||
describe("chatChanged", function() {
|
||||
it('if a new chat arrives, the new user name is extracted', function() {
|
||||
var chatChanged = sandbox.spy(scope, 'chatChanged');
|
||||
scope.$watch('group.chat',scope.chatChanged); // reinstantiate watch so spy works
|
||||
|
||||
scope.$digest(); // trigger watch
|
||||
scope.group.chat.push({msg: "new chat", user: "boo"});
|
||||
expect(chatChanged.callCount).to.be.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,774 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Challenges Controller', function() {
|
||||
var rootScope, scope, user, User, ctrl, groups, members, notification, state, challenges, tasks, tavernId;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
user = specHelper.newUser();
|
||||
User = {
|
||||
getBalanceInGems: sandbox.stub(),
|
||||
sync: sandbox.stub(),
|
||||
user: user
|
||||
}
|
||||
$provide.value('User', User);
|
||||
});
|
||||
|
||||
inject(function($rootScope, $controller, _$state_, _Groups_, _Members_, _Notification_, _Challenges_, _Tasks_, _TAVERN_ID_){
|
||||
scope = $rootScope.$new();
|
||||
rootScope = $rootScope;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: User});
|
||||
|
||||
ctrl = $controller('ChallengesCtrl', {$scope: scope, User: User});
|
||||
|
||||
challenges = _Challenges_;
|
||||
tasks = _Tasks_;
|
||||
groups = _Groups_;
|
||||
members = _Members_;
|
||||
notification = _Notification_;
|
||||
state = _$state_;
|
||||
tavernId = _TAVERN_ID_;
|
||||
});
|
||||
});
|
||||
|
||||
context('filtering', function() {
|
||||
describe('filterChallenges', function() {
|
||||
var ownMem, ownNotMem, notOwnMem, notOwnNotMem;
|
||||
|
||||
beforeEach(function() {
|
||||
ownMem = specHelper.newChallenge({
|
||||
description: 'You are the owner and member',
|
||||
leader: user._id,
|
||||
members: [user],
|
||||
_isMember: true,
|
||||
_id: 'ownMem-id',
|
||||
});
|
||||
|
||||
ownNotMem = specHelper.newChallenge({
|
||||
description: 'You are the owner, but not a member',
|
||||
leader: user._id,
|
||||
members: [],
|
||||
_isMember: false,
|
||||
_id: 'ownNotMem-id',
|
||||
});
|
||||
|
||||
notOwnMem = specHelper.newChallenge({
|
||||
description: 'Not owner but a member',
|
||||
leader: {_id:"test"},
|
||||
members: [user],
|
||||
_isMember: true,
|
||||
_id: 'notOwnMem-id',
|
||||
});
|
||||
|
||||
notOwnNotMem = specHelper.newChallenge({
|
||||
description: 'Not owner or member',
|
||||
leader: {_id:"test"},
|
||||
members: [],
|
||||
_isMember: false,
|
||||
_id: 'notOwnNotMem-id',
|
||||
});
|
||||
|
||||
user.challenges = [ownMem._id, notOwnMem._id];
|
||||
|
||||
scope.search = {
|
||||
group: _.transform(groups, function(m,g){m[g._id]=true;})
|
||||
};
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: either and owner: either', function() {
|
||||
scope.search._isMember = 'either';
|
||||
scope.search._isOwner = 'either';
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(true);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: either and owner: true', function() {
|
||||
scope.search._isMember = 'either';
|
||||
scope.search._isOwner = true;
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(false);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: either and owner: false', function() {
|
||||
scope.search._isMember = 'either';
|
||||
scope.search._isOwner = false;
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(true);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: true and owner: either', function() {
|
||||
scope.search._isMember = true;
|
||||
scope.search._isOwner = 'either';
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(false);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: true and owner: true', function() {
|
||||
scope.search._isMember = true;
|
||||
scope.search._isOwner = true;
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(false);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: true and owner: false', function() {
|
||||
scope.search._isMember = true;
|
||||
scope.search._isOwner = false;
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(false);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: false and owner: either', function() {
|
||||
scope.search._isMember = false;
|
||||
scope.search._isOwner = 'either';
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(true);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: false and owner: true', function() {
|
||||
scope.search._isMember = false;
|
||||
scope.search._isOwner = true;
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(true);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(false);
|
||||
});
|
||||
|
||||
it('displays challenges that match membership: false and owner: false', function() {
|
||||
scope.search._isMember = false;
|
||||
scope.search._isOwner = false;
|
||||
expect(scope.filterChallenges(ownMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(ownNotMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnMem)).to.eql(false);
|
||||
expect(scope.filterChallenges(notOwnNotMem)).to.eql(true);
|
||||
});
|
||||
|
||||
it('filters challenges to a single group when group id filter is set', inject(function($controller) {
|
||||
scope.search = { };
|
||||
scope.groupsFilter = {
|
||||
0: specHelper.newGroup({_id: 'group-one'}),
|
||||
1: specHelper.newGroup({_id: 'group-two'}),
|
||||
2: specHelper.newGroup({_id: 'group-three'})
|
||||
};
|
||||
|
||||
scope.groupIdFilter = 'group-one';
|
||||
scope.filterInitialChallenges();
|
||||
expect(scope.search.group).to.eql({'group-one': true});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('selectAll', function() {
|
||||
it('sets all groups in seach.group to true', function() {
|
||||
scope.search = { };
|
||||
scope.groupsFilter = {
|
||||
0: specHelper.newGroup({_id: 'group-one'}),
|
||||
1: specHelper.newGroup({_id: 'group-two'}),
|
||||
2: specHelper.newGroup({_id: 'group-three'})
|
||||
};
|
||||
scope.selectAll();
|
||||
|
||||
expect(scope.search.group).to.eql({
|
||||
'group-one': true,
|
||||
'group-two': true,
|
||||
'group-three': true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectNone', function() {
|
||||
it('sets all groups in seach.group to false', function() {
|
||||
scope.search = { };
|
||||
scope.groupsFilter = {
|
||||
0: specHelper.newGroup({_id: 'group-one'}),
|
||||
1: specHelper.newGroup({_id: 'group-two'}),
|
||||
2: specHelper.newGroup({_id: 'group-three'})
|
||||
};
|
||||
scope.selectNone();
|
||||
|
||||
expect(scope.search.group).to.eql({
|
||||
'group-one': false,
|
||||
'group-two': false,
|
||||
'group-three': false
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('task manipulation', function() {
|
||||
|
||||
describe('shouldShow', function() {
|
||||
it('overrides task controller function by always returning true', function() {
|
||||
expect(scope.shouldShow()).to.eq(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addTask', function() {
|
||||
var challenge;
|
||||
|
||||
beforeEach(function () {
|
||||
challenge = specHelper.newChallenge({
|
||||
description: 'You are the owner and member',
|
||||
leader: user._id,
|
||||
members: [user],
|
||||
_isMember: true
|
||||
});
|
||||
});
|
||||
|
||||
it('adds default task to array', function() {
|
||||
var taskArray = [];
|
||||
var listDef = {
|
||||
newTask: 'new todo text',
|
||||
type: 'todo'
|
||||
}
|
||||
|
||||
scope.addTask(listDef, challenge);
|
||||
|
||||
expect(challenge['todos'].length).to.eql(1);
|
||||
expect(challenge['todos'][0].text).to.eql('new todo text');
|
||||
expect(challenge['todos'][0].type).to.eql('todo');
|
||||
});
|
||||
|
||||
it('adds the task to the front of the array', function() {
|
||||
var previousTask = specHelper.newTodo({ text: 'previous task' });
|
||||
var taskArray = [];
|
||||
challenge['todos'] = [previousTask];
|
||||
var listDef = {
|
||||
newTask: 'new todo',
|
||||
type: 'todo'
|
||||
}
|
||||
|
||||
scope.addTask(listDef, challenge);
|
||||
|
||||
expect(challenge['todos'].length).to.eql(2);
|
||||
expect(challenge['todos'][0].text).to.eql('new todo');
|
||||
expect(challenge['todos'][1].text).to.eql('previous task');
|
||||
});
|
||||
|
||||
it('removes text from new task input box', function() {
|
||||
var taskArray = [];
|
||||
var listDef = {
|
||||
newTask: 'new todo text',
|
||||
type: 'todo'
|
||||
}
|
||||
|
||||
scope.addTask(listDef, challenge);
|
||||
|
||||
expect(listDef.newTask).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('editTask', function() {
|
||||
it('is Tasks.editTask', function() {
|
||||
inject(function(Tasks) {
|
||||
// @TODO: Currently we override the task function in the challenge ctrl, but we should abstract it again
|
||||
// expect(scope.editTask).to.eql(Tasks.editTask);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeTask', function() {
|
||||
var task, challenge;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(window, 'confirm');
|
||||
task = specHelper.newTodo();
|
||||
challenge = specHelper.newChallenge({
|
||||
description: 'You are the owner and member',
|
||||
leader: user._id,
|
||||
members: [user],
|
||||
_isMember: true
|
||||
});
|
||||
challenge['todos'] = [task];
|
||||
});
|
||||
|
||||
it('asks user to confirm deletion', function() {
|
||||
scope.removeTask(task, challenge);
|
||||
expect(window.confirm).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('does not remove task from list if not confirmed', function() {
|
||||
window.confirm.returns(false);
|
||||
scope.removeTask(task, challenge);
|
||||
|
||||
expect(challenge['todos']).to.include(task);
|
||||
});
|
||||
|
||||
it('removes task from list', function() {
|
||||
window.confirm.returns(true);
|
||||
scope.removeTask(task, challenge);
|
||||
|
||||
expect(challenge['todos']).to.not.include(task);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveTask', function() {
|
||||
it('sets task._editing to false', function() {
|
||||
var task = specHelper.newTask({ _editing: true });
|
||||
|
||||
scope.saveTask(task);
|
||||
|
||||
expect(task._editing).to.be.eql(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('challenge owner interactions', function() {
|
||||
describe("save challenge", function() {
|
||||
var alert, createChallengeSpy, challengeResponse, taskChallengeCreateSpy;
|
||||
|
||||
beforeEach(function(){
|
||||
alert = sandbox.stub(window, "alert");
|
||||
createChallengeSpy = sinon.stub(challenges, 'createChallenge');
|
||||
challengeResponse = {data: {data: {_id: 'new-challenge'}}};
|
||||
createChallengeSpy.returns(Promise.resolve(challengeResponse));
|
||||
|
||||
taskChallengeCreateSpy = sinon.stub(tasks, 'createChallengeTasks');
|
||||
var taskResponse = {data: {data: []}};
|
||||
taskChallengeCreateSpy.returns(Promise.resolve(taskResponse));
|
||||
});
|
||||
|
||||
it("opens an alert box if challenge.group is not specified", function() {
|
||||
var challenge = specHelper.newChallenge({
|
||||
name: 'Challenge without a group',
|
||||
shortName: 'chal without group',
|
||||
group: null
|
||||
});
|
||||
|
||||
scope.save(challenge);
|
||||
|
||||
expect(alert).to.be.calledOnce;
|
||||
expect(alert).to.be.calledWith(window.env.t('selectGroup'));
|
||||
});
|
||||
|
||||
it("opens an alert box if isNew and user does not have enough gems", function() {
|
||||
var challenge = specHelper.newChallenge({
|
||||
name: 'Challenge without enough gems',
|
||||
shortName: 'chal without gem',
|
||||
prize: 5
|
||||
});
|
||||
|
||||
scope.maxPrize = 4;
|
||||
scope.save(challenge);
|
||||
|
||||
expect(alert).to.be.calledOnce;
|
||||
expect(alert).to.be.calledWith(window.env.t('challengeNotEnoughGems'));
|
||||
});
|
||||
|
||||
it("saves the challenge if user does not have enough gems, but the challenge is not new", function() {
|
||||
var updateChallengeSpy = sinon.spy(challenges, 'updateChallenge');
|
||||
|
||||
var challenge = specHelper.newChallenge({
|
||||
_id: 'challenge-has-id-so-its-not-new',
|
||||
name: 'Challenge without enough gems',
|
||||
shortName: 'chal without gem',
|
||||
prize: 5,
|
||||
});
|
||||
|
||||
scope.maxPrize = 0;
|
||||
scope.save(challenge);
|
||||
|
||||
expect(updateChallengeSpy).to.be.calledOnce;
|
||||
expect(alert).to.not.be.called;
|
||||
});
|
||||
|
||||
it("saves the challenge if user has enough gems and challenge is new", function() {
|
||||
var challenge = specHelper.newChallenge({
|
||||
name: 'Challenge without enough gems',
|
||||
shortName: 'chal without gem',
|
||||
prize: 5,
|
||||
});
|
||||
|
||||
scope.maxPrize = 5;
|
||||
scope.save(challenge);
|
||||
|
||||
expect(createChallengeSpy).to.be.calledOnce;
|
||||
expect(alert).to.not.be.called;
|
||||
});
|
||||
|
||||
it('saves challenge and then proceeds to detail page', function(done) {
|
||||
sandbox.stub(state, 'transitionTo');
|
||||
|
||||
var challenge = specHelper.newChallenge({
|
||||
name: 'Challenge',
|
||||
shortName: 'chal',
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(createChallengeSpy).to.be.calledOnce;
|
||||
expect(state.transitionTo).to.be.calledWith(
|
||||
'options.social.challenges.detail',
|
||||
{ cid: 'new-challenge' },
|
||||
{
|
||||
reload: true, inherit: false, notify: true
|
||||
}
|
||||
);
|
||||
done();
|
||||
}, 1000);
|
||||
|
||||
scope.save(challenge);
|
||||
});
|
||||
|
||||
it('saves new challenge and syncs User', function(done) {
|
||||
var challenge = specHelper.newChallenge();
|
||||
challenge.shortName = 'chal';
|
||||
|
||||
setTimeout(function() {
|
||||
expect(User.sync).to.be.calledOnce;
|
||||
done();
|
||||
}, 1000);
|
||||
|
||||
scope.save(challenge);
|
||||
});
|
||||
|
||||
it('saves new challenge and syncs User', function(done) {
|
||||
sinon.stub(notification, 'text');
|
||||
|
||||
var challenge = specHelper.newChallenge();
|
||||
challenge.shortName = 'chal';
|
||||
|
||||
setTimeout(function() {
|
||||
expect(notification.text).to.be.calledOnce;
|
||||
expect(notification.text).to.be.calledWith(window.env.t('challengeCreated'));
|
||||
done();
|
||||
}, 1000);
|
||||
|
||||
scope.save(challenge);
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', function() {
|
||||
it('creates new challenge with group that user has selected in filter', function() {
|
||||
var party = specHelper.newGroup({
|
||||
type: 'party',
|
||||
_id: 'user-party'
|
||||
});
|
||||
scope.groupsFilter = [party];
|
||||
scope.search = {
|
||||
group: {
|
||||
'user-party': true
|
||||
}
|
||||
};
|
||||
|
||||
scope.create();
|
||||
|
||||
expect(scope.newChallenge.group).to.eql('user-party');
|
||||
});
|
||||
|
||||
it('uses first group in $scope.groups if more than one exists', function() {
|
||||
var party = specHelper.newGroup({
|
||||
type: 'party',
|
||||
_id: 'user-party'
|
||||
});
|
||||
var guild = specHelper.newGroup({
|
||||
type: 'guild',
|
||||
_id: 'guild'
|
||||
});
|
||||
scope.groups = [party, guild];
|
||||
scope.groupsFilter = [party, guild];
|
||||
scope.search = {
|
||||
group: {
|
||||
'user-party': true,
|
||||
'guild': true
|
||||
}
|
||||
};
|
||||
|
||||
scope.create();
|
||||
|
||||
expect(scope.newChallenge.group).to.eql('user-party');
|
||||
});
|
||||
|
||||
it('defaults to tavern if no group can be set as default', function() {
|
||||
scope.create();
|
||||
|
||||
expect(scope.newChallenge.group).to.eql(tavernId);
|
||||
});
|
||||
|
||||
it('calculates maxPrize', function() {
|
||||
User.getBalanceInGems.returns(20);
|
||||
scope.create();
|
||||
|
||||
expect(scope.maxPrize).to.eql(20);
|
||||
});
|
||||
|
||||
it('sets newChallenge to a blank challenge', function() {
|
||||
scope.create();
|
||||
|
||||
var chal = scope.newChallenge;
|
||||
|
||||
expect(chal.name).to.eql('');
|
||||
expect(chal.description).to.eql('');
|
||||
expect(chal.habits).to.eql([]);
|
||||
expect(chal.dailys).to.eql([]);
|
||||
expect(chal.todos).to.eql([]);
|
||||
expect(chal.rewards).to.eql([]);
|
||||
expect(chal.leader).to.eql('unique-user-id');
|
||||
expect(chal.group).to.eql(tavernId);
|
||||
expect(chal.timestamp).to.be.greaterThan(0);
|
||||
expect(chal.official).to.eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insufficientGemsForTavernChallenge', function() {
|
||||
context('tavern challenge', function() {
|
||||
it('returns true if user has no gems', function() {
|
||||
User.user.balance = 0;
|
||||
scope.newChallenge = specHelper.newChallenge({
|
||||
group: tavernId
|
||||
});
|
||||
|
||||
var cannotCreateTavernChallenge = scope.insufficientGemsForTavernChallenge();
|
||||
expect(cannotCreateTavernChallenge).to.eql(true);
|
||||
});
|
||||
|
||||
it('returns false if user has gems', function() {
|
||||
User.user.balance = .25;
|
||||
scope.newChallenge = specHelper.newChallenge({
|
||||
group: tavernId
|
||||
});
|
||||
|
||||
var cannotCreateTavernChallenge = scope.insufficientGemsForTavernChallenge();
|
||||
expect(cannotCreateTavernChallenge).to.eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
context('non-tavern challenge', function() {
|
||||
it('returns false', function() {
|
||||
User.user.balance = 0;
|
||||
scope.newChallenge = specHelper.newChallenge({
|
||||
group: 'not-tavern'
|
||||
});
|
||||
|
||||
var cannotCreateTavernChallenge = scope.insufficientGemsForTavernChallenge();
|
||||
expect(cannotCreateTavernChallenge).to.eql(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit', function() {
|
||||
it('transitions to edit page', function() {
|
||||
sandbox.stub(state, 'transitionTo');
|
||||
var challenge = specHelper.newChallenge({
|
||||
_id: 'challenge-id'
|
||||
});
|
||||
|
||||
scope.edit(challenge);
|
||||
|
||||
expect(state.transitionTo).to.be.calledOnce;
|
||||
expect(state.transitionTo).to.be.calledWith(
|
||||
'options.social.challenges.edit',
|
||||
{ cid: challenge._id },
|
||||
{ reload: true, inherit: false, notify: true }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('discard', function() {
|
||||
it('sets new challenge to null', function() {
|
||||
scope.newChallenge = specHelper.newChallenge();
|
||||
|
||||
scope.discard();
|
||||
|
||||
expect(scope.newChallenge).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('clone', function() {
|
||||
|
||||
var challengeToClone = {
|
||||
name: 'copyChallenge',
|
||||
description: 'copyChallenge',
|
||||
habits: [specHelper.newHabit()],
|
||||
dailys: [specHelper.newDaily()],
|
||||
todos: [specHelper.newTodo()],
|
||||
rewards: [specHelper.newReward()],
|
||||
leader: 'unique-user-id',
|
||||
group: { _id: "copyGroup" },
|
||||
timestamp: new Date("October 13, 2014 11:13:00"),
|
||||
members: ['id', 'another-id'],
|
||||
official: true,
|
||||
_isMember: true,
|
||||
prize: 1
|
||||
};
|
||||
|
||||
it('Clones the basic challenge info', function() {
|
||||
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.name).to.eql(challengeToClone.name);
|
||||
expect(scope.newChallenge.shortName).to.eql(challengeToClone.shortName);
|
||||
expect(scope.newChallenge.description).to.eql(challengeToClone.description);
|
||||
expect(scope.newChallenge.leader).to.eql(user._id);
|
||||
expect(scope.newChallenge.group).to.eql(challengeToClone.group._id);
|
||||
expect(scope.newChallenge.official).to.eql(challengeToClone.official);
|
||||
expect(scope.newChallenge.prize).to.eql(challengeToClone.prize);
|
||||
});
|
||||
|
||||
it('does not clone members', function() {
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.members).to.not.exist;
|
||||
});
|
||||
|
||||
it('does not clone timestamp', function() {
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.timestamp).to.not.exist;
|
||||
});
|
||||
|
||||
it('clones habits', function() {
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.habits.length).to.eql(challengeToClone.habits.length);
|
||||
expect(scope.newChallenge.habits[0].text).to.eql(challengeToClone.habits[0].text);
|
||||
expect(scope.newChallenge.habits[0].notes).to.eql(challengeToClone.habits[0].notes);
|
||||
});
|
||||
|
||||
it('clones dailys', function() {
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.dailys.length).to.eql(challengeToClone.dailys.length);
|
||||
expect(scope.newChallenge.dailys[0].text).to.eql(challengeToClone.dailys[0].text);
|
||||
expect(scope.newChallenge.dailys[0].notes).to.eql(challengeToClone.dailys[0].notes);
|
||||
});
|
||||
|
||||
it('clones todos', function() {
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.todos.length).to.eql(challengeToClone.todos.length);
|
||||
expect(scope.newChallenge.todos[0].text).to.eql(challengeToClone.todos[0].text);
|
||||
expect(scope.newChallenge.todos[0].notes).to.eql(challengeToClone.todos[0].notes);
|
||||
});
|
||||
|
||||
it('clones rewards', function() {
|
||||
scope.clone(challengeToClone);
|
||||
|
||||
expect(scope.newChallenge.rewards.length).to.eql(challengeToClone.rewards.length);
|
||||
expect(scope.newChallenge.rewards[0].text).to.eql(challengeToClone.rewards[0].text);
|
||||
expect(scope.newChallenge.rewards[0].notes).to.eql(challengeToClone.rewards[0].notes);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('User interactions', function() {
|
||||
describe('join', function() {
|
||||
it('calls challenge join', function(){
|
||||
var joinChallengeSpy = sinon.spy(challenges, 'joinChallenge');
|
||||
|
||||
var challenge = specHelper.newChallenge({
|
||||
_id: 'challenge-to-join',
|
||||
});
|
||||
|
||||
scope.join(challenge);
|
||||
|
||||
expect(joinChallengeSpy).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('clickLeave', function() {
|
||||
var clickEvent = {
|
||||
target: 'button'
|
||||
};
|
||||
|
||||
it('sets selectedChal to passed in challenge', function() {
|
||||
var challenge = specHelper.newChallenge({
|
||||
_id: 'popover-challenge-to-leave'
|
||||
});
|
||||
|
||||
expect(scope.selectedChal).to.not.exist;
|
||||
|
||||
scope.clickLeave(challenge, clickEvent);
|
||||
expect(scope.selectedChal).to.eql(challenge);
|
||||
});
|
||||
|
||||
it('creates popover element', function() {
|
||||
var challenge = specHelper.newChallenge({
|
||||
_id: 'popover-challenge-to-leave'
|
||||
});
|
||||
|
||||
expect(scope.popoverEl).to.not.exist;
|
||||
scope.clickLeave(challenge, clickEvent);
|
||||
expect(scope.popoverEl).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('leave', function() {
|
||||
var challenge = specHelper.newChallenge({
|
||||
_id: 'challenge-to-leave',
|
||||
});
|
||||
|
||||
var clickEvent = {
|
||||
target: 'button'
|
||||
};
|
||||
|
||||
it('removes selectedChal when cancel is chosen', function() {
|
||||
scope.clickLeave(challenge, clickEvent);
|
||||
|
||||
expect(scope.selectedChal).to.eql(challenge);
|
||||
|
||||
scope.leave('cancel');
|
||||
expect(scope.selectedChal).to.not.exist;
|
||||
});
|
||||
|
||||
it('calls challenge leave when anything but cancel is chosen', function() {
|
||||
var leaveChallengeSpy = sinon.spy(challenges, 'leaveChallenge');
|
||||
scope.clickLeave(challenge, clickEvent);
|
||||
|
||||
scope.leave('not-cancel', challenge);
|
||||
expect(leaveChallengeSpy).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('modal actions', function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(members, 'selectMember');
|
||||
sandbox.stub(rootScope, 'openModal');
|
||||
members.selectMember.returns(Promise.resolve());
|
||||
});
|
||||
|
||||
describe('sendMessageToChallengeParticipant', function() {
|
||||
it('opens private-message modal', function(done) {
|
||||
scope.sendMessageToChallengeParticipant(user._id);
|
||||
|
||||
setTimeout(function() {
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith(
|
||||
'private-message',
|
||||
{ controller: 'MemberModalCtrl' }
|
||||
);
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendGiftToChallengeParticipant', function() {
|
||||
it('opens send-gift modal', function(done) {
|
||||
scope.sendGiftToChallengeParticipant(user._id);
|
||||
|
||||
setTimeout(function() {
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith(
|
||||
'send-gift',
|
||||
{ controller: 'MemberModalCtrl' }
|
||||
);
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe("Chat Controller", function() {
|
||||
var scope, ctrl, user, $rootScope, $controller, $httpBackend, html;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(function(_$rootScope_, _$controller_, _$compile_, _$httpBackend_){
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
$rootScope = _$rootScope_;
|
||||
|
||||
scope = _$rootScope_.$new();
|
||||
|
||||
$controller = _$controller_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
html = _$compile_('<div><form ng-submit="postChat(group, message.content)"><textarea submit-on-meta-enter ng-model="message.content" ng-model-options="{debounce: 250}"></textarea></form></div>')(scope);
|
||||
document.body.appendChild(html[0]);
|
||||
ctrl = $controller('ChatCtrl', {$scope: scope, $element: html});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
html.remove();
|
||||
});
|
||||
|
||||
describe('copyToDo', function() {
|
||||
it('when copying a user message it opens modal with information from message', function() {
|
||||
scope.group = {
|
||||
name: "Princess Bride"
|
||||
};
|
||||
|
||||
var modalSpy = sandbox.spy($rootScope, "openModal");
|
||||
var message = {
|
||||
uuid: 'the-dread-pirate-roberts',
|
||||
user: 'Wesley',
|
||||
text: 'As you wish'
|
||||
};
|
||||
|
||||
scope.copyToDo(message);
|
||||
|
||||
modalSpy.should.have.been.calledOnce;
|
||||
|
||||
modalSpy.should.have.been.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){
|
||||
return callArgToMatch.controller == 'CopyMessageModalCtrl'
|
||||
&& callArgToMatch.scope.text == message.text
|
||||
}));
|
||||
});
|
||||
|
||||
it('when copying a system message it opens modal with information from message', function() {
|
||||
scope.group = {
|
||||
name: "Princess Bride"
|
||||
};
|
||||
|
||||
var modalSpy = sandbox.spy($rootScope, "openModal");
|
||||
var message = {
|
||||
uuid: 'system',
|
||||
text: 'Wesley attacked the ROUS in the Fire Swamp'
|
||||
};
|
||||
|
||||
scope.copyToDo(message);
|
||||
|
||||
modalSpy.should.have.been.calledOnce;
|
||||
|
||||
modalSpy.should.have.been.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){
|
||||
return callArgToMatch.controller == 'CopyMessageModalCtrl'
|
||||
&& callArgToMatch.scope.text == message.text
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('updates model on enter key press', function() {
|
||||
// Set initial state of the page with some dummy data.
|
||||
scope.group = { name: 'group' };
|
||||
|
||||
// The main controller is going to try to fetch the template right off.
|
||||
// No big deal, just return an empty string.
|
||||
$httpBackend.when('GET', 'partials/main.html').respond('');
|
||||
|
||||
// Let the page settle, and the controllers set their initial state.
|
||||
$rootScope.$digest();
|
||||
|
||||
// Watch for calls to postChat & make sure it doesn't do anything.
|
||||
let postChatSpy = sandbox.stub(scope, 'postChat');
|
||||
|
||||
// Pretend we typed 'aaa' into the textarea.
|
||||
var textarea = html.find('textarea');
|
||||
textarea[0].value = 'aaa';
|
||||
let inputEvent = new Event('input');
|
||||
textarea[0].dispatchEvent(inputEvent);
|
||||
|
||||
// Give a change for the ng-model watchers to notice that the value in the
|
||||
// textarea has changed.
|
||||
$rootScope.$digest();
|
||||
|
||||
// Since no time has elapsed and we debounce the model change, we should
|
||||
// see no model update just yet.
|
||||
expect(scope.message.content).to.equal('');
|
||||
|
||||
// Now, press the enter key in the textarea. We use jquery here to paper
|
||||
// over browser differences with initializing the keyboard event.
|
||||
var keyboardEvent = jQuery.Event('keydown', {keyCode: 13, key: 'Enter', metaKey: true});
|
||||
jQuery(textarea).trigger(keyboardEvent);
|
||||
|
||||
// Now, allow the model to update given the changes to the page still
|
||||
// without letting any time elapse...
|
||||
$rootScope.$digest();
|
||||
|
||||
// ... and nevertheless seeing the desired call to postChat with the right
|
||||
// data. Yay!
|
||||
postChatSpy.should.have.been.calledWith(scope.group, 'aaa');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe("CopyMessageModal controller", function() {
|
||||
var scope, ctrl, user, Notification, $rootScope, $controller;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
var mockWindow = {href: '', alert: sandbox.spy(), location: {search: '', pathname: '', href: ''}};
|
||||
|
||||
$provide.value('$window', mockWindow);
|
||||
});
|
||||
|
||||
inject(function($rootScope, _$controller_, _Notification_, User){
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
user.ops = {
|
||||
addTask: sandbox.spy()
|
||||
};
|
||||
|
||||
scope = $rootScope.$new();
|
||||
scope.$close = sandbox.spy();
|
||||
|
||||
$controller = _$controller_;
|
||||
|
||||
User.setUser(user);
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: User});
|
||||
|
||||
ctrl = $controller('CopyMessageModalCtrl', {$scope: scope, User: User});
|
||||
|
||||
Notification = _Notification_;
|
||||
Notification.text = sandbox.spy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("saveTodo", function() {
|
||||
it('saves todo', function() {
|
||||
|
||||
scope.text = "A Tavern msg";
|
||||
scope.notes = "Some notes";
|
||||
var payload = {
|
||||
body: {
|
||||
text: scope.text,
|
||||
type: 'todo',
|
||||
notes: scope.notes
|
||||
}
|
||||
};
|
||||
|
||||
scope.saveTodo();
|
||||
|
||||
user.ops.addTask.should.have.been.calledOnce;
|
||||
user.ops.addTask.should.have.been.calledWith(payload);
|
||||
Notification.text.should.have.been.calledOnce;
|
||||
Notification.text.should.have.been.calledWith(window.env.t('messageAddedAsToDo'));
|
||||
scope.$close.should.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,51 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Filters Controller', function() {
|
||||
var scope, user, userService;
|
||||
|
||||
beforeEach(function () {
|
||||
module(function($provide) {
|
||||
var mockWindow = {href: '', alert: sandbox.spy(), location: {search: '', pathname: '', href: ''}};
|
||||
|
||||
$provide.value('$window', mockWindow);
|
||||
});
|
||||
|
||||
inject(function($rootScope, $controller, Shared, User) {
|
||||
user = specHelper.newUser();
|
||||
Shared.wrap(user);
|
||||
scope = $rootScope.$new();
|
||||
// user.filters = {};
|
||||
User.setUser(user);
|
||||
User.user.filters = {};
|
||||
userService = User;
|
||||
$controller('FiltersCtrl', {$scope: scope, User: User});
|
||||
})
|
||||
});
|
||||
|
||||
describe('tags', function(){
|
||||
it('creates a tag', function(){
|
||||
scope._newTag = {name:'tagName'}
|
||||
scope.createTag();
|
||||
expect(user.tags).to.have.length(1);
|
||||
expect(user.tags[0].name).to.eql('tagName');
|
||||
expect(user.tags[0]).to.have.property('id');
|
||||
});
|
||||
|
||||
it('toggles tag filtering', inject(function(Shared){
|
||||
var tag = {id: Shared.uuid(), name: 'myTag'};
|
||||
scope.toggleFilter(tag);
|
||||
expect(userService.user.filters[tag.id]).to.eql(true);
|
||||
scope.toggleFilter(tag);
|
||||
expect(userService.user.filters[tag.id]).to.not.eql(true);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('updateTaskFilter', function(){
|
||||
it('updatest user\'s filter query with the value of filterQuery', function () {
|
||||
scope.filterQuery = 'task';
|
||||
scope.updateTaskFilter();
|
||||
|
||||
expect(userService.user.filterQuery).to.eql(scope.filterQuery);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,86 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Footer Controller', function() {
|
||||
var scope, user, User;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
user = specHelper.newUser();
|
||||
User = {
|
||||
log: sandbox.stub(),
|
||||
set: sandbox.stub(),
|
||||
addTenGems: sandbox.stub(),
|
||||
addHourglass: sandbox.stub(),
|
||||
user: user
|
||||
};
|
||||
scope = $rootScope.$new();
|
||||
$controller('FooterCtrl', {$scope: scope, User: User, Social: {}});
|
||||
}));
|
||||
|
||||
context('Debug mode', function() {
|
||||
before(function() {
|
||||
window.env.NODE_ENV = 'test';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
delete window.env.NODE_ENV;
|
||||
});
|
||||
|
||||
describe('#setHealthLow', function(){
|
||||
it('sets user health to 1');
|
||||
});
|
||||
|
||||
describe('#addMissedDay', function(){
|
||||
beforeEach(function() {
|
||||
sandbox.stub(confirm).returns(true);
|
||||
});
|
||||
|
||||
it('Cancels if confirm box is not confirmed');
|
||||
|
||||
it('allows multiple days');
|
||||
|
||||
it('sets users last cron');
|
||||
|
||||
it('notifies uers');
|
||||
});
|
||||
|
||||
describe('#addTenGems', function() {
|
||||
it('posts to /user/addTenGems', inject(function($httpBackend) {
|
||||
scope.addTenGems();
|
||||
|
||||
expect(User.addTenGems).to.have.been.called;
|
||||
}));
|
||||
});
|
||||
|
||||
describe('#addHourglass', function() {
|
||||
it('posts to /user/addHourglass', inject(function($httpBackend) {
|
||||
scope.addHourglass();
|
||||
|
||||
expect(User.addHourglass).to.have.been.called;
|
||||
}));
|
||||
});
|
||||
|
||||
describe('#addGold', function() {
|
||||
it('adds 500 gold to user');
|
||||
});
|
||||
|
||||
describe('#addMana', function() {
|
||||
it('adds 500 mana to user');
|
||||
});
|
||||
|
||||
describe('#addLevelsAndGold', function() {
|
||||
it('adds 10000 experience to user');
|
||||
|
||||
it('adds 10000 gp to user');
|
||||
|
||||
it('adds 10000 mp to user');
|
||||
});
|
||||
|
||||
describe('#addOneLevel', function() {
|
||||
it('adds one level to user');
|
||||
});
|
||||
|
||||
describe('#addBossQuestProgressUp', function() {
|
||||
it('adds 1000 progress to quest.progress.up');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,279 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Groups Controller', function() {
|
||||
var scope, ctrl, groups, user, guild, $rootScope;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(function($rootScope, $controller, Groups){
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
|
||||
scope = $rootScope.$new();
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
ctrl = $controller('GroupsCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
groups = Groups;
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMemberOfPendingQuest", function() {
|
||||
var party;
|
||||
var partyStub;
|
||||
|
||||
beforeEach(function () {
|
||||
party = specHelper.newGroup({
|
||||
_id: "unique-party-id",
|
||||
type: 'party',
|
||||
members: ['leader-id'] // Ensure we wouldn't pass automatically.
|
||||
});
|
||||
|
||||
partyStub = sandbox.stub(groups, "party", function() {
|
||||
return party;
|
||||
});
|
||||
});
|
||||
|
||||
it("returns false if group is does not have a quest", function() {
|
||||
expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok;
|
||||
});
|
||||
|
||||
it("returns false if group quest has not members", function() {
|
||||
party.quest = {
|
||||
'key': 'random-key',
|
||||
};
|
||||
expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok;
|
||||
});
|
||||
|
||||
it("returns false if group quest is active", function() {
|
||||
party.quest = {
|
||||
'key': 'random-key',
|
||||
'members': {},
|
||||
'active': true,
|
||||
};
|
||||
party.quest.members[user._id] = true;
|
||||
expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok;
|
||||
});
|
||||
|
||||
it("returns true if user is a member of a pending quest", function() {
|
||||
party.quest = {
|
||||
'key': 'random-key',
|
||||
'members': {},
|
||||
};
|
||||
party.quest.members[user._id] = true;
|
||||
expect(scope.isMemberOfPendingQuest(user._id, party)).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMemberOfGroup", function() {
|
||||
it("returns true if group is the user's party retrieved from groups service", function() {
|
||||
var party = specHelper.newGroup({
|
||||
_id: "unique-party-id",
|
||||
type: 'party',
|
||||
members: ['leader-id'] // Ensure we wouldn't pass automatically.
|
||||
});
|
||||
|
||||
var partyStub = sandbox.stub(groups, "party", function() {
|
||||
return party;
|
||||
});
|
||||
|
||||
expect(scope.isMemberOfGroup(user._id, party)).to.be.ok;
|
||||
});
|
||||
|
||||
it('returns true if guild is included in myGuilds call', function(){
|
||||
|
||||
var guild = specHelper.newGroup({
|
||||
_id: "unique-guild-id",
|
||||
type: 'guild',
|
||||
members: [user._id]
|
||||
});
|
||||
|
||||
user.guilds = [guild._id];
|
||||
|
||||
expect(scope.isMemberOfGroup(user._id, guild)).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not return true if guild is not included in myGuilds call', function(){
|
||||
|
||||
var guild = specHelper.newGroup({
|
||||
_id: "unique-guild-id",
|
||||
type: 'guild',
|
||||
members: ['not-user-id']
|
||||
});
|
||||
|
||||
user.guilds = [];
|
||||
|
||||
expect(scope.isMemberOfGroup(user._id, guild)).to.not.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAbleToEditGroup', () => {
|
||||
var guild;
|
||||
|
||||
beforeEach(() => {
|
||||
user.contributor = {};
|
||||
guild = specHelper.newGroup({
|
||||
_id: 'unique-guild-id',
|
||||
type: 'guild',
|
||||
members: ['not-user-id'],
|
||||
$save: sandbox.spy(),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns true if user is an admin', () => {
|
||||
guild.leader = 'not-user-id';
|
||||
user.contributor.admin = true;
|
||||
expect(scope.isAbleToEditGroup(guild)).to.be.ok;
|
||||
});
|
||||
|
||||
it('returns true if user is group leader', () => {
|
||||
guild.leader = {_id: user._id}
|
||||
expect(scope.isAbleToEditGroup(guild)).to.be.ok;
|
||||
});
|
||||
|
||||
it('returns false is user is not a leader or admin', () => {
|
||||
expect(scope.isAbleToEditGroup(guild)).to.not.be.ok;
|
||||
});
|
||||
|
||||
it('returns false is user is an admin but group is a party', () => {
|
||||
guild.type = 'party';
|
||||
user.contributor.admin = true;
|
||||
expect(scope.isAbleToEditGroup(guild)).to.not.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('editGroup', () => {
|
||||
var guild;
|
||||
|
||||
beforeEach(() => {
|
||||
guild = specHelper.newGroup({
|
||||
_id: 'unique-guild-id',
|
||||
leader: 'old leader',
|
||||
type: 'guild',
|
||||
members: ['not-user-id'],
|
||||
$save: sandbox.spy(),
|
||||
});
|
||||
});
|
||||
|
||||
it('marks group as being in edit mode', () => {
|
||||
scope.editGroup(guild);
|
||||
|
||||
expect(guild._editing).to.eql(true);
|
||||
});
|
||||
|
||||
it('copies group to groupCopy', () => {
|
||||
scope.editGroup(guild);
|
||||
|
||||
for (var key in scope.groupCopy) {
|
||||
expect(scope.groupCopy[key]).to.eql(guild[key]);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not change original group when groupCopy is changed', () => {
|
||||
scope.editGroup(guild);
|
||||
|
||||
scope.groupCopy.leader = 'new leader';
|
||||
expect(scope.groupCopy.leader).to.not.eql(guild.leader);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveEdit', () => {
|
||||
let guild;
|
||||
|
||||
beforeEach(() => {
|
||||
guild = specHelper.newGroup({
|
||||
_id: 'unique-guild-id',
|
||||
name: 'old name',
|
||||
leader: 'old leader',
|
||||
type: 'guild',
|
||||
members: ['not-user-id'],
|
||||
$save: () => {},
|
||||
});
|
||||
|
||||
scope.editGroup(guild);
|
||||
});
|
||||
|
||||
it('calls group update', () => {
|
||||
let guildUpdate = sandbox.spy(groups.Group, 'update');
|
||||
|
||||
scope.saveEdit(guild);
|
||||
|
||||
expect(guildUpdate).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('calls cancelEdit', () => {
|
||||
sandbox.stub(scope, 'cancelEdit');
|
||||
|
||||
scope.saveEdit(guild);
|
||||
|
||||
expect(scope.cancelEdit).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('applies changes to groupCopy to original group', () => {
|
||||
scope.groupCopy.name = 'new name';
|
||||
|
||||
scope.saveEdit(guild);
|
||||
|
||||
expect(guild.name).to.eql('new name');
|
||||
});
|
||||
|
||||
it('assigns leader id to group if leader has changed', () => {
|
||||
scope.groupCopy._newLeader = { _id: 'some leader id' };
|
||||
|
||||
scope.saveEdit(guild);
|
||||
|
||||
expect(guild.leader).to.eql('some leader id');
|
||||
});
|
||||
|
||||
it('does not assign new leader id if leader object is not passed in', () => {
|
||||
scope.groupCopy._newLeader = 'not an object';
|
||||
|
||||
scope.saveEdit(guild);
|
||||
|
||||
expect(guild.leader).to.eql('old leader');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelEdit', () => {
|
||||
beforeEach(() => {
|
||||
guild = specHelper.newGroup({
|
||||
_id: 'unique-guild-id',
|
||||
name: 'old name',
|
||||
leader: 'old leader',
|
||||
type: 'guild',
|
||||
members: ['not-user-id'],
|
||||
$save: () => {},
|
||||
});
|
||||
|
||||
scope.editGroup(guild);
|
||||
});
|
||||
|
||||
it('sets _editing to false on group', () => {
|
||||
expect(guild._editing).to.eql(true);
|
||||
|
||||
scope.cancelEdit(guild);
|
||||
|
||||
expect(guild._editing).to.eql(false);
|
||||
});
|
||||
|
||||
it('reset groupCopy to an empty object', () => {
|
||||
expect(scope.groupCopy).to.not.eql({});
|
||||
|
||||
scope.cancelEdit(guild);
|
||||
|
||||
expect(scope.groupCopy).to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
/* TODO: Modal testing */
|
||||
describe.skip("deleteAllMessages", function() { });
|
||||
describe.skip("clickMember", function() { });
|
||||
describe.skip("removeMember", function() { });
|
||||
describe.skip("confirmRemoveMember", function() { });
|
||||
describe.skip("quickReply", function() { });
|
||||
});
|
||||
@@ -1,63 +0,0 @@
|
||||
describe('Group Tasks Meta Actions Controller', () => {
|
||||
let rootScope, scope, user, userSerivce;
|
||||
|
||||
beforeEach(() => {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(($rootScope, $controller) => {
|
||||
rootScope = $rootScope;
|
||||
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
userSerivce = {user: user};
|
||||
|
||||
scope = $rootScope.$new();
|
||||
|
||||
scope.task = {
|
||||
group: {
|
||||
assignedUsers: [],
|
||||
approval: {
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
};
|
||||
scope.task._edit = angular.copy(scope.task);
|
||||
|
||||
$controller('GroupTaskActionsCtrl', {$scope: scope, User: userSerivce});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleTaskRequiresApproval', function () {
|
||||
it('toggles task approval required field from false to true', function () {
|
||||
scope.toggleTaskRequiresApproval();
|
||||
expect(scope.task._edit.group.approval.required).to.be.true;
|
||||
});
|
||||
|
||||
it('toggles task approval required field from true to false', function () {
|
||||
scope.task._edit.group.approval.required = true;
|
||||
scope.toggleTaskRequiresApproval();
|
||||
expect(scope.task._edit.group.approval.required).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('assign events', function () {
|
||||
it('adds a group member to assigned users on "addedGroupMember" event ', () => {
|
||||
var testId = 'test-id';
|
||||
rootScope.$broadcast('addedGroupMember', testId);
|
||||
expect(scope.task.group.assignedUsers).to.contain(testId);
|
||||
expect(scope.task._edit.group.assignedUsers).to.contain(testId);
|
||||
});
|
||||
|
||||
it('removes a group member to assigned users on "addedGroupMember" event ', () => {
|
||||
var testId = 'test-id';
|
||||
scope.task.group.assignedUsers.push(testId);
|
||||
scope.task._edit.group.assignedUsers.push(testId);
|
||||
rootScope.$broadcast('removedGroupMember', testId);
|
||||
expect(scope.task.group.assignedUsers).to.not.contain(testId);
|
||||
expect(scope.task._edit.group.assignedUsers).to.not.contain(testId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
describe('Group Task Actions Controller', () => {
|
||||
let scope, user, userSerivce;
|
||||
|
||||
beforeEach(() => {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(($rootScope, $controller) => {
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
userSerivce = {user: user};
|
||||
userSerivce.sync = sandbox.stub();
|
||||
|
||||
scope = $rootScope.$new();
|
||||
|
||||
$controller('GroupTaskMetaActionsCtrl', {$scope: scope, User: userSerivce});
|
||||
|
||||
scope.task = {
|
||||
group: {
|
||||
assignedUsers: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
describe('claim', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.stub(window, 'confirm').returns(true);
|
||||
});
|
||||
|
||||
it('adds user to assigned users of scope task ', () => {
|
||||
scope.claim();
|
||||
expect(scope.task.group.assignedUsers).to.contain(user._id);
|
||||
});
|
||||
|
||||
it('syncs user tasks ', () => {
|
||||
scope.claim();
|
||||
expect(userSerivce.sync).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Hall of Heroes Controller', function() {
|
||||
var scope, ctrl, user, $rootScope;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(function($rootScope, $controller){
|
||||
user = specHelper.newUser();
|
||||
|
||||
scope = $rootScope.$new();
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
ctrl = $controller('HallHeroesCtrl', {$scope: scope, User: {user: user}});
|
||||
});
|
||||
});
|
||||
|
||||
it('populates contributor input with selected hero id', function(){
|
||||
var loadHero = sandbox.spy(scope, "loadHero");
|
||||
var scrollTo = sandbox.spy(window, "scrollTo");
|
||||
|
||||
scope.populateContributorInput(user._id);
|
||||
expect(scope._heroID).to.eql(user._id);
|
||||
expect(loadHero.callCount).to.eql(1);
|
||||
expect(scrollTo.callCount).to.eql(1);
|
||||
});
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Header Controller', function() {
|
||||
var scope, ctrl, user, $location, $rootScope;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id"
|
||||
$provide.value('User', {user: user});
|
||||
});
|
||||
|
||||
inject(function(_$rootScope_, _$controller_, _$location_){
|
||||
scope = _$rootScope_.$new();
|
||||
$rootScope = _$rootScope_;
|
||||
|
||||
$location = _$location_;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
_$controller_('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
ctrl = _$controller_('HeaderCtrl', {$scope: scope, User: {user: user}});
|
||||
});
|
||||
});
|
||||
|
||||
context('inviteOrStartParty', function(){
|
||||
beforeEach(function(){
|
||||
sandbox.stub($location, 'path');
|
||||
sandbox.stub($rootScope, 'openModal');
|
||||
});
|
||||
|
||||
it('redirects to party page if user does not have a party', function(){
|
||||
var group = {};
|
||||
scope.inviteOrStartParty(group);
|
||||
|
||||
expect($location.path).to.be.calledWith("/options/groups/party");
|
||||
expect($rootScope.openModal).to.not.be.called;
|
||||
});
|
||||
|
||||
it('Opens invite-friends modal if user has a party', function(){
|
||||
var group = {
|
||||
type: 'party'
|
||||
};
|
||||
scope.inviteOrStartParty(group);
|
||||
|
||||
expect($rootScope.openModal).to.be.calledOnce;
|
||||
expect($location.path).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,73 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('inbox Controller', function() {
|
||||
var scope, ctrl, user, $rootScope, $controller;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
});
|
||||
|
||||
inject(function(_$rootScope_, _$controller_){
|
||||
user = specHelper.newUser();
|
||||
user._id = 'unique-user-id';
|
||||
$rootScope = _$rootScope_;
|
||||
|
||||
scope = _$rootScope_.$new();
|
||||
|
||||
$controller = _$controller_;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
ctrl = $controller('InboxCtrl', {$scope: scope});
|
||||
});
|
||||
});
|
||||
|
||||
describe('copyToDo', function() {
|
||||
it('when copying a user message it opens modal with information from message', function() {
|
||||
scope.group = {
|
||||
name: 'Princess Bride'
|
||||
};
|
||||
|
||||
sandbox.spy($rootScope, 'openModal');
|
||||
var message = {
|
||||
uuid: 'the-dread-pirate-roberts',
|
||||
user: 'Wesley',
|
||||
text: 'As you wish'
|
||||
};
|
||||
|
||||
scope.copyToDo(message);
|
||||
|
||||
expect($rootScope.openModal).to.be.calledOnce;
|
||||
expect($rootScope.openModal).to.be.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){
|
||||
var taskText = env.t('taskTextFromInbox', {
|
||||
from: message.user
|
||||
});
|
||||
return callArgToMatch.controller == 'CopyMessageModalCtrl'
|
||||
&& callArgToMatch.scope.text == taskText
|
||||
}));
|
||||
});
|
||||
|
||||
it('when copying a system message it opens modal with information from message', function() {
|
||||
|
||||
var modalSpy = sandbox.spy($rootScope, 'openModal');
|
||||
var message = {
|
||||
uuid: 'system',
|
||||
text: 'Wesley attacked the ROUS in the Fire Swamp'
|
||||
};
|
||||
|
||||
scope.copyToDo(message);
|
||||
|
||||
modalSpy.should.have.been.calledOnce;
|
||||
|
||||
modalSpy.should.have.been.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){
|
||||
var taskText = env.t('taskTextFromInbox', {
|
||||
from: 'system'
|
||||
});
|
||||
return callArgToMatch.controller == 'CopyMessageModalCtrl'
|
||||
&& callArgToMatch.scope.text == taskText
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,538 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Inventory Controller', function() {
|
||||
var scope, ctrl, user, rootScope, shared, achievement;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
var mockWindow = {
|
||||
confirm: function(msg) {
|
||||
return true;
|
||||
},
|
||||
location: {search: '', pathname: '', href: ''},
|
||||
};
|
||||
|
||||
$provide.value('$window', mockWindow);
|
||||
});
|
||||
|
||||
inject(function($rootScope, $controller, Shared, User, $location, $window, Achievement) {
|
||||
user = specHelper.newUser({
|
||||
balance: 4,
|
||||
items: {
|
||||
gear: { owned: {} },
|
||||
eggs: { Cactus: 1 },
|
||||
hatchingPotions: { Base: 1 },
|
||||
food: { Meat: 1 },
|
||||
pets: {},
|
||||
mounts: {}
|
||||
},
|
||||
preferences: {
|
||||
suppressModals: {}
|
||||
},
|
||||
purchased: {
|
||||
plan: {
|
||||
mysteryItems: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Shared.wrap(user);
|
||||
shared = Shared;
|
||||
achievement = Achievement;
|
||||
|
||||
scope = $rootScope.$new();
|
||||
rootScope = $rootScope;
|
||||
|
||||
User.user = user;
|
||||
User.setUser(user);
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: User});
|
||||
|
||||
ctrl = $controller('InventoryCtrl', {$scope: scope, User: User});
|
||||
});
|
||||
});
|
||||
|
||||
it('starts without any item selected', function(){
|
||||
expect(scope.selectedEgg).to.eql(null);
|
||||
expect(scope.selectedPotion).to.eql(null);
|
||||
expect(scope.selectedFood).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('chooses an egg', function(){
|
||||
scope.chooseEgg('Cactus');
|
||||
expect(scope.selectedEgg.key).to.eql('Cactus');
|
||||
});
|
||||
|
||||
it('chooses a potion', function(){
|
||||
scope.choosePotion('Base');
|
||||
expect(scope.selectedPotion.key).to.eql('Base');
|
||||
});
|
||||
|
||||
describe('Hatching Pets', function(){
|
||||
beforeEach(function() {
|
||||
sandbox.stub(rootScope, 'openModal');
|
||||
});
|
||||
|
||||
it('hatches a pet', function(){
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
expect(user.items.eggs).to.eql({Cactus: 0});
|
||||
expect(user.items.hatchingPotions).to.eql({Base: 0});
|
||||
expect(user.items.pets).to.eql({'Cactus-Base': 5});
|
||||
expect(scope.selectedEgg).to.eql(null);
|
||||
expect(scope.selectedPotion).to.eql(null);
|
||||
});
|
||||
|
||||
it('shows a modal for pet hatching', function(){
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
|
||||
expect(rootScope.openModal).to.have.been.calledOnce;
|
||||
expect(rootScope.openModal).to.have.been.calledWith('hatchPet');
|
||||
});
|
||||
|
||||
it('shows modal even if user has raised that pet to a mount', function(){
|
||||
user.items.pets['Cactus-Base'] = -1;
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
|
||||
expect(rootScope.openModal).to.have.been.calledOnce;
|
||||
expect(rootScope.openModal).to.have.been.calledWith('hatchPet');
|
||||
});
|
||||
|
||||
//@TODO: Fix Common hatch
|
||||
xit('does not show modal if user tries to hatch a pet they own', function(){
|
||||
user.items.pets['Cactus-Base'] = 5;
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
expect(rootScope.openModal).to.not.have.been.called;
|
||||
});
|
||||
|
||||
//@TODO: Fix Common hatch
|
||||
xit('does not show modal if user tries to hatch a premium quest pet', function(){
|
||||
user.items.eggs = {Snake: 1};
|
||||
user.items.hatchingPotions = {Peppermint: 1};
|
||||
scope.chooseEgg('Snake');
|
||||
scope.choosePotion('Peppermint');
|
||||
expect(rootScope.openModal).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('does not show pet hatching modal if user has opted out', function(){
|
||||
user.preferences.suppressModals.hatchPet = true;
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
|
||||
expect(rootScope.openModal).to.not.be.called;
|
||||
});
|
||||
|
||||
it('shows beastMaster achievement modal if user has all 90 pets', function(){
|
||||
sandbox.stub(achievement, 'displayAchievement');
|
||||
sandbox.stub(shared.count, "beastMasterProgress").returns(90);
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('beastMaster');
|
||||
});
|
||||
|
||||
it('shows triadBingo achievement modal if user has all pets twice and all mounts', function(){
|
||||
sandbox.stub(achievement, 'displayAchievement');
|
||||
sandbox.stub(shared.count, "mountMasterProgress").returns(90);
|
||||
sandbox.stub(shared.count, "dropPetsCurrentlyOwned").returns(90);
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.choosePotion('Base');
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('triadBingo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Feeding and Raising Pets', function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(rootScope, 'openModal');
|
||||
user.items.pets = {'PandaCub-Base':5};
|
||||
user.items.mounts = {'PandaCub-Base':false};
|
||||
});
|
||||
|
||||
it('feeds a pet', function() {
|
||||
scope.chooseFood('Meat');
|
||||
scope.choosePet('PandaCub','Base');
|
||||
|
||||
expect(user.items.pets['PandaCub-Base']).to.eql(10);
|
||||
});
|
||||
|
||||
it('gives weaker benefit when feeding inappropriate food', function() {
|
||||
user.items.food.Honey = 1;
|
||||
|
||||
scope.chooseFood('Honey');
|
||||
scope.choosePet('PandaCub','Base');
|
||||
|
||||
expect(user.items.pets['PandaCub-Base']).to.eql(7);
|
||||
});
|
||||
|
||||
it('raises pet to a mount when feeding gauge maxes out', function() {
|
||||
user.items.pets['PandaCub-Base'] = 45;
|
||||
|
||||
scope.chooseFood('Meat');
|
||||
scope.choosePet('PandaCub','Base');
|
||||
|
||||
expect(user.items.pets['PandaCub-Base']).to.eql(-1);
|
||||
expect(user.items.mounts['PandaCub-Base']).to.exist;
|
||||
});
|
||||
|
||||
it('raises pet to a mount instantly when using a Saddle', function() {
|
||||
user.items.food.Saddle = 1;
|
||||
|
||||
scope.chooseFood('Saddle');
|
||||
scope.choosePet('PandaCub','Base');
|
||||
|
||||
expect(user.items.pets['PandaCub-Base']).to.eql(-1);
|
||||
expect(user.items.mounts['PandaCub-Base']).to.exist;
|
||||
});
|
||||
|
||||
it('displays mount raising modal for drop pets', function() {
|
||||
user.items.food.Saddle = 1;
|
||||
|
||||
scope.chooseFood('Saddle');
|
||||
scope.choosePet('PandaCub','Base');
|
||||
|
||||
expect(rootScope.openModal).to.have.been.calledOnce;
|
||||
expect(rootScope.openModal).to.have.been.calledWith('raisePet');
|
||||
});
|
||||
|
||||
it('displays mount raising modal for quest pets', function() {
|
||||
user.items.food.Saddle = 1;
|
||||
user.items.pets['Snake-Base'] = 1;
|
||||
|
||||
scope.chooseFood('Saddle');
|
||||
scope.choosePet('Snake','Base');
|
||||
|
||||
expect(rootScope.openModal).to.have.been.calledOnce;
|
||||
expect(rootScope.openModal).to.have.been.calledWith('raisePet');
|
||||
});
|
||||
|
||||
it('displays mount raising modal for premium pets', function() {
|
||||
user.items.food.Saddle = 1;
|
||||
user.items.pets['TigerCub-Spooky'] = 1;
|
||||
|
||||
scope.chooseFood('Saddle');
|
||||
scope.choosePet('TigerCub','Spooky');
|
||||
|
||||
expect(rootScope.openModal).to.have.been.calledOnce;
|
||||
expect(rootScope.openModal).to.have.been.calledWith('raisePet');
|
||||
});
|
||||
|
||||
it('shows mountMaster achievement modal if user has all 90 mounts', function(){
|
||||
sandbox.stub(achievement, 'displayAchievement');
|
||||
sandbox.stub(shared.count, "mountMasterProgress").returns(90);
|
||||
scope.chooseFood('Meat');
|
||||
scope.choosePet('PandaCub','Base');
|
||||
|
||||
expect(achievement.displayAchievement).to.be.calledOnce;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('mountMaster');
|
||||
});
|
||||
});
|
||||
|
||||
it('sells an egg', function(){
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.sellInventory();
|
||||
expect(user.items.eggs).to.eql({Cactus: 0});
|
||||
expect(user.stats.gp).to.eql(3);
|
||||
});
|
||||
|
||||
it('sells a potion', function(){
|
||||
scope.choosePotion('Base');
|
||||
scope.sellInventory();
|
||||
expect(user.items.hatchingPotions).to.eql({Base: 0});
|
||||
expect(user.stats.gp).to.eql(2);
|
||||
});
|
||||
|
||||
it('sells food', function(){
|
||||
scope.chooseFood('Meat');
|
||||
scope.sellInventory();
|
||||
expect(user.items.food).to.eql({Meat: 0});
|
||||
expect(user.stats.gp).to.eql(1);
|
||||
});
|
||||
|
||||
it('chooses a pet', function(){
|
||||
user.items.pets['Cactus-Base'] = 5;
|
||||
scope.choosePet('Cactus', 'Base');
|
||||
expect(user.items.currentPet).to.eql('Cactus-Base');
|
||||
});
|
||||
|
||||
it('purchases an egg', inject(function(Content){
|
||||
scope.purchase('eggs', Content.eggs['Wolf']);
|
||||
expect(user.balance).to.eql(3.25);
|
||||
expect(user.items.eggs).to.eql({Cactus: 1, Wolf: 1})
|
||||
}));
|
||||
|
||||
describe('Deselecting Items', function() {
|
||||
it('deselects a food', function(){
|
||||
scope.chooseFood('Meat');
|
||||
scope.deselectItem();
|
||||
expect(scope.selectedFood).to.eql(null);
|
||||
});
|
||||
|
||||
it('deselects a potion', function(){
|
||||
scope.choosePotion('Base');
|
||||
scope.deselectItem();
|
||||
expect(scope.selectedPotion).to.eql(null);
|
||||
});
|
||||
|
||||
it('deselects a egg', function(){
|
||||
scope.chooseEgg('Cactus');
|
||||
scope.deselectItem();
|
||||
expect(scope.selectedEgg).to.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('openCardsModal', function(type, numberOfVariations) {
|
||||
var cardsModalScope;
|
||||
|
||||
beforeEach(function() {
|
||||
cardsModalScope = {};
|
||||
sandbox.stub(rootScope, 'openModal');
|
||||
sandbox.stub(rootScope, '$new').returns(cardsModalScope);
|
||||
});
|
||||
|
||||
it('opens cards modal', function() {
|
||||
scope.openCardsModal('valentine', 4);
|
||||
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith(
|
||||
'cards'
|
||||
);
|
||||
});
|
||||
|
||||
it('instantiates a new scope for the modal', function() {
|
||||
scope.openCardsModal('valentine', 4);
|
||||
|
||||
expect(rootScope.$new).to.be.calledOnce;
|
||||
expect(cardsModalScope.cardType).to.eql('valentine');
|
||||
expect(cardsModalScope.cardMessage).to.exist;
|
||||
});
|
||||
|
||||
it('provides a card message', function() {
|
||||
scope.openCardsModal('valentine', 1);
|
||||
|
||||
expect(cardsModalScope.cardMessage).to.eql(env.t('valentine0'));
|
||||
});
|
||||
|
||||
it('randomly generates message from x number of messages', function() {
|
||||
var possibleValues = [env.t('valentine0'), env.t('valentine1')];
|
||||
|
||||
scope.openCardsModal('valentine', 2);
|
||||
|
||||
expect(possibleValues).to.contain(cardsModalScope.cardMessage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#buyQuest', function() {
|
||||
var quests, questObject;
|
||||
|
||||
beforeEach(inject(function(Quests) {
|
||||
quests = Quests;
|
||||
questObject = { key: 'whale' };
|
||||
|
||||
sandbox.stub(quests, 'buyQuest').returns({ then: function(res) { res(questObject); } });
|
||||
}));
|
||||
|
||||
it('calls Quests.buyQuest', function() {
|
||||
scope.buyQuest('foo');
|
||||
|
||||
expect(quests.buyQuest).to.be.calledOnce;
|
||||
expect(quests.buyQuest).to.be.calledWith('foo');
|
||||
});
|
||||
|
||||
it('sets selectedQuest to resolved quest object', function() {
|
||||
scope.buyQuest('whale');
|
||||
|
||||
expect(rootScope.selectedQuest).to.eql(questObject);
|
||||
});
|
||||
|
||||
it('opens buyQuest modal', function() {
|
||||
sandbox.spy(rootScope, 'openModal');
|
||||
|
||||
scope.buyQuest('whale');
|
||||
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith('buyQuest', {controller: 'InventoryCtrl'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#showQuest', function() {
|
||||
var quests, questObject;
|
||||
|
||||
beforeEach(inject(function(Quests) {
|
||||
quests = Quests;
|
||||
questObject = { key: 'whale' };
|
||||
|
||||
sandbox.stub(quests, 'showQuest').returns({ then: function(res) { res(questObject); } });
|
||||
}));
|
||||
|
||||
it('calls Quests.showQuest', function() {
|
||||
scope.showQuest('foo');
|
||||
|
||||
expect(quests.showQuest).to.be.calledOnce;
|
||||
expect(quests.showQuest).to.be.calledWith('foo');
|
||||
});
|
||||
|
||||
it('sets selectedQuest to resolved quest object', function() {
|
||||
scope.showQuest('whale');
|
||||
|
||||
expect(rootScope.selectedQuest).to.eql(questObject);
|
||||
});
|
||||
|
||||
it('opens showQuest modal', function() {
|
||||
sandbox.spy(rootScope, 'openModal');
|
||||
|
||||
scope.showQuest('whale');
|
||||
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith('showQuest', {controller: 'InventoryCtrl'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#hasAllTimeTravelerItems', function() {
|
||||
it('returns false if items remain for purchase with Mystic Hourglasses', function() {
|
||||
expect(scope.hasAllTimeTravelerItems()).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns true if there are no items left to purchase', inject(function(Content) {
|
||||
_.forEach(Content.gear.flat, function(v,item) {
|
||||
if (item.indexOf('mystery') > -1) {
|
||||
user.items.gear.owned[item] = true;
|
||||
}
|
||||
});
|
||||
_.forEach(Content.timeTravelStable.pets, function(v,pet) {
|
||||
user.items.pets[pet] = 5;
|
||||
});
|
||||
_.forEach(Content.timeTravelStable.mounts, function(v,mount) {
|
||||
user.items.mounts[mount] = true;
|
||||
});
|
||||
|
||||
expect(scope.hasAllTimeTravelerItems()).to.eql(true);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('#hasAllTimeTravelerItemsOfType', function() {
|
||||
it('returns false for Mystery Sets if there are sets left in the time traveler store', function() {
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('mystery')).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns true for Mystery Sets if there are no sets left to purchase', inject(function(Content) {
|
||||
_.forEach(Content.gear.flat, function(v,item) {
|
||||
if (item.indexOf('mystery') > -1) {
|
||||
user.items.gear.owned[item] = true;
|
||||
}
|
||||
});
|
||||
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('mystery')).to.eql(true);
|
||||
}));
|
||||
|
||||
it('returns false for pets if user does not own all pets in the Time Travel Stable', function() {
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('pets')).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns true for pets if user owns all pets in the Time Travel Stable', inject(function(Content) {
|
||||
_.forEach(Content.timeTravelStable.pets, function(v,pet) {
|
||||
user.items.pets[pet] = 5;
|
||||
});
|
||||
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('pets')).to.eql(true);
|
||||
}));
|
||||
|
||||
it('returns false for mounts if user does not own all mounts in the Time Travel Stable', function() {
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('mounts')).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns true for mounts if user owns all mounts in the Time Travel Stable', inject(function(Content) {
|
||||
_.forEach(Content.timeTravelStable.mounts, function(v,mount) {
|
||||
user.items.mounts[mount] = true;
|
||||
});
|
||||
|
||||
expect(scope.hasAllTimeTravelerItemsOfType('mounts')).to.eql(true);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Gear search filter', function() {
|
||||
var wrap = function(text) {
|
||||
return {'text': function() {return text;}};
|
||||
}
|
||||
|
||||
var toText = function(list) {
|
||||
return _.map(list, function(ele) { return ele.text(); });
|
||||
}
|
||||
|
||||
var gearByClass, gearByType;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.$digest();
|
||||
gearByClass = {'raw': [wrap('kale'), wrap('sashimi')],
|
||||
'cooked': [wrap('chicken'), wrap('potato')]};
|
||||
|
||||
gearByType = {'veg': [wrap('kale'), wrap('potato')],
|
||||
'not': [wrap('chicken'), wrap('sashimi')]};
|
||||
scope.gearByClass = gearByClass;
|
||||
scope.gearByType = gearByType;
|
||||
scope.equipmentQuery.query = 'a';
|
||||
});
|
||||
|
||||
it('filters nothing if equipmentQuery is nothing', function() {
|
||||
scope.equipmentQuery.query = '';
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['chicken', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['chicken', 'sashimi']);
|
||||
});
|
||||
|
||||
it('filters out gear if class gear changes', function() {
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['potato']);
|
||||
|
||||
scope.gearByClass['raw'].push(wrap('zucchini'));
|
||||
scope.gearByClass['cooked'].push(wrap('pizza'));
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['potato', 'pizza']);
|
||||
});
|
||||
|
||||
it('filters out gear if typed gear changes', function() {
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['sashimi']);
|
||||
|
||||
scope.gearByType['veg'].push(wrap('zucchini'));
|
||||
scope.gearByType['not'].push(wrap('pizza'));
|
||||
|
||||
scope.$digest();
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['sashimi', 'pizza']);
|
||||
});
|
||||
|
||||
it('filters out gear if filter query changes', function() {
|
||||
scope.equipmentQuery.query = 'c';
|
||||
scope.$digest();
|
||||
|
||||
expect(toText(scope.filteredGearByClass['raw'])).to.eql([]);
|
||||
expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['chicken']);
|
||||
expect(toText(scope.filteredGearByType['veg'])).to.eql([]);
|
||||
expect(toText(scope.filteredGearByType['not'])).to.eql(['chicken']);
|
||||
});
|
||||
|
||||
it('returns the right filtered gear', function() {
|
||||
var equipment = [wrap('spicy tuna'), wrap('dragon'), wrap('rainbow'), wrap('caterpillar')];
|
||||
expect(toText(scope.equipmentSearch(equipment, 'ra'))).to.eql(['dragon', 'rainbow']);
|
||||
});
|
||||
|
||||
it('returns the right filtered gear if the source gear has unicode', function() {
|
||||
// blue hat, red hat, red shield
|
||||
var equipment = [wrap('藍色軟帽'), wrap('紅色軟帽'), wrap('紅色盾牌')];
|
||||
// searching for 'red' gives red hat, red shield
|
||||
expect(toText(scope.equipmentSearch(equipment, '紅色'))).to.eql(['紅色軟帽', '紅色盾牌']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,257 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Invite to Group Controller', function() {
|
||||
var scope, ctrl, groups, user, guild, rootScope, $controller;
|
||||
|
||||
beforeEach(function() {
|
||||
user = specHelper.newUser({
|
||||
profile: { name: 'Mario' }
|
||||
});
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
$provide.value('injectedGroup', { user: user });
|
||||
});
|
||||
|
||||
inject(function(_$rootScope_, _$controller_, Groups) {
|
||||
rootScope = _$rootScope_;
|
||||
|
||||
scope = _$rootScope_.$new();
|
||||
|
||||
$controller = _$controller_;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
ctrl = $controller('InviteToGroupCtrl', {$scope: scope, User: {user: user}});
|
||||
|
||||
groups = Groups;
|
||||
});
|
||||
});
|
||||
|
||||
describe('addEmail', function() {
|
||||
it('adds blank email to email list', function() {
|
||||
scope.emails = [{name: 'Mario', email: 'mario@mushroomkingdom.com'}];
|
||||
scope.addEmail();
|
||||
|
||||
expect(scope.emails).to.eql([{name: 'Mario', email: 'mario@mushroomkingdom.com'}, {name: '', email: ''}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addUuid', function() {
|
||||
it('adds blank uuid to invitees list', function() {
|
||||
scope.invitees = [{uuid: 'user1'}];
|
||||
scope.addUuid();
|
||||
|
||||
expect(scope.invitees).to.eql([{uuid: 'user1'}, {uuid: ''}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inviteNewUsers', function() {
|
||||
var groupInvite, groupCreate;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.group = specHelper.newGroup({
|
||||
type: 'party',
|
||||
});
|
||||
|
||||
groupCreate = sandbox.stub(groups.Group, 'create');
|
||||
groupInvite = sandbox.stub(groups.Group, 'invite');
|
||||
});
|
||||
|
||||
context('if the party does not already exist', function() {
|
||||
var groupResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
delete scope.group._id;
|
||||
groupResponse = {data: {data: scope.group}}
|
||||
});
|
||||
|
||||
it('saves the group if a new group is being created', function() {
|
||||
groupCreate.returns(Promise.resolve(groupResponse));
|
||||
scope.inviteNewUsers('uuid');
|
||||
expect(groupCreate).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('uses provided name', function() {
|
||||
scope.group.name = 'test party';
|
||||
|
||||
groupCreate.returns(Promise.resolve(groupResponse));
|
||||
|
||||
scope.inviteNewUsers('uuid');
|
||||
|
||||
expect(groupCreate).to.be.calledWith(scope.group);
|
||||
expect(scope.group.name).to.eql('test party');
|
||||
});
|
||||
|
||||
it('names the group if no name is provided', function() {
|
||||
scope.group.name = '';
|
||||
|
||||
groupCreate.returns(Promise.resolve(groupResponse));
|
||||
|
||||
scope.inviteNewUsers('uuid');
|
||||
|
||||
expect(groupCreate).to.be.calledWith(scope.group);
|
||||
expect(scope.group.name).to.eql(env.t('possessiveParty', {name: user.profile.name}));
|
||||
});
|
||||
});
|
||||
|
||||
context('email', function() {
|
||||
beforeEach(function () {
|
||||
sandbox.stub(rootScope, 'hardRedirect');
|
||||
});
|
||||
|
||||
it('invites user with emails', function(done) {
|
||||
scope.emails = [
|
||||
{name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'},
|
||||
{name: 'Mario', email: 'mario@tmk.com'}
|
||||
];
|
||||
|
||||
var inviteDetails = {
|
||||
inviter: user.profile.name,
|
||||
emails: [
|
||||
{name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'},
|
||||
{name: 'Mario', email: 'mario@tmk.com'}
|
||||
]
|
||||
};
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
expect(groupInvite).to.be.calledOnce;
|
||||
expect(groupInvite).to.be.calledWith(scope.group._id, inviteDetails);
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('email');
|
||||
});
|
||||
|
||||
it('resets email list after sending', function(done) {
|
||||
scope.emails[0].name = 'Luigi';
|
||||
scope.emails[0].email = 'mario_bro@themushroomkingdom.com';
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
//We use a timeout to test items that happen after the promise is resolved
|
||||
setTimeout(function(){
|
||||
expect(scope.emails).to.eql([{name:'', email: ''},{name:'', email: ''}]);
|
||||
done();
|
||||
}, 1000);
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('email');
|
||||
});
|
||||
|
||||
it('filters out blank email inputs', function() {
|
||||
scope.emails = [
|
||||
{name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'},
|
||||
{name: 'Toad', email: ''},
|
||||
{name: 'Mario', email: 'mario@tmk.com'}
|
||||
];
|
||||
|
||||
var inviteDetails = {
|
||||
inviter: user.profile.name,
|
||||
emails: [
|
||||
{name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'},
|
||||
{name: 'Mario', email: 'mario@tmk.com'}
|
||||
]
|
||||
};
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
expect(groupInvite).to.be.calledOnce;
|
||||
expect(groupInvite).to.be.calledWith(scope.group._id, inviteDetails);
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('email');
|
||||
});
|
||||
});
|
||||
|
||||
context('uuid', function() {
|
||||
beforeEach(function () {
|
||||
sandbox.stub(rootScope, 'hardRedirect');
|
||||
});
|
||||
|
||||
it('invites user with uuid', function(done) {
|
||||
scope.invitees = [{uuid: '1234'}];
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
expect(groupInvite).to.be.calledOnce;
|
||||
expect(groupInvite).to.be.calledWith(scope.group._id, { uuids: ['1234'] });
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('uuid');
|
||||
});
|
||||
|
||||
it('invites users with uuids', function(done) {
|
||||
scope.invitees = [{uuid: 'user1'}, {uuid: 'user2'}, {uuid: 'user3'}];
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
expect(groupInvite).to.be.calledOnce;
|
||||
expect(groupInvite).to.be.calledWith(scope.group._id, { uuids: ['user1', 'user2', 'user3'] });
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('uuid');
|
||||
});
|
||||
|
||||
it('resets invitee list after sending', function(done) {
|
||||
scope.invitees = [{uuid: 'user1'}, {uuid: 'user2'}, {uuid: 'user3'}];
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
//We use a timeout to test items that happen after the promise is resolved
|
||||
setTimeout(function(){
|
||||
expect(scope.invitees).to.eql([{uuid: ''}]);
|
||||
done();
|
||||
}, 1000);
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('uuid');
|
||||
});
|
||||
|
||||
it('removes blank fields from being sent', function() {
|
||||
scope.invitees = [{uuid: 'user1'}, {uuid: ''}, {uuid: 'user3'}];
|
||||
|
||||
groupInvite.returns(
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
expect(groupInvite).to.be.calledOnce;
|
||||
expect(groupInvite).to.be.calledWith(scope.group._id, { uuids: ['user1', 'user3'] });
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
scope.inviteNewUsers('uuid');
|
||||
});
|
||||
});
|
||||
|
||||
context('invalid invite method', function() {
|
||||
it('logs error', function() {
|
||||
sandbox.stub(console, 'log');
|
||||
|
||||
scope.inviteNewUsers();
|
||||
expect(groups.Group.invite).to.not.be.called;
|
||||
expect(console.log).to.be.calledOnce;
|
||||
expect(console.log).to.be.calledWith('Invalid invite method.');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Menu Controller', function() {
|
||||
|
||||
describe('MenuCtrl', function(){
|
||||
var scope, ctrl, user, $httpBackend, $window;
|
||||
|
||||
beforeEach(function(){
|
||||
module(function($provide) {
|
||||
$provide.value('Chat', { seenMessage: function() {} });
|
||||
});
|
||||
|
||||
inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
|
||||
ctrl = $controller('MenuCtrl', {$scope: scope, $window: $window, User: user});
|
||||
})
|
||||
});
|
||||
|
||||
describe('clearMessage', function() {
|
||||
it('is Chat.seenMessage', inject(function(Chat) {
|
||||
expect(scope.clearMessages).to.eql(Chat.markChatSeen);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,191 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Notification Controller', function() {
|
||||
var user, scope, rootScope, fakeBackend, achievement, ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
user.needsCron = false;
|
||||
|
||||
var userSync = sinon.stub().returns({
|
||||
then: function then (f) { f(); }
|
||||
});
|
||||
|
||||
let User = {
|
||||
user,
|
||||
readNotification: function noop () {},
|
||||
readNotifications: function noop () {},
|
||||
sync: userSync
|
||||
};
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('User', User);
|
||||
$provide.value('Guide', {});
|
||||
});
|
||||
|
||||
inject(function(_$rootScope_, $httpBackend, _$controller_, Achievement, Shared) {
|
||||
scope = _$rootScope_.$new();
|
||||
rootScope = _$rootScope_;
|
||||
|
||||
fakeBackend = $httpBackend;
|
||||
fakeBackend.when('GET', 'partials/main.html').respond({});
|
||||
|
||||
achievement = Achievement;
|
||||
|
||||
Shared.wrap(user);
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
_$controller_('RootCtrl', {$scope: scope, User});
|
||||
|
||||
ctrl = _$controller_('NotificationCtrl', {$scope: scope, User});
|
||||
});
|
||||
|
||||
sandbox.stub(rootScope, 'openModal');
|
||||
sandbox.stub(achievement, 'displayAchievement');
|
||||
});
|
||||
|
||||
describe('Quest Invitation modal watch', function() {
|
||||
it('opens quest invitation modal', function() {
|
||||
user.party.quest.RSVPNeeded = true;
|
||||
delete user.party.quest.completed;
|
||||
scope.$digest();
|
||||
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith('questInvitation', {controller:'PartyCtrl'});
|
||||
});
|
||||
|
||||
it('does not open quest invitation modal if RSVPNeeded is not true', function() {
|
||||
user.party.quest.RSVPNeeded = false;
|
||||
delete user.party.quest.completed;
|
||||
scope.$digest();
|
||||
|
||||
expect(rootScope.openModal).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not open quest invitation modal if quest.completed contains a quest key', function() {
|
||||
user.party.quest.RSVPNeeded = true;
|
||||
user.party.quest.completed = "hedgebeast";
|
||||
scope.$digest();
|
||||
|
||||
expect(rootScope.openModal).to.not.be.calledWith('questInvitation', {controller:'PartyCtrl'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Quest Completion modal watch', function() {
|
||||
it('opens quest completion modal', function() {
|
||||
user.party.quest.completed = "hedgebeast";
|
||||
scope.$digest();
|
||||
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith('questCompleted', {controller:'InventoryCtrl'});
|
||||
});
|
||||
|
||||
// Ensures that the completion modal opens before the invitation modal
|
||||
it('opens quest completion modal if RSVPNeeded is true', function() {
|
||||
user.party.quest.RSVPNeeded = true;
|
||||
user.party.quest.completed = "hedgebeast";
|
||||
scope.$digest();
|
||||
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith('questCompleted', {controller:'InventoryCtrl'});
|
||||
});
|
||||
|
||||
it('does not open quest completion modal if quest.completed is null', function() {
|
||||
user.party.quest.completed = null;
|
||||
scope.$digest();
|
||||
|
||||
expect(rootScope.openModal).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('User challenge won notification watch', function() {
|
||||
it('opens challenge won modal when a challenge-won notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.userNotifications.push({type: 'WON_CHALLENGE'});
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('wonChallenge');
|
||||
});
|
||||
|
||||
it('does not open challenge won modal if no new challenge-won notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.not.be.calledWith('wonChallenge');
|
||||
});
|
||||
});
|
||||
|
||||
describe('User streak achievement notification watch', function() {
|
||||
it('opens streak achievement modal when a streak-achievement notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.userNotifications.push({type: 'STREAK_ACHIEVEMENT'});
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('streak', {size: 'md'});
|
||||
});
|
||||
|
||||
it('does not open streak achievement modal if no new streak-achievement notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.not.be.calledWith('streak', {size: 'md'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('User ultimate gear set achievement notification watch', function() {
|
||||
it('opens ultimate gear set achievement modal when an ultimate-gear-achievement notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.userNotifications.push({type: 'ULTIMATE_GEAR_ACHIEVEMENT'});
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('ultimateGear', {size: 'md'});
|
||||
});
|
||||
|
||||
it('does not open ultimate gear set achievement modal if no new ultimate-gear-achievement notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.not.be.calledWith('ultimateGear', {size: 'md'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('User rebirth achievement notification watch', function() {
|
||||
it('opens rebirth achievement modal when a rebirth-achievement notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.userNotifications.push({type: 'REBIRTH_ACHIEVEMENT'});
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('rebirth');
|
||||
});
|
||||
|
||||
it('does not open rebirth achievement modal if no new rebirth-achievement notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.not.be.calledWith('rebirth');
|
||||
});
|
||||
});
|
||||
|
||||
describe('User contributor achievement notification watch', function() {
|
||||
it('opens contributor achievement modal when a new-contributor-level notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.userNotifications.push({type: 'NEW_CONTRIBUTOR_LEVEL'});
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.be.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('contributor', {size: 'md'});
|
||||
});
|
||||
|
||||
it('does not open contributor achievement modal if no new new-contributor-level notification is recieved', function() {
|
||||
rootScope.$digest();
|
||||
rootScope.$digest();
|
||||
|
||||
expect(achievement.displayAchievement).to.not.be.calledWith('contributor', {size: 'md'});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,489 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe("Party Controller", function() {
|
||||
var scope, ctrl, user, User, questsService, groups, achievement, rootScope, $controller, deferred, party;
|
||||
|
||||
beforeEach(function() {
|
||||
user = specHelper.newUser(),
|
||||
user._id = "unique-user-id";
|
||||
User = {
|
||||
user: user,
|
||||
sync: sandbox.spy(),
|
||||
set: sandbox.spy()
|
||||
};
|
||||
|
||||
party = specHelper.newGroup({
|
||||
_id: "unique-party-id",
|
||||
type: 'party',
|
||||
members: ['leader-id'] // Ensure we wouldn't pass automatically.
|
||||
});
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('User', User);
|
||||
});
|
||||
|
||||
inject(function(_$rootScope_, _$controller_, Groups, Quests, _$q_, Achievement){
|
||||
|
||||
rootScope = _$rootScope_;
|
||||
|
||||
scope = _$rootScope_.$new();
|
||||
|
||||
$controller = _$controller_;
|
||||
|
||||
groups = Groups;
|
||||
questsService = Quests;
|
||||
achievement = Achievement;
|
||||
|
||||
// Load RootCtrl to ensure shared behaviors are loaded
|
||||
$controller('RootCtrl', {$scope: scope, User: User});
|
||||
|
||||
ctrl = $controller('PartyCtrl', {$scope: scope, User: User});
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
var groupResponse;
|
||||
|
||||
function initializeControllerWithStubbedState() {
|
||||
inject(function(_$state_) {
|
||||
var state = _$state_;
|
||||
sandbox.stub(state, 'is').returns(true);
|
||||
|
||||
var syncParty = sinon.stub(groups.Group, 'syncParty')
|
||||
syncParty.returns(Promise.resolve(groupResponse));
|
||||
|
||||
var froceSyncParty = sinon.stub(groups, 'party')
|
||||
froceSyncParty.returns(Promise.resolve(groupResponse));
|
||||
|
||||
$controller('PartyCtrl', { $scope: scope, $state: state, User: User });
|
||||
expect(state.is).to.be.calledOnce;
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(achievement, 'displayAchievement');
|
||||
});
|
||||
|
||||
context('party has 1 member', function() {
|
||||
it('awards no new achievements', function() {
|
||||
groupResponse = {_id: "test", type: "party", memberCount: 1};
|
||||
|
||||
initializeControllerWithStubbedState();
|
||||
|
||||
expect(User.set).to.not.be.called;
|
||||
expect(achievement.displayAchievement).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
context('party has 2 members', function() {
|
||||
context('user does not have "Party Up" achievement', function() {
|
||||
it('awards "Party Up" achievement', function(done) {
|
||||
groupResponse = {_id: "test", type: "party", memberCount: 2};
|
||||
|
||||
initializeControllerWithStubbedState();
|
||||
|
||||
setTimeout(function() {
|
||||
expect(User.set).to.be.calledOnce;
|
||||
expect(User.set).to.be.calledWith(
|
||||
{ 'achievements.partyUp': true }
|
||||
);
|
||||
expect(achievement.displayAchievement).to.be.calledOnce;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('partyUp');
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('party has 4 members', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
groupResponse = {_id: "test", type: "party", memberCount: 4};
|
||||
});
|
||||
|
||||
context('user has "Party Up" but not "Party On" achievement', function() {
|
||||
it('awards "Party On" achievement', function(done) {
|
||||
user.achievements.partyUp = true;
|
||||
|
||||
initializeControllerWithStubbedState();
|
||||
|
||||
setTimeout(function(){
|
||||
expect(User.set).to.be.calledOnce;
|
||||
expect(User.set).to.be.calledWith(
|
||||
{ 'achievements.partyOn': true }
|
||||
);
|
||||
expect(achievement.displayAchievement).to.be.calledOnce;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('partyOn');
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
context('user has neither "Party Up" nor "Party On" achievements', function() {
|
||||
it('awards "Party Up" and "Party On" achievements', function(done) {
|
||||
initializeControllerWithStubbedState();
|
||||
|
||||
setTimeout(function(){
|
||||
expect(User.set).to.have.been.called;
|
||||
expect(User.set).to.be.calledWith(
|
||||
{ 'achievements.partyUp': true}
|
||||
);
|
||||
expect(User.set).to.be.calledWith(
|
||||
{ 'achievements.partyOn': true}
|
||||
);
|
||||
expect(achievement.displayAchievement).to.have.been.called;
|
||||
expect(achievement.displayAchievement).to.be.calledWith('partyUp');
|
||||
expect(achievement.displayAchievement).to.be.calledWith('partyOn');
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
context('user has both "Party Up" and "Party On" achievements', function() {
|
||||
it('awards no new achievements', function() {
|
||||
user.achievements.partyUp = true;
|
||||
user.achievements.partyOn = true;
|
||||
|
||||
initializeControllerWithStubbedState();
|
||||
|
||||
expect(User.set).to.not.be.called;
|
||||
expect(achievement.displayAchievement).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("create", function() {
|
||||
var partyStub;
|
||||
|
||||
beforeEach(function () {
|
||||
partyStub = sinon.stub(groups.Group, "create");
|
||||
partyStub.returns(Promise.resolve(party));
|
||||
sinon.stub(rootScope, 'hardRedirect');
|
||||
});
|
||||
|
||||
it("creates a new party", function() {
|
||||
var group = {
|
||||
type: 'party',
|
||||
};
|
||||
scope.create(group);
|
||||
expect(partyStub).to.be.calledOnce;
|
||||
//@TODO: Check user party console.log(User.user.party.id)
|
||||
});
|
||||
});
|
||||
|
||||
describe('questAccept', function() {
|
||||
var sendAction;
|
||||
var memberResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.group = {
|
||||
quest: { members: { 'user-id': true } }
|
||||
};
|
||||
|
||||
memberResponse = {members: {another: true}};
|
||||
sinon.stub(questsService, 'sendAction')
|
||||
questsService.sendAction.returns(Promise.resolve(memberResponse));
|
||||
});
|
||||
|
||||
it('calls Quests.sendAction', function() {
|
||||
scope.questAccept();
|
||||
|
||||
expect(questsService.sendAction).to.be.calledOnce;
|
||||
expect(questsService.sendAction).to.be.calledWith('quests/accept');
|
||||
});
|
||||
|
||||
|
||||
it('updates quest object with new participants list', function(done) {
|
||||
scope.group.quest = {
|
||||
members: { user: true, another: true }
|
||||
};
|
||||
|
||||
setTimeout(function(){
|
||||
expect(scope.group.quest).to.eql(memberResponse);
|
||||
done();
|
||||
}, 1000);
|
||||
|
||||
scope.questAccept();
|
||||
});
|
||||
});
|
||||
|
||||
describe('questReject', function() {
|
||||
var memberResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.group = {
|
||||
quest: { members: { 'user-id': true } }
|
||||
};
|
||||
|
||||
memberResponse = {members: {another: true}};
|
||||
var sendAction = sinon.stub(questsService, 'sendAction')
|
||||
sendAction.returns(Promise.resolve(memberResponse));
|
||||
});
|
||||
|
||||
it('calls Quests.sendAction', function() {
|
||||
scope.questReject();
|
||||
|
||||
expect(questsService.sendAction).to.be.calledOnce;
|
||||
expect(questsService.sendAction).to.be.calledWith('quests/reject');
|
||||
});
|
||||
|
||||
|
||||
it('updates quest object with new participants list', function(done) {
|
||||
scope.group.quest = {
|
||||
members: { user: true, another: true }
|
||||
};
|
||||
|
||||
setTimeout(function(){
|
||||
expect(scope.group.quest).to.eql(memberResponse);
|
||||
done();
|
||||
}, 1000);
|
||||
|
||||
scope.questReject();
|
||||
});
|
||||
});
|
||||
|
||||
describe('questCancel', function() {
|
||||
var party, cancelSpy, windowSpy, memberResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.group = {
|
||||
quest: { members: { 'user-id': true } }
|
||||
};
|
||||
|
||||
memberResponse = {members: {another: true}};
|
||||
sinon.stub(questsService, 'sendAction')
|
||||
questsService.sendAction.returns(Promise.resolve(memberResponse));
|
||||
});
|
||||
|
||||
it('calls Quests.sendAction when alert box is confirmed', function() {
|
||||
sandbox.stub(window, "confirm").returns(true);
|
||||
|
||||
scope.questCancel();
|
||||
|
||||
expect(window.confirm).to.be.calledOnce;
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('sureCancel'));
|
||||
expect(questsService.sendAction).to.be.calledOnce;
|
||||
expect(questsService.sendAction).to.be.calledWith('quests/cancel');
|
||||
});
|
||||
|
||||
it('does not call Quests.sendAction when alert box is not confirmed', function() {
|
||||
sandbox.stub(window, "confirm").returns(false);
|
||||
|
||||
scope.questCancel();
|
||||
|
||||
expect(window.confirm).to.be.calledOnce;
|
||||
expect(questsService.sendAction).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('questAbort', function() {
|
||||
var memberResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.group = {
|
||||
quest: { members: { 'user-id': true } }
|
||||
};
|
||||
|
||||
memberResponse = {members: {another: true}};
|
||||
sinon.stub(questsService, 'sendAction')
|
||||
questsService.sendAction.returns(Promise.resolve(memberResponse));
|
||||
});
|
||||
|
||||
it('calls Quests.sendAction when two alert boxes are confirmed', function() {
|
||||
sandbox.stub(window, "confirm", function(){return true});
|
||||
|
||||
scope.questAbort();
|
||||
expect(window.confirm).to.be.calledTwice;
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('sureAbort'));
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('doubleSureAbort'));
|
||||
|
||||
expect(questsService.sendAction).to.be.calledOnce;
|
||||
expect(questsService.sendAction).to.be.calledWith('quests/abort');
|
||||
});
|
||||
|
||||
it('does not call Quests.sendAction when first alert box is not confirmed', function() {
|
||||
sandbox.stub(window, "confirm", function(){return false});
|
||||
|
||||
scope.questAbort();
|
||||
|
||||
expect(window.confirm).to.be.calledOnce;
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('sureAbort'));
|
||||
expect(window.confirm).to.not.be.calledWith(window.env.t('doubleSureAbort'));
|
||||
|
||||
expect(questsService.sendAction).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not call Quests.sendAction when first alert box is confirmed but second one is not', function() {
|
||||
// Hack to confirm first window, but not second
|
||||
// Should not be necessary when we upgrade sinon
|
||||
var shouldReturn = false;
|
||||
sandbox.stub(window, 'confirm', function(){
|
||||
shouldReturn = !shouldReturn;
|
||||
return shouldReturn;
|
||||
});
|
||||
|
||||
scope.questAbort();
|
||||
|
||||
expect(window.confirm).to.be.calledTwice;
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('sureAbort'));
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('doubleSureAbort'));
|
||||
expect(questsService.sendAction).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#questLeave', function() {
|
||||
var memberResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.group = {
|
||||
quest: { members: { 'user-id': true } }
|
||||
};
|
||||
|
||||
memberResponse = {members: {another: true}};
|
||||
sinon.stub(questsService, 'sendAction')
|
||||
questsService.sendAction.returns(Promise.resolve(memberResponse));
|
||||
});
|
||||
|
||||
it('calls Quests.sendAction when alert box is confirmed', function() {
|
||||
sandbox.stub(window, "confirm").returns(true);
|
||||
|
||||
scope.questLeave();
|
||||
|
||||
expect(window.confirm).to.be.calledOnce;
|
||||
expect(window.confirm).to.be.calledWith(window.env.t('sureLeave'));
|
||||
expect(questsService.sendAction).to.be.calledOnce;
|
||||
expect(questsService.sendAction).to.be.calledWith('quests/leave');
|
||||
});
|
||||
|
||||
it('does not call Quests.sendAction when alert box is not confirmed', function() {
|
||||
sandbox.stub(window, "confirm").returns(false);
|
||||
|
||||
scope.questLeave();
|
||||
|
||||
expect(window.confirm).to.be.calledOnce;
|
||||
questsService.sendAction.should.not.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('updates quest object with new participants list', function(done) {
|
||||
scope.group.quest = {
|
||||
members: { user: true, another: true }
|
||||
};
|
||||
sandbox.stub(window, "confirm").returns(true);
|
||||
|
||||
setTimeout(function(){
|
||||
expect(scope.group.quest).to.eql(memberResponse);
|
||||
done();
|
||||
}, 1000);
|
||||
|
||||
scope.questLeave();
|
||||
});
|
||||
});
|
||||
|
||||
describe('clickStartQuest', function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(rootScope, 'openModal');
|
||||
sandbox.stub(rootScope.$state, 'go');
|
||||
});
|
||||
|
||||
it('opens quest modal if user has a quest', function() {
|
||||
user.items.quests = {
|
||||
whale: 1
|
||||
};
|
||||
|
||||
scope.clickStartQuest();
|
||||
|
||||
expect(rootScope.$state.go).to.not.be.called;
|
||||
expect(rootScope.openModal).to.be.calledOnce;
|
||||
expect(rootScope.openModal).to.be.calledWith(
|
||||
'ownedQuests',
|
||||
{ controller: 'InventoryCtrl' }
|
||||
);
|
||||
});
|
||||
|
||||
it('does not open modal if user has no quests', function() {
|
||||
user.items.quests = { };
|
||||
|
||||
scope.clickStartQuest();
|
||||
|
||||
expect(rootScope.openModal).to.not.be.called;
|
||||
expect(rootScope.$state.go).to.be.calledOnce;
|
||||
expect(rootScope.$state.go).to.be.calledWith('options.inventory.quests');
|
||||
});
|
||||
|
||||
it('does not open modal if user had quests previously, but does not now', function() {
|
||||
user.items.quests = {
|
||||
whale: 0,
|
||||
atom1: 0
|
||||
};
|
||||
|
||||
scope.clickStartQuest();
|
||||
|
||||
expect(rootScope.openModal).to.not.be.called;
|
||||
expect(rootScope.$state.go).to.be.calledOnce;
|
||||
expect(rootScope.$state.go).to.be.calledWith('options.inventory.quests');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#leaveOldPartyAndJoinNewParty', function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(scope, 'join');
|
||||
groups.data.party = { _id: 'old-party' };
|
||||
var groupLeave = sandbox.stub(groups.Group, 'leave');
|
||||
groupLeave.returns(Promise.resolve({}));
|
||||
sandbox.stub(groups, 'party').returns({
|
||||
_id: 'old-party'
|
||||
});
|
||||
sandbox.stub(window, 'confirm').returns(true);
|
||||
});
|
||||
|
||||
it('does nothing if user declines confirmation', function() {
|
||||
window.confirm.returns(false);
|
||||
scope.leaveOldPartyAndJoinNewParty('some-id', 'some-name');
|
||||
|
||||
expect(groups.Group.leave).to.not.be.called;
|
||||
})
|
||||
|
||||
it('leaves user\'s current party', function() {
|
||||
scope.leaveOldPartyAndJoinNewParty('some-id', 'some-name');
|
||||
|
||||
expect(groups.Group.leave).to.be.calledOnce;
|
||||
expect(groups.Group.leave).to.be.calledWith('old-party', false);
|
||||
});
|
||||
|
||||
it('joins the new party', function(done) {
|
||||
scope.leaveOldPartyAndJoinNewParty('some-id', 'some-name');
|
||||
|
||||
setTimeout(function() {
|
||||
expect(scope.join).to.be.calledOnce;
|
||||
expect(scope.join).to.be.calledWith({id: 'some-id', name: 'some-name'});
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#canEditQuest', function() {
|
||||
var party;
|
||||
|
||||
beforeEach(function() {
|
||||
party = specHelper.newGroup({
|
||||
type: 'party',
|
||||
leader: {},
|
||||
quest: {}
|
||||
});
|
||||
scope.group = party;
|
||||
});
|
||||
|
||||
it('returns false if user is not the quest leader', function() {
|
||||
party.quest.leader = 'another-user';
|
||||
|
||||
expect(scope.canEditQuest(party)).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns true if user is quest leader', function() {
|
||||
party.quest.leader = 'unique-user-id';
|
||||
|
||||
expect(scope.canEditQuest(party)).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,187 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('Root Controller', function() {
|
||||
var scope, rootscope, user, User, notification, ctrl, $httpBackend;
|
||||
|
||||
beforeEach(function () {
|
||||
module(function($provide) {
|
||||
$provide.value('User', {});
|
||||
$provide.service('$templateCache', function () {
|
||||
return {
|
||||
get: function () {},
|
||||
put: function () {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($rootScope, $controller, _$httpBackend_, Notification) {
|
||||
scope = $rootScope.$new();
|
||||
scope.loginUsername = 'user';
|
||||
scope.loginPassword = 'pass';
|
||||
|
||||
rootscope = $rootScope;
|
||||
|
||||
$httpBackend = _$httpBackend_;
|
||||
|
||||
notification = Notification;
|
||||
sandbox.stub(notification, 'text');
|
||||
sandbox.stub(notification, 'markdown');
|
||||
|
||||
user = specHelper.newUser();
|
||||
User = {user: user};
|
||||
User.save = sandbox.spy();
|
||||
User.sync = sandbox.spy();
|
||||
|
||||
$httpBackend.whenGET(/partials/).respond();
|
||||
|
||||
ctrl = $controller('RootCtrl', {$scope: scope, User: User});
|
||||
});
|
||||
});
|
||||
|
||||
describe('contribText', function(){
|
||||
it('shows contributor level text', function(){
|
||||
expect(scope.contribText()).to.eql(undefined);
|
||||
expect(scope.contribText(null, {npc: 'NPC'})).to.eql('NPC');
|
||||
expect(scope.contribText({level: 0, text: 'Blacksmith'})).to.eql(undefined);
|
||||
expect(scope.contribText({level: 1, text: 'Blacksmith'})).to.eql('Friend Blacksmith');
|
||||
expect(scope.contribText({level: 2, text: 'Blacksmith'})).to.eql('Friend Blacksmith');
|
||||
expect(scope.contribText({level: 3, text: 'Blacksmith'})).to.eql('Elite Blacksmith');
|
||||
expect(scope.contribText({level: 4, text: 'Blacksmith'})).to.eql('Elite Blacksmith');
|
||||
expect(scope.contribText({level: 5, text: 'Blacksmith'})).to.eql('Champion Blacksmith');
|
||||
expect(scope.contribText({level: 6, text: 'Blacksmith'})).to.eql('Champion Blacksmith');
|
||||
expect(scope.contribText({level: 7, text: 'Blacksmith'})).to.eql('Legendary Blacksmith');
|
||||
expect(scope.contribText({level: 8, text: 'Blacksmith'})).to.eql('Guardian Blacksmith');
|
||||
expect(scope.contribText({level: 9, text: 'Blacksmith'})).to.eql('Heroic Blacksmith');
|
||||
expect(scope.contribText({level: 9, text: 'Blacksmith'}, {npc: 'NPC'})).to.eql('NPC');
|
||||
});
|
||||
});
|
||||
|
||||
describe('castEnd', function(){
|
||||
var task_target, type;
|
||||
|
||||
beforeEach(function(){
|
||||
task_target = {
|
||||
id: 'task-id',
|
||||
text: 'task'
|
||||
};
|
||||
type = 'task';
|
||||
scope.spell = {
|
||||
target: 'task',
|
||||
key: 'fireball',
|
||||
mana: 10,
|
||||
text: function() { return env.t('spellWizardFireballText') },
|
||||
cast: function(){}
|
||||
};
|
||||
rootscope.applyingAction = true;
|
||||
});
|
||||
|
||||
context('fails', function(){
|
||||
it('exits early if there is no applying action', function(){
|
||||
rootscope.applyingAction = null;
|
||||
expect(scope.castEnd(task_target, type)).to.be.eql('No applying action');
|
||||
});
|
||||
|
||||
it('sends notification if target is invalid', function(){
|
||||
scope.spell.target = 'not_the_same_target';
|
||||
|
||||
scope.castEnd(task_target, type);
|
||||
|
||||
notification.text.should.have.been.calledWith(window.env.t('invalidTarget'));
|
||||
});
|
||||
});
|
||||
|
||||
context('succeeds', function(){
|
||||
it('sets scope.spell and rootScope.applyingAction to falsy values', function(){
|
||||
|
||||
scope.castEnd(task_target, type);
|
||||
|
||||
expect(rootscope.applyingAction).to.eql(false);
|
||||
expect(scope.spell).to.eql(null);
|
||||
});
|
||||
|
||||
it('calls $scope.spell.cast', function(){
|
||||
// Kind of a hack, would prefer to use sinon.spy,
|
||||
// but scope.spell gets turned to null in scope.castEnd
|
||||
var spellWasCast = false;
|
||||
scope.spell.cast = function(){ spellWasCast = true };
|
||||
|
||||
scope.castEnd(task_target, type);
|
||||
|
||||
expect(spellWasCast).to.eql(true);
|
||||
});
|
||||
|
||||
it('calls cast endpoint', function() {
|
||||
$httpBackend.expectPOST(/cast/).respond(201);
|
||||
scope.castEnd(task_target, type);
|
||||
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('sends notification that spell was cast on task', function() {
|
||||
$httpBackend.expectPOST(/cast/).respond(201);
|
||||
scope.castEnd(task_target, type);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(notification.markdown).to.be.calledOnce;
|
||||
expect(notification.markdown).to.be.calledWith('You cast Burst of Flames on task.');
|
||||
expect(User.sync).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('sends notification that spell was cast on user', function() {
|
||||
var user_target = {
|
||||
profile: { name: 'Lefnire' }
|
||||
};
|
||||
scope.spell = {
|
||||
target: 'user',
|
||||
key: 'snowball',
|
||||
mana: 0,
|
||||
text: function() { return env.t('spellSpecialSnowballAuraText') },
|
||||
cast: function(){}
|
||||
};
|
||||
$httpBackend.expectPOST(/cast/).respond(201);
|
||||
scope.castEnd(user_target, 'user');
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(notification.markdown).to.be.calledOnce;
|
||||
expect(notification.markdown).to.be.calledWith('You cast Snowball on Lefnire.');
|
||||
expect(User.sync).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('sends notification that spell was cast on party', function() {
|
||||
var party_target = {};
|
||||
scope.spell = {
|
||||
target: 'party',
|
||||
key: 'healAll',
|
||||
mana: 25,
|
||||
text: function() { return env.t('spellHealerHealAllText') },
|
||||
cast: function(){}
|
||||
};
|
||||
$httpBackend.expectPOST(/cast/).respond(201);
|
||||
scope.castEnd(party_target, 'party');
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(notification.markdown).to.be.calledOnce;
|
||||
expect(notification.markdown).to.be.calledWith('You cast Blessing for the party.');
|
||||
expect(User.sync).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('sends notification that spell was cast on self', function() {
|
||||
var self_target = {};
|
||||
scope.spell = {
|
||||
target: 'self',
|
||||
key: 'stealth',
|
||||
mana: 45,
|
||||
text: function() { return env.t('spellRogueStealthText') },
|
||||
cast: function(){}
|
||||
};
|
||||
$httpBackend.expectPOST(/cast/).respond(201);
|
||||
scope.castEnd(self_target, 'self');
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(notification.markdown).to.be.calledOnce;
|
||||
expect(notification.markdown).to.be.calledWith('You cast Stealth.');
|
||||
expect(User.sync).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user