mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 14:17:22 +01:00
Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d52d759733 | ||
|
|
5e5a755022 | ||
|
|
8d148b4d69 | ||
|
|
248b64a43f | ||
|
|
57ed0f0a10 | ||
|
|
3455adaef5 | ||
|
|
4d0295a60d | ||
|
|
a002bc5e20 | ||
|
|
b9d1086e24 | ||
|
|
412a0ecc8c | ||
|
|
d44c9ea853 | ||
|
|
c44c581265 | ||
|
|
f666f3cd04 | ||
|
|
236bd6cec4 | ||
|
|
e124c36274 | ||
|
|
d5511a0047 | ||
|
|
87f003f392 | ||
|
|
930a869365 | ||
|
|
809da8add0 | ||
|
|
1dad176320 | ||
|
|
1596c6218f | ||
|
|
0d3aba950a | ||
|
|
f1110e0f89 | ||
|
|
7273f8f6d9 | ||
|
|
a0ae200a54 | ||
|
|
ca448f081d | ||
|
|
cd27afa9f0 | ||
|
|
8bc8183895 | ||
|
|
c26c52f1fe | ||
|
|
8d5becc9ce | ||
|
|
cf7f6e2a67 | ||
|
|
acad3b8873 | ||
|
|
04f4eb8490 | ||
|
|
8c8af83dfc | ||
|
|
86d65956d9 | ||
|
|
42c5e6c22b | ||
|
|
79b51a40ce | ||
|
|
79c3efaf9c | ||
|
|
74c6a891fc | ||
|
|
9a5d17f538 | ||
|
|
070c4a8fbd | ||
|
|
2bbc4f4f4d | ||
|
|
39c00ea433 | ||
|
|
dd6c1c764a | ||
|
|
9b456d1760 | ||
|
|
acf1031317 | ||
|
|
5d45204d8b | ||
|
|
37a71924fe | ||
|
|
9cf2408988 | ||
|
|
638525f8d8 | ||
|
|
2c37ba3cee | ||
|
|
ad5b2fe540 | ||
|
|
bfb6daad51 | ||
|
|
281b8e2b7c | ||
|
|
3f88ea2378 | ||
|
|
9c6275d4ab | ||
|
|
fd3c8ddc8b | ||
|
|
72f47ad4e6 | ||
|
|
74c9a1b02d | ||
|
|
ffa561473c | ||
|
|
080ffae4e1 | ||
|
|
e395182c95 | ||
|
|
68f4275c44 | ||
|
|
4bf4c3a6c2 | ||
|
|
f00bb29192 | ||
|
|
016447ec77 | ||
|
|
fa024e071b | ||
|
|
28fec237fe | ||
|
|
e4bb82768c | ||
|
|
65eca22407 | ||
|
|
cea1597ee1 | ||
|
|
3906952154 | ||
|
|
6169b9d0ae | ||
|
|
69cac7e504 | ||
|
|
febf3f0024 | ||
|
|
563f40e4b7 | ||
|
|
e2b06161e1 | ||
|
|
e7de8b8e2f | ||
|
|
a0624d9507 | ||
|
|
cddd0df4f2 | ||
|
|
220bfb3517 | ||
|
|
2106a5ebd3 | ||
|
|
bbffa9830b | ||
|
|
caa546eb62 | ||
|
|
4e83059652 | ||
|
|
903cdb36ef | ||
|
|
d8128cc3db | ||
|
|
f888e80b01 | ||
|
|
fd7aedbff2 | ||
|
|
6c5234313d | ||
|
|
08aa5758b4 | ||
|
|
1415e344c0 | ||
|
|
2ce7915f06 | ||
|
|
838c8b4e08 | ||
|
|
1590d955cd | ||
|
|
2690caed35 | ||
|
|
dc2d4fa10b | ||
|
|
1540ec89ee | ||
|
|
f304d4fe52 | ||
|
|
023e433a5c | ||
|
|
ef4aeb29ab | ||
|
|
2b80931202 | ||
|
|
2950713712 | ||
|
|
118f3bd1bb | ||
|
|
69f1343ea8 | ||
|
|
918ee02d64 | ||
|
|
0cac34dd26 | ||
|
|
1c859fc91f | ||
|
|
857aa5827b | ||
|
|
28e8ec2d2c | ||
|
|
856f13d213 | ||
|
|
121fd38bd1 | ||
|
|
36d72f5f7a | ||
|
|
f1b8bd80e7 | ||
|
|
84d2ce6a3f | ||
|
|
76010e6c8f | ||
|
|
c707b6c99b | ||
|
|
e4bd466cc7 | ||
|
|
001b8eb894 | ||
|
|
9abcfe8614 | ||
|
|
bc6102551d | ||
|
|
959a3ff85b | ||
|
|
518b874f64 | ||
|
|
6cc359a935 | ||
|
|
514d35c0be | ||
|
|
13da92ea68 | ||
|
|
03c4d82b7d | ||
|
|
d905ab7f86 | ||
|
|
c6560b6b1b | ||
|
|
c61f660255 | ||
|
|
2f1b683ec9 | ||
|
|
47bb217068 | ||
|
|
f49fd05da1 | ||
|
|
b0341aa06f | ||
|
|
b07ec18e33 | ||
|
|
12930a2bac | ||
|
|
91f5c47d9d | ||
|
|
fe7850d10f | ||
|
|
c5c2da75bf | ||
|
|
969607cd3b | ||
|
|
2a1f52a359 | ||
|
|
47d9594679 | ||
|
|
97e40c81f3 | ||
|
|
c8b61a2f7d | ||
|
|
e9543f0d28 | ||
|
|
77b88490e4 | ||
|
|
7fc2500bfd | ||
|
|
fb229acb58 | ||
|
|
6ce83d1fa4 | ||
|
|
2be4815aea | ||
|
|
1dbc42f48a | ||
|
|
89279c8aed | ||
|
|
faedb13598 | ||
|
|
c0c74659c5 | ||
|
|
bf5ad2db1f | ||
|
|
7d99873960 | ||
|
|
e02ef00397 | ||
|
|
23c5c4211c | ||
|
|
69cc134fff | ||
|
|
ffd9400cb7 | ||
|
|
5be91ef842 | ||
|
|
3123183e46 | ||
|
|
49cca7a601 | ||
|
|
7fbd38d18c | ||
|
|
1f95376d39 | ||
|
|
2da0a1e88c | ||
|
|
afacd3e1cf |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
website
|
||||||
@@ -21,7 +21,3 @@ test/content/**/*
|
|||||||
Gruntfile.js
|
Gruntfile.js
|
||||||
gulpfile.js
|
gulpfile.js
|
||||||
gulp
|
gulp
|
||||||
webpack
|
|
||||||
test/client/e2e
|
|
||||||
test/client/unit/index.js
|
|
||||||
test/client/unit/karma.conf.js
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"node": true,
|
"node": true,
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"habitrpg/esnext",
|
"habitrpg",
|
||||||
"habitrpg"
|
"habitrpg/esnext"
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
11
.travis.yml
11
.travis.yml
@@ -1,7 +1,15 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- '6'
|
- '6'
|
||||||
|
sudo: required
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-4.8
|
||||||
before_install:
|
before_install:
|
||||||
|
- $CXX --version
|
||||||
- npm install -g npm@4
|
- npm install -g npm@4
|
||||||
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
|
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
|
||||||
before_script:
|
before_script:
|
||||||
@@ -12,6 +20,9 @@ after_script:
|
|||||||
- ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js
|
- ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js
|
||||||
script: npm run $TEST
|
script: npm run $TEST
|
||||||
env:
|
env:
|
||||||
|
global:
|
||||||
|
- CXX=g++-4.8
|
||||||
|
- DISABLE_REQUEST_LOGGING=true
|
||||||
matrix:
|
matrix:
|
||||||
- TEST="lint"
|
- TEST="lint"
|
||||||
- TEST="test:api-v3" REQUIRES_SERVER=true
|
- TEST="test:api-v3" REQUIRES_SERVER=true
|
||||||
|
|||||||
@@ -20,17 +20,19 @@ RUN apt-get install -y \
|
|||||||
RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||||
RUN apt-get install -y nodejs
|
RUN apt-get install -y nodejs
|
||||||
|
|
||||||
|
# Install npm@latest
|
||||||
|
RUN curl -sL https://www.npmjs.org/install.sh | sh
|
||||||
|
|
||||||
# Clean up package management
|
# Clean up package management
|
||||||
RUN apt-get clean
|
RUN apt-get clean
|
||||||
RUN rm -rf /var/lib/apt/lists/*
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install global packages
|
# Install global packages
|
||||||
RUN npm install -g npm@4
|
RUN npm install -g gulp grunt-cli bower mocha
|
||||||
RUN npm install -g gulp grunt-cli bower
|
|
||||||
|
|
||||||
# Clone Habitica repo and install dependencies
|
# Clone Habitica repo and install dependencies
|
||||||
WORKDIR /habitrpg
|
WORKDIR /habitrpg
|
||||||
RUN git clone https://github.com/HabitRPG/habitrpg.git /habitrpg
|
RUN git clone https://github.com/HabitRPG/habitica.git /habitrpg
|
||||||
RUN npm install
|
RUN npm install
|
||||||
RUN bower install --allow-root
|
RUN bower install --allow-root
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/r/HabitRPG/habitrpg?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/github/HabitRPG/habitica?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||||
===============
|
===============
|
||||||
|
|
||||||
[Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
[Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
||||||
|
|||||||
@@ -2,13 +2,18 @@
|
|||||||
"PORT":3000,
|
"PORT":3000,
|
||||||
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
||||||
"IP":"0.0.0.0",
|
"IP":"0.0.0.0",
|
||||||
"CORES":1,
|
"WEB_CONCURRENCY":1,
|
||||||
"BASE_URL":"http://localhost:3000",
|
"BASE_URL":"http://localhost:3000",
|
||||||
"FACEBOOK_ANALYTICS":"1234567890123456",
|
|
||||||
"FACEBOOK_KEY":"123456789012345",
|
"FACEBOOK_KEY":"123456789012345",
|
||||||
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||||
"GOOGLE_CLIENT_ID":"123456789012345",
|
"GOOGLE_CLIENT_ID":"123456789012345",
|
||||||
"GOOGLE_CLIENT_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
"GOOGLE_CLIENT_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"PLAY_API": {
|
||||||
|
"CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"ACCESS_TOKEN":"aaaabbbbccccddddeeeeffff00001111",
|
||||||
|
"REFRESH_TOKEN":"aaaabbbbccccddddeeeeffff00001111"
|
||||||
|
},
|
||||||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
||||||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
||||||
"NODE_ENV":"development",
|
"NODE_ENV":"development",
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ gulp.task('transifex:malformedStrings', () => {
|
|||||||
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
|
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||||
stringsWithMalformedInterpolations.push(malformedString);
|
stringsWithMalformedInterpolations.push(malformedString);
|
||||||
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
||||||
let missingInterploationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterploationString);
|
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
var migrationName = '20161201_takeThis.js';
|
var migrationName = '20161230_nye_hats.js';
|
||||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Award Take This ladder items to participants in this month's challenge
|
* Yearly New Year's party hat award
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var mongo = require('mongoskin');
|
var mongo = require('mongoskin');
|
||||||
@@ -15,12 +15,12 @@ var dbUsers = mongo.db(connectionString).collection('users');
|
|||||||
// specify a query to limit the affected users (empty for all users):
|
// specify a query to limit the affected users (empty for all users):
|
||||||
var query = {
|
var query = {
|
||||||
'migration':{$ne:migrationName},
|
'migration':{$ne:migrationName},
|
||||||
'challenges':{$in:['ff674aba-a114-4a6f-8ebc-1de27ffb646e']}
|
'auth.timestamps.loggedin':{$gt:new Date('2016-11-30')} // Remove after first run
|
||||||
};
|
};
|
||||||
|
|
||||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
var fields = {
|
var fields = {
|
||||||
'items.gear.owned': 1
|
'items.gear.owned': 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.warn('Updating users...');
|
console.warn('Updating users...');
|
||||||
@@ -38,18 +38,14 @@ dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
|||||||
// specify user data to change:
|
// specify user data to change:
|
||||||
var set = {};
|
var set = {};
|
||||||
|
|
||||||
if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
|
||||||
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
|
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2016':false};
|
||||||
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
|
||||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false};
|
||||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
|
||||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false};
|
||||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
|
||||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
|
||||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
|
||||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
|
||||||
} else {
|
} else {
|
||||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false};
|
||||||
}
|
}
|
||||||
|
|
||||||
dbUsers.update({_id:user._id}, {$set:set});
|
dbUsers.update({_id:user._id}, {$set:set});
|
||||||
@@ -75,4 +71,3 @@ function exiting(code, msg) {
|
|||||||
process.exit(code);
|
process.exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
113
migrations/20170120_missing_incentive.js
Normal file
113
migrations/20170120_missing_incentive.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
var migrationName = '20170120_missing_incentive.js';
|
||||||
|
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Award missing Royal Purple Hatching Potion to users with 55+ check-ins
|
||||||
|
* Reduce users with impossible check-in counts to a reasonable number
|
||||||
|
*/
|
||||||
|
|
||||||
|
import monk from 'monk';
|
||||||
|
import common from '../website/common';
|
||||||
|
|
||||||
|
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||||
|
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers(lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
var query = {
|
||||||
|
'loginIncentives': {$gt:54},
|
||||||
|
'migration': {$ne: migrationName},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, 'ERROR! ' + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var progressCount = 1000;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPromises = users.map(updateUser);
|
||||||
|
var lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(function () {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
var language = user.preferences.language || 'en';
|
||||||
|
var set = {'migration': migrationName};
|
||||||
|
var inc = {'items.hatchingPotions.RoyalPurple': 1};
|
||||||
|
if (user.loginIncentives > 58) {
|
||||||
|
set = {'migration': migrationName, 'loginIncentives': 58};
|
||||||
|
}
|
||||||
|
var push = {
|
||||||
|
'notifications': {
|
||||||
|
'type': 'LOGIN_INCENTIVE',
|
||||||
|
'data': {
|
||||||
|
'nextRewardAt': 60,
|
||||||
|
'rewardKey': [
|
||||||
|
'Pet_HatchingPotion_Purple',
|
||||||
|
],
|
||||||
|
'rewardText': common.i18n.t('potion', {potionType: common.i18n.t('hatchingPotionRoyalPurple', language)}, language),
|
||||||
|
'reward': [
|
||||||
|
{
|
||||||
|
'premium': true,
|
||||||
|
'key': 'RoyalPurple',
|
||||||
|
'limited': true,
|
||||||
|
'value': 2,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'message': common.i18n.t('unlockedCheckInReward', language),
|
||||||
|
},
|
||||||
|
'id': common.uuid(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set:set, $push:push, $inc:inc});
|
||||||
|
|
||||||
|
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||||
|
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData() {
|
||||||
|
console.warn('\n' + count + ' users processed\n');
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting(code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) { msg = 'ERROR!'; }
|
||||||
|
if (msg) {
|
||||||
|
if (code) { console.error(msg); }
|
||||||
|
else { console.log( msg); }
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
109
migrations/20170131_habit_birthday.js
Normal file
109
migrations/20170131_habit_birthday.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
var migrationName = '20170131_habit_birthday.js';
|
||||||
|
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Award 2017 party robes if user has 2016 robes, 2016 robes if they have the 2015 robes,
|
||||||
|
* 2015 robes if they have the 2014 robes, and 2014 robes otherwise. Also cake!
|
||||||
|
*/
|
||||||
|
|
||||||
|
var monk = require('monk');
|
||||||
|
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||||
|
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers(lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
var query = {
|
||||||
|
'migration':{$ne:migrationName},
|
||||||
|
'auth.timestamps.loggedin':{$gt:new Date('2017-01-24')}, // remove after first run to cover remaining users
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data)
|
||||||
|
'items.gear.owned'
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, 'ERROR! ' + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var progressCount = 1000;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPromises = users.map(updateUser);
|
||||||
|
var lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(function () {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
var set = {'migration':migrationName};
|
||||||
|
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2017'] = false;
|
||||||
|
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2016'] = false;
|
||||||
|
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2015'] = false;
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.armor_special_birthday'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inc = {
|
||||||
|
'items.food.Cake_Skeleton':1,
|
||||||
|
'items.food.Cake_Base':1,
|
||||||
|
'items.food.Cake_CottonCandyBlue':1,
|
||||||
|
'items.food.Cake_CottonCandyPink':1,
|
||||||
|
'items.food.Cake_Shade':1,
|
||||||
|
'items.food.Cake_White':1,
|
||||||
|
'items.food.Cake_Golden':1,
|
||||||
|
'items.food.Cake_Zombie':1,
|
||||||
|
'items.food.Cake_Desert':1,
|
||||||
|
'items.food.Cake_Red':1,
|
||||||
|
'achievements.habitBirthdays':1
|
||||||
|
};
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set:set, $inc:inc});
|
||||||
|
|
||||||
|
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||||
|
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData() {
|
||||||
|
console.warn('\n' + count + ' users processed\n');
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting(code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) { msg = 'ERROR!'; }
|
||||||
|
if (msg) {
|
||||||
|
if (code) { console.error(msg); }
|
||||||
|
else { console.log( msg); }
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
21
migrations/migration-runner.js
Normal file
21
migrations/migration-runner.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
require("babel-register");
|
||||||
|
require("babel-polyfill");
|
||||||
|
|
||||||
|
// This file must use ES5, everything required can be in ES6
|
||||||
|
|
||||||
|
function setUpServer () {
|
||||||
|
var nconf = require('nconf');
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Bluebird = require('bluebird');
|
||||||
|
var setupNconf = require('../website/server/libs/setupNconf');
|
||||||
|
setupNconf();
|
||||||
|
// We require src/server and npt src/index because
|
||||||
|
// 1. nconf is already setup
|
||||||
|
// 2. we don't need clustering
|
||||||
|
require('../website/server/server'); // eslint-disable-line global-require
|
||||||
|
}
|
||||||
|
setUpServer();
|
||||||
|
|
||||||
|
// Replace this with your migration
|
||||||
|
var processUsers = require('./new_stuff');
|
||||||
|
processUsers();
|
||||||
@@ -2,7 +2,7 @@ var _id = '';
|
|||||||
var update = {
|
var update = {
|
||||||
$addToSet: {
|
$addToSet: {
|
||||||
'purchased.plan.mysteryItems':{
|
'purchased.plan.mysteryItems':{
|
||||||
$each:['head_mystery_201612','armor_mystery_201612']
|
$each:['shield_mystery_201701','eyewear_mystery_201701']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,49 +6,70 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
|||||||
* set the newStuff flag in all user accounts so they see a Bailey message
|
* set the newStuff flag in all user accounts so they see a Bailey message
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var mongo = require('mongoskin');
|
var monk = require('monk');
|
||||||
|
|
||||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||||
|
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
var dbUsers = mongo.db(connectionString).collection('users');
|
|
||||||
|
|
||||||
|
function processUsers(lastId) {
|
||||||
// specify a query to limit the affected users (empty for all users):
|
// specify a query to limit the affected users (empty for all users):
|
||||||
var query = {
|
var query = {
|
||||||
'flags.newStuff':{$ne:true}
|
'flags.newStuff': {$ne:true},
|
||||||
};
|
};
|
||||||
|
|
||||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
if (lastId) {
|
||||||
var fields = {
|
query._id = {
|
||||||
};
|
$gt: lastId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, 'ERROR! ' + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.warn('Updating users...');
|
|
||||||
var progressCount = 1000;
|
var progressCount = 1000;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
|
||||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
function updateUsers (users) {
|
||||||
if (!user) {
|
if (!users || users.length === 0) {
|
||||||
console.warn('All appropriate users found and modified.');
|
console.warn('All appropriate users found and modified.');
|
||||||
setTimeout(displayData, 300000);
|
displayData();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var userPaymentPromises = users.map(updateUser);
|
||||||
|
var lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPaymentPromises)
|
||||||
|
.then(function () {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
// specify user data to change:
|
|
||||||
var set = {'flags.newStuff': true};
|
var set = {'flags.newStuff': true};
|
||||||
|
|
||||||
dbUsers.update({_id: user._id}, {$set:set});
|
dbUsers.update({_id: user._id}, {$set:set});
|
||||||
|
|
||||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
||||||
function displayData() {
|
function displayData() {
|
||||||
console.warn('\n' + count + ' users processed\n');
|
console.warn('\n' + count + ' users processed\n');
|
||||||
return exiting(0);
|
return exiting(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function exiting(code, msg) {
|
function exiting(code, msg) {
|
||||||
code = code || 0; // 0 = success
|
code = code || 0; // 0 = success
|
||||||
if (code && !msg) { msg = 'ERROR!'; }
|
if (code && !msg) { msg = 'ERROR!'; }
|
||||||
@@ -58,3 +79,5 @@ function exiting(code, msg) {
|
|||||||
}
|
}
|
||||||
process.exit(code);
|
process.exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
|
|||||||
115
migrations/restore-profile-data.js
Normal file
115
migrations/restore-profile-data.js
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
var migrationName = 'restore_profile_data.js';
|
||||||
|
var authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
|
||||||
|
var authorUuid = ''; //... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if users have empty profile data in new database and update it with old database info
|
||||||
|
*/
|
||||||
|
|
||||||
|
var monk = require('monk');
|
||||||
|
var connectionString = ''; // FOR TEST DATABASE
|
||||||
|
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
|
var monk2 = require('monk');
|
||||||
|
var oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||||
|
var olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers(lastId)
|
||||||
|
{
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
var query = {
|
||||||
|
// 'profile.name': 'profile name not found',
|
||||||
|
'profile.blurb': null,
|
||||||
|
// 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: ['_id', 'profile', 'auth.timestamps.loggedin'] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, 'ERROR! ' + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var progressCount = 1000;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
setTimeout(displayData, 300000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPaymentPromises = users.map(updateUser);
|
||||||
|
var lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPaymentPromises)
|
||||||
|
.then(function () {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (!user.profile.name || user.profile.name === 'profile name not found' || !user.profile.imageUrl || !user.profile.blurb) {
|
||||||
|
return olDbUsers.findOne({_id: user._id}, '_id profile')
|
||||||
|
.then((oldUserData) => {
|
||||||
|
if (!oldUserData) return;
|
||||||
|
// specify user data to change:
|
||||||
|
var set = {};
|
||||||
|
|
||||||
|
if (oldUserData.profile.name === 'profile name not found') return;
|
||||||
|
|
||||||
|
var userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
|
||||||
|
if (userNeedsProfileName && oldUserData.profile.name) {
|
||||||
|
set['profile.name'] = oldUserData.profile.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.profile.imageUrl && oldUserData.profile.imageUrl) {
|
||||||
|
set['profile.imageUrl'] = oldUserData.profile.imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.profile.blurb && oldUserData.profile.blurb) {
|
||||||
|
set['profile.blurb'] = oldUserData.profile.blurb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(set).length !== 0 && set.constructor === Object) {
|
||||||
|
console.log(set)
|
||||||
|
return dbUsers.update({_id: user._id}, {$set:set});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||||
|
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData() {
|
||||||
|
console.warn('\n' + count + ' users processed\n');
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting(code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) { msg = 'ERROR!'; }
|
||||||
|
if (msg) {
|
||||||
|
if (code) { console.error(msg); }
|
||||||
|
else { console.log( msg); }
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
processUsers()
|
||||||
101
migrations/takeThis.js
Normal file
101
migrations/takeThis.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
var migrationName = '20170201_takeThis.js'; // Update per month
|
||||||
|
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Award Take This ladder items to participants in this month's challenge
|
||||||
|
*/
|
||||||
|
|
||||||
|
var 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},
|
||||||
|
'challenges':{$in:['b1d436b5-c784-42e3-9b07-7072479a6f8e']} // Update per month
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [
|
||||||
|
'items.gear.owned',
|
||||||
|
] // 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 (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') {
|
||||||
|
set = {'migration':migrationName};
|
||||||
|
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||||
|
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||||
|
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||||
|
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||||
|
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||||
|
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||||
|
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||||
|
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||||
|
} else {
|
||||||
|
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set:set});
|
||||||
|
|
||||||
|
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||||
|
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData() {
|
||||||
|
console.warn('\n' + count + ' users processed\n');
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting(code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) { msg = 'ERROR!'; }
|
||||||
|
if (msg) {
|
||||||
|
if (code) { console.error(msg); }
|
||||||
|
else { console.log( msg); }
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
2043
npm-shrinkwrap.json
generated
2043
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "habitica",
|
"name": "habitica",
|
||||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||||
"version": "3.63.0",
|
"version": "3.74.1",
|
||||||
"main": "./website/server/index.js",
|
"main": "./website/server/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@slack/client": "3.6.0",
|
"@slack/client": "3.6.0",
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"async": "^1.5.0",
|
"async": "^1.5.0",
|
||||||
"autoprefixer": "^6.4.0",
|
"autoprefixer": "^6.4.0",
|
||||||
"aws-sdk": "^2.0.25",
|
"aws-sdk": "^2.0.25",
|
||||||
|
"axios": "^0.15.3",
|
||||||
"babel-core": "^6.0.0",
|
"babel-core": "^6.0.0",
|
||||||
"babel-loader": "^6.0.0",
|
"babel-loader": "^6.0.0",
|
||||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
"babel-register": "^6.6.0",
|
"babel-register": "^6.6.0",
|
||||||
"babel-runtime": "^6.11.6",
|
"babel-runtime": "^6.11.6",
|
||||||
"babelify": "^7.2.0",
|
"babelify": "^7.2.0",
|
||||||
|
"bcrypt": "^1.0.2",
|
||||||
"bluebird": "^3.3.5",
|
"bluebird": "^3.3.5",
|
||||||
"body-parser": "^1.15.0",
|
"body-parser": "^1.15.0",
|
||||||
"bower": "~1.3.12",
|
"bower": "~1.3.12",
|
||||||
@@ -118,7 +120,6 @@
|
|||||||
"vue": "^2.1.0",
|
"vue": "^2.1.0",
|
||||||
"vue-hot-reload-api": "^1.2.0",
|
"vue-hot-reload-api": "^1.2.0",
|
||||||
"vue-loader": "^10.0.0",
|
"vue-loader": "^10.0.0",
|
||||||
"vue-resource": "^1.0.2",
|
|
||||||
"vue-router": "^2.0.0-rc.5",
|
"vue-router": "^2.0.0-rc.5",
|
||||||
"vue-template-compiler": "^2.1.0",
|
"vue-template-compiler": "^2.1.0",
|
||||||
"webpack": "^1.12.2",
|
"webpack": "^1.12.2",
|
||||||
@@ -199,6 +200,7 @@
|
|||||||
"mocha": "^2.3.3",
|
"mocha": "^2.3.3",
|
||||||
"mongodb": "^2.0.46",
|
"mongodb": "^2.0.46",
|
||||||
"mongoskin": "~2.1.0",
|
"mongoskin": "~2.1.0",
|
||||||
|
"monk": "^3.1.3",
|
||||||
"nightwatch": "^0.8.18",
|
"nightwatch": "^0.8.18",
|
||||||
"phantomjs-prebuilt": "^2.1.12",
|
"phantomjs-prebuilt": "^2.1.12",
|
||||||
"protractor": "^3.1.1",
|
"protractor": "^3.1.1",
|
||||||
|
|||||||
@@ -35,6 +35,24 @@ describe('POST /chat', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Returns an error when an empty message is provided', async () => {
|
||||||
|
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: ' '}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Returns an error when an message containing only newlines is provided', async () => {
|
||||||
|
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: '\n\n'}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Returns an error when group is not found', async () => {
|
it('Returns an error when group is not found', async () => {
|
||||||
await expect(user.post('/groups/invalidID/chat', { message: testMessage})).to.eventually.be.rejected.and.eql({
|
await expect(user.post('/groups/invalidID/chat', { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
|
|||||||
@@ -300,6 +300,26 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
message: t('userAlreadyInGroup'),
|
message: t('userAlreadyInGroup'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @TODO: Add this after we are able to mock the group plan route
|
||||||
|
xit('returns an error when a non-leader invites to a group plan', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
|
||||||
|
let nonGroupLeader = await generateUser();
|
||||||
|
await inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [nonGroupLeader._id],
|
||||||
|
});
|
||||||
|
await nonGroupLeader.post(`/groups/${group._id}/join`);
|
||||||
|
|
||||||
|
await expect(nonGroupLeader.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('onlyGroupLeaderCanInviteToGroupPlan'),
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('party invites', () => {
|
describe('party invites', () => {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ describe('GET /models/:model/paths', () => {
|
|||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when model is not accessible or doesn\'t exists', async () => {
|
it('returns an error when model is not accessible or doesn\'t exist', async () => {
|
||||||
await expect(user.get('/models/1234/paths')).to.eventually.be.rejected.and.eql({
|
await expect(user.get('/models/1234/paths')).to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments : amazon #subscribeCancel', () => {
|
|
||||||
let endpoint = '/amazon/subscribe/cancel';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies subscription', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
xdescribe('payments : paypal #checkout', () => {
|
|
||||||
let endpoint = '/paypal/checkout';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies subscription', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
xdescribe('payments : paypal #checkoutSuccess', () => {
|
|
||||||
let endpoint = '/paypal/checkout/success';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies subscription', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
xdescribe('payments : paypal #subscribe', () => {
|
|
||||||
let endpoint = '/paypal/subscribe';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments : paypal #subscribeCancel', () => {
|
|
||||||
let endpoint = '/paypal/subscribe/cancel';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
xdescribe('payments : paypal #subscribeSuccess', () => {
|
|
||||||
let endpoint = '/paypal/subscribe/success';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments - stripe - #subscribeCancel', () => {
|
|
||||||
let endpoint = '/stripe/subscribe/cancel';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments - amazon - #checkout', () => {
|
|
||||||
let endpoint = '/amazon/checkout';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: 'Missing req.body.orderReferenceId',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments - amazon - #subscribe', () => {
|
|
||||||
let endpoint = '/amazon/subscribe';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies subscription code', async () => {
|
|
||||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('missingSubscriptionCode'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments - paypal - #ipn', () => {
|
|
||||||
let endpoint = '/paypal/ipn';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
let result = await user.post(endpoint);
|
|
||||||
expect(result).to.eql('OK');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments - stripe - #checkout', () => {
|
|
||||||
let endpoint = '/stripe/checkout';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.post(endpoint, {id: 123})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'Error',
|
|
||||||
message: 'Invalid API Key provided: ****************************1111',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
|
|
||||||
describe('payments - stripe - #subscribeEdit', () => {
|
|
||||||
let endpoint = '/stripe/subscribe/edit';
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verifies credentials', async () => {
|
|
||||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingSubscription'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||||
|
|
||||||
|
describe('payments : amazon #subscribeCancel', () => {
|
||||||
|
let endpoint = '/amazon/subscribe/cancel';
|
||||||
|
let user, group, amazonSubscribeCancelStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error when there users has no subscription', async () => {
|
||||||
|
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
amazonSubscribeCancelStub = sinon.stub(amzLib, 'cancelSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
amzLib.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a user subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(endpoint);
|
||||||
|
|
||||||
|
expect(amazonSubscribeCancelStub).to.be.calledOnce;
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].groupId).to.eql(undefined);
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a group subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
group = await generateGroup(user, {
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(`${endpoint}?groupId=${group._id}`);
|
||||||
|
|
||||||
|
expect(amazonSubscribeCancelStub).to.be.calledOnce;
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].groupId).to.eql(group._id);
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||||
|
|
||||||
|
describe('payments - amazon - #checkout', () => {
|
||||||
|
let endpoint = '/amazon/checkout';
|
||||||
|
let user, amazonCheckoutStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies credentials', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'Missing req.body.orderReferenceId',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
amazonCheckoutStub = sinon.stub(amzLib, 'checkout').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
amzLib.checkout.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purcahse with amazon checkout', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
let gift = {
|
||||||
|
type: 'gems',
|
||||||
|
gems: {
|
||||||
|
amount: 16,
|
||||||
|
uuid: user._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let orderReferenceId = 'orderReferenceId-example';
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
gift,
|
||||||
|
orderReferenceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(amazonCheckoutStub).to.be.calledOnce;
|
||||||
|
expect(amazonCheckoutStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(amazonCheckoutStub.args[0][0].gift).to.eql(gift);
|
||||||
|
expect(amazonCheckoutStub.args[0][0].orderReferenceId).to.eql(orderReferenceId);
|
||||||
|
expect(amazonCheckoutStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(amazonCheckoutStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('payments - amazon - #createOrderReferenceId', () => {
|
describe('payments - amazon - #createOrderReferenceId', () => {
|
||||||
let endpoint = '/amazon/createOrderReferenceId';
|
let endpoint = '/amazon/createOrderReferenceId';
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||||
|
|
||||||
|
describe('payments - amazon - #subscribe', () => {
|
||||||
|
let endpoint = '/amazon/subscribe';
|
||||||
|
let user, group, subscribeWithAmazonStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies subscription code', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingSubscriptionCode'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let billingAgreementId = 'billingAgreementId-example';
|
||||||
|
let subscription = 'basic_3mo';
|
||||||
|
let coupon;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
subscribeWithAmazonStub = sinon.stub(amzLib, 'subscribe').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
amzLib.subscribe.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a user subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
billingAgreementId,
|
||||||
|
subscription,
|
||||||
|
coupon,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeWithAmazonStub).to.be.calledOnce;
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].billingAgreementId).to.eql(billingAgreementId);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].sub).to.exist;
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].coupon).to.eql(coupon);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].groupId).not.exist;
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a group subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
group = await generateGroup(user, {
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
billingAgreementId,
|
||||||
|
subscription,
|
||||||
|
coupon,
|
||||||
|
groupId: group._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeWithAmazonStub).to.be.calledOnce;
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].billingAgreementId).to.eql(billingAgreementId);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].sub).to.exist;
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].coupon).to.eql(coupon);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].groupId).to.eql(group._id);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('payments : amazon', () => {
|
describe('payments : amazon', () => {
|
||||||
let endpoint = '/amazon/verifyAccessToken';
|
let endpoint = '/amazon/verifyAccessToken';
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||||
|
|
||||||
|
describe('payments : google #cancelSubscribe', () => {
|
||||||
|
let endpoint = '/iap/android/subscribe/cancel';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let cancelStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
cancelStub = sinon.stub(googlePayments, 'cancelSubscribe').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
googlePayments.cancelSubscribe.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(endpoint);
|
||||||
|
|
||||||
|
expect(cancelStub).to.be.calledOnce;
|
||||||
|
expect(cancelStub.args[0][0]._id).to.eql(user._id);
|
||||||
|
expect(cancelStub.args[0][1]['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(cancelStub.args[0][1]['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||||
|
|
||||||
|
describe('payments : google #subscribe', () => {
|
||||||
|
let endpoint = '/iap/android/subscribe';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies sub key', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingSubscriptionCode'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeStub = sinon.stub(googlePayments, 'subscribe').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
googlePayments.subscribe.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
let sku = 'com.habitrpg.android.habitica.subscription.3month';
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
transaction: {receipt: 'receipt', signature: 'signature'},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0]).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][1]._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][2]).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][3]).to.eql('signature');
|
||||||
|
expect(subscribeStub.args[0][4]['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][4]['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||||
|
|
||||||
|
describe('payments : google #verify', () => {
|
||||||
|
let endpoint = '/iap/android/verify';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let verifyStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
verifyStub = sinon.stub(googlePayments, 'verifyGemPurchase').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
googlePayments.verifyGemPurchase.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
transaction: {receipt: 'receipt', signature: 'signature'},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(verifyStub).to.be.calledOnce;
|
||||||
|
expect(verifyStub.args[0][0]._id).to.eql(user._id);
|
||||||
|
expect(verifyStub.args[0][1]).to.eql('receipt');
|
||||||
|
expect(verifyStub.args[0][2]).to.eql('signature');
|
||||||
|
expect(verifyStub.args[0][3]['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(verifyStub.args[0][3]['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||||
|
|
||||||
|
describe('payments : paypal #checkout', () => {
|
||||||
|
let endpoint = '/paypal/checkout';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let checkoutStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
checkoutStub = sinon.stub(paypalPayments, 'checkout').returnsPromise().resolves('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.checkout.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a purchase link', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(endpoint);
|
||||||
|
|
||||||
|
expect(checkoutStub).to.be.calledOnce;
|
||||||
|
expect(checkoutStub.args[0][0].gift).to.eql(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||||
|
|
||||||
|
describe('payments : paypal #checkoutSuccess', () => {
|
||||||
|
let endpoint = '/paypal/checkout/success';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies paymentId', async () => {
|
||||||
|
await expect(user.get(endpoint))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingPaymentId'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies customerId', async () => {
|
||||||
|
await expect(user.get(`${endpoint}?paymentId=test-paymentid`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingCustomerId'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let checkoutSuccessStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
checkoutSuccessStub = sinon.stub(paypalPayments, 'checkoutSuccess').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.checkoutSuccess.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
let paymentId = 'test-paymentid';
|
||||||
|
let customerId = 'test-customerId';
|
||||||
|
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(`${endpoint}?PayerID=${customerId}&paymentId=${paymentId}`);
|
||||||
|
|
||||||
|
expect(checkoutSuccessStub).to.be.calledOnce;
|
||||||
|
|
||||||
|
expect(checkoutSuccessStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(checkoutSuccessStub.args[0][0].gift).to.eql(undefined);
|
||||||
|
expect(checkoutSuccessStub.args[0][0].paymentId).to.eql(paymentId);
|
||||||
|
expect(checkoutSuccessStub.args[0][0].customerId).to.eql(customerId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||||
|
import shared from '../../../../../../website/common';
|
||||||
|
|
||||||
|
describe('payments : paypal #subscribe', () => {
|
||||||
|
let endpoint = '/paypal/subscribe';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies sub key', async () => {
|
||||||
|
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingSubKey'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeStub = sinon.stub(paypalPayments, 'subscribe').returnsPromise().resolves('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.subscribe.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
let subKey = 'basic_3mo';
|
||||||
|
let sub = shared.content.subscriptionBlocks[subKey];
|
||||||
|
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(`${endpoint}?sub=${subKey}`);
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
|
||||||
|
expect(subscribeStub.args[0][0].sub).to.eql(sub);
|
||||||
|
expect(subscribeStub.args[0][0].coupon).to.eql(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||||
|
|
||||||
|
describe('payments : paypal #subscribeCancel', () => {
|
||||||
|
let endpoint = '/paypal/subscribe/cancel';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies credentials', async () => {
|
||||||
|
await expect(user.get(endpoint))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeCancelStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeCancelStub = sinon.stub(paypalPayments, 'subscribeCancel').returnsPromise().resolves('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.subscribeCancel.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(endpoint);
|
||||||
|
|
||||||
|
expect(subscribeCancelStub).to.be.calledOnce;
|
||||||
|
|
||||||
|
expect(subscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeCancelStub.args[0][0].groupId).to.eql(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||||
|
|
||||||
|
describe('payments : paypal #subscribeSuccess', () => {
|
||||||
|
let endpoint = '/paypal/subscribe/success';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies Paypal Block', async () => {
|
||||||
|
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingPaypalBlock'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
xdescribe('success', () => {
|
||||||
|
let subscribeSuccessStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeSuccessStub = sinon.stub(paypalPayments, 'subscribeSuccess').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.subscribeSuccess.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a subscription', async () => {
|
||||||
|
let token = 'test-token';
|
||||||
|
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(`${endpoint}?token=${token}`);
|
||||||
|
|
||||||
|
expect(subscribeSuccessStub).to.be.calledOnce;
|
||||||
|
|
||||||
|
expect(subscribeSuccessStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeSuccessStub.args[0][0].block).to.eql(undefined);
|
||||||
|
expect(subscribeSuccessStub.args[0][0].groupId).to.eql(undefined);
|
||||||
|
expect(subscribeSuccessStub.args[0][0].token).to.eql(token);
|
||||||
|
expect(subscribeSuccessStub.args[0][0].headers).to.exist;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||||
|
|
||||||
|
describe('payments - paypal - #ipn', () => {
|
||||||
|
let endpoint = '/paypal/ipn';
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies credentials', async () => {
|
||||||
|
let result = await user.post(endpoint);
|
||||||
|
expect(result).to.eql('OK');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let ipnStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
ipnStub = sinon.stub(paypalPayments, 'ipn').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.ipn.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint);
|
||||||
|
|
||||||
|
expect(ipnStub).to.be.calledOnce;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||||
|
|
||||||
|
describe('payments - stripe - #subscribeCancel', () => {
|
||||||
|
let endpoint = '/stripe/subscribe/cancel';
|
||||||
|
let user, group, stripeCancelSubscriptionStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies credentials', async () => {
|
||||||
|
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
stripeCancelSubscriptionStub = sinon.stub(stripePayments, 'cancelSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
stripePayments.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a user subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(`${endpoint}?redirect=none`);
|
||||||
|
|
||||||
|
expect(stripeCancelSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeCancelSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(stripeCancelSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a group subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
group = await generateGroup(user, {
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.get(`${endpoint}?groupId=${group._id}&redirect=none`);
|
||||||
|
|
||||||
|
expect(stripeCancelSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeCancelSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(stripeCancelSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||||
|
|
||||||
|
describe('payments - stripe - #checkout', () => {
|
||||||
|
let endpoint = '/stripe/checkout';
|
||||||
|
let user, group;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies credentials', async () => {
|
||||||
|
await expect(user.post(endpoint, {id: 123})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'Error',
|
||||||
|
message: 'Invalid API Key provided: ****************************1111',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let stripeCheckoutSubscriptionStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
stripeCheckoutSubscriptionStub = sinon.stub(stripePayments, 'checkout').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
stripePayments.checkout.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a user subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint);
|
||||||
|
|
||||||
|
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a group subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
group = await generateGroup(user, {
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(`${endpoint}?groupId=${group._id}`);
|
||||||
|
|
||||||
|
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||||
|
|
||||||
|
describe('payments - stripe - #subscribeEdit', () => {
|
||||||
|
let endpoint = '/stripe/subscribe/edit';
|
||||||
|
let user, group;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies credentials', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let stripeEditSubscriptionStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
stripeEditSubscriptionStub = sinon.stub(stripePayments, 'editSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
stripePayments.editSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a user subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint);
|
||||||
|
|
||||||
|
expect(stripeEditSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a group subscription', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'profile.name': 'sender',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
group = await generateGroup(user, {
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
'purchased.plan.customerId': 'customer-id',
|
||||||
|
'purchased.plan.planId': 'basic_3mo',
|
||||||
|
'purchased.plan.lastBillingDate': new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
groupId: group._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stripeEditSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -148,5 +148,20 @@ describe('POST /groups/:groupId/quests/accept', () => {
|
|||||||
expect(rejectingMember.party.quest.key).to.not.exist;
|
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||||
expect(rejectingMember.party.quest.completed).to.not.exist;
|
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('begins the quest if accepting the last pending invite and verifies chat', async () => {
|
||||||
|
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
|
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
// quest will start after everyone has accepted
|
||||||
|
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
|
||||||
|
await questingGroup.sync();
|
||||||
|
expect(questingGroup.chat[0].text).to.exist;
|
||||||
|
expect(questingGroup.chat[0]._meta).to.exist;
|
||||||
|
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||||
|
|
||||||
|
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||||
|
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -231,5 +231,22 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
|||||||
expect(questingGroup.quest.members[partyMembers[0]._id]).to.exist;
|
expect(questingGroup.quest.members[partyMembers[0]._id]).to.exist;
|
||||||
expect(questingGroup.quest.members[leader._id]).to.exist;
|
expect(questingGroup.quest.members[leader._id]).to.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('allows group leader to force start quest and verifies chat', async () => {
|
||||||
|
let questLeader = partyMembers[0];
|
||||||
|
await questLeader.update({[`items.quests.${PET_QUEST}`]: 1});
|
||||||
|
await questLeader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
|
|
||||||
|
await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
|
||||||
|
|
||||||
|
await questingGroup.sync();
|
||||||
|
|
||||||
|
expect(questingGroup.chat[0].text).to.exist;
|
||||||
|
expect(questingGroup.chat[0]._meta).to.exist;
|
||||||
|
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||||
|
|
||||||
|
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||||
|
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -188,5 +188,25 @@ describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
|||||||
|
|
||||||
expect(group.quest.active).to.eql(true);
|
expect(group.quest.active).to.eql(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('starts quest automatically if user is in a solo party and verifies chat', async () => {
|
||||||
|
let leaderDetails = { balance: 10 };
|
||||||
|
leaderDetails[`items.quests.${PET_QUEST}`] = 1;
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
leaderDetails,
|
||||||
|
});
|
||||||
|
|
||||||
|
await groupLeader.post(`/groups/${group._id}/quests/invite/${PET_QUEST}`);
|
||||||
|
|
||||||
|
await group.sync();
|
||||||
|
|
||||||
|
expect(group.chat[0].text).to.exist;
|
||||||
|
expect(group.chat[0]._meta).to.exist;
|
||||||
|
expect(group.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||||
|
|
||||||
|
let returnedGroup = await groupLeader.get(`/groups/${group._id}`);
|
||||||
|
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -86,11 +86,12 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('aborts a quest', async () => {
|
it('aborts a quest', async () => {
|
||||||
sandbox.stub(Group.prototype, 'sendChat');
|
|
||||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
|
||||||
|
let stub = sandbox.stub(Group.prototype, 'sendChat');
|
||||||
|
|
||||||
let res = await leader.post(`/groups/${questingGroup._id}/quests/abort`);
|
let res = await leader.post(`/groups/${questingGroup._id}/quests/abort`);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
leader.sync(),
|
leader.sync(),
|
||||||
@@ -127,6 +128,7 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
|||||||
});
|
});
|
||||||
expect(Group.prototype.sendChat).to.be.calledOnce;
|
expect(Group.prototype.sendChat).to.be.calledOnce;
|
||||||
expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/);
|
expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/);
|
||||||
Group.prototype.sendChat.restore();
|
|
||||||
|
stub.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -180,5 +180,19 @@ describe('POST /groups/:groupId/quests/reject', () => {
|
|||||||
expect(rejectingMember.party.quest.key).to.not.exist;
|
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||||
expect(rejectingMember.party.quest.completed).to.not.exist;
|
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('starts the quest when the last user reject and verifies chat', async () => {
|
||||||
|
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
|
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`);
|
||||||
|
await questingGroup.sync();
|
||||||
|
|
||||||
|
expect(questingGroup.chat[0].text).to.exist;
|
||||||
|
expect(questingGroup.chat[0]._meta).to.exist;
|
||||||
|
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||||
|
|
||||||
|
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||||
|
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('POST /tasks/clearCompletedTodos', () => {
|
describe('POST /tasks/clearCompletedTodos', () => {
|
||||||
it('deletes all completed todos except the ones from a challenge', async () => {
|
it('deletes all completed todos except the ones from a challenge and group', async () => {
|
||||||
let user = await generateUser({balance: 1});
|
let user = await generateUser({balance: 1});
|
||||||
let guild = await generateGroup(user);
|
let guild = await generateGroup(user);
|
||||||
let challenge = await generateChallenge(user, guild);
|
let challenge = await generateChallenge(user, guild);
|
||||||
@@ -24,8 +24,14 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
|||||||
type: 'todo',
|
type: 'todo',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
text: 'todo 7',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||||
|
|
||||||
let tasks = await user.get('/tasks/user?type=todos');
|
let tasks = await user.get('/tasks/user?type=todos');
|
||||||
expect(tasks.length).to.equal(initialTodoCount + 6);
|
expect(tasks.length).to.equal(initialTodoCount + 7);
|
||||||
|
|
||||||
for (let task of tasks) {
|
for (let task of tasks) {
|
||||||
if (['todo 2', 'todo 3', 'todo 6'].indexOf(task.text) !== -1) {
|
if (['todo 2', 'todo 3', 'todo 6'].indexOf(task.text) !== -1) {
|
||||||
@@ -37,7 +43,7 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
|||||||
let completedTodos = await user.get('/tasks/user?type=completedTodos');
|
let completedTodos = await user.get('/tasks/user?type=completedTodos');
|
||||||
let todos = await user.get('/tasks/user?type=todos');
|
let todos = await user.get('/tasks/user?type=todos');
|
||||||
let allTodos = todos.concat(completedTodos);
|
let allTodos = todos.concat(completedTodos);
|
||||||
expect(allTodos.length).to.equal(initialTodoCount + 4); // + 6 - 3 completed (but one is from challenge)
|
expect(allTodos.length).to.equal(initialTodoCount + 5); // + 7 - 3 completed (but one is from challenge)
|
||||||
expect(allTodos[allTodos.length - 1].text).to.equal('todo 6');
|
expect(allTodos[allTodos.length - 1].text).to.equal('todo 6'); // last completed todo
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -81,4 +81,36 @@ describe('DELETE /tasks/:id', () => {
|
|||||||
message: t('cantDeleteAssignedGroupTasks'),
|
message: t('cantDeleteAssignedGroupTasks'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('allows a user to delete a broken task', async () => {
|
||||||
|
let memberTasks = await member.get('/tasks/user');
|
||||||
|
let syncedTask = find(memberTasks, findAssignedTask);
|
||||||
|
|
||||||
|
await user.del(`/tasks/${task._id}`);
|
||||||
|
|
||||||
|
await member.del(`/tasks/${syncedTask._id}`);
|
||||||
|
|
||||||
|
await expect(member.get(`/tasks/${syncedTask._id}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: 'Task not found.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows a user to delete a task after leaving a group', async () => {
|
||||||
|
let memberTasks = await member.get('/tasks/user');
|
||||||
|
let syncedTask = find(memberTasks, findAssignedTask);
|
||||||
|
|
||||||
|
await member.post(`/groups/${guild._id}/leave`);
|
||||||
|
|
||||||
|
await member.del(`/tasks/${syncedTask._id}`);
|
||||||
|
|
||||||
|
await expect(member.get(`/tasks/${syncedTask._id}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: 'Task not found.',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ describe('POST /tasks/:id/score/:direction', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('prevents user from scoring a task that needs to be approved', async () => {
|
it('prevents user from scoring a task that needs to be approved', async () => {
|
||||||
|
await user.update({
|
||||||
|
'preferences.language': 'cs',
|
||||||
|
});
|
||||||
|
|
||||||
let memberTasks = await member.get('/tasks/user');
|
let memberTasks = await member.get('/tasks/user');
|
||||||
let syncedTask = find(memberTasks, findAssignedTask);
|
let syncedTask = find(memberTasks, findAssignedTask);
|
||||||
|
|
||||||
@@ -52,7 +56,7 @@ describe('POST /tasks/:id/score/:direction', () => {
|
|||||||
expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', {
|
expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', {
|
||||||
user: member.auth.local.username,
|
user: member.auth.local.username,
|
||||||
taskName: updatedTask.text,
|
taskName: updatedTask.text,
|
||||||
}));
|
}, 'cs')); // This test only works if we have the notification translated
|
||||||
expect(user.notifications[0].data.groupId).to.equal(guild._id);
|
expect(user.notifications[0].data.groupId).to.equal(guild._id);
|
||||||
|
|
||||||
expect(updatedTask.group.approval.requested).to.equal(true);
|
expect(updatedTask.group.approval.requested).to.equal(true);
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ describe('POST /tasks/:taskId', () => {
|
|||||||
let updateGroup = await user.get(`/groups/${guild._id}`);
|
let updateGroup = await user.get(`/groups/${guild._id}`);
|
||||||
|
|
||||||
expect(updateGroup.chat[0].text).to.equal(t('userIsClamingTask', {username: member.profile.name, task: task.text}));
|
expect(updateGroup.chat[0].text).to.equal(t('userIsClamingTask', {username: member.profile.name, task: task.text}));
|
||||||
|
expect(updateGroup.chat[0].uuid).to.equal('system');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('assigns a task to a user', async () => {
|
it('assigns a task to a user', async () => {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
} from '../../../../../helpers/api-v3-integration.helper';
|
||||||
|
|
||||||
|
describe('POST group-tasks/:taskId/move/to/:position', () => {
|
||||||
|
let user, guild;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser({balance: 1});
|
||||||
|
guild = await generateGroup(user, {type: 'guild'});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can move task to new position', async () => {
|
||||||
|
let tasks = await user.post(`/tasks/group/${guild._id}`, [
|
||||||
|
{type: 'habit', text: 'habit 1'},
|
||||||
|
{type: 'habit', text: 'habit 2'},
|
||||||
|
{type: 'daily', text: 'daily 1'},
|
||||||
|
{type: 'habit', text: 'habit 3'},
|
||||||
|
{type: 'habit', text: 'habit 4'},
|
||||||
|
{type: 'todo', text: 'todo 1'},
|
||||||
|
{type: 'habit', text: 'habit 5'},
|
||||||
|
]);
|
||||||
|
|
||||||
|
let taskToMove = tasks[1];
|
||||||
|
expect(taskToMove.text).to.equal('habit 2');
|
||||||
|
let newOrder = await user.post(`/group-tasks/${tasks[1]._id}/move/to/3`);
|
||||||
|
expect(newOrder[3]).to.equal(taskToMove._id);
|
||||||
|
expect(newOrder.length).to.equal(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can push to bottom', async () => {
|
||||||
|
let tasks = await user.post(`/tasks/group/${guild._id}`, [
|
||||||
|
{type: 'habit', text: 'habit 1'},
|
||||||
|
{type: 'habit', text: 'habit 2'},
|
||||||
|
{type: 'daily', text: 'daily 1'},
|
||||||
|
{type: 'habit', text: 'habit 3'},
|
||||||
|
{type: 'habit', text: 'habit 4'},
|
||||||
|
{type: 'todo', text: 'todo 1'},
|
||||||
|
{type: 'habit', text: 'habit 5'},
|
||||||
|
]);
|
||||||
|
|
||||||
|
let taskToMove = tasks[1];
|
||||||
|
expect(taskToMove.text).to.equal('habit 2');
|
||||||
|
let newOrder = await user.post(`/group-tasks/${tasks[1]._id}/move/to/-1`);
|
||||||
|
expect(newOrder[4]).to.equal(taskToMove._id);
|
||||||
|
expect(newOrder.length).to.equal(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -11,6 +11,10 @@ import {
|
|||||||
map,
|
map,
|
||||||
} from 'lodash';
|
} from 'lodash';
|
||||||
import Bluebird from 'bluebird';
|
import Bluebird from 'bluebird';
|
||||||
|
import {
|
||||||
|
sha1MakeSalt,
|
||||||
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
|
} from '../../../../../website/server/libs/password';
|
||||||
|
|
||||||
describe('DELETE /user', () => {
|
describe('DELETE /user', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -67,6 +71,30 @@ describe('DELETE /user', () => {
|
|||||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('deletes the user with a legacy sha1 password', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
await user.update({
|
||||||
|
'auth.local.hashed_password': sha1HashedPassword,
|
||||||
|
'auth.local.passwordHashMethod': 'sha1',
|
||||||
|
'auth.local.salt': salt,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||||
|
expect(user.auth.local.salt).to.equal(salt);
|
||||||
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
// delete the user
|
||||||
|
await user.del('/user', {
|
||||||
|
password: textPassword,
|
||||||
|
});
|
||||||
|
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
context('last member of a party', () => {
|
context('last member of a party', () => {
|
||||||
let party;
|
let party;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ describe('GET /user', () => {
|
|||||||
let returnedUser = await user.get('/user');
|
let returnedUser = await user.get('/user');
|
||||||
|
|
||||||
expect(returnedUser.auth.local.hashed_password).to.not.exist;
|
expect(returnedUser.auth.local.hashed_password).to.not.exist;
|
||||||
|
expect(returnedUser.auth.local.passwordHashMethod).to.not.exist;
|
||||||
expect(returnedUser.auth.local.salt).to.not.exist;
|
expect(returnedUser.auth.local.salt).to.not.exist;
|
||||||
expect(returnedUser.apiToken).to.not.exist;
|
expect(returnedUser.apiToken).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,15 +62,6 @@ describe('POST /user/buy/:key', () => {
|
|||||||
await user.post(`/user/buy/${key}`);
|
await user.post(`/user/buy/${key}`);
|
||||||
await user.sync();
|
await user.sync();
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({
|
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||||
armor_warrior_1: true,
|
|
||||||
eyewear_special_blackTopFrame: true,
|
|
||||||
eyewear_special_blueTopFrame: true,
|
|
||||||
eyewear_special_greenTopFrame: true,
|
|
||||||
eyewear_special_pinkTopFrame: true,
|
|
||||||
eyewear_special_redTopFrame: true,
|
|
||||||
eyewear_special_whiteTopFrame: true,
|
|
||||||
eyewear_special_yellowTopFrame: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,15 +31,6 @@ describe('POST /user/buy-gear/:key', () => {
|
|||||||
await user.post(`/user/buy-gear/${key}`);
|
await user.post(`/user/buy-gear/${key}`);
|
||||||
await user.sync();
|
await user.sync();
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({
|
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||||
armor_warrior_1: true,
|
|
||||||
eyewear_special_blackTopFrame: true,
|
|
||||||
eyewear_special_blueTopFrame: true,
|
|
||||||
eyewear_special_greenTopFrame: true,
|
|
||||||
eyewear_special_pinkTopFrame: true,
|
|
||||||
eyewear_special_redTopFrame: true,
|
|
||||||
eyewear_special_whiteTopFrame: true,
|
|
||||||
eyewear_special_yellowTopFrame: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
translate as t,
|
translate as t,
|
||||||
createAndPopulateGroup,
|
createAndPopulateGroup,
|
||||||
|
generateGroup,
|
||||||
generateChallenge,
|
generateChallenge,
|
||||||
sleep,
|
sleep,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
describe('POST /user/class/cast/:spellId', () => {
|
describe('POST /user/class/cast/:spellId', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -120,6 +122,31 @@ describe('POST /user/class/cast/:spellId', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an error if a group task was targeted', async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup();
|
||||||
|
|
||||||
|
let groupTask = await groupLeader.post(`/tasks/group/${group._id}`, {
|
||||||
|
text: 'todo group',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await groupLeader.post(`/tasks/${groupTask._id}/assign/${groupLeader._id}`);
|
||||||
|
let memberTasks = await groupLeader.get('/tasks/user');
|
||||||
|
let syncedGroupTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.group.id === group._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
await groupLeader.update({'stats.class': 'rogue', 'stats.lvl': 11});
|
||||||
|
await sleep(0.5);
|
||||||
|
await groupLeader.sync();
|
||||||
|
|
||||||
|
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${syncedGroupTask._id}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('groupTasksNoCast'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('returns an error if targeted party member doesn\'t exist', async () => {
|
it('returns an error if targeted party member doesn\'t exist', async () => {
|
||||||
let {groupLeader} = await createAndPopulateGroup({
|
let {groupLeader} = await createAndPopulateGroup({
|
||||||
groupDetails: { type: 'party', privacy: 'private' },
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
@@ -160,6 +187,40 @@ describe('POST /user/class/cast/:spellId', () => {
|
|||||||
expect(group.chat[0].uuid).to.equal('system');
|
expect(group.chat[0].uuid).to.equal('system');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('searing brightness does not affect challenge or group tasks', async () => {
|
||||||
|
let guild = await generateGroup(user);
|
||||||
|
let challenge = await generateChallenge(user, guild);
|
||||||
|
await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test challenge habit',
|
||||||
|
type: 'habit',
|
||||||
|
});
|
||||||
|
|
||||||
|
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
text: 'todo group',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await user.update({'stats.class': 'healer', 'stats.mp': 200, 'stats.lvl': 15});
|
||||||
|
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||||
|
|
||||||
|
await user.post('/user/class/cast/brightness');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
let memberTasks = await user.get('/tasks/user');
|
||||||
|
|
||||||
|
let syncedGroupTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.group.id === guild._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
let userChallengeTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.challenge.id === challenge._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(userChallengeTask).to.exist;
|
||||||
|
expect(syncedGroupTask).to.exist;
|
||||||
|
expect(userChallengeTask.value).to.equal(0);
|
||||||
|
expect(syncedGroupTask.value).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
// TODO find a way to have sinon working in integration tests
|
// TODO find a way to have sinon working in integration tests
|
||||||
// it doesn't work when tests are running separately from server
|
// it doesn't work when tests are running separately from server
|
||||||
it('passes correct target to spell when targetType === \'task\'');
|
it('passes correct target to spell when targetType === \'task\'');
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
generateChallenge,
|
generateChallenge,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
describe('POST /user/reset', () => {
|
describe('POST /user/reset', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -86,19 +87,34 @@ describe('POST /user/reset', () => {
|
|||||||
expect(user.tasksOrder.rewards).to.be.empty;
|
expect(user.tasksOrder.rewards).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not delete challenge tasks', async () => {
|
it('does not delete challenge or group tasks', async () => {
|
||||||
let guild = await generateGroup(user);
|
let guild = await generateGroup(user);
|
||||||
let challenge = await generateChallenge(user, guild);
|
let challenge = await generateChallenge(user, guild);
|
||||||
let task = await user.post(`/tasks/challenge/${challenge._id}`, {
|
await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
text: 'test challenge habit',
|
text: 'test challenge habit',
|
||||||
type: 'habit',
|
type: 'habit',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
text: 'todo group',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||||
|
|
||||||
await user.post('/user/reset');
|
await user.post('/user/reset');
|
||||||
await user.sync();
|
await user.sync();
|
||||||
|
|
||||||
let userChallengeTask = await user.get(`/tasks/${task._id}`);
|
let memberTasks = await user.get('/tasks/user');
|
||||||
|
|
||||||
expect(userChallengeTask).to.eql(task);
|
let syncedGroupTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.group.id === guild._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
let userChallengeTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.challenge.id === challenge._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(userChallengeTask).to.exist;
|
||||||
|
expect(syncedGroupTask).to.exist;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,6 +26,32 @@ describe('PUT /user', () => {
|
|||||||
expect(user.preferences.costume).to.eql(true);
|
expect(user.preferences.costume).to.eql(true);
|
||||||
expect(user.stats.hp).to.eql(14);
|
expect(user.stats.hp).to.eql(14);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('profile.name cannot be an empty string or null', async () => {
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': ' ', // string should be trimmed
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': '',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': null,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Top Level Protected Operations', () => {
|
context('Top Level Protected Operations', () => {
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import {
|
|||||||
requester,
|
requester,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import {
|
||||||
|
bcryptCompare,
|
||||||
|
sha1MakeSalt,
|
||||||
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
|
} from '../../../../../../website/server/libs/password';
|
||||||
|
|
||||||
describe('POST /user/auth/local/login', () => {
|
describe('POST /user/auth/local/login', () => {
|
||||||
let api;
|
let api;
|
||||||
@@ -72,4 +77,35 @@ describe('POST /user/auth/local/login', () => {
|
|||||||
message: t('invalidReqParams'),
|
message: t('invalidReqParams'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
await user.update({
|
||||||
|
'auth.local.hashed_password': sha1HashedPassword,
|
||||||
|
'auth.local.passwordHashMethod': 'sha1',
|
||||||
|
'auth.local.salt': salt,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||||
|
expect(user.auth.local.salt).to.equal(salt);
|
||||||
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
// login
|
||||||
|
await api.post(endpoint, {
|
||||||
|
username: user.auth.local.email,
|
||||||
|
password: textPassword,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||||
|
expect(user.auth.local.salt).to.be.undefined;
|
||||||
|
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare(textPassword, user.auth.local.hashed_password);
|
||||||
|
expect(isValidPassword).to.equal(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ describe('POST /user/auth/local/register', () => {
|
|||||||
expect(user._id).to.exist;
|
expect(user._id).to.exist;
|
||||||
expect(user.apiToken).to.exist;
|
expect(user.apiToken).to.exist;
|
||||||
expect(user.auth.local.username).to.eql(username);
|
expect(user.auth.local.username).to.eql(username);
|
||||||
|
expect(user.profile.name).to.eql(username);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('provides default tags and tasks', async () => {
|
it('provides default tags and tasks', async () => {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
|
|
||||||
describe('facebook', () => {
|
describe('facebook', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
let expectedResult = {id: facebookId};
|
let expectedResult = {id: facebookId, displayName: 'a facebook user'};
|
||||||
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
|
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
|
||||||
network = 'facebook';
|
network = 'facebook';
|
||||||
});
|
});
|
||||||
@@ -47,6 +47,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
expect(response.apiToken).to.exist;
|
expect(response.apiToken).to.exist;
|
||||||
expect(response.id).to.exist;
|
expect(response.id).to.exist;
|
||||||
expect(response.newUser).to.be.true;
|
expect(response.newUser).to.be.true;
|
||||||
|
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('logs an existing user in', async () => {
|
it('logs an existing user in', async () => {
|
||||||
@@ -88,7 +89,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
|
|
||||||
describe('google', () => {
|
describe('google', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
let expectedResult = {id: googleId};
|
let expectedResult = {id: googleId, displayName: 'a google user'};
|
||||||
sandbox.stub(passport._strategies.google, 'userProfile').yields(null, expectedResult);
|
sandbox.stub(passport._strategies.google, 'userProfile').yields(null, expectedResult);
|
||||||
network = 'google';
|
network = 'google';
|
||||||
});
|
});
|
||||||
@@ -102,6 +103,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
expect(response.apiToken).to.exist;
|
expect(response.apiToken).to.exist;
|
||||||
expect(response.id).to.exist;
|
expect(response.id).to.exist;
|
||||||
expect(response.newUser).to.be.true;
|
expect(response.newUser).to.be.true;
|
||||||
|
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('logs an existing user in', async () => {
|
it('logs an existing user in', async () => {
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import {
|
||||||
|
sha1MakeSalt,
|
||||||
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
|
} from '../../../../../../website/server/libs/password';
|
||||||
|
|
||||||
describe('POST /user/reset-password', async () => {
|
describe('POST /user/reset-password', async () => {
|
||||||
let endpoint = '/user/reset-password';
|
let endpoint = '/user/reset-password';
|
||||||
@@ -21,6 +25,33 @@ describe('POST /user/reset-password', async () => {
|
|||||||
expect(user.auth.local.hashed_password).to.not.eql(previousPassword);
|
expect(user.auth.local.hashed_password).to.not.eql(previousPassword);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
await user.update({
|
||||||
|
'auth.local.hashed_password': sha1HashedPassword,
|
||||||
|
'auth.local.passwordHashMethod': 'sha1',
|
||||||
|
'auth.local.salt': salt,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||||
|
expect(user.auth.local.salt).to.equal(salt);
|
||||||
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
// update email
|
||||||
|
await user.post(endpoint, {
|
||||||
|
email: user.auth.local.email,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||||
|
expect(user.auth.local.salt).to.be.undefined;
|
||||||
|
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||||
|
});
|
||||||
|
|
||||||
it('same message on error as on success', async () => {
|
it('same message on error as on success', async () => {
|
||||||
let response = await user.post(endpoint, {
|
let response = await user.post(endpoint, {
|
||||||
email: 'nonExistent@email.com',
|
email: 'nonExistent@email.com',
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../../helpers/api-v3-integration.helper';
|
} from '../../../../../helpers/api-v3-integration.helper';
|
||||||
|
import {
|
||||||
|
bcryptCompare,
|
||||||
|
sha1MakeSalt,
|
||||||
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
|
} from '../../../../../../website/server/libs/password';
|
||||||
|
|
||||||
const ENDPOINT = '/user/auth/update-email';
|
const ENDPOINT = '/user/auth/update-email';
|
||||||
|
|
||||||
@@ -66,6 +71,41 @@ describe('PUT /user/auth/update-email', () => {
|
|||||||
message: t('cannotFulfillReq'),
|
message: t('cannotFulfillReq'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
let myNewEmail = 'my-new-random-email@example.net';
|
||||||
|
|
||||||
|
await user.update({
|
||||||
|
'auth.local.hashed_password': sha1HashedPassword,
|
||||||
|
'auth.local.passwordHashMethod': 'sha1',
|
||||||
|
'auth.local.salt': salt,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||||
|
expect(user.auth.local.salt).to.equal(salt);
|
||||||
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
// update email
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
newEmail: myNewEmail,
|
||||||
|
password: textPassword,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ email: myNewEmail });
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.auth.local.email).to.equal(myNewEmail);
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||||
|
expect(user.auth.local.salt).to.be.undefined;
|
||||||
|
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare(textPassword, user.auth.local.hashed_password);
|
||||||
|
expect(isValidPassword).to.equal(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Social Login User', async () => {
|
context('Social Login User', async () => {
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../../helpers/api-v3-integration.helper';
|
} from '../../../../../helpers/api-v3-integration.helper';
|
||||||
|
import {
|
||||||
|
bcryptCompare,
|
||||||
|
sha1MakeSalt,
|
||||||
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
|
} from '../../../../../../website/server/libs/password';
|
||||||
|
|
||||||
const ENDPOINT = '/user/auth/update-password';
|
const ENDPOINT = '/user/auth/update-password';
|
||||||
|
|
||||||
@@ -89,4 +94,36 @@ describe('PUT /user/auth/update-password', async () => {
|
|||||||
message: t('invalidReqParams'),
|
message: t('invalidReqParams'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
await user.update({
|
||||||
|
'auth.local.hashed_password': sha1HashedPassword,
|
||||||
|
'auth.local.passwordHashMethod': 'sha1',
|
||||||
|
'auth.local.salt': salt,
|
||||||
|
});
|
||||||
|
|
||||||
|
user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||||
|
expect(user.auth.local.salt).to.equal(salt);
|
||||||
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
// update email
|
||||||
|
await user.put(ENDPOINT, {
|
||||||
|
password: textPassword,
|
||||||
|
newPassword,
|
||||||
|
confirmPassword: newPassword,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||||
|
expect(user.auth.local.salt).to.be.undefined;
|
||||||
|
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare(newPassword, user.auth.local.hashed_password);
|
||||||
|
expect(isValidPassword).to.equal(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../../helpers/api-v3-integration.helper';
|
} from '../../../../../helpers/api-v3-integration.helper';
|
||||||
|
import {
|
||||||
|
bcryptCompare,
|
||||||
|
sha1MakeSalt,
|
||||||
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
|
} from '../../../../../../website/server/libs/password';
|
||||||
|
|
||||||
const ENDPOINT = '/user/auth/update-username';
|
const ENDPOINT = '/user/auth/update-username';
|
||||||
|
|
||||||
@@ -24,6 +29,41 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
expect(user.auth.local.username).to.eql(newUsername);
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
|
let myNewUsername = 'my-new-username';
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
await user.update({
|
||||||
|
'auth.local.hashed_password': sha1HashedPassword,
|
||||||
|
'auth.local.passwordHashMethod': 'sha1',
|
||||||
|
'auth.local.salt': salt,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||||
|
expect(user.auth.local.salt).to.equal(salt);
|
||||||
|
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
// update email
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: myNewUsername,
|
||||||
|
password: textPassword,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: myNewUsername });
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.auth.local.username).to.eql(myNewUsername);
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||||
|
expect(user.auth.local.salt).to.be.undefined;
|
||||||
|
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare(textPassword, user.auth.local.hashed_password);
|
||||||
|
expect(isValidPassword).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
context('errors', async () => {
|
context('errors', async () => {
|
||||||
it('prevents username update if new username is already taken', async () => {
|
it('prevents username update if new username is already taken', async () => {
|
||||||
let existingUsername = 'existing-username';
|
let existingUsername = 'existing-username';
|
||||||
|
|||||||
562
test/api/v3/unit/libs/amazonPayments.test.js
Normal file
562
test/api/v3/unit/libs/amazonPayments.test.js
Normal file
@@ -0,0 +1,562 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import cc from 'coupon-code';
|
||||||
|
|
||||||
|
import {
|
||||||
|
generateGroup,
|
||||||
|
} from '../../../../helpers/api-unit.helper.js';
|
||||||
|
import { model as User } from '../../../../../website/server/models/user';
|
||||||
|
import { model as Coupon } from '../../../../../website/server/models/coupon';
|
||||||
|
import amzLib from '../../../../../website/server/libs/amazonPayments';
|
||||||
|
import payments from '../../../../../website/server/libs/payments';
|
||||||
|
import common from '../../../../../website/common';
|
||||||
|
|
||||||
|
const i18n = common.i18n;
|
||||||
|
|
||||||
|
describe('Amazon Payments', () => {
|
||||||
|
let subKey = 'basic_3mo';
|
||||||
|
|
||||||
|
describe('checkout', () => {
|
||||||
|
let user, orderReferenceId, headers;
|
||||||
|
let setOrderReferenceDetailsSpy;
|
||||||
|
let confirmOrderReferenceSpy;
|
||||||
|
let authorizeSpy;
|
||||||
|
let closeOrderReferenceSpy;
|
||||||
|
|
||||||
|
let paymentBuyGemsStub;
|
||||||
|
let paymentCreateSubscritionStub;
|
||||||
|
let amount = 5;
|
||||||
|
|
||||||
|
function expectAmazonStubs () {
|
||||||
|
expect(setOrderReferenceDetailsSpy).to.be.calledOnce;
|
||||||
|
expect(setOrderReferenceDetailsSpy).to.be.calledWith({
|
||||||
|
AmazonOrderReferenceId: orderReferenceId,
|
||||||
|
OrderReferenceAttributes: {
|
||||||
|
OrderTotal: {
|
||||||
|
CurrencyCode: amzLib.constants.CURRENCY_CODE,
|
||||||
|
Amount: amount,
|
||||||
|
},
|
||||||
|
SellerNote: amzLib.constants.SELLER_NOTE,
|
||||||
|
SellerOrderAttributes: {
|
||||||
|
SellerOrderId: common.uuid(),
|
||||||
|
StoreName: amzLib.constants.STORE_NAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(confirmOrderReferenceSpy).to.be.calledOnce;
|
||||||
|
expect(confirmOrderReferenceSpy).to.be.calledWith({ AmazonOrderReferenceId: orderReferenceId });
|
||||||
|
|
||||||
|
expect(authorizeSpy).to.be.calledOnce;
|
||||||
|
expect(authorizeSpy).to.be.calledWith({
|
||||||
|
AmazonOrderReferenceId: orderReferenceId,
|
||||||
|
AuthorizationReferenceId: common.uuid().substring(0, 32),
|
||||||
|
AuthorizationAmount: {
|
||||||
|
CurrencyCode: amzLib.constants.CURRENCY_CODE,
|
||||||
|
Amount: amount,
|
||||||
|
},
|
||||||
|
SellerAuthorizationNote: amzLib.constants.SELLER_NOTE,
|
||||||
|
TransactionTimeout: 0,
|
||||||
|
CaptureNow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(closeOrderReferenceSpy).to.be.calledOnce;
|
||||||
|
expect(closeOrderReferenceSpy).to.be.calledWith({ AmazonOrderReferenceId: orderReferenceId });
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
user = new User();
|
||||||
|
headers = {};
|
||||||
|
orderReferenceId = 'orderReferenceId';
|
||||||
|
|
||||||
|
setOrderReferenceDetailsSpy = sinon.stub(amzLib, 'setOrderReferenceDetails');
|
||||||
|
setOrderReferenceDetailsSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
confirmOrderReferenceSpy = sinon.stub(amzLib, 'confirmOrderReference');
|
||||||
|
confirmOrderReferenceSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
authorizeSpy = sinon.stub(amzLib, 'authorize');
|
||||||
|
authorizeSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
closeOrderReferenceSpy = sinon.stub(amzLib, 'closeOrderReference');
|
||||||
|
closeOrderReferenceSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems');
|
||||||
|
paymentBuyGemsStub.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription');
|
||||||
|
paymentCreateSubscritionStub.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
sinon.stub(common, 'uuid').returns('uuid-generated');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
amzLib.setOrderReferenceDetails.restore();
|
||||||
|
amzLib.confirmOrderReference.restore();
|
||||||
|
amzLib.authorize.restore();
|
||||||
|
amzLib.closeOrderReference.restore();
|
||||||
|
payments.buyGems.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
common.uuid.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should purchase gems', async () => {
|
||||||
|
await amzLib.checkout({user, orderReferenceId, headers});
|
||||||
|
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
expectAmazonStubs();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should gift gems', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
receivingUser.save();
|
||||||
|
let gift = {
|
||||||
|
type: 'gems',
|
||||||
|
gems: {
|
||||||
|
amount: 16,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
amount = 16 / 4;
|
||||||
|
await amzLib.checkout({gift, user, orderReferenceId, headers});
|
||||||
|
|
||||||
|
gift.member = receivingUser;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON_GIFT,
|
||||||
|
headers,
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
expectAmazonStubs();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should gift a subscription', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
receivingUser.save();
|
||||||
|
let gift = {
|
||||||
|
type: 'subscription',
|
||||||
|
subscription: {
|
||||||
|
key: subKey,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
amount = common.content.subscriptionBlocks[subKey].price;
|
||||||
|
|
||||||
|
await amzLib.checkout({user, orderReferenceId, headers, gift});
|
||||||
|
|
||||||
|
gift.member = receivingUser;
|
||||||
|
expect(paymentCreateSubscritionStub).to.be.calledOnce;
|
||||||
|
expect(paymentCreateSubscritionStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON_GIFT,
|
||||||
|
headers,
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
expectAmazonStubs();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subscribe', () => {
|
||||||
|
let user, group, amount, billingAgreementId, sub, coupon, groupId, headers;
|
||||||
|
let amazonSetBillingAgreementDetailsSpy;
|
||||||
|
let amazonConfirmBillingAgreementSpy;
|
||||||
|
let amazongAuthorizeOnBillingAgreementSpy;
|
||||||
|
let createSubSpy;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = 'customer-id';
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
amount = common.content.subscriptionBlocks[subKey].price;
|
||||||
|
billingAgreementId = 'billingAgreementId';
|
||||||
|
sub = {
|
||||||
|
key: subKey,
|
||||||
|
price: amount,
|
||||||
|
};
|
||||||
|
groupId = group._id;
|
||||||
|
headers = {};
|
||||||
|
|
||||||
|
amazonSetBillingAgreementDetailsSpy = sinon.stub(amzLib, 'setBillingAgreementDetails');
|
||||||
|
amazonSetBillingAgreementDetailsSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
amazonConfirmBillingAgreementSpy = sinon.stub(amzLib, 'confirmBillingAgreement');
|
||||||
|
amazonConfirmBillingAgreementSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
amazongAuthorizeOnBillingAgreementSpy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
||||||
|
amazongAuthorizeOnBillingAgreementSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
createSubSpy = sinon.stub(payments, 'createSubscription');
|
||||||
|
createSubSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
sinon.stub(common, 'uuid').returns('uuid-generated');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
amzLib.setBillingAgreementDetails.restore();
|
||||||
|
amzLib.confirmBillingAgreement.restore();
|
||||||
|
amzLib.authorizeOnBillingAgreement.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
common.uuid.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if we are missing a subscription', async () => {
|
||||||
|
await expect(amzLib.subscribe({
|
||||||
|
billingAgreementId,
|
||||||
|
coupon,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
headers,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: i18n.t('missingSubscriptionCode'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if we are missing a billingAgreementId', async () => {
|
||||||
|
await expect(amzLib.subscribe({
|
||||||
|
sub,
|
||||||
|
coupon,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
headers,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: 'Missing req.body.billingAgreementId',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when coupon code is missing', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
|
||||||
|
await expect(amzLib.subscribe({
|
||||||
|
billingAgreementId,
|
||||||
|
sub,
|
||||||
|
coupon,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
headers,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: i18n.t('couponCodeRequired'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when coupon code is invalid', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
sub.key = 'google_6mo';
|
||||||
|
coupon = 'example-coupon';
|
||||||
|
|
||||||
|
let couponModel = new Coupon();
|
||||||
|
couponModel.event = 'google_6mo';
|
||||||
|
await couponModel.save();
|
||||||
|
|
||||||
|
sinon.stub(cc, 'validate').returns('invalid');
|
||||||
|
|
||||||
|
await expect(amzLib.subscribe({
|
||||||
|
billingAgreementId,
|
||||||
|
sub,
|
||||||
|
coupon,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
headers,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('invalidCoupon'),
|
||||||
|
});
|
||||||
|
cc.validate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes with amazon with a coupon', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
sub.key = 'google_6mo';
|
||||||
|
coupon = 'example-coupon';
|
||||||
|
|
||||||
|
let couponModel = new Coupon();
|
||||||
|
couponModel.event = 'google_6mo';
|
||||||
|
let updatedCouponModel = await couponModel.save();
|
||||||
|
|
||||||
|
sinon.stub(cc, 'validate').returns(updatedCouponModel._id);
|
||||||
|
|
||||||
|
await amzLib.subscribe({
|
||||||
|
billingAgreementId,
|
||||||
|
sub,
|
||||||
|
coupon,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createSubSpy).to.be.calledOnce;
|
||||||
|
expect(createSubSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: billingAgreementId,
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
sub,
|
||||||
|
headers,
|
||||||
|
groupId,
|
||||||
|
});
|
||||||
|
|
||||||
|
cc.validate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes with amazon', async () => {
|
||||||
|
await amzLib.subscribe({
|
||||||
|
billingAgreementId,
|
||||||
|
sub,
|
||||||
|
coupon,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(amazonSetBillingAgreementDetailsSpy).to.be.calledOnce;
|
||||||
|
expect(amazonSetBillingAgreementDetailsSpy).to.be.calledWith({
|
||||||
|
AmazonBillingAgreementId: billingAgreementId,
|
||||||
|
BillingAgreementAttributes: {
|
||||||
|
SellerNote: amzLib.constants.SELLER_NOTE_SUBSCRIPTION,
|
||||||
|
SellerBillingAgreementAttributes: {
|
||||||
|
SellerBillingAgreementId: common.uuid(),
|
||||||
|
StoreName: amzLib.constants.STORE_NAME,
|
||||||
|
CustomInformation: amzLib.constants.SELLER_NOTE_SUBSCRIPTION,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(amazonConfirmBillingAgreementSpy).to.be.calledOnce;
|
||||||
|
expect(amazonConfirmBillingAgreementSpy).to.be.calledWith({
|
||||||
|
AmazonBillingAgreementId: billingAgreementId,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(amazongAuthorizeOnBillingAgreementSpy).to.be.calledOnce;
|
||||||
|
expect(amazongAuthorizeOnBillingAgreementSpy).to.be.calledWith({
|
||||||
|
AmazonBillingAgreementId: billingAgreementId,
|
||||||
|
AuthorizationReferenceId: common.uuid().substring(0, 32),
|
||||||
|
AuthorizationAmount: {
|
||||||
|
CurrencyCode: amzLib.constants.CURRENCY_CODE,
|
||||||
|
Amount: amount,
|
||||||
|
},
|
||||||
|
SellerAuthorizationNote: amzLib.constants.SELLER_NOTE_ATHORIZATION_SUBSCRIPTION,
|
||||||
|
TransactionTimeout: 0,
|
||||||
|
CaptureNow: true,
|
||||||
|
SellerNote: amzLib.constants.SELLER_NOTE_ATHORIZATION_SUBSCRIPTION,
|
||||||
|
SellerOrderAttributes: {
|
||||||
|
SellerOrderId: common.uuid(),
|
||||||
|
StoreName: amzLib.constants.STORE_NAME,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createSubSpy).to.be.calledOnce;
|
||||||
|
expect(createSubSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: billingAgreementId,
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
sub,
|
||||||
|
headers,
|
||||||
|
groupId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancelSubscription', () => {
|
||||||
|
let user, group, headers, billingAgreementId, subscriptionBlock, subscriptionLength;
|
||||||
|
let getBillingAgreementDetailsSpy;
|
||||||
|
let paymentCancelSubscriptionSpy;
|
||||||
|
|
||||||
|
function expectAmazonStubs () {
|
||||||
|
expect(getBillingAgreementDetailsSpy).to.be.calledOnce;
|
||||||
|
expect(getBillingAgreementDetailsSpy).to.be.calledWith({
|
||||||
|
AmazonBillingAgreementId: billingAgreementId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = 'customer-id';
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
group.purchased.plan.lastBillingDate = new Date();
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
subscriptionBlock = common.content.subscriptionBlocks[subKey];
|
||||||
|
subscriptionLength = subscriptionBlock.months * 30;
|
||||||
|
|
||||||
|
headers = {};
|
||||||
|
|
||||||
|
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails');
|
||||||
|
getBillingAgreementDetailsSpy.returnsPromise().resolves({
|
||||||
|
BillingAgreementDetails: {
|
||||||
|
BillingAgreementStatus: {State: 'Closed'},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription');
|
||||||
|
paymentCancelSubscriptionSpy.returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
amzLib.getBillingAgreementDetails.restore();
|
||||||
|
payments.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if we are missing a subscription', async () => {
|
||||||
|
user.purchased.plan.customerId = undefined;
|
||||||
|
|
||||||
|
await expect(amzLib.cancelSubscription({user}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a user subscription', async () => {
|
||||||
|
billingAgreementId = user.purchased.plan.customerId;
|
||||||
|
|
||||||
|
await amzLib.cancelSubscription({user, headers});
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
expectAmazonStubs();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close a user subscription if amazon not closed', async () => {
|
||||||
|
amzLib.getBillingAgreementDetails.restore();
|
||||||
|
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||||
|
.returnsPromise()
|
||||||
|
.resolves({
|
||||||
|
BillingAgreementDetails: {
|
||||||
|
BillingAgreementStatus: {State: 'Open'},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').returnsPromise().resolves({});
|
||||||
|
billingAgreementId = user.purchased.plan.customerId;
|
||||||
|
|
||||||
|
await amzLib.cancelSubscription({user, headers});
|
||||||
|
|
||||||
|
expectAmazonStubs();
|
||||||
|
expect(closeBillingAgreementSpy).to.be.calledOnce;
|
||||||
|
expect(closeBillingAgreementSpy).to.be.calledWith({
|
||||||
|
AmazonBillingAgreementId: billingAgreementId,
|
||||||
|
});
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
amzLib.closeBillingAgreement.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if group is not found', async () => {
|
||||||
|
await expect(amzLib.cancelSubscription({user, groupId: 'fake-id'}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 404,
|
||||||
|
name: 'NotFound',
|
||||||
|
message: i18n.t('groupNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if user is not group leader', async () => {
|
||||||
|
let nonLeader = new User();
|
||||||
|
nonLeader.guilds.push(group._id);
|
||||||
|
await nonLeader.save();
|
||||||
|
|
||||||
|
await expect(amzLib.cancelSubscription({user: nonLeader, groupId: group._id}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('onlyGroupLeaderCanManageSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a group subscription', async () => {
|
||||||
|
billingAgreementId = group.purchased.plan.customerId;
|
||||||
|
|
||||||
|
await amzLib.cancelSubscription({user, groupId: group._id, headers});
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId: group._id,
|
||||||
|
nextBill: moment(group.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
expectAmazonStubs();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close a group subscription if amazon not closed', async () => {
|
||||||
|
amzLib.getBillingAgreementDetails.restore();
|
||||||
|
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||||
|
.returnsPromise()
|
||||||
|
.resolves({
|
||||||
|
BillingAgreementDetails: {
|
||||||
|
BillingAgreementStatus: {State: 'Open'},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').returnsPromise().resolves({});
|
||||||
|
billingAgreementId = group.purchased.plan.customerId;
|
||||||
|
|
||||||
|
await amzLib.cancelSubscription({user, groupId: group._id, headers});
|
||||||
|
|
||||||
|
expectAmazonStubs();
|
||||||
|
expect(closeBillingAgreementSpy).to.be.calledOnce;
|
||||||
|
expect(closeBillingAgreementSpy).to.be.calledWith({
|
||||||
|
AmazonBillingAgreementId: billingAgreementId,
|
||||||
|
});
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId: group._id,
|
||||||
|
nextBill: moment(group.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
|
||||||
|
paymentMethod: amzLib.constants.PAYMENT_METHOD_AMAZON,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
amzLib.closeBillingAgreement.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -303,6 +303,7 @@ describe('analyticsService', () => {
|
|||||||
contributorLevel: 1,
|
contributorLevel: 1,
|
||||||
subscription: 'foo-plan',
|
subscription: 'foo-plan',
|
||||||
balance: 12,
|
balance: 12,
|
||||||
|
balanceGemAmount: 48,
|
||||||
loginIncentives: 1,
|
loginIncentives: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -975,19 +975,18 @@ describe('recoverCron', () => {
|
|||||||
sandbox.restore();
|
sandbox.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if user cannot be found', async (done) => {
|
it('throws an error if user cannot be found', async () => {
|
||||||
execStub.returns(Bluebird.resolve(null));
|
execStub.returns(Bluebird.resolve(null));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await recoverCron(status, locals);
|
await recoverCron(status, locals);
|
||||||
|
throw new Error('no exception when user cannot be found');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err.message).to.eql(`User ${locals.user._id} not found while recovering.`);
|
expect(err.message).to.eql(`User ${locals.user._id} not found while recovering.`);
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('increases status.times count and reruns up to 4 times', async (done) => {
|
it('increases status.times count and reruns up to 4 times', async () => {
|
||||||
execStub.returns(Bluebird.resolve({_cronSignature: 'RUNNING_CRON'}));
|
execStub.returns(Bluebird.resolve({_cronSignature: 'RUNNING_CRON'}));
|
||||||
execStub.onCall(4).returns(Bluebird.resolve({_cronSignature: 'NOT_RUNNING'}));
|
execStub.onCall(4).returns(Bluebird.resolve({_cronSignature: 'NOT_RUNNING'}));
|
||||||
|
|
||||||
@@ -995,20 +994,17 @@ describe('recoverCron', () => {
|
|||||||
|
|
||||||
expect(status.times).to.eql(4);
|
expect(status.times).to.eql(4);
|
||||||
expect(locals.user).to.eql({_cronSignature: 'NOT_RUNNING'});
|
expect(locals.user).to.eql({_cronSignature: 'NOT_RUNNING'});
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if recoverCron runs 5 times', async (done) => {
|
it('throws an error if recoverCron runs 5 times', async () => {
|
||||||
execStub.returns(Bluebird.resolve({_cronSignature: 'RUNNING_CRON'}));
|
execStub.returns(Bluebird.resolve({_cronSignature: 'RUNNING_CRON'}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await recoverCron(status, locals);
|
await recoverCron(status, locals);
|
||||||
|
throw new Error('no exception when recoverCron runs 5 times');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(status.times).to.eql(5);
|
expect(status.times).to.eql(5);
|
||||||
expect(err.message).to.eql(`Impossible to recover from cron for user ${locals.user._id}.`);
|
expect(err.message).to.eql(`Impossible to recover from cron for user ${locals.user._id}.`);
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
268
test/api/v3/unit/libs/googlePayments.test.js
Normal file
268
test/api/v3/unit/libs/googlePayments.test.js
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
import iapModule from '../../../../../website/server/libs/inAppPurchases';
|
||||||
|
import payments from '../../../../../website/server/libs/payments';
|
||||||
|
import googlePayments from '../../../../../website/server/libs/googlePayments';
|
||||||
|
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||||
|
import {model as User} from '../../../../../website/server/models/user';
|
||||||
|
import common from '../../../../../website/common';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const i18n = common.i18n;
|
||||||
|
|
||||||
|
describe('Google Payments', () => {
|
||||||
|
let subKey = 'basic_3mo';
|
||||||
|
|
||||||
|
describe('verifyGemPurchase', () => {
|
||||||
|
let sku, user, token, receipt, signature, headers;
|
||||||
|
let iapSetupStub, iapValidateStub, iapIsValidatedStub, paymentBuyGemsStub;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sku = 'com.habitrpg.android.habitica.iap.21gems';
|
||||||
|
user = new User();
|
||||||
|
receipt = `{"token": "${token}", "productId": "${sku}"}`;
|
||||||
|
signature = '';
|
||||||
|
headers = {};
|
||||||
|
|
||||||
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
|
.returnsPromise().resolves();
|
||||||
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
|
.returnsPromise().resolves({});
|
||||||
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
|
.returns(true);
|
||||||
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
iapModule.setup.restore();
|
||||||
|
iapModule.validate.restore();
|
||||||
|
iapModule.isValidated.restore();
|
||||||
|
payments.buyGems.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if receipt is invalid', async () => {
|
||||||
|
iapModule.isValidated.restore();
|
||||||
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
|
.returns(false);
|
||||||
|
|
||||||
|
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: googlePayments.constants.RESPONSE_INVALID_RECEIPT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if productId is invalid', async () => {
|
||||||
|
receipt = `{"token": "${token}", "productId": "invalid"}`;
|
||||||
|
|
||||||
|
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: googlePayments.constants.RESPONSE_INVALID_ITEM,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('purchases gems', async () => {
|
||||||
|
await googlePayments.verifyGemPurchase(user, receipt, signature, headers);
|
||||||
|
|
||||||
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledWith(iap.GOOGLE, {
|
||||||
|
data: receipt,
|
||||||
|
signature,
|
||||||
|
});
|
||||||
|
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||||
|
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||||
|
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||||
|
amount: 5.25,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subscribe', () => {
|
||||||
|
let sub, sku, user, token, receipt, signature, headers, nextPaymentProcessing;
|
||||||
|
let iapSetupStub, iapValidateStub, iapIsValidatedStub, paymentsCreateSubscritionStub;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sub = common.content.subscriptionBlocks[subKey];
|
||||||
|
sku = 'com.habitrpg.android.habitica.subscription.3month';
|
||||||
|
|
||||||
|
token = 'test-token';
|
||||||
|
headers = {};
|
||||||
|
receipt = `{"token": "${token}"}`;
|
||||||
|
signature = '';
|
||||||
|
nextPaymentProcessing = moment.utc().add({days: 2});
|
||||||
|
|
||||||
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
|
.returnsPromise().resolves();
|
||||||
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
|
.returnsPromise().resolves({});
|
||||||
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
|
.returns(true);
|
||||||
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
iapModule.setup.restore();
|
||||||
|
iapModule.validate.restore();
|
||||||
|
iapModule.isValidated.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if receipt is invalid', async () => {
|
||||||
|
iapModule.isValidated.restore();
|
||||||
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
|
.returns(false);
|
||||||
|
|
||||||
|
await expect(googlePayments.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: googlePayments.constants.RESPONSE_INVALID_RECEIPT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if sku is invalid', async () => {
|
||||||
|
sku = 'invalid';
|
||||||
|
|
||||||
|
await expect(googlePayments.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: googlePayments.constants.RESPONSE_INVALID_ITEM,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a user subscription', async () => {
|
||||||
|
await googlePayments.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing);
|
||||||
|
|
||||||
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledWith(iap.GOOGLE, {
|
||||||
|
data: receipt,
|
||||||
|
signature,
|
||||||
|
});
|
||||||
|
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||||
|
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||||
|
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: token,
|
||||||
|
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||||
|
sub,
|
||||||
|
headers,
|
||||||
|
additionalData: {data: receipt, signature},
|
||||||
|
nextPaymentProcessing,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancelSubscribe ', () => {
|
||||||
|
let user, token, receipt, signature, headers, customerId, expirationDate;
|
||||||
|
let iapSetupStub, iapValidateStub, iapIsValidatedStub, iapGetPurchaseDataStub, paymentCancelSubscriptionSpy;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
token = 'test-token';
|
||||||
|
headers = {};
|
||||||
|
receipt = `{"token": "${token}"}`;
|
||||||
|
signature = '';
|
||||||
|
customerId = 'test-customerId';
|
||||||
|
expirationDate = moment.utc();
|
||||||
|
|
||||||
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
|
.returnsPromise().resolves();
|
||||||
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
|
.returnsPromise().resolves({
|
||||||
|
expirationDate,
|
||||||
|
});
|
||||||
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
|
.returns([{expirationDate: expirationDate.toDate()}]);
|
||||||
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
|
.returns(true);
|
||||||
|
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = customerId;
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.additionalData = {data: receipt, signature};
|
||||||
|
|
||||||
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
iapModule.setup.restore();
|
||||||
|
iapModule.validate.restore();
|
||||||
|
iapModule.isValidated.restore();
|
||||||
|
iapModule.getPurchaseData.restore();
|
||||||
|
payments.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if we are missing a subscription', async () => {
|
||||||
|
user.purchased.plan.additionalData = undefined;
|
||||||
|
|
||||||
|
await expect(googlePayments.cancelSubscribe(user, headers))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if subscription is still valid', async () => {
|
||||||
|
iapModule.getPurchaseData.restore();
|
||||||
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
|
.returns([{expirationDate: expirationDate.add({day: 1}).toDate()}]);
|
||||||
|
|
||||||
|
await expect(googlePayments.cancelSubscribe(user, headers))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: googlePayments.constants.RESPONSE_STILL_VALID,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if receipt is invalid', async () => {
|
||||||
|
iapModule.isValidated.restore();
|
||||||
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
|
.returns(false);
|
||||||
|
|
||||||
|
await expect(googlePayments.cancelSubscribe(user, headers))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: googlePayments.constants.RESPONSE_INVALID_RECEIPT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a user subscription', async () => {
|
||||||
|
await googlePayments.cancelSubscribe(user, headers);
|
||||||
|
|
||||||
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledWith(iap.GOOGLE, {
|
||||||
|
data: receipt,
|
||||||
|
signature,
|
||||||
|
});
|
||||||
|
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||||
|
expect(iapIsValidatedStub).to.be.calledWith({
|
||||||
|
expirationDate,
|
||||||
|
});
|
||||||
|
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||||
|
nextBill: expirationDate.toDate(),
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,24 +1,222 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
encrypt as encryptPassword,
|
sha1Encrypt as sha1EncryptPassword,
|
||||||
makeSalt,
|
sha1MakeSalt,
|
||||||
|
bcryptHash,
|
||||||
|
bcryptCompare,
|
||||||
|
compare,
|
||||||
|
convertToBcrypt,
|
||||||
} from '../../../../../website/server/libs/password';
|
} from '../../../../../website/server/libs/password';
|
||||||
|
|
||||||
describe('Password Utilities', () => {
|
describe('Password Utilities', () => {
|
||||||
|
describe('compare', () => {
|
||||||
|
it('can compare a correct password hashed with SHA1', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
let user = {
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
hashed_password: hashedPassword,
|
||||||
|
salt,
|
||||||
|
passwordHashMethod: 'sha1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let isValidPassword = await compare(user, textPassword);
|
||||||
|
expect(isValidPassword).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can compare an invalid password hashed with SHA1', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
let user = {
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
hashed_password: hashedPassword,
|
||||||
|
salt,
|
||||||
|
passwordHashMethod: 'sha1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let isValidPassword = await compare(user, 'wrongPassword');
|
||||||
|
expect(isValidPassword).to.eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can compare a correct password hashed with bcrypt', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
let user = {
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
hashed_password: hashedPassword,
|
||||||
|
passwordHashMethod: 'bcrypt',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let isValidPassword = await compare(user, textPassword);
|
||||||
|
expect(isValidPassword).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can compare an invalid password hashed with bcrypt', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
let user = {
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
hashed_password: hashedPassword,
|
||||||
|
passwordHashMethod: 'bcrypt',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let isValidPassword = await compare(user, 'wrongPassword');
|
||||||
|
expect(isValidPassword).to.eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if user is missing', async () => {
|
||||||
|
try {
|
||||||
|
await compare(null, 'some password');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.toString()).to.equal('Error: user and passwordToCheck are required parameters.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if passwordToCheck is missing', async () => {
|
||||||
|
try {
|
||||||
|
await compare({a: true});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.toString()).to.equal('Error: user and passwordToCheck are required parameters.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if an invalid hashing method is used', async () => {
|
||||||
|
try {
|
||||||
|
await compare({
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
passwordHashMethod: 'invalid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, 'pass');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.toString()).to.equal('Error: Invalid password hash method.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if comparing the same password', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
|
||||||
|
expect(isValidPassword).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if comparing a different password', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
|
||||||
|
expect(isValidPassword).to.eql(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convertToBcrypt', () => {
|
||||||
|
it('converts an user password hashed with sha1 to bcrypt', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
let user = {
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
hashed_password: hashedPassword,
|
||||||
|
salt,
|
||||||
|
passwordHashMethod: 'sha1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await convertToBcrypt(user, textPassword);
|
||||||
|
expect(user.auth.local.salt).to.be.undefined;
|
||||||
|
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||||
|
expect(user.auth.local.hashed_password).to.be.a.string;
|
||||||
|
|
||||||
|
let isValidPassword = await compare(user, textPassword);
|
||||||
|
expect(isValidPassword).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if user is missing', async () => {
|
||||||
|
try {
|
||||||
|
await convertToBcrypt(null, 'string');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.toString()).to.equal('Error: user and plainTextPassword are required parameters.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if plainTextPassword is missing', async () => {
|
||||||
|
try {
|
||||||
|
await convertToBcrypt({a: true});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.toString()).to.equal('Error: user and plainTextPassword are required parameters.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bcrypt', () => {
|
||||||
|
describe('Hash', () => {
|
||||||
|
it('returns a hashed string', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
expect(hashedPassword).to.be.a.string;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Compare', () => {
|
||||||
|
it('returns true if comparing the same password', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
|
||||||
|
expect(isValidPassword).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if comparing a different password', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let hashedPassword = await bcryptHash(textPassword);
|
||||||
|
|
||||||
|
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
|
||||||
|
expect(isValidPassword).to.eql(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SHA1', () => {
|
||||||
describe('Encrypt', () => {
|
describe('Encrypt', () => {
|
||||||
it('always encrypt the same password to the same value when using the same salt', () => {
|
it('always encrypt the same password to the same value when using the same salt', () => {
|
||||||
let textPassword = 'mySecretPassword';
|
let textPassword = 'mySecretPassword';
|
||||||
let salt = makeSalt();
|
let salt = sha1MakeSalt();
|
||||||
let encryptedPassword = encryptPassword(textPassword, salt);
|
let encryptedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
expect(encryptPassword(textPassword, salt)).to.eql(encryptedPassword);
|
expect(sha1EncryptPassword(textPassword, salt)).to.eql(encryptedPassword);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('never encrypt the same password to the same value when using a different salt', () => {
|
it('never encrypt the same password to the same value when using a different salt', () => {
|
||||||
let textPassword = 'mySecretPassword';
|
let textPassword = 'mySecretPassword';
|
||||||
let aSalt = makeSalt();
|
let aSalt = sha1MakeSalt();
|
||||||
let anotherSalt = makeSalt();
|
let anotherSalt = sha1MakeSalt();
|
||||||
let anEncryptedPassword = encryptPassword(textPassword, aSalt);
|
let anEncryptedPassword = sha1EncryptPassword(textPassword, aSalt);
|
||||||
let anotherEncryptedPassword = encryptPassword(textPassword, anotherSalt);
|
let anotherEncryptedPassword = sha1EncryptPassword(textPassword, anotherSalt);
|
||||||
|
|
||||||
expect(anEncryptedPassword).not.to.eql(anotherEncryptedPassword);
|
expect(anEncryptedPassword).not.to.eql(anotherEncryptedPassword);
|
||||||
});
|
});
|
||||||
@@ -26,16 +224,17 @@ describe('Password Utilities', () => {
|
|||||||
|
|
||||||
describe('Make Salt', () => {
|
describe('Make Salt', () => {
|
||||||
it('creates a salt with length 10 by default', () => {
|
it('creates a salt with length 10 by default', () => {
|
||||||
let salt = makeSalt();
|
let salt = sha1MakeSalt();
|
||||||
|
|
||||||
expect(salt.length).to.eql(10);
|
expect(salt.length).to.eql(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can create a salt of any length', () => {
|
it('can create a salt of any length', () => {
|
||||||
let length = 24;
|
let length = 24;
|
||||||
let salt = makeSalt(length);
|
let salt = sha1MakeSalt(length);
|
||||||
|
|
||||||
expect(salt.length).to.eql(length);
|
expect(salt.length).to.eql(length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
generateGroup,
|
generateGroup,
|
||||||
} from '../../../../helpers/api-unit.helper.js';
|
} from '../../../../helpers/api-unit.helper.js';
|
||||||
import i18n from '../../../../../website/common/script/i18n';
|
import i18n from '../../../../../website/common/script/i18n';
|
||||||
import amzLib from '../../../../../website/server/libs/amazonPayments';
|
|
||||||
|
|
||||||
describe('payments/index', () => {
|
describe('payments/index', () => {
|
||||||
let user, group, data, plan;
|
let user, group, data, plan;
|
||||||
@@ -748,15 +747,17 @@ describe('payments/index', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sends gem donation message in each participant\'s language', async () => {
|
it('sends gem donation message in each participant\'s language', async () => {
|
||||||
|
// TODO using english for both users because other languages are not loaded
|
||||||
|
// for api.buyGems
|
||||||
await recipient.update({
|
await recipient.update({
|
||||||
'preferences.language': 'es',
|
'preferences.language': 'en',
|
||||||
});
|
});
|
||||||
await user.update({
|
await user.update({
|
||||||
'preferences.language': 'cs',
|
'preferences.language': 'en',
|
||||||
});
|
});
|
||||||
await api.buyGems(data);
|
await api.buyGems(data);
|
||||||
|
|
||||||
let [recipientsMessageContent, sendersMessageContent] = ['es', 'cs'].map((lang) => {
|
let [recipientsMessageContent, sendersMessageContent] = ['en', 'en'].map((lang) => {
|
||||||
let messageContent = t('giftedGemsFull', {
|
let messageContent = t('giftedGemsFull', {
|
||||||
username: recipient.profile.name,
|
username: recipient.profile.name,
|
||||||
sender: user.profile.name,
|
sender: user.profile.name,
|
||||||
@@ -816,110 +817,4 @@ describe('payments/index', () => {
|
|||||||
expect(updatedGroup.purchased.plan.quantity).to.eql(3);
|
expect(updatedGroup.purchased.plan.quantity).to.eql(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('payWithStripe', () => {
|
|
||||||
let spy;
|
|
||||||
let stripeCreateCustomerSpy;
|
|
||||||
let createSubSpy;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
spy = sinon.stub(stripe.subscriptions, 'update');
|
|
||||||
spy.returnsPromise().resolves;
|
|
||||||
|
|
||||||
stripeCreateCustomerSpy = sinon.stub(stripe.customers, 'create');
|
|
||||||
let stripCustomerResponse = {
|
|
||||||
subscriptions: {
|
|
||||||
data: [{id: 'test-id'}],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
stripeCreateCustomerSpy.returnsPromise().resolves(stripCustomerResponse);
|
|
||||||
|
|
||||||
createSubSpy = sinon.stub(api, 'createSubscription');
|
|
||||||
createSubSpy.returnsPromise().resolves({});
|
|
||||||
|
|
||||||
data.groupId = group._id;
|
|
||||||
data.sub.quantity = 3;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
sinon.restore(stripe.subscriptions.update);
|
|
||||||
stripe.customers.create.restore();
|
|
||||||
api.createSubscription.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('subscribes with stripe', async () => {
|
|
||||||
let token = 'test-token';
|
|
||||||
let gift;
|
|
||||||
let sub = data.sub;
|
|
||||||
let groupId = group._id;
|
|
||||||
let email = 'test@test.com';
|
|
||||||
let headers = {};
|
|
||||||
let coupon;
|
|
||||||
|
|
||||||
await api.payWithStripe([
|
|
||||||
token,
|
|
||||||
user,
|
|
||||||
gift,
|
|
||||||
sub,
|
|
||||||
groupId,
|
|
||||||
email,
|
|
||||||
headers,
|
|
||||||
coupon,
|
|
||||||
], stripe);
|
|
||||||
|
|
||||||
expect(stripeCreateCustomerSpy.calledOnce).to.be.true;
|
|
||||||
expect(createSubSpy.calledOnce).to.be.true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('subscribeWithAmazon', () => {
|
|
||||||
let amazonSetBillingAgreementDetailsSpy;
|
|
||||||
let amazonConfirmBillingAgreementSpy;
|
|
||||||
let amazongAuthorizeOnBillingAgreementSpy;
|
|
||||||
let createSubSpy;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
amazonSetBillingAgreementDetailsSpy = sinon.stub(amzLib, 'setBillingAgreementDetails');
|
|
||||||
amazonSetBillingAgreementDetailsSpy.returnsPromise().resolves({});
|
|
||||||
|
|
||||||
amazonConfirmBillingAgreementSpy = sinon.stub(amzLib, 'confirmBillingAgreement');
|
|
||||||
amazonConfirmBillingAgreementSpy.returnsPromise().resolves({});
|
|
||||||
|
|
||||||
amazongAuthorizeOnBillingAgreementSpy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
|
||||||
amazongAuthorizeOnBillingAgreementSpy.returnsPromise().resolves({});
|
|
||||||
|
|
||||||
createSubSpy = sinon.stub(api, 'createSubscription');
|
|
||||||
createSubSpy.returnsPromise().resolves({});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
amzLib.setBillingAgreementDetails.restore();
|
|
||||||
amzLib.confirmBillingAgreement.restore();
|
|
||||||
amzLib.authorizeOnBillingAgreement.restore();
|
|
||||||
api.createSubscription.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('subscribes with stripe', async () => {
|
|
||||||
let billingAgreementId = 'billingAgreementId';
|
|
||||||
let sub = data.sub;
|
|
||||||
let coupon;
|
|
||||||
let groupId = group._id;
|
|
||||||
let headers = {};
|
|
||||||
|
|
||||||
await api.subscribeWithAmazon([
|
|
||||||
billingAgreementId,
|
|
||||||
sub,
|
|
||||||
coupon,
|
|
||||||
sub,
|
|
||||||
user,
|
|
||||||
groupId,
|
|
||||||
headers,
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(amazonSetBillingAgreementDetailsSpy.calledOnce).to.be.true;
|
|
||||||
expect(amazonConfirmBillingAgreementSpy.calledOnce).to.be.true;
|
|
||||||
expect(amazongAuthorizeOnBillingAgreementSpy.calledOnce).to.be.true;
|
|
||||||
expect(createSubSpy.calledOnce).to.be.true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
528
test/api/v3/unit/libs/paypalPayments.test.js
Normal file
528
test/api/v3/unit/libs/paypalPayments.test.js
Normal file
@@ -0,0 +1,528 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
import nconf from 'nconf';
|
||||||
|
import moment from 'moment';
|
||||||
|
import cc from 'coupon-code';
|
||||||
|
|
||||||
|
import payments from '../../../../../website/server/libs/payments';
|
||||||
|
import paypalPayments from '../../../../../website/server/libs/paypalPayments';
|
||||||
|
import {
|
||||||
|
generateGroup,
|
||||||
|
} from '../../../../helpers/api-unit.helper.js';
|
||||||
|
import { model as User } from '../../../../../website/server/models/user';
|
||||||
|
import { model as Coupon } from '../../../../../website/server/models/coupon';
|
||||||
|
import common from '../../../../../website/common';
|
||||||
|
|
||||||
|
const BASE_URL = nconf.get('BASE_URL');
|
||||||
|
const i18n = common.i18n;
|
||||||
|
|
||||||
|
describe('Paypal Payments', () => {
|
||||||
|
let subKey = 'basic_3mo';
|
||||||
|
|
||||||
|
describe('checkout', () => {
|
||||||
|
let paypalPaymentCreateStub;
|
||||||
|
let approvalHerf;
|
||||||
|
|
||||||
|
function getPaypalCreateOptions (description, amount) {
|
||||||
|
return {
|
||||||
|
intent: 'sale',
|
||||||
|
payer: { payment_method: 'Paypal' },
|
||||||
|
redirect_urls: {
|
||||||
|
return_url: `${BASE_URL}/paypal/checkout/success`,
|
||||||
|
cancel_url: `${BASE_URL}`,
|
||||||
|
},
|
||||||
|
transactions: [{
|
||||||
|
item_list: {
|
||||||
|
items: [{
|
||||||
|
name: description,
|
||||||
|
price: amount,
|
||||||
|
currency: 'USD',
|
||||||
|
quantity: 1,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
currency: 'USD',
|
||||||
|
total: amount,
|
||||||
|
},
|
||||||
|
description,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
approvalHerf = 'approval_href';
|
||||||
|
paypalPaymentCreateStub = sinon.stub(paypalPayments, 'paypalPaymentCreate')
|
||||||
|
.returnsPromise().resolves({
|
||||||
|
links: [{ rel: 'approval_url', href: approvalHerf }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.paypalPaymentCreate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a link for gem purchases', async () => {
|
||||||
|
let link = await paypalPayments.checkout();
|
||||||
|
|
||||||
|
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||||
|
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems', 5.00));
|
||||||
|
expect(link).to.eql(approvalHerf);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a link for gifting gems', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
let gift = {
|
||||||
|
type: 'gems',
|
||||||
|
gems: {
|
||||||
|
amount: 16,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let link = await paypalPayments.checkout({gift});
|
||||||
|
|
||||||
|
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||||
|
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems (Gift)', '4.00'));
|
||||||
|
expect(link).to.eql(approvalHerf);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a link for gifting a subscription', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
receivingUser.save();
|
||||||
|
let gift = {
|
||||||
|
type: 'subscription',
|
||||||
|
subscription: {
|
||||||
|
key: subKey,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let link = await paypalPayments.checkout({gift});
|
||||||
|
|
||||||
|
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||||
|
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('mo. Habitica Subscription (Gift)', '15.00'));
|
||||||
|
expect(link).to.eql(approvalHerf);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkout success', () => {
|
||||||
|
let user, gift, customerId, paymentId;
|
||||||
|
let paypalPaymentExecuteStub, paymentBuyGemsStub, paymentsCreateSubscritionStub;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = new User();
|
||||||
|
customerId = 'customerId-test';
|
||||||
|
paymentId = 'paymentId-test';
|
||||||
|
|
||||||
|
paypalPaymentExecuteStub = sinon.stub(paypalPayments, 'paypalPaymentExecute').returnsPromise().resolves({});
|
||||||
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
||||||
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.paypalPaymentExecute.restore();
|
||||||
|
payments.buyGems.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('purchases gems', async () => {
|
||||||
|
await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId});
|
||||||
|
|
||||||
|
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||||
|
expect(paypalPaymentExecuteStub).to.be.calledWith(paymentId, { payer_id: customerId });
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId,
|
||||||
|
paymentMethod: 'Paypal',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts gems', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
await receivingUser.save();
|
||||||
|
gift = {
|
||||||
|
type: 'gems',
|
||||||
|
gems: {
|
||||||
|
amount: 16,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId});
|
||||||
|
|
||||||
|
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||||
|
expect(paypalPaymentExecuteStub).to.be.calledWith(paymentId, { payer_id: customerId });
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId,
|
||||||
|
paymentMethod: 'PayPal (Gift)',
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts subscription', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
await receivingUser.save();
|
||||||
|
gift = {
|
||||||
|
type: 'subscription',
|
||||||
|
subscription: {
|
||||||
|
key: subKey,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId});
|
||||||
|
|
||||||
|
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||||
|
expect(paypalPaymentExecuteStub).to.be.calledWith(paymentId, { payer_id: customerId });
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId,
|
||||||
|
paymentMethod: 'PayPal (Gift)',
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subscribe', () => {
|
||||||
|
let coupon, sub, approvalHerf;
|
||||||
|
let paypalBillingAgreementCreateStub;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
approvalHerf = 'approvalHerf-test';
|
||||||
|
sub = common.content.subscriptionBlocks[subKey];
|
||||||
|
|
||||||
|
paypalBillingAgreementCreateStub = sinon.stub(paypalPayments, 'paypalBillingAgreementCreate')
|
||||||
|
.returnsPromise().resolves({
|
||||||
|
links: [{ rel: 'approval_url', href: approvalHerf }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.paypalBillingAgreementCreate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when coupon code is missing', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
|
||||||
|
await expect(paypalPayments.subscribe({sub, coupon}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: i18n.t('couponCodeRequired'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when coupon code is invalid', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
sub.key = 'google_6mo';
|
||||||
|
coupon = 'example-coupon';
|
||||||
|
|
||||||
|
let couponModel = new Coupon();
|
||||||
|
couponModel.event = 'google_6mo';
|
||||||
|
await couponModel.save();
|
||||||
|
|
||||||
|
sinon.stub(cc, 'validate').returns('invalid');
|
||||||
|
|
||||||
|
await expect(paypalPayments.subscribe({sub, coupon}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('invalidCoupon'),
|
||||||
|
});
|
||||||
|
cc.validate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes with amazon with a coupon', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
sub.key = 'google_6mo';
|
||||||
|
coupon = 'example-coupon';
|
||||||
|
|
||||||
|
let couponModel = new Coupon();
|
||||||
|
couponModel.event = 'google_6mo';
|
||||||
|
let updatedCouponModel = await couponModel.save();
|
||||||
|
|
||||||
|
sinon.stub(cc, 'validate').returns(updatedCouponModel._id);
|
||||||
|
|
||||||
|
let link = await paypalPayments.subscribe({sub, coupon});
|
||||||
|
|
||||||
|
expect(link).to.eql(approvalHerf);
|
||||||
|
expect(paypalBillingAgreementCreateStub).to.be.calledOnce;
|
||||||
|
let billingPlanTitle = `Habitica Subscription ($${sub.price} every ${sub.months} months, recurring)`;
|
||||||
|
expect(paypalBillingAgreementCreateStub).to.be.calledWith({
|
||||||
|
name: billingPlanTitle,
|
||||||
|
description: billingPlanTitle,
|
||||||
|
start_date: moment().add({ minutes: 5 }).format(),
|
||||||
|
plan: {
|
||||||
|
id: sub.paypalKey,
|
||||||
|
},
|
||||||
|
payer: {
|
||||||
|
payment_method: 'Paypal',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
cc.validate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a link for a subscription', async () => {
|
||||||
|
delete sub.discount;
|
||||||
|
|
||||||
|
let link = await paypalPayments.subscribe({sub, coupon});
|
||||||
|
|
||||||
|
expect(link).to.eql(approvalHerf);
|
||||||
|
expect(paypalBillingAgreementCreateStub).to.be.calledOnce;
|
||||||
|
let billingPlanTitle = `Habitica Subscription ($${sub.price} every ${sub.months} months, recurring)`;
|
||||||
|
expect(paypalBillingAgreementCreateStub).to.be.calledWith({
|
||||||
|
name: billingPlanTitle,
|
||||||
|
description: billingPlanTitle,
|
||||||
|
start_date: moment().add({ minutes: 5 }).format(),
|
||||||
|
plan: {
|
||||||
|
id: sub.paypalKey,
|
||||||
|
},
|
||||||
|
payer: {
|
||||||
|
payment_method: 'Paypal',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subscribeSuccess', () => {
|
||||||
|
let user, group, block, groupId, token, headers, customerId;
|
||||||
|
let paypalBillingAgreementExecuteStub, paymentsCreateSubscritionStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
token = 'test-token';
|
||||||
|
headers = {};
|
||||||
|
block = common.content.subscriptionBlocks[subKey];
|
||||||
|
customerId = 'test-customerId';
|
||||||
|
|
||||||
|
paypalBillingAgreementExecuteStub = sinon.stub(paypalPayments, 'paypalBillingAgreementExecute')
|
||||||
|
.returnsPromise({}).resolves({
|
||||||
|
id: customerId,
|
||||||
|
});
|
||||||
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
paypalPayments.paypalBillingAgreementExecute.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a user subscription', async () => {
|
||||||
|
await paypalPayments.subscribeSuccess({user, block, groupId, token, headers});
|
||||||
|
|
||||||
|
expect(paypalBillingAgreementExecuteStub).to.be.calledOnce;
|
||||||
|
expect(paypalBillingAgreementExecuteStub).to.be.calledWith(token, {});
|
||||||
|
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
customerId,
|
||||||
|
paymentMethod: 'Paypal',
|
||||||
|
sub: block,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create a group subscription', async () => {
|
||||||
|
groupId = group._id;
|
||||||
|
|
||||||
|
await paypalPayments.subscribeSuccess({user, block, groupId, token, headers});
|
||||||
|
|
||||||
|
expect(paypalBillingAgreementExecuteStub).to.be.calledOnce;
|
||||||
|
expect(paypalBillingAgreementExecuteStub).to.be.calledWith(token, {});
|
||||||
|
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||||
|
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
customerId,
|
||||||
|
paymentMethod: 'Paypal',
|
||||||
|
sub: block,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subscribeCancel', () => {
|
||||||
|
let user, group, groupId, customerId, groupCustomerId, nextBillingDate;
|
||||||
|
let paymentCancelSubscriptionSpy, paypalBillingAgreementCancelStub, paypalBillingAgreementGetStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
customerId = 'customer-id';
|
||||||
|
groupCustomerId = 'groupCustomerId-test';
|
||||||
|
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = customerId;
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = groupCustomerId;
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
group.purchased.plan.lastBillingDate = new Date();
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
nextBillingDate = new Date();
|
||||||
|
|
||||||
|
paypalBillingAgreementCancelStub = sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').returnsPromise().resolves({});
|
||||||
|
paypalBillingAgreementGetStub = sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
||||||
|
.returnsPromise().resolves({
|
||||||
|
agreement_details: {
|
||||||
|
next_billing_date: nextBillingDate,
|
||||||
|
cycles_completed: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
paypalPayments.paypalBillingAgreementGet.restore();
|
||||||
|
paypalPayments.paypalBillingAgreementCancel.restore();
|
||||||
|
payments.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if we are missing a subscription', async () => {
|
||||||
|
user.purchased.plan.customerId = undefined;
|
||||||
|
|
||||||
|
await expect(paypalPayments.subscribeCancel({user}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if group is not found', async () => {
|
||||||
|
await expect(paypalPayments.subscribeCancel({user, groupId: 'fake-id'}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 404,
|
||||||
|
name: 'NotFound',
|
||||||
|
message: i18n.t('groupNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if user is not group leader', async () => {
|
||||||
|
let nonLeader = new User();
|
||||||
|
nonLeader.guilds.push(group._id);
|
||||||
|
await nonLeader.save();
|
||||||
|
|
||||||
|
await expect(paypalPayments.subscribeCancel({user: nonLeader, groupId: group._id}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('onlyGroupLeaderCanManageSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a user subscription', async () => {
|
||||||
|
await paypalPayments.subscribeCancel({user});
|
||||||
|
|
||||||
|
expect(paypalBillingAgreementGetStub).to.be.calledOnce;
|
||||||
|
expect(paypalBillingAgreementGetStub).to.be.calledWith(customerId);
|
||||||
|
expect(paypalBillingAgreementCancelStub).to.be.calledOnce;
|
||||||
|
expect(paypalBillingAgreementCancelStub).to.be.calledWith(customerId, { note: i18n.t('cancelingSubscription') });
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
paymentMethod: 'Paypal',
|
||||||
|
nextBill: nextBillingDate,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a group subscription', async () => {
|
||||||
|
await paypalPayments.subscribeCancel({user, groupId: group._id});
|
||||||
|
|
||||||
|
expect(paypalBillingAgreementGetStub).to.be.calledOnce;
|
||||||
|
expect(paypalBillingAgreementGetStub).to.be.calledWith(groupCustomerId);
|
||||||
|
expect(paypalBillingAgreementCancelStub).to.be.calledOnce;
|
||||||
|
expect(paypalBillingAgreementCancelStub).to.be.calledWith(groupCustomerId, { note: i18n.t('cancelingSubscription') });
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId: group._id,
|
||||||
|
paymentMethod: 'Paypal',
|
||||||
|
nextBill: nextBillingDate,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ipn', () => {
|
||||||
|
let user, group, txn_type, userPaymentId, groupPaymentId;
|
||||||
|
let ipnVerifyAsyncStub, paymentCancelSubscriptionSpy;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
txn_type = 'recurring_payment_profile_cancel';
|
||||||
|
userPaymentId = 'userPaymentId-test';
|
||||||
|
groupPaymentId = 'groupPaymentId-test';
|
||||||
|
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = userPaymentId;
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = groupPaymentId;
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
group.purchased.plan.lastBillingDate = new Date();
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
ipnVerifyAsyncStub = sinon.stub(paypalPayments, 'ipnVerifyAsync').returnsPromise().resolves({});
|
||||||
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
paypalPayments.ipnVerifyAsync.restore();
|
||||||
|
payments.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a user subscription', async () => {
|
||||||
|
await paypalPayments.ipn({txn_type, recurring_payment_id: userPaymentId});
|
||||||
|
|
||||||
|
expect(ipnVerifyAsyncStub).to.be.calledOnce;
|
||||||
|
expect(ipnVerifyAsyncStub).to.be.calledWith({txn_type, recurring_payment_id: userPaymentId});
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(paymentCancelSubscriptionSpy.args[0][0].paymentMethod).to.eql('Paypal');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a group subscription', async () => {
|
||||||
|
await paypalPayments.ipn({txn_type, recurring_payment_id: groupPaymentId});
|
||||||
|
|
||||||
|
expect(ipnVerifyAsyncStub).to.be.calledOnce;
|
||||||
|
expect(ipnVerifyAsyncStub).to.be.calledWith({txn_type, recurring_payment_id: groupPaymentId});
|
||||||
|
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
|
||||||
|
expect(paymentCancelSubscriptionSpy).to.be.calledWith({ groupId: group._id, paymentMethod: 'Paypal' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
661
test/api/v3/unit/libs/stripePayments.test.js
Normal file
661
test/api/v3/unit/libs/stripePayments.test.js
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
import stripeModule from 'stripe';
|
||||||
|
import cc from 'coupon-code';
|
||||||
|
|
||||||
|
import {
|
||||||
|
generateGroup,
|
||||||
|
} from '../../../../helpers/api-unit.helper.js';
|
||||||
|
import { model as User } from '../../../../../website/server/models/user';
|
||||||
|
import { model as Coupon } from '../../../../../website/server/models/coupon';
|
||||||
|
import stripePayments from '../../../../../website/server/libs/stripePayments';
|
||||||
|
import payments from '../../../../../website/server/libs/payments';
|
||||||
|
import common from '../../../../../website/common';
|
||||||
|
|
||||||
|
const i18n = common.i18n;
|
||||||
|
|
||||||
|
describe('Stripe Payments', () => {
|
||||||
|
let subKey = 'basic_3mo';
|
||||||
|
let stripe = stripeModule('test');
|
||||||
|
|
||||||
|
describe('checkout', () => {
|
||||||
|
let stripeChargeStub, paymentBuyGemsStub, paymentCreateSubscritionStub;
|
||||||
|
let user, gift, groupId, email, headers, coupon, customerIdResponse, token;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
token = 'test-token';
|
||||||
|
|
||||||
|
customerIdResponse = 'example-customerIdResponse';
|
||||||
|
let stripCustomerResponse = {
|
||||||
|
id: customerIdResponse,
|
||||||
|
};
|
||||||
|
stripeChargeStub = sinon.stub(stripe.charges, 'create').returnsPromise().resolves(stripCustomerResponse);
|
||||||
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
||||||
|
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
stripe.charges.create.restore();
|
||||||
|
payments.buyGems.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should purchase gems', async () => {
|
||||||
|
await stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeChargeStub).to.be.calledOnce;
|
||||||
|
expect(stripeChargeStub).to.be.calledWith({
|
||||||
|
amount: 500,
|
||||||
|
currency: 'usd',
|
||||||
|
card: token,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: customerIdResponse,
|
||||||
|
paymentMethod: 'Stripe',
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should gift gems', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
receivingUser.save();
|
||||||
|
gift = {
|
||||||
|
type: 'gems',
|
||||||
|
gems: {
|
||||||
|
amount: 16,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
gift.member = receivingUser;
|
||||||
|
expect(stripeChargeStub).to.be.calledOnce;
|
||||||
|
expect(stripeChargeStub).to.be.calledWith({
|
||||||
|
amount: '400',
|
||||||
|
currency: 'usd',
|
||||||
|
card: token,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: customerIdResponse,
|
||||||
|
paymentMethod: 'Gift',
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should gift a subscription', async () => {
|
||||||
|
let receivingUser = new User();
|
||||||
|
receivingUser.save();
|
||||||
|
gift = {
|
||||||
|
type: 'subscription',
|
||||||
|
subscription: {
|
||||||
|
key: subKey,
|
||||||
|
uuid: receivingUser._id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
gift.member = receivingUser;
|
||||||
|
expect(stripeChargeStub).to.be.calledOnce;
|
||||||
|
expect(stripeChargeStub).to.be.calledWith({
|
||||||
|
amount: '1500',
|
||||||
|
currency: 'usd',
|
||||||
|
card: token,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(paymentCreateSubscritionStub).to.be.calledOnce;
|
||||||
|
expect(paymentCreateSubscritionStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: customerIdResponse,
|
||||||
|
paymentMethod: 'Gift',
|
||||||
|
gift,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkout with subscription', () => {
|
||||||
|
let user, group, data, gift, sub, groupId, email, headers, coupon, customerIdResponse, subscriptionId, token;
|
||||||
|
let spy;
|
||||||
|
let stripeCreateCustomerSpy;
|
||||||
|
let stripePaymentsCreateSubSpy;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = 'customer-id';
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
sub = {
|
||||||
|
key: 'basic_3mo',
|
||||||
|
};
|
||||||
|
|
||||||
|
data = {
|
||||||
|
user,
|
||||||
|
sub,
|
||||||
|
customerId: 'customer-id',
|
||||||
|
paymentMethod: 'Payment Method',
|
||||||
|
};
|
||||||
|
|
||||||
|
email = 'example@example.com';
|
||||||
|
customerIdResponse = 'test-id';
|
||||||
|
subscriptionId = 'test-sub-id';
|
||||||
|
token = 'test-token';
|
||||||
|
|
||||||
|
spy = sinon.stub(stripe.subscriptions, 'update');
|
||||||
|
spy.returnsPromise().resolves;
|
||||||
|
|
||||||
|
stripeCreateCustomerSpy = sinon.stub(stripe.customers, 'create');
|
||||||
|
let stripCustomerResponse = {
|
||||||
|
id: customerIdResponse,
|
||||||
|
subscriptions: {
|
||||||
|
data: [{id: subscriptionId}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
stripeCreateCustomerSpy.returnsPromise().resolves(stripCustomerResponse);
|
||||||
|
|
||||||
|
stripePaymentsCreateSubSpy = sinon.stub(payments, 'createSubscription');
|
||||||
|
stripePaymentsCreateSubSpy.returnsPromise().resolves({});
|
||||||
|
|
||||||
|
data.groupId = group._id;
|
||||||
|
data.sub.quantity = 3;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
sinon.restore(stripe.subscriptions.update);
|
||||||
|
stripe.customers.create.restore();
|
||||||
|
payments.createSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if we are missing a token', async () => {
|
||||||
|
await expect(stripePayments.checkout({
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
sub,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: 'Missing req.body.id',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when coupon code is missing', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
|
||||||
|
await expect(stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
sub,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: i18n.t('couponCodeRequired'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when coupon code is invalid', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
sub.key = 'google_6mo';
|
||||||
|
coupon = 'example-coupon';
|
||||||
|
|
||||||
|
let couponModel = new Coupon();
|
||||||
|
couponModel.event = 'google_6mo';
|
||||||
|
await couponModel.save();
|
||||||
|
|
||||||
|
sinon.stub(cc, 'validate').returns('invalid');
|
||||||
|
|
||||||
|
await expect(stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
sub,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: i18n.t('invalidCoupon'),
|
||||||
|
});
|
||||||
|
cc.validate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes with amazon with a coupon', async () => {
|
||||||
|
sub.discount = 40;
|
||||||
|
sub.key = 'google_6mo';
|
||||||
|
coupon = 'example-coupon';
|
||||||
|
|
||||||
|
let couponModel = new Coupon();
|
||||||
|
couponModel.event = 'google_6mo';
|
||||||
|
let updatedCouponModel = await couponModel.save();
|
||||||
|
|
||||||
|
sinon.stub(cc, 'validate').returns(updatedCouponModel._id);
|
||||||
|
|
||||||
|
await stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
sub,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeCreateCustomerSpy).to.be.calledOnce;
|
||||||
|
expect(stripeCreateCustomerSpy).to.be.calledWith({
|
||||||
|
email,
|
||||||
|
metadata: { uuid: user._id },
|
||||||
|
card: token,
|
||||||
|
plan: sub.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stripePaymentsCreateSubSpy).to.be.calledOnce;
|
||||||
|
expect(stripePaymentsCreateSubSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: customerIdResponse,
|
||||||
|
paymentMethod: 'Stripe',
|
||||||
|
sub,
|
||||||
|
headers,
|
||||||
|
groupId: undefined,
|
||||||
|
subscriptionId: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
cc.validate.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes a user', async () => {
|
||||||
|
sub = data.sub;
|
||||||
|
|
||||||
|
await stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
sub,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeCreateCustomerSpy).to.be.calledOnce;
|
||||||
|
expect(stripeCreateCustomerSpy).to.be.calledWith({
|
||||||
|
email,
|
||||||
|
metadata: { uuid: user._id },
|
||||||
|
card: token,
|
||||||
|
plan: sub.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stripePaymentsCreateSubSpy).to.be.calledOnce;
|
||||||
|
expect(stripePaymentsCreateSubSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: customerIdResponse,
|
||||||
|
paymentMethod: 'Stripe',
|
||||||
|
sub,
|
||||||
|
headers,
|
||||||
|
groupId: undefined,
|
||||||
|
subscriptionId: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes a group', async () => {
|
||||||
|
token = 'test-token';
|
||||||
|
sub = data.sub;
|
||||||
|
groupId = group._id;
|
||||||
|
email = 'test@test.com';
|
||||||
|
headers = {};
|
||||||
|
|
||||||
|
await stripePayments.checkout({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
gift,
|
||||||
|
sub,
|
||||||
|
groupId,
|
||||||
|
email,
|
||||||
|
headers,
|
||||||
|
coupon,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeCreateCustomerSpy).to.be.calledOnce;
|
||||||
|
expect(stripeCreateCustomerSpy).to.be.calledWith({
|
||||||
|
email,
|
||||||
|
metadata: { uuid: user._id },
|
||||||
|
card: token,
|
||||||
|
plan: sub.key,
|
||||||
|
quantity: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stripePaymentsCreateSubSpy).to.be.calledOnce;
|
||||||
|
expect(stripePaymentsCreateSubSpy).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
customerId: customerIdResponse,
|
||||||
|
paymentMethod: 'Stripe',
|
||||||
|
sub,
|
||||||
|
headers,
|
||||||
|
groupId,
|
||||||
|
subscriptionId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('edit subscription', () => {
|
||||||
|
let user, groupId, group, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = 'customer-id';
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
groupId = group._id;
|
||||||
|
|
||||||
|
token = 'test-token';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if there is no customer id', async () => {
|
||||||
|
user.purchased.plan.customerId = undefined;
|
||||||
|
|
||||||
|
await expect(stripePayments.editSubscription({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if a token is not provided', async () => {
|
||||||
|
await expect(stripePayments.editSubscription({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 400,
|
||||||
|
name: 'BadRequest',
|
||||||
|
message: 'Missing req.body.id',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if the group is not found', async () => {
|
||||||
|
await expect(stripePayments.editSubscription({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
groupId: 'fake-group',
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 404,
|
||||||
|
name: 'NotFound',
|
||||||
|
message: i18n.t('groupNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if user is not the group leader', async () => {
|
||||||
|
let nonLeader = new User();
|
||||||
|
nonLeader.guilds.push(groupId);
|
||||||
|
await nonLeader.save();
|
||||||
|
|
||||||
|
await expect(stripePayments.editSubscription({
|
||||||
|
token,
|
||||||
|
user: nonLeader,
|
||||||
|
groupId,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('onlyGroupLeaderCanManageSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let stripeListSubscriptionStub, stripeUpdateSubscriptionStub, subscriptionId;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
subscriptionId = 'subId';
|
||||||
|
stripeListSubscriptionStub = sinon.stub(stripe.customers, 'listSubscriptions')
|
||||||
|
.returnsPromise().resolves({
|
||||||
|
data: [{id: subscriptionId}],
|
||||||
|
});
|
||||||
|
|
||||||
|
stripeUpdateSubscriptionStub = sinon.stub(stripe.customers, 'updateSubscription').returnsPromise().resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
stripe.customers.listSubscriptions.restore();
|
||||||
|
stripe.customers.updateSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edits a user subscription', async () => {
|
||||||
|
await stripePayments.editSubscription({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeListSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeListSubscriptionStub).to.be.calledWith(user.purchased.plan.customerId);
|
||||||
|
expect(stripeUpdateSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeUpdateSubscriptionStub).to.be.calledWith(
|
||||||
|
user.purchased.plan.customerId,
|
||||||
|
subscriptionId,
|
||||||
|
{ card: token }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edits a group subscription', async () => {
|
||||||
|
await stripePayments.editSubscription({
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeListSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeListSubscriptionStub).to.be.calledWith(group.purchased.plan.customerId);
|
||||||
|
expect(stripeUpdateSubscriptionStub).to.be.calledOnce;
|
||||||
|
expect(stripeUpdateSubscriptionStub).to.be.calledWith(
|
||||||
|
group.purchased.plan.customerId,
|
||||||
|
subscriptionId,
|
||||||
|
{ card: token }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancel subscription', () => {
|
||||||
|
let user, groupId, group;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User();
|
||||||
|
user.profile.name = 'sender';
|
||||||
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
|
user.purchased.plan.planId = subKey;
|
||||||
|
user.purchased.plan.lastBillingDate = new Date();
|
||||||
|
|
||||||
|
group = generateGroup({
|
||||||
|
name: 'test group',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'public',
|
||||||
|
leader: user._id,
|
||||||
|
});
|
||||||
|
group.purchased.plan.customerId = 'customer-id';
|
||||||
|
group.purchased.plan.planId = subKey;
|
||||||
|
await group.save();
|
||||||
|
|
||||||
|
groupId = group._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if there is no customer id', async () => {
|
||||||
|
user.purchased.plan.customerId = undefined;
|
||||||
|
|
||||||
|
await expect(stripePayments.cancelSubscription({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('missingSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if the group is not found', async () => {
|
||||||
|
await expect(stripePayments.cancelSubscription({
|
||||||
|
user,
|
||||||
|
groupId: 'fake-group',
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 404,
|
||||||
|
name: 'NotFound',
|
||||||
|
message: i18n.t('groupNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if user is not the group leader', async () => {
|
||||||
|
let nonLeader = new User();
|
||||||
|
nonLeader.guilds.push(groupId);
|
||||||
|
await nonLeader.save();
|
||||||
|
|
||||||
|
await expect(stripePayments.cancelSubscription({
|
||||||
|
user: nonLeader,
|
||||||
|
groupId,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
httpCode: 401,
|
||||||
|
name: 'NotAuthorized',
|
||||||
|
message: i18n.t('onlyGroupLeaderCanManageSubscription'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let stripeDeleteCustomerStub, paymentsCancelSubStub, stripeRetrieveStub, subscriptionId, currentPeriodEndTimeStamp;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
subscriptionId = 'subId';
|
||||||
|
stripeDeleteCustomerStub = sinon.stub(stripe.customers, 'del').returnsPromise().resolves({});
|
||||||
|
paymentsCancelSubStub = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
||||||
|
|
||||||
|
currentPeriodEndTimeStamp = (new Date()).getTime();
|
||||||
|
stripeRetrieveStub = sinon.stub(stripe.customers, 'retrieve')
|
||||||
|
.returnsPromise().resolves({
|
||||||
|
subscriptions: {
|
||||||
|
data: [{id: subscriptionId, current_period_end: currentPeriodEndTimeStamp}], // eslint-disable-line camelcase
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
stripe.customers.del.restore();
|
||||||
|
stripe.customers.retrieve.restore();
|
||||||
|
payments.cancelSubscription.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a user subscription', async () => {
|
||||||
|
await stripePayments.cancelSubscription({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeDeleteCustomerStub).to.be.calledOnce;
|
||||||
|
expect(stripeDeleteCustomerStub).to.be.calledWith(user.purchased.plan.customerId);
|
||||||
|
expect(stripeRetrieveStub).to.be.calledOnce;
|
||||||
|
expect(stripeRetrieveStub).to.be.calledWith(user.purchased.plan.customerId);
|
||||||
|
expect(paymentsCancelSubStub).to.be.calledOnce;
|
||||||
|
expect(paymentsCancelSubStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId: undefined,
|
||||||
|
nextBill: currentPeriodEndTimeStamp * 1000, // timestamp in seconds
|
||||||
|
paymentMethod: 'Stripe',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a group subscription', async () => {
|
||||||
|
await stripePayments.cancelSubscription({
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
}, stripe);
|
||||||
|
|
||||||
|
expect(stripeDeleteCustomerStub).to.be.calledOnce;
|
||||||
|
expect(stripeDeleteCustomerStub).to.be.calledWith(group.purchased.plan.customerId);
|
||||||
|
expect(stripeRetrieveStub).to.be.calledOnce;
|
||||||
|
expect(stripeRetrieveStub).to.be.calledWith(user.purchased.plan.customerId);
|
||||||
|
expect(paymentsCancelSubStub).to.be.calledOnce;
|
||||||
|
expect(paymentsCancelSubStub).to.be.calledWith({
|
||||||
|
user,
|
||||||
|
groupId,
|
||||||
|
nextBill: currentPeriodEndTimeStamp * 1000, // timestamp in seconds
|
||||||
|
paymentMethod: 'Stripe',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
createTasks,
|
createTasks,
|
||||||
getTasks,
|
getTasks,
|
||||||
syncableAttrs,
|
syncableAttrs,
|
||||||
|
moveTask,
|
||||||
} from '../../../../../website/server/libs/taskManager';
|
} from '../../../../../website/server/libs/taskManager';
|
||||||
import i18n from '../../../../../website/common/script/i18n';
|
import i18n from '../../../../../website/common/script/i18n';
|
||||||
import {
|
import {
|
||||||
@@ -169,4 +170,12 @@ describe('taskManager', () => {
|
|||||||
expect(syncableTask.notes).to.not.exist;
|
expect(syncableTask.notes).to.not.exist;
|
||||||
expect(syncableTask.updatedAt).to.not.exist;
|
expect(syncableTask.updatedAt).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('moves tasks to a specified position', async() => {
|
||||||
|
let order = ['task-id-1', 'task-id-2'];
|
||||||
|
|
||||||
|
moveTask(order, 'task-id-2', 0);
|
||||||
|
|
||||||
|
expect(order).to.eql(['task-id-2', 'task-id-1']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -170,15 +170,19 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Boss Quests', () => {
|
context('Boss Quests', () => {
|
||||||
|
let sendChatStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
party.quest.key = 'whale';
|
party.quest.key = 'whale';
|
||||||
|
|
||||||
await party.startQuest(questLeader);
|
await party.startQuest(questLeader);
|
||||||
await party.save();
|
await party.save();
|
||||||
|
|
||||||
sandbox.stub(Group.prototype, 'sendChat');
|
sendChatStub = sandbox.stub(Group.prototype, 'sendChat');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => sendChatStub.restore());
|
||||||
|
|
||||||
it('applies user\'s progress to quest boss hp', async () => {
|
it('applies user\'s progress to quest boss hp', async () => {
|
||||||
await Group.processQuestProgress(participatingMember, progress);
|
await Group.processQuestProgress(participatingMember, progress);
|
||||||
|
|
||||||
@@ -322,15 +326,19 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Collection Quests', () => {
|
context('Collection Quests', () => {
|
||||||
|
let sendChatStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
party.quest.key = 'atom1';
|
party.quest.key = 'atom1';
|
||||||
|
|
||||||
await party.startQuest(questLeader);
|
await party.startQuest(questLeader);
|
||||||
await party.save();
|
await party.save();
|
||||||
|
|
||||||
sandbox.stub(Group.prototype, 'sendChat');
|
sendChatStub = sandbox.stub(Group.prototype, 'sendChat');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => sendChatStub.restore());
|
||||||
|
|
||||||
it('applies user\'s progress to found quest items', async () => {
|
it('applies user\'s progress to found quest items', async () => {
|
||||||
await Group.processQuestProgress(participatingMember, progress);
|
await Group.processQuestProgress(participatingMember, progress);
|
||||||
|
|
||||||
@@ -365,6 +373,7 @@ describe('Group Model', () => {
|
|||||||
party.quest.active = false;
|
party.quest.active = false;
|
||||||
|
|
||||||
await party.startQuest(questLeader);
|
await party.startQuest(questLeader);
|
||||||
|
Group.prototype.sendChat.reset();
|
||||||
await party.save();
|
await party.save();
|
||||||
|
|
||||||
await Group.processQuestProgress(participatingMember, progress);
|
await Group.processQuestProgress(participatingMember, progress);
|
||||||
@@ -383,6 +392,7 @@ describe('Group Model', () => {
|
|||||||
party.quest.active = false;
|
party.quest.active = false;
|
||||||
|
|
||||||
await party.startQuest(questLeader);
|
await party.startQuest(questLeader);
|
||||||
|
Group.prototype.sendChat.reset();
|
||||||
await party.save();
|
await party.save();
|
||||||
|
|
||||||
await Group.processQuestProgress(participatingMember, progress);
|
await Group.processQuestProgress(participatingMember, progress);
|
||||||
@@ -809,6 +819,20 @@ describe('Group Model', () => {
|
|||||||
expect(party.chat).to.have.a.lengthOf(200);
|
expect(party.chat).to.have.a.lengthOf(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('cuts down chat to 400 messages when group is subcribed', () => {
|
||||||
|
party.purchased.plan.customerId = 'test-customer-id';
|
||||||
|
|
||||||
|
for (let i = 0; i < 420; i++) {
|
||||||
|
party.chat.push({ text: 'a message' });
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(party.chat).to.have.a.lengthOf(420);
|
||||||
|
|
||||||
|
party.sendChat('message');
|
||||||
|
|
||||||
|
expect(party.chat).to.have.a.lengthOf(400);
|
||||||
|
});
|
||||||
|
|
||||||
it('updates users about new messages in party', () => {
|
it('updates users about new messages in party', () => {
|
||||||
party.sendChat('message');
|
party.sendChat('message');
|
||||||
|
|
||||||
@@ -1259,14 +1283,34 @@ describe('Group Model', () => {
|
|||||||
expect(updatedParticipatingMember.items.hatchingPotions.Shade).to.eql(2);
|
expect(updatedParticipatingMember.items.hatchingPotions.Shade).to.eql(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('awards quests', async () => {
|
it('awards quest scrolls to owner', async () => {
|
||||||
|
let questAwardQuest = questScrolls.vice2;
|
||||||
|
|
||||||
|
await party.finishQuest(questAwardQuest);
|
||||||
|
|
||||||
|
let updatedLeader = await User.findById(questLeader._id);
|
||||||
|
|
||||||
|
expect(updatedLeader.items.quests.vice3).to.eql(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('awards non quest leader rewards to quest leader', async () => {
|
||||||
|
let gearQuest = questScrolls.vice3;
|
||||||
|
|
||||||
|
await party.finishQuest(gearQuest);
|
||||||
|
|
||||||
|
let updatedLeader = await User.findById(questLeader._id);
|
||||||
|
|
||||||
|
expect(updatedLeader.items.gear.owned.weapon_special_2).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('doesn\'t award quest owner rewards to all participants', async () => {
|
||||||
let questAwardQuest = questScrolls.vice2;
|
let questAwardQuest = questScrolls.vice2;
|
||||||
|
|
||||||
await party.finishQuest(questAwardQuest);
|
await party.finishQuest(questAwardQuest);
|
||||||
|
|
||||||
let updatedParticipatingMember = await User.findById(participatingMember._id);
|
let updatedParticipatingMember = await User.findById(participatingMember._id);
|
||||||
|
|
||||||
expect(updatedParticipatingMember.items.quests.vice3).to.eql(1);
|
expect(updatedParticipatingMember.items.quests.vice3).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('awards pets', async () => {
|
it('awards pets', async () => {
|
||||||
|
|||||||
@@ -91,25 +91,23 @@ describe('Task Model', () => {
|
|||||||
sandbox.spy(Tasks.Task, 'findOne');
|
sandbox.spy(Tasks.Task, 'findOne');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if task identifier is not passed in', async (done) => {
|
it('throws an error if task identifier is not passed in', async () => {
|
||||||
try {
|
try {
|
||||||
await Tasks.Task.findByIdOrAlias(null, user._id);
|
await Tasks.Task.findByIdOrAlias(null, user._id);
|
||||||
|
throw new Error('No exception when Id is None');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(err).to.eql(new InternalServerError('Task identifier is a required argument'));
|
expect(err).to.eql(new InternalServerError('Task identifier is a required argument'));
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if user identifier is not passed in', async (done) => {
|
it('throws an error if user identifier is not passed in', async () => {
|
||||||
try {
|
try {
|
||||||
await Tasks.Task.findByIdOrAlias(taskWithAlias._id);
|
await Tasks.Task.findByIdOrAlias(taskWithAlias._id);
|
||||||
|
throw new Error('No exception when user_id is undefined');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(err).to.eql(new InternalServerError('User identifier is a required argument'));
|
expect(err).to.eql(new InternalServerError('User identifier is a required argument'));
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { model as User } from '../../../../../website/server/models/user';
|
import { model as User } from '../../../../../website/server/models/user';
|
||||||
import common from '../../../../../website/common';
|
import common from '../../../../../website/common';
|
||||||
|
import Bluebird from 'bluebird';
|
||||||
|
|
||||||
describe('User Model', () => {
|
describe('User Model', () => {
|
||||||
it('keeps user._tmp when calling .toJSON', () => {
|
it('keeps user._tmp when calling .toJSON', () => {
|
||||||
@@ -48,7 +49,7 @@ describe('User Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('notifications', () => {
|
context('notifications', () => {
|
||||||
it('can add notifications with data', () => {
|
it('can add notifications without data', () => {
|
||||||
let user = new User();
|
let user = new User();
|
||||||
|
|
||||||
user.addNotification('CRON');
|
user.addNotification('CRON');
|
||||||
@@ -60,7 +61,7 @@ describe('User Model', () => {
|
|||||||
expect(userToJSON.notifications[0].data).to.eql({});
|
expect(userToJSON.notifications[0].data).to.eql({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add notifications without data', () => {
|
it('can add notifications with data', () => {
|
||||||
let user = new User();
|
let user = new User();
|
||||||
|
|
||||||
user.addNotification('CRON', {field: 1});
|
user.addNotification('CRON', {field: 1});
|
||||||
@@ -71,5 +72,77 @@ describe('User Model', () => {
|
|||||||
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||||
expect(userToJSON.notifications[0].data).to.eql({field: 1});
|
expect(userToJSON.notifications[0].data).to.eql({field: 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context('static push method', () => {
|
||||||
|
it('adds notifications for a single member via static method', async() => {
|
||||||
|
let user = new User();
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
await User.pushNotification({_id: user._id}, 'CRON');
|
||||||
|
|
||||||
|
user = await User.findOne({_id: user._id}).exec();
|
||||||
|
|
||||||
|
let userToJSON = user.toJSON();
|
||||||
|
expect(user.notifications.length).to.equal(1);
|
||||||
|
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||||
|
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||||
|
expect(userToJSON.notifications[0].data).to.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validates notifications via static method', async() => {
|
||||||
|
let user = new User();
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
expect(User.pushNotification({_id: user._id}, 'BAD_TYPE')).to.eventually.be.rejected;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds notifications without data for all given users via static method', async() => {
|
||||||
|
let user = new User();
|
||||||
|
let otherUser = new User();
|
||||||
|
await Bluebird.all([user.save(), otherUser.save()]);
|
||||||
|
|
||||||
|
await User.pushNotification({_id: {$in: [user._id, otherUser._id]}}, 'CRON');
|
||||||
|
|
||||||
|
user = await User.findOne({_id: user._id}).exec();
|
||||||
|
|
||||||
|
let userToJSON = user.toJSON();
|
||||||
|
expect(user.notifications.length).to.equal(1);
|
||||||
|
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||||
|
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||||
|
expect(userToJSON.notifications[0].data).to.eql({});
|
||||||
|
|
||||||
|
user = await User.findOne({_id: otherUser._id}).exec();
|
||||||
|
|
||||||
|
userToJSON = user.toJSON();
|
||||||
|
expect(user.notifications.length).to.equal(1);
|
||||||
|
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||||
|
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||||
|
expect(userToJSON.notifications[0].data).to.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds notifications with data for all given users via static method', async() => {
|
||||||
|
let user = new User();
|
||||||
|
let otherUser = new User();
|
||||||
|
await Bluebird.all([user.save(), otherUser.save()]);
|
||||||
|
|
||||||
|
await User.pushNotification({_id: {$in: [user._id, otherUser._id]}}, 'CRON', {field: 1});
|
||||||
|
|
||||||
|
user = await User.findOne({_id: user._id}).exec();
|
||||||
|
|
||||||
|
let userToJSON = user.toJSON();
|
||||||
|
expect(user.notifications.length).to.equal(1);
|
||||||
|
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||||
|
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||||
|
expect(userToJSON.notifications[0].data).to.eql({field: 1});
|
||||||
|
|
||||||
|
user = await User.findOne({_id: otherUser._id}).exec();
|
||||||
|
|
||||||
|
userToJSON = user.toJSON();
|
||||||
|
expect(user.notifications.length).to.equal(1);
|
||||||
|
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type']);
|
||||||
|
expect(userToJSON.notifications[0].type).to.equal('CRON');
|
||||||
|
expect(userToJSON.notifications[0].data).to.eql({field: 1});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -240,6 +240,5 @@ describe('Groups Controller', function() {
|
|||||||
describe.skip("clickMember", function() { });
|
describe.skip("clickMember", function() { });
|
||||||
describe.skip("removeMember", function() { });
|
describe.skip("removeMember", function() { });
|
||||||
describe.skip("confirmRemoveMember", function() { });
|
describe.skip("confirmRemoveMember", function() { });
|
||||||
describe.skip("openInviteModal", function() { });
|
|
||||||
describe.skip("quickReply", function() { });
|
describe.skip("quickReply", function() { });
|
||||||
});
|
});
|
||||||
|
|||||||
63
test/client-old/spec/controllers/groupTaskActionsCtrlSpec.js
Normal file
63
test/client-old/spec/controllers/groupTaskActionsCtrlSpec.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('Settings Controller', function () {
|
describe('Settings Controller', function () {
|
||||||
var rootScope, scope, user, User, ctrl;
|
var rootScope, scope, $httpBackend, user, User, ctrl, Notification;
|
||||||
|
|
||||||
const actionClickEvent = {
|
const actionClickEvent = {
|
||||||
target: document.createElement('button'),
|
target: document.createElement('button'),
|
||||||
@@ -29,18 +29,27 @@ describe('Settings Controller', function () {
|
|||||||
releaseBoth: sandbox.stub(),
|
releaseBoth: sandbox.stub(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Notification = {
|
||||||
|
error: sandbox.stub(),
|
||||||
|
text: sandbox.stub()
|
||||||
|
};
|
||||||
|
|
||||||
|
$provide.value('Notification', Notification);
|
||||||
$provide.value('User', User);
|
$provide.value('User', User);
|
||||||
$provide.value('Guide', sandbox.stub());
|
$provide.value('Guide', sandbox.stub());
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function(_$rootScope_, _$controller_) {
|
inject(function(_$rootScope_, _$controller_, _$httpBackend_) {
|
||||||
scope = _$rootScope_.$new();
|
scope = _$rootScope_.$new();
|
||||||
rootScope = _$rootScope_;
|
rootScope = _$rootScope_;
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
|
||||||
|
$httpBackend.whenGET(/partials/).respond();
|
||||||
|
|
||||||
// Load RootCtrl to ensure shared behaviors are loaded
|
// Load RootCtrl to ensure shared behaviors are loaded
|
||||||
_$controller_('RootCtrl', {$scope: scope, User: User});
|
_$controller_('RootCtrl', {$scope: scope, User: User, Notification: Notification});
|
||||||
|
|
||||||
ctrl = _$controller_('SettingsCtrl', {$scope: scope, User: User});
|
ctrl = _$controller_('SettingsCtrl', {$scope: scope, User: User, Notification: Notification});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -281,4 +290,48 @@ describe('Settings Controller', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context('Validating coupons', function () {
|
||||||
|
describe('#applyCoupon', function () {
|
||||||
|
it('displays an error when an invalid coupon is applied', function () {
|
||||||
|
$httpBackend
|
||||||
|
.whenPOST('/api/v3/coupons/validate/INVALID_COUPON?userV=undefined')
|
||||||
|
.respond(200, {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
valid: false
|
||||||
|
},
|
||||||
|
notifications: [],
|
||||||
|
userV: 'undefined'
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.applyCoupon('INVALID_COUPON');
|
||||||
|
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(Notification.error).to.be.called;
|
||||||
|
expect(Notification.error).to.be.calledWith(env.t('invalidCoupon'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays an confirmation when a valid coupon is applied', function () {
|
||||||
|
$httpBackend
|
||||||
|
.whenPOST('/api/v3/coupons/validate/VALID_COUPON?userV=undefined')
|
||||||
|
.respond(200, {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
valid: true
|
||||||
|
},
|
||||||
|
notifications: [],
|
||||||
|
userV: 'undefined'
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.applyCoupon('VALID_COUPON');
|
||||||
|
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(Notification.error).to.not.be.called;
|
||||||
|
expect(Notification.text).to.be.calledWith('Coupon applied!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ module.exports = function karmaConfig (config) {
|
|||||||
'../../../website/client-old/js/filters/**/*.js',
|
'../../../website/client-old/js/filters/**/*.js',
|
||||||
'../../../website/client-old/js/directives/**/*.js',
|
'../../../website/client-old/js/directives/**/*.js',
|
||||||
'../../../website/client-old/js/controllers/**/*.js',
|
'../../../website/client-old/js/controllers/**/*.js',
|
||||||
|
'../../../website/client-old/js/components/**/*.js',
|
||||||
|
|
||||||
'../../../test/client-old/spec/specHelper.js',
|
'../../../test/client-old/spec/specHelper.js',
|
||||||
'../../../test/client-old/spec/**/*.js',
|
'../../../test/client-old/spec/**/*.js',
|
||||||
|
|||||||
@@ -26,14 +26,22 @@ describe('Analytics Service', function () {
|
|||||||
describe('register', function() {
|
describe('register', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox.stub(window, 'fbq');
|
sandbox.stub(amplitude, 'setUserId');
|
||||||
|
sandbox.stub(window, 'ga');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('records a registration event on Facebook', function() {
|
it('sets up user with Amplitude', function() {
|
||||||
analytics.register();
|
analytics.register();
|
||||||
clock.tick();
|
clock.tick();
|
||||||
expect(fbq).to.have.been.calledOnce;
|
expect(amplitude.setUserId).to.have.been.calledOnce;
|
||||||
expect(fbq).to.have.been.calledWith('track', 'CompleteRegistration');
|
expect(amplitude.setUserId).to.have.been.calledWith(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets up user with Google Analytics', function() {
|
||||||
|
analytics.register();
|
||||||
|
clock.tick();
|
||||||
|
expect(ga).to.have.been.calledOnce;
|
||||||
|
expect(ga).to.have.been.calledWith('set', {userId: user._id});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,7 +74,6 @@ describe('Analytics Service', function () {
|
|||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox.stub(amplitude, 'logEvent');
|
sandbox.stub(amplitude, 'logEvent');
|
||||||
sandbox.stub(window, 'ga');
|
sandbox.stub(window, 'ga');
|
||||||
sandbox.stub(window, 'fbq');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('successful tracking', function() {
|
context('successful tracking', function() {
|
||||||
@@ -106,15 +113,6 @@ describe('Analytics Service', function () {
|
|||||||
expect(ga).to.have.been.calledOnce;
|
expect(ga).to.have.been.calledOnce;
|
||||||
expect(ga).to.have.been.calledWith('send', properties);
|
expect(ga).to.have.been.calledWith('send', properties);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tracks a page view with Facebook', function() {
|
|
||||||
var properties = {'hitType':'pageview','eventCategory':'behavior','eventAction':'tasks'};
|
|
||||||
analytics.track(properties);
|
|
||||||
clock.tick();
|
|
||||||
|
|
||||||
expect(fbq).to.have.been.calledOnce;
|
|
||||||
expect(fbq).to.have.been.calledWith('track', 'PageView');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('unsuccessful tracking', function() {
|
context('unsuccessful tracking', function() {
|
||||||
@@ -194,6 +192,7 @@ describe('Analytics Service', function () {
|
|||||||
rewards: 1
|
rewards: 1
|
||||||
};
|
};
|
||||||
expectedProperties.balance = 12;
|
expectedProperties.balance = 12;
|
||||||
|
expectedProperties.balanceGemAmount = 48;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
user._id = 'unique-user-id';
|
user._id = 'unique-user-id';
|
||||||
@@ -243,7 +242,8 @@ describe('Analytics Service', function () {
|
|||||||
habits: 1,
|
habits: 1,
|
||||||
rewards: 1
|
rewards: 1
|
||||||
},
|
},
|
||||||
balance: 12
|
balance: 12,
|
||||||
|
balanceGemAmount: 48
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('groupServices', function() {
|
describe('groupServices', function() {
|
||||||
var $httpBackend, $http, groups, user;
|
var $httpBackend, $http, groups, user, $rootScope;
|
||||||
var groupApiUrlPrefix = '/api/v3/groups';
|
var groupApiUrlPrefix = '/api/v3/groups';
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@@ -13,8 +13,10 @@ describe('groupServices', function() {
|
|||||||
$provide.value('User', {user: user});
|
$provide.value('User', {user: user});
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function(_$httpBackend_, Groups, User) {
|
inject(function(_$httpBackend_, _$rootScope_, Groups, User) {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$rootScope = _$rootScope_;
|
||||||
|
$rootScope.openModal = function() {}
|
||||||
groups = Groups;
|
groups = Groups;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -166,4 +168,33 @@ describe('groupServices', function() {
|
|||||||
|
|
||||||
$httpBackend.flush()
|
$httpBackend.flush()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets a "sendInviteText" property on a party to "Send Invitations"', function() {
|
||||||
|
var sendInviteText = window.env.t('sendInvitations');
|
||||||
|
var party = {
|
||||||
|
type: 'party',
|
||||||
|
data: {
|
||||||
|
_id: '1234',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
groups.inviteOrStartParty(party);
|
||||||
|
expect(party.sendInviteText).to.eql(sendInviteText);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets a "sendInviteText" proptery on a guild to "Send Invitations +$3.00/month/user"', function() {
|
||||||
|
var sendInviteText = window.env.t('sendInvitations');
|
||||||
|
var guild = {
|
||||||
|
type: 'guild',
|
||||||
|
data: {
|
||||||
|
_id: '12345',
|
||||||
|
},
|
||||||
|
purchased: {
|
||||||
|
plan: {
|
||||||
|
customerId: '123',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
groups.inviteOrStartParty(guild);
|
||||||
|
expect(guild.sendInviteText).to.eql(sendInviteText + window.env.t('groupAdditionalUserCost'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,14 @@ describe('Tasks Service', function() {
|
|||||||
tasks = Tasks;
|
tasks = Tasks;
|
||||||
});
|
});
|
||||||
|
|
||||||
rootScope.openModal = function () {};
|
rootScope.openModal = function() {
|
||||||
|
return {
|
||||||
|
result: {
|
||||||
|
then: function() {},
|
||||||
|
catch: function() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls get user tasks endpoint', function() {
|
it('calls get user tasks endpoint', function() {
|
||||||
@@ -83,6 +90,14 @@ describe('Tasks Service', function() {
|
|||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('calls group move task endpoint', function() {
|
||||||
|
var taskId = 1;
|
||||||
|
var position = 0;
|
||||||
|
$httpBackend.expectPOST('/api/v3/group-tasks/' + taskId + '/move/to/' + position).respond({});
|
||||||
|
tasks.moveGroupTask(taskId, position);
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
|
||||||
it('calls add check list item endpoint', function() {
|
it('calls add check list item endpoint', function() {
|
||||||
var taskId = 1;
|
var taskId = 1;
|
||||||
$httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/checklist').respond({});
|
$httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/checklist').respond({});
|
||||||
|
|||||||
6
test/client/.eslintrc
Normal file
6
test/client/.eslintrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"browser": true,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
// for how to write custom assertions see
|
// for how to write custom assertions see
|
||||||
// http://nightwatchjs.org/guide#writing-custom-assertions
|
// http://nightwatchjs.org/guide#writing-custom-assertions
|
||||||
exports.assertion = function (selector, count) {
|
exports.assertion = function (selector, count) {
|
||||||
this.message = 'Testing if element <' + selector + '> has count: ' + count;
|
this.message = `Testing if element <${selector}> has count: ${count}`;
|
||||||
this.expected = count;
|
this.expected = count;
|
||||||
this.pass = function (val) {
|
this.pass = function (val) {
|
||||||
return val === this.expected;
|
return val === this.expected;
|
||||||
@@ -16,11 +16,10 @@ exports.assertion = function (selector, count) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
};
|
};
|
||||||
this.command = function (cb) {
|
this.command = function (cb) {
|
||||||
var self = this;
|
return this.api.execute((el) => {
|
||||||
return this.api.execute(function (selector) {
|
return document.querySelectorAll(el).length;
|
||||||
return document.querySelectorAll(selector).length;
|
}, [selector], (res) => {
|
||||||
}, [selector], function (res) {
|
cb.call(this, res);
|
||||||
cb.call(self, res);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,45 +1,48 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
require('babel-register');
|
require('babel-register');
|
||||||
var config = require('../../../webpack/config');
|
const config = require('../../../webpack/config');
|
||||||
|
const chromeDriverPath = require('chromedriver').path;
|
||||||
|
|
||||||
// http://nightwatchjs.org/guide#settings-file
|
// http://nightwatchjs.org/guide#settings-file
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'src_folders': ['test/client/e2e/specs'],
|
src_folders: ['test/client/e2e/specs'],
|
||||||
'output_folder': 'test/client/e2e/reports',
|
output_folder: 'test/client/e2e/reports',
|
||||||
'custom_assertions_path': ['test/client/e2e/custom-assertions'],
|
custom_assertions_path: ['test/client/e2e/custom-assertions'],
|
||||||
|
|
||||||
'selenium': {
|
selenium: {
|
||||||
'start_process': true,
|
start_process: true,
|
||||||
'server_path': 'node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.0.jar',
|
server_path: 'node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.0.jar',
|
||||||
'host': '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
'port': 4444,
|
port: 4444,
|
||||||
'cli_args': {
|
cli_args: {
|
||||||
'webdriver.chrome.driver': require('chromedriver').path,
|
'webdriver.chrome.driver': chromeDriverPath,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'test_settings': {
|
test_settings: {
|
||||||
'default': {
|
default: {
|
||||||
'selenium_port': 4444,
|
selenium_port: 4444,
|
||||||
'selenium_host': 'localhost',
|
selenium_host: 'localhost',
|
||||||
'silent': true,
|
silent: true,
|
||||||
'globals': {
|
globals: {
|
||||||
'devServerURL': 'http://localhost:' + (process.env.PORT || config.dev.port),
|
devServerURL: `http://localhost:${process.env.PORT || config.dev.port}`, // eslint-disable-line no-process-env
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'chrome': {
|
chrome: {
|
||||||
'desiredCapabilities': {
|
desiredCapabilities: {
|
||||||
'browserName': 'chrome',
|
browserName: 'chrome',
|
||||||
'javascriptEnabled': true,
|
javascriptEnabled: true,
|
||||||
'acceptSslCerts': true,
|
acceptSslCerts: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'firefox': {
|
firefox: {
|
||||||
'desiredCapabilities': {
|
desiredCapabilities: {
|
||||||
'browserName': 'firefox',
|
browserName: 'firefox',
|
||||||
'javascriptEnabled': true,
|
javascriptEnabled: true,
|
||||||
'acceptSslCerts': true,
|
acceptSslCerts: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// 1. start the dev server using production config
|
// 1. start the dev server using production config
|
||||||
process.env.NODE_ENV = 'testing';
|
process.env.NODE_ENV = 'testing'; // eslint-disable-line no-process-env
|
||||||
var server = require('../../../webpack/dev-server.js');
|
const server = require('../../../webpack/dev-server.js');
|
||||||
|
|
||||||
// 2. run the nightwatch test suite against it
|
// 2. run the nightwatch test suite against it
|
||||||
// to run in additional browsers:
|
// to run in additional browsers:
|
||||||
@@ -9,7 +9,7 @@ var server = require('../../../webpack/dev-server.js');
|
|||||||
// or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
|
// or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
|
||||||
// For more information on Nightwatch's config file, see
|
// For more information on Nightwatch's config file, see
|
||||||
// http://nightwatchjs.org/guide#settings-file
|
// http://nightwatchjs.org/guide#settings-file
|
||||||
var opts = process.argv.slice(2);
|
let opts = process.argv.slice(2);
|
||||||
if (opts.indexOf('--config') === -1) {
|
if (opts.indexOf('--config') === -1) {
|
||||||
opts = opts.concat(['--config', 'test/client/e2e/nightwatch.conf.js']);
|
opts = opts.concat(['--config', 'test/client/e2e/nightwatch.conf.js']);
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,8 @@ if (opts.indexOf('--env') === -1) {
|
|||||||
opts = opts.concat(['--env', 'chrome']);
|
opts = opts.concat(['--env', 'chrome']);
|
||||||
}
|
}
|
||||||
|
|
||||||
var spawn = require('cross-spawn');
|
const spawn = require('cross-spawn');
|
||||||
var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' });
|
const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' });
|
||||||
|
|
||||||
runner.on('exit', function (code) {
|
runner.on('exit', function (code) {
|
||||||
server.close();
|
server.close();
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
// http://nightwatchjs.org/guide#usage
|
// http://nightwatchjs.org/guide#usage
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'default e2e tests': function (browser) {
|
'default e2e tests' (browser) {
|
||||||
|
|
||||||
// automatically uses dev Server port from /config.index.js
|
// automatically uses dev Server port from /config.index.js
|
||||||
// default: http://localhost:8080
|
// default: http://localhost:8080
|
||||||
// see nightwatch.conf.js
|
// see nightwatch.conf.js
|
||||||
var devServer = browser.globals.devServerURL;
|
const devServer = browser.globals.devServerURL;
|
||||||
|
|
||||||
browser
|
browser
|
||||||
.url(devServer)
|
.url(devServer)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// TODO verify if it's needed, added because Vuex require Promise in the global scope
|
// TODO verify if it's needed, added because Axios require Promise in the global scope
|
||||||
// and babel-runtime doesn't affect external libraries
|
// and babel-runtime doesn't affect external libraries
|
||||||
require('babel-polyfill');
|
require('babel-polyfill');
|
||||||
|
|
||||||
// require all test files (files that ends with .spec.js)
|
// require all test files (files that ends with .spec.js)
|
||||||
var testsContext = require.context('./specs', true, /\.spec$/);
|
let testsContext = require.context('./specs', true, /\.spec$/);
|
||||||
testsContext.keys().forEach(testsContext);
|
testsContext.keys().forEach(testsContext);
|
||||||
|
|
||||||
// require all .vue and .js files except main.js for coverage.
|
// require all .vue and .js files except main.js for coverage.
|
||||||
var srcContext = require.context('../../../website/client', true, /^\.\/(?=(?!main(\.js)?$))(?=(.*\.(vue|js)$))/);
|
let srcContext = require.context('../../../website/client', true, /^\.\/(?=(?!main(\.js)?$))(?=(.*\.(vue|js)$))/);
|
||||||
srcContext.keys().forEach(srcContext);
|
srcContext.keys().forEach(srcContext);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user