Merge branch 'develop' of https://github.com/HabitRPG/habitica into negue/flagpm
@@ -1,6 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- '8'
|
- '10'
|
||||||
services:
|
services:
|
||||||
- mongodb
|
- mongodb
|
||||||
cache:
|
cache:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:8
|
FROM node:10
|
||||||
|
|
||||||
ENV ADMIN_EMAIL admin@habitica.com
|
ENV ADMIN_EMAIL admin@habitica.com
|
||||||
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
||||||
@@ -8,6 +8,7 @@ ENV BASE_URL https://habitica.com
|
|||||||
ENV FACEBOOK_KEY 128307497299777
|
ENV FACEBOOK_KEY 128307497299777
|
||||||
ENV GA_ID UA-33510635-1
|
ENV GA_ID UA-33510635-1
|
||||||
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
||||||
|
ENV LOGGLY_CLIENT_TOKEN ab5663bf-241f-4d14-8783-7d80db77089a
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:8
|
FROM node:10
|
||||||
|
|
||||||
# Install global packages
|
# Install global packages
|
||||||
RUN npm install -g gulp-cli mocha
|
RUN npm install -g gulp-cli mocha
|
||||||
|
|||||||
@@ -1,116 +1,82 @@
|
|||||||
{
|
{
|
||||||
"PORT":3000,
|
"ADMIN_EMAIL": "you@example.com",
|
||||||
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
"AMAZON_PAYMENTS_CLIENT_ID": "CLIENT_ID",
|
||||||
"IP":"0.0.0.0",
|
"AMAZON_PAYMENTS_MODE": "sandbox",
|
||||||
"WEB_CONCURRENCY":1,
|
"AMAZON_PAYMENTS_MWS_KEY": "MWS_KEY",
|
||||||
"BASE_URL":"http://localhost:3000",
|
"AMAZON_PAYMENTS_MWS_SECRET": "MWS_SECRET",
|
||||||
"FACEBOOK_KEY":"123456789012345",
|
"AMAZON_PAYMENTS_SELLER_ID": "SELLER_ID",
|
||||||
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||||
"GOOGLE_CLIENT_ID":"123456789012345",
|
"AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
|
||||||
"GOOGLE_CLIENT_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
"BASE_URL": "http://localhost:3000",
|
||||||
"PLAY_API": {
|
"CRON_SAFE_MODE": "false",
|
||||||
"CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
"CRON_SEMI_SAFE_MODE": "false",
|
||||||
"CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
"DISABLE_REQUEST_LOGGING": "true",
|
||||||
"ACCESS_TOKEN":"aaaabbbbccccddddeeeeffff00001111",
|
"EMAILS_COMMUNITY_MANAGER_EMAIL": "admin@habitica.com",
|
||||||
"REFRESH_TOKEN":"aaaabbbbccccddddeeeeffff00001111"
|
"EMAILS_PRESS_ENQUIRY_EMAIL": "admin@habitica.com",
|
||||||
},
|
"EMAILS_TECH_ASSISTANCE_EMAIL": "admin@habitica.com",
|
||||||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
"EMAIL_SERVER_AUTH_PASSWORD": "password",
|
||||||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
"EMAIL_SERVER_AUTH_USER": "user",
|
||||||
"NODE_ENV":"development",
|
"EMAIL_SERVER_URL": "http://example.com",
|
||||||
"ENABLE_CONSOLE_LOGS_IN_TEST": "false",
|
"ENABLE_CONSOLE_LOGS_IN_PROD": "false",
|
||||||
"CRON_SAFE_MODE":"false",
|
"ENABLE_CONSOLE_LOGS_IN_TEST": "false",
|
||||||
"CRON_SEMI_SAFE_MODE":"false",
|
"FACEBOOK_KEY": "123456789012345",
|
||||||
"MAINTENANCE_MODE": "false",
|
"FACEBOOK_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"SESSION_SECRET":"YOUR SECRET HERE",
|
"FLAG_REPORT_EMAIL": "email@example.com, email2@example.com",
|
||||||
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
|
"GA_ID": "GA_ID",
|
||||||
"SESSION_SECRET_IV": "12345678912345678912345678912345",
|
"GOOGLE_CLIENT_ID": "123456789012345",
|
||||||
"ADMIN_EMAIL": "you@example.com",
|
"GOOGLE_CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"SMTP_USER":"user@example.com",
|
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
||||||
"SMTP_PASS":"password",
|
"IGNORE_REDIRECT": "true",
|
||||||
"SMTP_SERVICE":"Gmail",
|
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"SMTP_HOST":"example.com",
|
"LOGGLY_CLIENT_TOKEN": "token",
|
||||||
"SMTP_PORT": 587,
|
"LOGGLY_SUBDOMAIN": "example-subdomain",
|
||||||
"SMTP_TLS": true,
|
"LOGGLY_TOKEN": "example-token",
|
||||||
"STRIPE_API_KEY":"aaaabbbbccccddddeeeeffff00001111",
|
"MAINTENANCE_MODE": "false",
|
||||||
"STRIPE_PUB_KEY":"22223333444455556666777788889999",
|
"NODE_DB_URI": "mongodb://localhost/habitrpg",
|
||||||
"NEW_RELIC_LICENSE_KEY":"NEW_RELIC_LICENSE_KEY",
|
"NODE_ENV": "development",
|
||||||
"NEW_RELIC_NO_CONFIG_FILE":"true",
|
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
|
||||||
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",
|
||||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
"PAYPAL_BILLING_PLANS_basic_3mo": "basic_3mo",
|
||||||
"GA_ID": "GA_ID",
|
"PAYPAL_BILLING_PLANS_basic_6mo": "basic_6mo",
|
||||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
"PAYPAL_BILLING_PLANS_basic_earned": "basic_earned",
|
||||||
"AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
|
"PAYPAL_BILLING_PLANS_google_6mo": "google_6mo",
|
||||||
"AMAZON_PAYMENTS": {
|
"PAYPAL_CLIENT_ID": "client_id",
|
||||||
"SELLER_ID": "SELLER_ID",
|
"PAYPAL_CLIENT_SECRET": "client_secret",
|
||||||
"CLIENT_ID": "CLIENT_ID",
|
"PAYPAL_EXPERIENCE_PROFILE_ID": "xp_profile_id",
|
||||||
"MWS_KEY": "",
|
"PAYPAL_MODE": "sandbox",
|
||||||
"MWS_SECRET": "",
|
"PLAY_API_ACCESS_TOKEN": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"MODE": "sandbox"
|
"PLAY_API_CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
},
|
"PLAY_API_CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"FLAG_REPORT_EMAIL": "email@mod.com,email2@mod.com",
|
"PLAY_API_REFRESH_TOKEN": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"EMAIL_SERVER": {
|
"PORT": 3000,
|
||||||
"url": "http://example.com",
|
"PUSH_CONFIGS_APN_ENABLED": "false",
|
||||||
"authUser": "user",
|
"PUSH_CONFIGS_APN_KEY": "xxxxxxxxxx",
|
||||||
"authPassword": "password"
|
"PUSH_CONFIGS_APN_KEY_ID": "xxxxxxxxxx",
|
||||||
},
|
"PUSH_CONFIGS_APN_TEAM_ID": "aaabbbcccd",
|
||||||
"S3":{
|
"PUSH_CONFIGS_FCM_SERVER_API_KEY": "aaabbbcccd",
|
||||||
"bucket":"bucket",
|
"S3_ACCESS_KEY_ID": "accessKeyId",
|
||||||
"accessKeyId":"accessKeyId",
|
"S3_BUCKET": "bucket",
|
||||||
"secretAccessKey":"secretAccessKey"
|
"S3_SECRET_ACCESS_KEY": "secretAccessKey",
|
||||||
},
|
"SESSION_SECRET": "YOUR SECRET HERE",
|
||||||
"SLACK_URL": "https://hooks.slack.com/services/some-url",
|
"SESSION_SECRET_IV": "12345678912345678912345678912345",
|
||||||
"TRANSIFEX_SLACK_CHANNEL": "transifex",
|
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
|
||||||
"PAYPAL":{
|
"SITE_HTTP_AUTH_ENABLED": "false",
|
||||||
"billing_plans": {
|
"SITE_HTTP_AUTH_PASSWORD": "password",
|
||||||
"basic_earned":"basic_earned",
|
"SITE_HTTP_AUTH_USERNAME": "admin",
|
||||||
"basic_3mo":"basic_3mo",
|
"SLACK_FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||||
"basic_6mo":"basic_6mo",
|
"SLACK_FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||||
"google_6mo":"google_6mo",
|
"SLACK_SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id",
|
||||||
"basic_12mo":"basic_12mo"
|
"SLACK_URL": "https://hooks.slack.com/services/some-url",
|
||||||
},
|
"SMTP_HOST": "example.com",
|
||||||
"mode":"sandbox",
|
"SMTP_PASS": "password",
|
||||||
"client_id":"client_id",
|
"SMTP_PORT": 587,
|
||||||
"client_secret":"client_secret",
|
"SMTP_SERVICE": "Gmail",
|
||||||
"experience_profile_id": ""
|
"SMTP_TLS": "true",
|
||||||
},
|
"SMTP_USER": "user@example.com",
|
||||||
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
"STRIPE_API_KEY": "aaaabbbbccccddddeeeeffff00001111",
|
||||||
"LOGGLY_TOKEN": "token",
|
"STRIPE_PUB_KEY": "22223333444455556666777788889999",
|
||||||
"LOGGLY_CLIENT_TOKEN": "token",
|
"TEST_DB_URI": "mongodb://localhost/habitrpg_test",
|
||||||
"LOGGLY_ACCOUNT": "account",
|
"TRANSIFEX_SLACK_CHANNEL": "transifex",
|
||||||
"PUSH_CONFIGS": {
|
"WEB_CONCURRENCY": 1,
|
||||||
"GCM_SERVER_API_KEY": "",
|
"SKIP_SSL_CHECK_KEY": "key"
|
||||||
"APN_ENABLED": "false",
|
|
||||||
"APN_KEY_ID": "xxxxxxxxxx",
|
|
||||||
"APN_KEY": "xxxxxxxxxx",
|
|
||||||
"APN_TEAM_ID": "aaabbbcccd",
|
|
||||||
"FCM_SERVER_API_KEY": ""
|
|
||||||
},
|
|
||||||
"SITE_HTTP_AUTH": {
|
|
||||||
"ENABLED": "false",
|
|
||||||
"USERNAME": "admin",
|
|
||||||
"PASSWORD": "password"
|
|
||||||
},
|
|
||||||
"SLACK": {
|
|
||||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
|
||||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
|
||||||
"SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id"
|
|
||||||
},
|
|
||||||
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
|
||||||
"EMAILS" : {
|
|
||||||
"COMMUNITY_MANAGER_EMAIL" : "admin@habitica.com",
|
|
||||||
"TECH_ASSISTANCE_EMAIL" : "admin@habitica.com",
|
|
||||||
"PRESS_ENQUIRY_EMAIL" : "admin@habitica.com"
|
|
||||||
},
|
|
||||||
"LOGGLY" : {
|
|
||||||
"TOKEN" : "example-token",
|
|
||||||
"SUBDOMAIN" : "exmaple-subdomain"
|
|
||||||
},
|
|
||||||
"KAFKA": {
|
|
||||||
"GROUP_ID": "",
|
|
||||||
"CLOUDKARAFKA_BROKERS": "",
|
|
||||||
"CLOUDKARAFKA_USERNAME": "",
|
|
||||||
"CLOUDKARAFKA_PASSWORD": "",
|
|
||||||
"CLOUDKARAFKA_TOPIC_PREFIX": ""
|
|
||||||
},
|
|
||||||
"MIGRATION_CONNECT_STRING": "mongodb://localhost:27017/habitrpg?auto_reconnect=true"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => {
|
|||||||
|
|
||||||
gulp.task('test:api:unit', (done) => {
|
gulp.task('test:api:unit', (done) => {
|
||||||
let runner = exec(
|
let runner = exec(
|
||||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
|
testBin('istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -180,12 +180,12 @@ gulp.task('test:api:unit', (done) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test:api:unit:watch', () => {
|
gulp.task('test:api:unit:watch', () => {
|
||||||
return gulp.watch(['website/server/libs/*', 'test/api/v3/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done()));
|
return gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done()));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test:api-v3:integration', (done) => {
|
gulp.task('test:api-v3:integration', (done) => {
|
||||||
let runner = exec(
|
let runner = exec(
|
||||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
testBin('istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||||
{maxBuffer: 500 * 1024},
|
{maxBuffer: 500 * 1024},
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -217,7 +217,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => {
|
|||||||
|
|
||||||
gulp.task('test:api-v4:integration', (done) => {
|
gulp.task('test:api-v4:integration', (done) => {
|
||||||
let runner = exec(
|
let runner = exec(
|
||||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
|
testBin('istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
|
||||||
{maxBuffer: 500 * 1024},
|
{maxBuffer: 500 * 1024},
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -254,4 +254,4 @@ gulp.task('test:api-v3', gulp.series(
|
|||||||
'test:api:unit',
|
'test:api:unit',
|
||||||
'test:api-v3:integration',
|
'test:api-v3:integration',
|
||||||
done => done()
|
done => done()
|
||||||
));
|
));
|
||||||
|
|||||||
110
migrations/archive/2018/20181231_nye.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181231_nye';
|
||||||
|
import { model as User } from '../../../website/server/models/user';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {'flags.newStuff': true};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2018'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2018',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2017'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2017',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2016'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2016',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2015'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2015',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_nye2014'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye2014',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.head_special_nye'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_nye',
|
||||||
|
_id: uuid(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -17,7 +17,7 @@ function setUpServer () {
|
|||||||
setUpServer();
|
setUpServer();
|
||||||
|
|
||||||
// Replace this with your migration
|
// Replace this with your migration
|
||||||
const processUsers = require('./users/20181122_turkey_day.js');
|
const processUsers = require('./archive/2018/20181231_nye.js');
|
||||||
processUsers()
|
processUsers()
|
||||||
.then(function success () {
|
.then(function success () {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
61
migrations/users/bulk-email.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
import { sendTxn } from '../../../website/server/libs/email';
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
import moment from 'moment';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
const BASE_URL = nconf.get('BASE_URL');
|
||||||
|
const EMAIL_SLUG = 'mandrill-email-slug'; // Set email template to send
|
||||||
|
const MIGRATION_NAME = 'bulk-email';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
sendTxn(
|
||||||
|
user,
|
||||||
|
EMAIL_SLUG,
|
||||||
|
[{name: 'BASE_URL', content: BASE_URL}] // Add variables from template
|
||||||
|
);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'auth.timestamps.loggedin': {$gt: moment().subtract(2, 'weeks').toDate()}, // customize or remove to target different populations
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
auth: 1,
|
||||||
|
preferences: 1,
|
||||||
|
profile: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
const MIGRATION_NAME = 'mystery_items_201811';
|
const MIGRATION_NAME = 'mystery_items_201812';
|
||||||
const MYSTERY_ITEMS = ['head_mystery_201811', 'weapon_mystery_201811'];
|
const MYSTERY_ITEMS = ['headAccessory_mystery_201812', 'back_mystery_201812'];
|
||||||
import { model as User } from '../../website/server/models/user';
|
import { model as User } from '../../website/server/models/user';
|
||||||
import { model as UserNotification } from '../../website/server/models/userNotification';
|
import { model as UserNotification } from '../../website/server/models/userNotification';
|
||||||
|
|
||||||
|
|||||||
81
migrations/users/take-this.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181203_take_this';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') {
|
||||||
|
push = false;
|
||||||
|
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.back_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.body_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.armor_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: uuid()}};
|
||||||
|
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||||
|
set['items.gear.owned.weapon_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: uuid()}};
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.shield_special_takeThis'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: uuid()}};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
if (push) {
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: push}).exec();
|
||||||
|
} else {
|
||||||
|
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
challenges: '00708425-d477-41a5-bf27-6270466e7976',
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
11652
package-lock.json
generated
31
package.json
@@ -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": "4.74.0",
|
"version": "4.80.8",
|
||||||
"main": "./website/server/index.js",
|
"main": "./website/server/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@slack/client": "^3.8.1",
|
"@slack/client": "^3.8.1",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"compression": "^1.7.2",
|
"compression": "^1.7.2",
|
||||||
"cookie-session": "^1.2.0",
|
"cookie-session": "^1.2.0",
|
||||||
"coupon-code": "^0.4.5",
|
"coupon-code": "^0.4.5",
|
||||||
"cross-env": "^5.1.5",
|
"cross-env": "^5.2.0",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"csv-stringify": "^4.3.1",
|
"csv-stringify": "^4.3.1",
|
||||||
"cwait": "^1.1.1",
|
"cwait": "^1.1.1",
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
"nconf": "^0.10.0",
|
"nconf": "^0.10.0",
|
||||||
"node-gcm": "^1.0.2",
|
"node-gcm": "^1.0.2",
|
||||||
"node-sass": "^4.9.0",
|
"node-sass": "^4.9.0",
|
||||||
"nodemailer": "^4.6.4",
|
"nodemailer": "^5.0.0",
|
||||||
"ora": "^3.0.0",
|
"ora": "^3.0.0",
|
||||||
"pageres": "^4.1.1",
|
"pageres": "^4.1.1",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
"ps-tree": "^1.0.0",
|
"ps-tree": "^1.0.0",
|
||||||
"pug": "^2.0.3",
|
"pug": "^2.0.3",
|
||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
"sass-loader": "^7.0.0",
|
"sass-loader": "^7.0.3",
|
||||||
"shelljs": "^0.8.2",
|
"shelljs": "^0.8.2",
|
||||||
"short-uuid": "^3.0.0",
|
"short-uuid": "^3.0.0",
|
||||||
"smartbanner.js": "^1.9.1",
|
"smartbanner.js": "^1.9.1",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"svg-url-loader": "^2.3.2",
|
"svg-url-loader": "^2.3.2",
|
||||||
"svgo": "^1.0.5",
|
"svgo": "^1.0.5",
|
||||||
"svgo-loader": "^2.1.0",
|
"svgo-loader": "^2.1.0",
|
||||||
"universal-analytics": "^0.4.16",
|
"universal-analytics": "^0.4.17",
|
||||||
"update": "^0.7.4",
|
"update": "^0.7.4",
|
||||||
"upgrade": "^1.1.0",
|
"upgrade": "^1.1.0",
|
||||||
"url-loader": "^1.0.0",
|
"url-loader": "^1.0.0",
|
||||||
@@ -107,15 +107,15 @@
|
|||||||
"vuedraggable": "^2.15.0",
|
"vuedraggable": "^2.15.0",
|
||||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
|
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
|
||||||
"webpack": "^3.12.0",
|
"webpack": "^3.12.0",
|
||||||
"webpack-merge": "^4.0.0",
|
"webpack-merge": "^4.1.3",
|
||||||
"winston": "^2.4.2",
|
"winston": "^2.4.3",
|
||||||
"winston-loggly-bulk": "^2.0.2",
|
"winston-loggly-bulk": "^2.0.2",
|
||||||
"xml2js": "^0.4.4"
|
"xml2js": "^0.4.4"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.9.4",
|
"node": "^10",
|
||||||
"npm": "^5.6.0"
|
"npm": "^6"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.vue .",
|
"lint": "eslint --ext .js,.vue .",
|
||||||
@@ -144,13 +144,13 @@
|
|||||||
"apidoc": "gulp apidoc"
|
"apidoc": "gulp apidoc"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/test-utils": "^1.0.0-beta.16",
|
"@vue/test-utils": "^1.0.0-beta.19",
|
||||||
"babel-plugin-istanbul": "^4.1.6",
|
"babel-plugin-istanbul": "^4.1.6",
|
||||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"chalk": "^2.4.1",
|
"chalk": "^2.4.1",
|
||||||
"chromedriver": "^2.38.3",
|
"chromedriver": "^2.40.0",
|
||||||
"connect-history-api-fallback": "^1.1.0",
|
"connect-history-api-fallback": "^1.1.0",
|
||||||
"coveralls": "^3.0.1",
|
"coveralls": "^3.0.1",
|
||||||
"cross-spawn": "^6.0.5",
|
"cross-spawn": "^6.0.5",
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
"expect.js": "^0.3.1",
|
"expect.js": "^0.3.1",
|
||||||
"http-proxy-middleware": "^0.19.0",
|
"http-proxy-middleware": "^0.19.0",
|
||||||
"istanbul": "^1.1.0-alpha.1",
|
"istanbul": "^1.1.0-alpha.1",
|
||||||
"karma": "^3.0.0",
|
"karma": "^3.1.3",
|
||||||
"karma-babel-preprocessor": "^7.0.0",
|
"karma-babel-preprocessor": "^7.0.0",
|
||||||
"karma-chai-plugins": "^0.9.0",
|
"karma-chai-plugins": "^0.9.0",
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
"mocha": "^5.1.1",
|
"mocha": "^5.1.1",
|
||||||
"monk": "^6.0.6",
|
"monk": "^6.0.6",
|
||||||
"nightwatch": "^0.9.21",
|
"nightwatch": "^0.9.21",
|
||||||
"puppeteer": "^1.4.0",
|
"puppeteer": "^1.5.0",
|
||||||
"require-again": "^2.0.0",
|
"require-again": "^2.0.0",
|
||||||
"selenium-server": "^3.12.0",
|
"selenium-server": "^3.12.0",
|
||||||
"sinon": "^6.3.5",
|
"sinon": "^6.3.5",
|
||||||
@@ -190,8 +190,5 @@
|
|||||||
"webpack-dev-middleware": "^2.0.5",
|
"webpack-dev-middleware": "^2.0.5",
|
||||||
"webpack-hot-middleware": "^2.22.2"
|
"webpack-hot-middleware": "^2.22.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {}
|
||||||
"memwatch-next": "^0.3.0",
|
|
||||||
"node-rdkafka": "^2.3.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ async function _deleteAmplitudeData (userId, email) {
|
|||||||
if (response) console.log(`${response.status} ${response.statusText}`);
|
if (response) console.log(`${response.status} ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _deleteHabiticaData (user) {
|
async function _deleteHabiticaData (user, email) {
|
||||||
await User.update(
|
await User.update(
|
||||||
{_id: user._id},
|
{_id: user._id},
|
||||||
{$set: {
|
{$set: {
|
||||||
'auth.local.passwordHashMethod': 'bcrypt',
|
'auth.local.email': email,
|
||||||
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
||||||
|
'auth.local.passwordHashMethod': 'bcrypt',
|
||||||
}}
|
}}
|
||||||
);
|
);
|
||||||
const response = await axios.delete(
|
const response = await axios.delete(
|
||||||
@@ -75,7 +76,7 @@ async function _processEmailAddress (email) {
|
|||||||
} else {
|
} else {
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
||||||
await _deleteHabiticaData(user); // eslint-disable-line no-await-in-loop
|
await _deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,28 +12,27 @@ const nconf = require('nconf');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const paypal = require('paypal-rest-sdk');
|
const paypal = require('paypal-rest-sdk');
|
||||||
const blocks = require('../website/common').content.subscriptionBlocks;
|
const blocks = require('../website/common').content.subscriptionBlocks;
|
||||||
const live = nconf.get('PAYPAL:mode') === 'live';
|
const live = nconf.get('PAYPAL_MODE') === 'live';
|
||||||
|
|
||||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
||||||
|
|
||||||
let OP = 'create'; // list create update remove
|
let OP = 'create'; // list get update create create-webprofile
|
||||||
|
|
||||||
paypal.configure({
|
paypal.configure({
|
||||||
mode: nconf.get('PAYPAL:mode'), // sandbox or live
|
mode: nconf.get('PAYPAL_MODE'), // sandbox or live
|
||||||
client_id: nconf.get('PAYPAL:client_id'),
|
client_id: nconf.get('PAYPAL_CLIENT_ID'),
|
||||||
client_secret: nconf.get('PAYPAL:client_secret'),
|
client_secret: nconf.get('PAYPAL_CLIENT_SECRET'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
|
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
|
||||||
let billingPlanTitle = 'Habitica Subscription';
|
let billingPlanTitle = 'Habitica Subscription';
|
||||||
let billingPlanAttributes = {
|
let billingPlanAttributes = {
|
||||||
name: billingPlanTitle,
|
|
||||||
description: billingPlanTitle,
|
description: billingPlanTitle,
|
||||||
type: 'INFINITE',
|
type: 'INFINITE',
|
||||||
merchant_preferences: {
|
merchant_preferences: {
|
||||||
auto_bill_amount: 'yes',
|
auto_bill_amount: 'yes',
|
||||||
cancel_url: live ? 'https://habitica.com' : 'http://localhost:3000',
|
cancel_url: live ? 'https://habitica.com' : 'http://localhost:3000',
|
||||||
return_url: `${live ? 'https://habitica.com' : 'http://localhost:3000' }/paypal/subscribe/success`,
|
return_url: `${live ? 'https://habitica.com' : 'http://localhost:3000'}/paypal/subscribe/success`,
|
||||||
},
|
},
|
||||||
payment_definitions: [{
|
payment_definitions: [{
|
||||||
type: 'REGULAR',
|
type: 'REGULAR',
|
||||||
@@ -45,7 +44,7 @@ let billingPlanAttributes = {
|
|||||||
_.each(blocks, (block) => {
|
_.each(blocks, (block) => {
|
||||||
block.definition = _.cloneDeep(billingPlanAttributes);
|
block.definition = _.cloneDeep(billingPlanAttributes);
|
||||||
_.merge(block.definition.payment_definitions[0], {
|
_.merge(block.definition.payment_definitions[0], {
|
||||||
name: `${billingPlanTitle } ($${block.price} every ${block.months} months, recurring)`,
|
name: `${billingPlanTitle} ($${block.price} every ${block.months} months, recurring)`,
|
||||||
frequency_interval: `${block.months}`,
|
frequency_interval: `${block.months}`,
|
||||||
amount: {
|
amount: {
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
@@ -63,7 +62,7 @@ switch (OP) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'get':
|
case 'get':
|
||||||
paypal.billingPlan.get(nconf.get('PAYPAL:billing_plans:12'), (err, plan) => {
|
paypal.billingPlan.get(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), (err, plan) => {
|
||||||
console.log({err, plan});
|
console.log({err, plan});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -75,7 +74,7 @@ switch (OP) {
|
|||||||
cancel_url: 'https://habitica.com',
|
cancel_url: 'https://habitica.com',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
paypal.billingPlan.update(nconf.get('PAYPAL:billing_plans:12'), updatePayload, (err, res) => {
|
paypal.billingPlan.update(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), updatePayload, (err, res) => {
|
||||||
console.log({err, plan: res});
|
console.log({err, plan: res});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -101,9 +100,6 @@ switch (OP) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'remove': break;
|
|
||||||
|
|
||||||
case 'create-webprofile':
|
case 'create-webprofile':
|
||||||
let webexpinfo = {
|
let webexpinfo = {
|
||||||
name: 'HabiticaProfile',
|
name: 'HabiticaProfile',
|
||||||
@@ -116,4 +112,4 @@ switch (OP) {
|
|||||||
console.log(error, result);
|
console.log(error, result);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
BadRequest,
|
BadRequest,
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
NotFound,
|
NotFound,
|
||||||
|
NotificationNotFound,
|
||||||
} from '../../../../website/server/libs/errors';
|
} from '../../../../website/server/libs/errors';
|
||||||
|
import i18n from '../../../../website/common/script/i18n';
|
||||||
|
|
||||||
describe('Custom Errors', () => {
|
describe('Custom Errors', () => {
|
||||||
describe('CustomError', () => {
|
describe('CustomError', () => {
|
||||||
@@ -66,6 +68,23 @@ describe('Custom Errors', () => {
|
|||||||
|
|
||||||
expect(notAuthorizedError.message).to.eql('Custom Error Message');
|
expect(notAuthorizedError.message).to.eql('Custom Error Message');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('NotificationNotFound', () => {
|
||||||
|
it('is an instance of NotFound', () => {
|
||||||
|
const notificationNotFoundErr = new NotificationNotFound();
|
||||||
|
expect(notificationNotFoundErr).to.be.an.instanceOf(NotFound);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it returns an http code of 404', () => {
|
||||||
|
const notificationNotFoundErr = new NotificationNotFound();
|
||||||
|
expect(notificationNotFoundErr.httpCode).to.eql(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a standard message', () => {
|
||||||
|
const notificationNotFoundErr = new NotificationNotFound();
|
||||||
|
expect(notificationNotFoundErr.message).to.eql(i18n.t('messageNotificationNotFound'));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BadRequest', () => {
|
describe('BadRequest', () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import iap from '../../../../../website/server/libs/inAppPurchases';
|
|||||||
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 moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import {mockFindById, restoreFindById} from '../../../../helpers/mongoose.helper';
|
||||||
|
|
||||||
const i18n = common.i18n;
|
const i18n = common.i18n;
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ describe('Apple Payments', () => {
|
|||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(false);
|
.returns(false);
|
||||||
|
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -61,7 +62,7 @@ describe('Apple Payments', () => {
|
|||||||
iapGetPurchaseDataStub.restore();
|
iapGetPurchaseDataStub.restore();
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
|
||||||
|
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -71,7 +72,7 @@ describe('Apple Payments', () => {
|
|||||||
|
|
||||||
it('errors if the user cannot purchase gems', async () => {
|
it('errors if the user cannot purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -89,7 +90,7 @@ describe('Apple Payments', () => {
|
|||||||
transactionId: token,
|
transactionId: token,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -131,7 +132,7 @@ describe('Apple Payments', () => {
|
|||||||
}]);
|
}]);
|
||||||
|
|
||||||
sinon.stub(user, 'canGetGems').resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await applePayments.verifyGemPurchase(user, receipt, headers);
|
await applePayments.verifyGemPurchase({user, receipt, headers});
|
||||||
|
|
||||||
expect(iapSetupStub).to.be.calledOnce;
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
expect(iapValidateStub).to.be.calledOnce;
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
@@ -151,6 +152,38 @@ describe('Apple Payments', () => {
|
|||||||
user.canGetGems.restore();
|
user.canGetGems.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('gifts gems', async () => {
|
||||||
|
const receivingUser = new User();
|
||||||
|
await receivingUser.save();
|
||||||
|
|
||||||
|
mockFindById(receivingUser);
|
||||||
|
|
||||||
|
iapGetPurchaseDataStub.restore();
|
||||||
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
|
.returns([{productId: gemsCanPurchase[0].productId,
|
||||||
|
transactionId: token,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const gift = {uuid: receivingUser._id};
|
||||||
|
await applePayments.verifyGemPurchase({user, gift, receipt, headers});
|
||||||
|
|
||||||
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
|
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||||
|
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||||
|
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||||
|
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||||
|
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||||
|
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||||
|
user: receivingUser,
|
||||||
|
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||||
|
amount: gemsCanPurchase[0].amount,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
restoreFindById();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('subscribe', () => {
|
describe('subscribe', () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import iap from '../../../../../website/server/libs/inAppPurchases';
|
|||||||
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 moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import {mockFindById, restoreFindById} from '../../../../helpers/mongoose.helper';
|
||||||
|
|
||||||
const i18n = common.i18n;
|
const i18n = common.i18n;
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ describe('Google Payments', () => {
|
|||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(false);
|
.returns(false);
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -55,7 +56,7 @@ describe('Google Payments', () => {
|
|||||||
it('should throw an error if productId is invalid', async () => {
|
it('should throw an error if productId is invalid', async () => {
|
||||||
receipt = `{"token": "${token}", "productId": "invalid"}`;
|
receipt = `{"token": "${token}", "productId": "invalid"}`;
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -66,7 +67,7 @@ describe('Google Payments', () => {
|
|||||||
it('should throw an error if user cannot purchase gems', async () => {
|
it('should throw an error if user cannot purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
name: 'NotAuthorized',
|
name: 'NotAuthorized',
|
||||||
@@ -78,7 +79,7 @@ describe('Google Payments', () => {
|
|||||||
|
|
||||||
it('purchases gems', async () => {
|
it('purchases gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await googlePayments.verifyGemPurchase(user, receipt, signature, headers);
|
await googlePayments.verifyGemPurchase({user, receipt, signature, headers});
|
||||||
|
|
||||||
expect(iapSetupStub).to.be.calledOnce;
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
expect(iapValidateStub).to.be.calledOnce;
|
expect(iapValidateStub).to.be.calledOnce;
|
||||||
@@ -99,6 +100,34 @@ describe('Google Payments', () => {
|
|||||||
expect(user.canGetGems).to.be.calledOnce;
|
expect(user.canGetGems).to.be.calledOnce;
|
||||||
user.canGetGems.restore();
|
user.canGetGems.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('gifts gems', async () => {
|
||||||
|
const receivingUser = new User();
|
||||||
|
await receivingUser.save();
|
||||||
|
|
||||||
|
mockFindById(receivingUser);
|
||||||
|
|
||||||
|
const gift = {uuid: receivingUser._id};
|
||||||
|
await googlePayments.verifyGemPurchase({user, gift, 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: receivingUser,
|
||||||
|
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||||
|
amount: 5.25,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
restoreFindById();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('subscribe', () => {
|
describe('subscribe', () => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ describe('checkout', () => {
|
|||||||
|
|
||||||
function getPaypalCreateOptions (description, amount) {
|
function getPaypalCreateOptions (description, amount) {
|
||||||
return {
|
return {
|
||||||
|
experience_profile_id: 'xp_profile_id',
|
||||||
intent: 'sale',
|
intent: 'sale',
|
||||||
payer: { payment_method: 'Paypal' },
|
payer: { payment_method: 'Paypal' },
|
||||||
redirect_urls: {
|
redirect_urls: {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ describe('slack', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('noops if no flagging url is provided', () => {
|
it('noops if no flagging url is provided', () => {
|
||||||
sandbox.stub(nconf, 'get').withArgs('SLACK:FLAGGING_URL').returns('');
|
sandbox.stub(nconf, 'get').withArgs('SLACK_FLAGGING_URL').returns('');
|
||||||
sandbox.stub(logger, 'error');
|
sandbox.stub(logger, 'error');
|
||||||
let reRequiredSlack = requireAgain('../../../../website/server/libs/slack');
|
let reRequiredSlack = requireAgain('../../../../website/server/libs/slack');
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,56 @@ describe('redirects middleware', () => {
|
|||||||
|
|
||||||
expect(res.redirect).to.have.not.been.called;
|
expect(res.redirect).to.have.not.been.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not redirect if passed skip ssl request param is passed with corrrect key', () => {
|
||||||
|
let nconfStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfStub.withArgs('BASE_URL').returns('https://habitica.com');
|
||||||
|
nconfStub.withArgs('IS_PROD').returns(true);
|
||||||
|
nconfStub.withArgs('SKIP_SSL_CHECK_KEY').returns('test-key');
|
||||||
|
|
||||||
|
req.header = sandbox.stub().withArgs('x-forwarded-proto').returns('http');
|
||||||
|
req.originalUrl = '/static/front';
|
||||||
|
req.query.skipSSLCheck = 'test-key';
|
||||||
|
|
||||||
|
const attachRedirects = requireAgain(pathToRedirectsMiddleware);
|
||||||
|
attachRedirects.forceSSL(req, res, next);
|
||||||
|
|
||||||
|
expect(res.redirect).to.have.not.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does redirect if skip ssl request param is passed with incorrrect key', () => {
|
||||||
|
let nconfStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfStub.withArgs('BASE_URL').returns('https://habitica.com');
|
||||||
|
nconfStub.withArgs('IS_PROD').returns(true);
|
||||||
|
nconfStub.withArgs('SKIP_SSL_CHECK_KEY').returns('test-key');
|
||||||
|
|
||||||
|
req.header = sandbox.stub().withArgs('x-forwarded-proto').returns('http');
|
||||||
|
req.originalUrl = '/static/front?skipSSLCheck=INVALID';
|
||||||
|
req.query.skipSSLCheck = 'INVALID';
|
||||||
|
|
||||||
|
const attachRedirects = requireAgain(pathToRedirectsMiddleware);
|
||||||
|
attachRedirects.forceSSL(req, res, next);
|
||||||
|
|
||||||
|
expect(res.redirect).to.be.calledOnce;
|
||||||
|
expect(res.redirect).to.be.calledWith('https://habitica.com/static/front?skipSSLCheck=INVALID');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does redirect if skip ssl check key is not set', () => {
|
||||||
|
let nconfStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfStub.withArgs('BASE_URL').returns('https://habitica.com');
|
||||||
|
nconfStub.withArgs('IS_PROD').returns(true);
|
||||||
|
nconfStub.withArgs('SKIP_SSL_CHECK_KEY').returns(null);
|
||||||
|
|
||||||
|
req.header = sandbox.stub().withArgs('x-forwarded-proto').returns('http');
|
||||||
|
req.originalUrl = '/static/front';
|
||||||
|
req.query.skipSSLCheck = 'INVALID';
|
||||||
|
|
||||||
|
const attachRedirects = requireAgain(pathToRedirectsMiddleware);
|
||||||
|
attachRedirects.forceSSL(req, res, next);
|
||||||
|
|
||||||
|
expect(res.redirect).to.be.calledOnce;
|
||||||
|
expect(res.redirect).to.be.calledWith('https://habitica.com/static/front');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('forceHabitica', () => {
|
context('forceHabitica', () => {
|
||||||
|
|||||||
@@ -32,8 +32,19 @@ describe('Group Model', () => {
|
|||||||
privacy: 'private',
|
privacy: 'private',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let _progress = {
|
||||||
|
up: 10,
|
||||||
|
down: 8,
|
||||||
|
collectedItems: 5,
|
||||||
|
};
|
||||||
|
|
||||||
questLeader = new User({
|
questLeader = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Quest Leader' },
|
profile: { name: 'Quest Leader' },
|
||||||
items: {
|
items: {
|
||||||
quests: {
|
quests: {
|
||||||
@@ -45,20 +56,40 @@ describe('Group Model', () => {
|
|||||||
party.leader = questLeader._id;
|
party.leader = questLeader._id;
|
||||||
|
|
||||||
participatingMember = new User({
|
participatingMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Participating Member' },
|
profile: { name: 'Participating Member' },
|
||||||
});
|
});
|
||||||
sleepingParticipatingMember = new User({
|
sleepingParticipatingMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Sleeping Participating Member' },
|
profile: { name: 'Sleeping Participating Member' },
|
||||||
preferences: { sleep: true },
|
preferences: { sleep: true },
|
||||||
});
|
});
|
||||||
nonParticipatingMember = new User({
|
nonParticipatingMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Non-Participating Member' },
|
profile: { name: 'Non-Participating Member' },
|
||||||
});
|
});
|
||||||
undecidedMember = new User({
|
undecidedMember = new User({
|
||||||
party: { _id: party._id },
|
party: {
|
||||||
|
_id: party._id,
|
||||||
|
quest: {
|
||||||
|
progress: _progress,
|
||||||
|
},
|
||||||
|
},
|
||||||
profile: { name: 'Undecided Member' },
|
profile: { name: 'Undecided Member' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1163,16 +1194,17 @@ describe('Group Model', () => {
|
|||||||
expect(party.quest.members).to.eql(expectedQuestMembers);
|
expect(party.quest.members).to.eql(expectedQuestMembers);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies updates to user object directly if user is participating', async () => {
|
it('applies updates to user object directly if user is participating (without resetting progress, except progress.down)', async () => {
|
||||||
await party.startQuest(participatingMember);
|
await party.startQuest(participatingMember);
|
||||||
|
|
||||||
expect(participatingMember.party.quest.key).to.eql('whale');
|
expect(participatingMember.party.quest.key).to.eql('whale');
|
||||||
|
expect(participatingMember.party.quest.progress.up).to.eql(10);
|
||||||
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
||||||
expect(participatingMember.party.quest.progress.collectedItems).to.eql(0);
|
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(participatingMember.party.quest.completed).to.eql(null);
|
expect(participatingMember.party.quest.completed).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies updates to other participating members', async () => {
|
it('applies updates to other participating members (without resetting progress, except progress.down)', async () => {
|
||||||
await party.startQuest(nonParticipatingMember);
|
await party.startQuest(nonParticipatingMember);
|
||||||
|
|
||||||
questLeader = await User.findById(questLeader._id);
|
questLeader = await User.findById(questLeader._id);
|
||||||
@@ -1180,18 +1212,21 @@ describe('Group Model', () => {
|
|||||||
sleepingParticipatingMember = await User.findById(sleepingParticipatingMember._id);
|
sleepingParticipatingMember = await User.findById(sleepingParticipatingMember._id);
|
||||||
|
|
||||||
expect(participatingMember.party.quest.key).to.eql('whale');
|
expect(participatingMember.party.quest.key).to.eql('whale');
|
||||||
|
expect(participatingMember.party.quest.progress.up).to.eql(10);
|
||||||
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
||||||
expect(participatingMember.party.quest.progress.collectedItems).to.eql(0);
|
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(participatingMember.party.quest.completed).to.eql(null);
|
expect(participatingMember.party.quest.completed).to.eql(null);
|
||||||
|
|
||||||
expect(sleepingParticipatingMember.party.quest.key).to.eql('whale');
|
expect(sleepingParticipatingMember.party.quest.key).to.eql('whale');
|
||||||
|
expect(sleepingParticipatingMember.party.quest.progress.up).to.eql(10);
|
||||||
expect(sleepingParticipatingMember.party.quest.progress.down).to.eql(0);
|
expect(sleepingParticipatingMember.party.quest.progress.down).to.eql(0);
|
||||||
expect(sleepingParticipatingMember.party.quest.progress.collectedItems).to.eql(0);
|
expect(sleepingParticipatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(sleepingParticipatingMember.party.quest.completed).to.eql(null);
|
expect(sleepingParticipatingMember.party.quest.completed).to.eql(null);
|
||||||
|
|
||||||
expect(questLeader.party.quest.key).to.eql('whale');
|
expect(questLeader.party.quest.key).to.eql('whale');
|
||||||
|
expect(questLeader.party.quest.progress.up).to.eql(10);
|
||||||
expect(questLeader.party.quest.progress.down).to.eql(0);
|
expect(questLeader.party.quest.progress.down).to.eql(0);
|
||||||
expect(questLeader.party.quest.progress.collectedItems).to.eql(0);
|
expect(questLeader.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(questLeader.party.quest.completed).to.eql(null);
|
expect(questLeader.party.quest.completed).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1202,6 +1237,9 @@ describe('Group Model', () => {
|
|||||||
undecidedMember = await User.findById(undecidedMember._id);
|
undecidedMember = await User.findById(undecidedMember._id);
|
||||||
|
|
||||||
expect(nonParticipatingMember.party.quest.key).to.not.eql('whale');
|
expect(nonParticipatingMember.party.quest.key).to.not.eql('whale');
|
||||||
|
expect(nonParticipatingMember.party.quest.progress.up).to.eql(10);
|
||||||
|
expect(nonParticipatingMember.party.quest.progress.down).to.eql(8);
|
||||||
|
expect(nonParticipatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(undecidedMember.party.quest.key).to.not.eql('whale');
|
expect(undecidedMember.party.quest.key).to.not.eql('whale');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1369,8 +1407,9 @@ describe('Group Model', () => {
|
|||||||
let userQuest = participatingMember.party.quest;
|
let userQuest = participatingMember.party.quest;
|
||||||
|
|
||||||
expect(userQuest.key).to.eql('whale');
|
expect(userQuest.key).to.eql('whale');
|
||||||
|
expect(userQuest.progress.up).to.eql(10);
|
||||||
expect(userQuest.progress.down).to.eql(0);
|
expect(userQuest.progress.down).to.eql(0);
|
||||||
expect(userQuest.progress.collectedItems).to.eql(0);
|
expect(userQuest.progress.collectedItems).to.eql(5);
|
||||||
expect(userQuest.completed).to.eql(null);
|
expect(userQuest.completed).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1670,16 +1709,23 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets user quest object to a clean state', async () => {
|
it('updates participating members quest object to a clean state (except for progress)', async () => {
|
||||||
await party.finishQuest(quest);
|
await party.finishQuest(quest);
|
||||||
|
|
||||||
let updatedLeader = await User.findById(questLeader._id);
|
questLeader = await User.findById(questLeader._id);
|
||||||
|
participatingMember = await User.findById(participatingMember._id);
|
||||||
|
|
||||||
expect(updatedLeader.party.quest.completed).to.eql('whale');
|
expect(questLeader.party.quest.completed).to.eql('whale');
|
||||||
expect(updatedLeader.party.quest.progress.up).to.eql(0);
|
expect(questLeader.party.quest.progress.up).to.eql(10);
|
||||||
expect(updatedLeader.party.quest.progress.down).to.eql(0);
|
expect(questLeader.party.quest.progress.down).to.eql(8);
|
||||||
expect(updatedLeader.party.quest.progress.collectedItems).to.eql(0);
|
expect(questLeader.party.quest.progress.collectedItems).to.eql(5);
|
||||||
expect(updatedLeader.party.quest.RSVPNeeded).to.eql(false);
|
expect(questLeader.party.quest.RSVPNeeded).to.eql(false);
|
||||||
|
|
||||||
|
expect(participatingMember.party.quest.completed).to.eql('whale');
|
||||||
|
expect(participatingMember.party.quest.progress.up).to.eql(10);
|
||||||
|
expect(participatingMember.party.quest.progress.down).to.eql(8);
|
||||||
|
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
|
||||||
|
expect(participatingMember.party.quest.RSVPNeeded).to.eql(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -65,11 +65,11 @@ describe('GET /challenges/:challengeId/export/csv', () => {
|
|||||||
const sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id');
|
const sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id');
|
||||||
const splitRes = res.split('\n');
|
const splitRes = res.split('\n');
|
||||||
|
|
||||||
expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
expect(splitRes[0]).to.equal('UUID,Display Name,Username,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
||||||
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},${sortedMembers[0].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},${sortedMembers[1].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},${sortedMembers[2].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},${sortedMembers[3].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[5]).to.equal('');
|
expect(splitRes[5]).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -78,10 +78,10 @@ describe('GET /challenges/:challengeId/export/csv', () => {
|
|||||||
const res = await members[1].get(`/challenges/${challenge._id}/export/csv`);
|
const res = await members[1].get(`/challenges/${challenge._id}/export/csv`);
|
||||||
const sortedMembers = _.sortBy([members[1], members[2], groupLeader], '_id');
|
const sortedMembers = _.sortBy([members[1], members[2], groupLeader], '_id');
|
||||||
const splitRes = res.split('\n');
|
const splitRes = res.split('\n');
|
||||||
expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
expect(splitRes[0]).to.equal('UUID,Display Name,Username,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
|
||||||
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},${sortedMembers[0].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},${sortedMembers[1].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},${sortedMembers[2].auth.local.username},habit:Task 1,0,,0,todo:Task 2,0,,0`);
|
||||||
expect(splitRes[4]).to.equal('');
|
expect(splitRes[4]).to.equal('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('messageCannotFlagSystemMessages', {communityManagerEmail: config.EMAILS.COMMUNITY_MANAGER_EMAIL}),
|
message: t('messageCannotFlagSystemMessages', {communityManagerEmail: config.EMAILS_COMMUNITY_MANAGER_EMAIL}),
|
||||||
});
|
});
|
||||||
// let messages = await members[0].get(`/groups/${group._id}/chat`);
|
// let messages = await members[0].get(`/groups/${group._id}/chat`);
|
||||||
// expect(messages[0].id).to.eql(skillMsg.id);
|
// expect(messages[0].id).to.eql(skillMsg.id);
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 401,
|
code: 401,
|
||||||
error: 'NotAuthorized',
|
error: 'NotAuthorized',
|
||||||
message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}),
|
message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL')}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe('POST /notifications/:notificationId/read', () => {
|
|||||||
|
|
||||||
await expect(user.post(`/notifications/${dummyId}/read`)).to.eventually.be.rejected.and.eql({
|
await expect(user.post(`/notifications/${dummyId}/read`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe('POST /notifications/:notificationId/see', () => {
|
|||||||
|
|
||||||
await expect(user.post(`/notifications/${dummyId}/see`)).to.eventually.be.rejected.and.eql({
|
await expect(user.post(`/notifications/${dummyId}/see`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('POST /notifications/read', () => {
|
|||||||
notificationIds: [dummyId],
|
notificationIds: [dummyId],
|
||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('POST /notifications/see', () => {
|
|||||||
notificationIds: [dummyId],
|
notificationIds: [dummyId],
|
||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotificationNotFound',
|
||||||
message: t('messageNotificationNotFound'),
|
message: t('messageNotificationNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||||
|
|
||||||
|
describe('payments : apple #norenewsubscribe', () => {
|
||||||
|
let endpoint = '/iap/ios/norenew-subscribe';
|
||||||
|
let sku = 'com.habitrpg.ios.habitica.subscription.3month';
|
||||||
|
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'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeStub = sinon.stub(applePayments, 'noRenewSubscribe').resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
applePayments.noRenewSubscribe.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, {
|
||||||
|
sku,
|
||||||
|
transaction: {receipt: 'receipt'},
|
||||||
|
gift: {
|
||||||
|
uuid: '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][0].sku).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
import applePayments from '../../../../../../website/server/libs/payments/apple';
|
||||||
|
|
||||||
describe('payments : apple #verify', () => {
|
describe('payments : apple #verify', () => {
|
||||||
@@ -9,6 +9,14 @@ describe('payments : apple #verify', () => {
|
|||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
let verifyStub;
|
let verifyStub;
|
||||||
|
|
||||||
@@ -31,10 +39,31 @@ describe('payments : apple #verify', () => {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
expect(verifyStub).to.be.calledOnce;
|
expect(verifyStub).to.be.calledOnce;
|
||||||
expect(verifyStub.args[0][0]._id).to.eql(user._id);
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
expect(verifyStub.args[0][1]).to.eql('receipt');
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
expect(verifyStub.args[0][2]['x-api-key']).to.eql(user.apiToken);
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
expect(verifyStub.args[0][2]['x-api-user']).to.eql(user._id);
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
transaction: {
|
||||||
|
receipt: 'receipt',
|
||||||
|
},
|
||||||
|
gift: {
|
||||||
|
uuid: '1',
|
||||||
|
}});
|
||||||
|
|
||||||
|
expect(verifyStub).to.be.calledOnce;
|
||||||
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(verifyStub.args[0][0].gift.uuid).to.eql('1');
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||||
|
|
||||||
|
describe('payments : google #norenewsubscribe', () => {
|
||||||
|
let endpoint = '/iap/android/norenew-subscribe';
|
||||||
|
let sku = 'com.habitrpg.android.habitica.subscription.3month';
|
||||||
|
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'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint, {
|
||||||
|
sku,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
let subscribeStub;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
subscribeStub = sinon.stub(googlePayments, 'noRenewSubscribe').resolves({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
googlePayments.noRenewSubscribe.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, {
|
||||||
|
sku,
|
||||||
|
transaction: {
|
||||||
|
receipt: 'receipt',
|
||||||
|
signature: 'signature',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][0].sku).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][0].signature).to.eql('signature');
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts 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, {
|
||||||
|
sku,
|
||||||
|
transaction: {
|
||||||
|
receipt: 'receipt',
|
||||||
|
signature: 'signature',
|
||||||
|
},
|
||||||
|
gift: {
|
||||||
|
uuid: '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(subscribeStub).to.be.calledOnce;
|
||||||
|
expect(subscribeStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(subscribeStub.args[0][0].sku).to.eql(sku);
|
||||||
|
expect(subscribeStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(subscribeStub.args[0][0].signature).to.eql('signature');
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(subscribeStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||||
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
import googlePayments from '../../../../../../website/server/libs/payments/google';
|
||||||
|
|
||||||
describe('payments : google #verify', () => {
|
describe('payments : google #verify', () => {
|
||||||
@@ -9,6 +9,14 @@ describe('payments : google #verify', () => {
|
|||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('verifies receipt existence', async () => {
|
||||||
|
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('missingReceipt'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
let verifyStub;
|
let verifyStub;
|
||||||
|
|
||||||
@@ -30,11 +38,30 @@ describe('payments : google #verify', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(verifyStub).to.be.calledOnce;
|
expect(verifyStub).to.be.calledOnce;
|
||||||
expect(verifyStub.args[0][0]._id).to.eql(user._id);
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
expect(verifyStub.args[0][1]).to.eql('receipt');
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
expect(verifyStub.args[0][2]).to.eql('signature');
|
expect(verifyStub.args[0][0].signature).to.eql('signature');
|
||||||
expect(verifyStub.args[0][3]['x-api-key']).to.eql(user.apiToken);
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
expect(verifyStub.args[0][3]['x-api-user']).to.eql(user._id);
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gifts a purchase', async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(endpoint, {
|
||||||
|
transaction: {receipt: 'receipt', signature: 'signature'},
|
||||||
|
gift: {uuid: '1'},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(verifyStub).to.be.calledOnce;
|
||||||
|
expect(verifyStub.args[0][0].user._id).to.eql(user._id);
|
||||||
|
expect(verifyStub.args[0][0].receipt).to.eql('receipt');
|
||||||
|
expect(verifyStub.args[0][0].signature).to.eql('signature');
|
||||||
|
expect(verifyStub.args[0][0].gift.uuid).to.eql('1');
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||||
|
expect(verifyStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ describe('POST /user/auth/local/login', () => {
|
|||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 401,
|
code: 401,
|
||||||
error: 'NotAuthorized',
|
error: 'NotAuthorized',
|
||||||
message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL'), userId: user._id }),
|
message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'), userId: user._id }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ describe('PUT /user/auth/update-email', () => {
|
|||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 401,
|
code: 401,
|
||||||
error: 'NotAuthorized',
|
error: 'NotAuthorized',
|
||||||
message: t('cannotFulfillReq', { techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL') }),
|
message: t('cannotFulfillReq', { techAssistanceEmail: nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL') }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
300
test/client/unit/specs/components/avatar.vue.test.js
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
import Avatar from 'client/components/avatar';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import generateStore from 'client/store';
|
||||||
|
|
||||||
|
context('avatar.vue', () => {
|
||||||
|
let Constructr;
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Constructr = Vue.extend(Avatar);
|
||||||
|
|
||||||
|
vm = new Constructr({
|
||||||
|
propsData: {
|
||||||
|
member: {
|
||||||
|
stats: {
|
||||||
|
buffs: {},
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
hair: {},
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
equipped: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).$mount();
|
||||||
|
|
||||||
|
vm.$store = generateStore();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
stats: { lvl: 17 },
|
||||||
|
preferences: { disableClasses: true },
|
||||||
|
flags: { classSelected: false },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accurately reports class status', () => {
|
||||||
|
expect(vm.hasClass).to.equal(false);
|
||||||
|
|
||||||
|
vm.member.preferences.disableClasses = false;
|
||||||
|
vm.member.flags.classSelected = true;
|
||||||
|
|
||||||
|
expect(vm.hasClass).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isBuffed', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
buffs: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accurately reports if buffed', () => {
|
||||||
|
expect(vm.isBuffed).to.equal(undefined);
|
||||||
|
|
||||||
|
vm.member.stats.buffs = { str: 1 };
|
||||||
|
|
||||||
|
expect(vm.isBuffed).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('paddingTop', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
items: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to 28px', () => {
|
||||||
|
vm.avatarOnly = true;
|
||||||
|
expect(vm.paddingTop).to.equal('28px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is 24.5px if user has a pet', () => {
|
||||||
|
vm.member.items = {
|
||||||
|
currentPet: { name: 'Foo' },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.paddingTop).to.equal('24.5px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is 0px if user has a mount', () => {
|
||||||
|
vm.member.items = {
|
||||||
|
currentMount: { name: 'Bar' },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.paddingTop).to.equal('0px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be overriden', () => {
|
||||||
|
vm.overrideTopPadding = '27px';
|
||||||
|
expect(vm.paddingTop).to.equal('27px');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('costumeClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
preferences: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns if showing equiped gear', () => {
|
||||||
|
expect(vm.costumeClass).to.equal('equipped');
|
||||||
|
});
|
||||||
|
it('returns if wearing a costume', () => {
|
||||||
|
vm.member.preferences = { costume: true };
|
||||||
|
expect(vm.costumeClass).to.equal('costume');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('visualBuffs', () => {
|
||||||
|
it('returns an array of buffs', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
class: 'Warrior',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.visualBuffs).to.include({snowball: 'snowman'});
|
||||||
|
expect(vm.visualBuffs).to.include({spookySparkles: 'ghost'});
|
||||||
|
expect(vm.visualBuffs).to.include({shinySeed: 'avatar_floral_Warrior'});
|
||||||
|
expect(vm.visualBuffs).to.include({seafoam: 'seafoam_star'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('backgroundClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member.preferences = { background: 'pony' };
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows the background', () => {
|
||||||
|
expect(vm.backgroundClass).to.equal('background_pony');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be overridden', () => {
|
||||||
|
vm.overrideAvatarGear = { background: 'character' };
|
||||||
|
|
||||||
|
expect(vm.backgroundClass).to.equal('background_character');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns to a blank string if not showing background', () => {
|
||||||
|
vm.withBackground = false;
|
||||||
|
vm.avatarOnly = true;
|
||||||
|
|
||||||
|
expect(vm.backgroundClass).to.equal('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('specialMountClass', () => {
|
||||||
|
it('checks if riding a Kangaroo', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
class: 'None',
|
||||||
|
},
|
||||||
|
items: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.specialMountClass).to.equal(undefined);
|
||||||
|
|
||||||
|
vm.member.items = {
|
||||||
|
currentMount: ['Kangaroo'],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.specialMountClass).to.equal('offset-kangaroo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('skinClass', () => {
|
||||||
|
it('returns current skin color', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {},
|
||||||
|
preferences: {
|
||||||
|
skin: 'blue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.skinClass).to.equal('skin_blue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns if sleep or not', () => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {},
|
||||||
|
preferences: {
|
||||||
|
skin: 'blue',
|
||||||
|
sleep: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(vm.skinClass).to.equal('skin_blue');
|
||||||
|
|
||||||
|
vm.member.preferences.sleep = true;
|
||||||
|
|
||||||
|
expect(vm.skinClass).to.equal('skin_blue_sleep');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('methods', () => {
|
||||||
|
describe('getGearClass', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
equipped: { Hat: 'Fancy Tophat' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preferences: { costume: false },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined if no match', () => {
|
||||||
|
expect(vm.getGearClass('foo')).to.equal(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the matching gear', () => {
|
||||||
|
expect(vm.getGearClass('Hat')).to.equal('Fancy Tophat');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be overridden', () => {
|
||||||
|
vm.overrideAvatarGear = { Hat: 'Dapper Bowler' };
|
||||||
|
|
||||||
|
expect(vm.getGearClass('Hat')).to.equal('Dapper Bowler');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hideGear', () => {
|
||||||
|
it('returns no weapon equipped', () => {
|
||||||
|
vm.member.items.gear.equipped = {};
|
||||||
|
expect(vm.hideGear('weapon')).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
equipped: {
|
||||||
|
weapon: {
|
||||||
|
baseWeapon: 'Spoon',
|
||||||
|
twoHanded: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preferences: { costume: false },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('show avatar', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm.member = {
|
||||||
|
stats: {
|
||||||
|
buffs: {
|
||||||
|
snowball: false,
|
||||||
|
seafoam: false,
|
||||||
|
spookySparkles: false,
|
||||||
|
shinySeed: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('does if not showing visual buffs', () => {
|
||||||
|
expect(vm.showAvatar()).to.equal(true);
|
||||||
|
|
||||||
|
let buffs = vm.member.stats.buffs;
|
||||||
|
|
||||||
|
buffs.snowball = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.snowball = false;
|
||||||
|
buffs.spookySparkles = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.spookySparkles = false;
|
||||||
|
buffs.shinySeed = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.shinySeed = false;
|
||||||
|
buffs.seafoam = true;
|
||||||
|
expect(vm.showAvatar()).to.equal(false);
|
||||||
|
|
||||||
|
buffs.seafoam = false;
|
||||||
|
vm.showVisualBuffs = false;
|
||||||
|
expect(vm.showAvatar()).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
76
test/client/unit/specs/store/getters/user/user.test.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { data, gems, buffs, preferences, tasksOrder } from 'client/store/getters/user';
|
||||||
|
|
||||||
|
context('user getters', () => {
|
||||||
|
describe('data', () => {
|
||||||
|
it('returns the user\'s data', () => {
|
||||||
|
expect(data({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: {
|
||||||
|
lvl: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).lvl).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('gems', () => {
|
||||||
|
it('returns the user\'s gems', () => {
|
||||||
|
expect(gems({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: { balance: 4.5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})).to.equal(18);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buffs', () => {
|
||||||
|
it('returns the user\'s buffs', () => {
|
||||||
|
expect(buffs({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: {
|
||||||
|
stats: {
|
||||||
|
buffs: [1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})(0)).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('preferences', () => {
|
||||||
|
it('returns the user\'s preferences', () => {
|
||||||
|
expect(preferences({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
data: {
|
||||||
|
preferences: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tasksOrder', () => {
|
||||||
|
it('returns the user\'s tasksOrder', () => {
|
||||||
|
expect(tasksOrder({
|
||||||
|
state: {
|
||||||
|
user: {
|
||||||
|
tasksOrder: {
|
||||||
|
masters: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})('master')).to.equal(1);
|
||||||
|
|
||||||
|
expect(tasksOrder()).to.not.equal('null');
|
||||||
|
expect(tasksOrder()).to.not.equal('undefined');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { gems as userGems } from 'client/store/getters/user';
|
|
||||||
|
|
||||||
describe('userGems getter', () => {
|
|
||||||
it('returns the user\'s gems', () => {
|
|
||||||
expect(userGems({
|
|
||||||
state: {
|
|
||||||
user: {
|
|
||||||
data: {balance: 4.5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})).to.equal(18);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
20
test/helpers/mongoose.helper.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
export async function mockFindById (response) {
|
||||||
|
const mockFind = {
|
||||||
|
select () {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
lean () {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
exec () {
|
||||||
|
return Promise.resolve(response);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
sinon.stub(mongoose.Model, 'findById').returns(mockFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreFindById () {
|
||||||
|
return mongoose.Model.findById.restore();
|
||||||
|
}
|
||||||
@@ -15,11 +15,11 @@ setupNconf(configFile);
|
|||||||
// @TODO: Do we need? const CLIENT_VARS = ['language', 'isStaticPage', 'availableLanguages', 'translations',
|
// @TODO: Do we need? const CLIENT_VARS = ['language', 'isStaticPage', 'availableLanguages', 'translations',
|
||||||
// 'FACEBOOK_KEY', 'GOOGLE_CLIENT_ID', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
// 'FACEBOOK_KEY', 'GOOGLE_CLIENT_ID', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
||||||
// 'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
// 'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
||||||
// 'worldDmg', 'mods', 'IS_MOBILE', 'PUSHER:KEY', 'PUSHER:ENABLED'];
|
// 'worldDmg', 'mods', 'IS_MOBILE'];
|
||||||
|
|
||||||
const AMAZON_SELLER_ID = nconf.get('AMAZON_PAYMENTS:SELLER_ID') || nconf.get('AMAZON_PAYMENTS_SELLER_ID');
|
const AMAZON_SELLER_ID = nconf.get('AMAZON_PAYMENTS_SELLER_ID');
|
||||||
const AMAZON_CLIENT_ID = nconf.get('AMAZON_PAYMENTS:CLIENT_ID') || nconf.get('AMAZON_PAYMENTS_CLIENT_ID');
|
const AMAZON_CLIENT_ID = nconf.get('AMAZON_PAYMENTS_CLIENT_ID');
|
||||||
const AMAZON_MODE = nconf.get('AMAZON_PAYMENTS:MODE') || nconf.get('AMAZON_PAYMENTS_MODE');
|
const AMAZON_MODE = nconf.get('AMAZON_PAYMENTS_MODE');
|
||||||
|
|
||||||
let env = {
|
let env = {
|
||||||
NODE_ENV: '"production"',
|
NODE_ENV: '"production"',
|
||||||
@@ -30,13 +30,13 @@ let env = {
|
|||||||
MODE: `"${AMAZON_MODE}"`,
|
MODE: `"${AMAZON_MODE}"`,
|
||||||
},
|
},
|
||||||
EMAILS: {
|
EMAILS: {
|
||||||
COMMUNITY_MANAGER_EMAIL: `"${nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL')}"`,
|
COMMUNITY_MANAGER_EMAIL: `"${nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL')}"`,
|
||||||
TECH_ASSISTANCE_EMAIL: `"${nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}"`,
|
TECH_ASSISTANCE_EMAIL: `"${nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL')}"`,
|
||||||
PRESS_ENQUIRY_EMAIL: `"${nconf.get('EMAILS:PRESS_ENQUIRY_EMAIL')}"`,
|
PRESS_ENQUIRY_EMAIL: `"${nconf.get('EMAILS_PRESS_ENQUIRY_EMAIL')}"`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY GOOGLE_CLIENT_ID AMPLITUDE_KEY PUSHER:KEY PUSHER:ENABLED LOGGLY_CLIENT_TOKEN'
|
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY GOOGLE_CLIENT_ID AMPLITUDE_KEY LOGGLY_CLIENT_TOKEN'
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.forEach(key => {
|
.forEach(key => {
|
||||||
env[key] = `"${nconf.get(key)}"`;
|
env[key] = `"${nconf.get(key)}"`;
|
||||||
|
|||||||
@@ -11,22 +11,23 @@ div
|
|||||||
#app(:class='{"casting-spell": castingSpell}')
|
#app(:class='{"casting-spell": castingSpell}')
|
||||||
banned-account-modal
|
banned-account-modal
|
||||||
amazon-payments-modal(v-if='!isStaticPage')
|
amazon-payments-modal(v-if='!isStaticPage')
|
||||||
|
payments-success-modal
|
||||||
snackbars
|
snackbars
|
||||||
router-view(v-if="!isUserLoggedIn || isStaticPage")
|
router-view(v-if="!isUserLoggedIn || isStaticPage")
|
||||||
template(v-else)
|
template(v-else)
|
||||||
template(v-if="isUserLoaded")
|
template(v-if="isUserLoaded")
|
||||||
div.resting-banner(v-show="showRestingBanner", ref="restingBanner")
|
.resting-banner(v-show="showRestingBanner", ref="restingBanner")
|
||||||
span.content
|
span.content
|
||||||
span.label.d-inline.d-sm-none {{ $t('innCheckOutBannerShort') }}
|
span.label.d-inline.d-sm-none {{ $t('innCheckOutBannerShort') }}
|
||||||
span.label.d-none.d-sm-inline {{ $t('innCheckOutBanner') }}
|
span.label.d-none.d-sm-inline {{ $t('innCheckOutBanner') }}
|
||||||
span.separator |
|
span.separator |
|
||||||
span.resume(@click="resumeDamage()") {{ $t('resumeDamage') }}
|
span.resume(@click="resumeDamage()") {{ $t('resumeDamage') }}
|
||||||
div.closepadding(@click="hideBanner()")
|
.closepadding(@click="hideBanner()")
|
||||||
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close")
|
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close")
|
||||||
notifications-display
|
notifications-display
|
||||||
app-menu(:class='{"restingInn": showRestingBanner}' :style="{ marginTop: bannerHeight + 'px' }")
|
app-menu
|
||||||
.container-fluid
|
.container-fluid
|
||||||
app-header(:class='{"restingInn": showRestingBanner}')
|
app-header
|
||||||
buyModal(
|
buyModal(
|
||||||
:item="selectedItemToBuy || {}",
|
:item="selectedItemToBuy || {}",
|
||||||
:withPin="true",
|
:withPin="true",
|
||||||
@@ -49,6 +50,13 @@ div
|
|||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
@import '~client/assets/scss/colors.scss';
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: calc(100% - 56px); /* 56px is the menu */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
#loading-screen-inapp {
|
#loading-screen-inapp {
|
||||||
#melior {
|
#melior {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -78,6 +86,11 @@ div
|
|||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
overflow-x: hidden;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
border-radius: 1000px;
|
border-radius: 1000px;
|
||||||
background-color: $green-10;
|
background-color: $green-10;
|
||||||
@@ -88,42 +101,10 @@ div
|
|||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-fluid {
|
|
||||||
overflow-x: hidden;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
|
||||||
height: calc(100% - 56px); /* 56px is the menu */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang='scss'>
|
|
||||||
@import '~client/assets/scss/colors.scss';
|
|
||||||
|
|
||||||
/* @TODO: The modal-open class is not being removed. Let's try this for now */
|
|
||||||
.modal {
|
|
||||||
overflow-y: scroll !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-backdrop.show {
|
|
||||||
opacity: .9 !important;
|
|
||||||
background-color: $purple-100 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Push progress bar above modals */
|
|
||||||
#nprogress .bar {
|
|
||||||
z-index: 1600 !important; /* Must stay above nav bar */
|
|
||||||
}
|
|
||||||
|
|
||||||
.resting-banner {
|
.resting-banner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
background-color: $blue-10;
|
background-color: $blue-10;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1300;
|
z-index: 1300;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -139,14 +120,10 @@ div
|
|||||||
.closepadding {
|
.closepadding {
|
||||||
margin: 11px 24px;
|
margin: 11px 24px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: relative;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
span svg path {
|
|
||||||
stroke: $blue-500;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
@@ -169,6 +146,30 @@ div
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.closepadding span svg path {
|
||||||
|
stroke: #FFF;
|
||||||
|
opacity: 0.48;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @TODO: The modal-open class is not being removed. Let's try this for now */
|
||||||
|
.modal {
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-backdrop.show {
|
||||||
|
opacity: .9 !important;
|
||||||
|
background-color: $purple-100 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push progress bar above modals */
|
||||||
|
#nprogress .bar {
|
||||||
|
z-index: 1600 !important; /* Must stay above nav bar */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { loadProgressBar } from 'axios-progress-bar';
|
import { loadProgressBar } from 'axios-progress-bar';
|
||||||
@@ -185,7 +186,10 @@ import SelectMembersModal from 'client/components/selectMembersModal.vue';
|
|||||||
import notifications from 'client/mixins/notifications';
|
import notifications from 'client/mixins/notifications';
|
||||||
import { setup as setupPayments } from 'client/libs/payments';
|
import { setup as setupPayments } from 'client/libs/payments';
|
||||||
import amazonPaymentsModal from 'client/components/payments/amazonModal';
|
import amazonPaymentsModal from 'client/components/payments/amazonModal';
|
||||||
|
import paymentsSuccessModal from 'client/components/payments/successModal';
|
||||||
|
|
||||||
import spellsMixin from 'client/mixins/spells';
|
import spellsMixin from 'client/mixins/spells';
|
||||||
|
import { CONSTANTS, getLocalSetting, removeLocalSetting } from 'client/libs/userlocalManager';
|
||||||
|
|
||||||
import svgClose from 'assets/svg/close.svg';
|
import svgClose from 'assets/svg/close.svg';
|
||||||
import bannedAccountModal from 'client/components/bannedAccountModal';
|
import bannedAccountModal from 'client/components/bannedAccountModal';
|
||||||
@@ -205,6 +209,7 @@ export default {
|
|||||||
SelectMembersModal,
|
SelectMembersModal,
|
||||||
amazonPaymentsModal,
|
amazonPaymentsModal,
|
||||||
bannedAccountModal,
|
bannedAccountModal,
|
||||||
|
paymentsSuccessModal,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -220,7 +225,6 @@ export default {
|
|||||||
loading: true,
|
loading: true,
|
||||||
currentTipNumber: 0,
|
currentTipNumber: 0,
|
||||||
bannerHidden: false,
|
bannerHidden: false,
|
||||||
bannerHeight: 0,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -313,6 +317,7 @@ export default {
|
|||||||
const errorMessage = errorData.message || errorData;
|
const errorMessage = errorData.message || errorData;
|
||||||
|
|
||||||
// Check for conditions to reset the user auth
|
// Check for conditions to reset the user auth
|
||||||
|
// TODO use a specific error like NotificationNotFound instead of checking for the string
|
||||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
||||||
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
||||||
this.$store.dispatch('auth:logout');
|
this.$store.dispatch('auth:logout');
|
||||||
@@ -322,12 +327,6 @@ export default {
|
|||||||
let snackbarTimeout = false;
|
let snackbarTimeout = false;
|
||||||
if (error.response.status === 502) snackbarTimeout = true;
|
if (error.response.status === 502) snackbarTimeout = true;
|
||||||
|
|
||||||
const notificationNotFoundMessage = [
|
|
||||||
this.$t('messageNotificationNotFound'),
|
|
||||||
this.$t('messageNotificationNotFound', 'en'),
|
|
||||||
];
|
|
||||||
if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true;
|
|
||||||
|
|
||||||
let errorsToShow = [];
|
let errorsToShow = [];
|
||||||
// show only the first error for each param
|
// show only the first error for each param
|
||||||
let paramErrorsFound = {};
|
let paramErrorsFound = {};
|
||||||
@@ -341,13 +340,17 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
errorsToShow.push(errorMessage);
|
errorsToShow.push(errorMessage);
|
||||||
}
|
}
|
||||||
// dispatch as one snackbar notification
|
|
||||||
this.$store.dispatch('snackbars:add', {
|
// Ignore NotificationNotFound errors, see https://github.com/HabitRPG/habitica/issues/10391
|
||||||
title: 'Habitica',
|
if (errorData.error !== 'NotificationNotFound') {
|
||||||
text: errorsToShow.join(' '),
|
// dispatch as one snackbar notification
|
||||||
type: 'error',
|
this.$store.dispatch('snackbars:add', {
|
||||||
timeout: snackbarTimeout,
|
title: 'Habitica',
|
||||||
});
|
text: errorsToShow.join(' '),
|
||||||
|
type: 'error',
|
||||||
|
timeout: snackbarTimeout,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
@@ -418,14 +421,6 @@ export default {
|
|||||||
|
|
||||||
this.hideLoadingScreen();
|
this.hideLoadingScreen();
|
||||||
|
|
||||||
window.addEventListener('resize', this.setBannerOffset);
|
|
||||||
// Adjust the positioning of the header banners
|
|
||||||
this.$watch('showRestingBanner', () => {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.setBannerOffset();
|
|
||||||
});
|
|
||||||
}, {immediate: true});
|
|
||||||
|
|
||||||
// Adjust the timezone offset
|
// Adjust the timezone offset
|
||||||
if (this.user.preferences.timezoneOffset !== this.browserTimezoneOffset) {
|
if (this.user.preferences.timezoneOffset !== this.browserTimezoneOffset) {
|
||||||
this.$store.dispatch('user:set', {
|
this.$store.dispatch('user:set', {
|
||||||
@@ -433,6 +428,14 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let appState = getLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
|
||||||
|
if (appState) {
|
||||||
|
appState = JSON.parse(appState);
|
||||||
|
if (appState.paymentCompleted) {
|
||||||
|
removeLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
|
||||||
|
this.$root.$emit('habitica:payment-success', appState);
|
||||||
|
}
|
||||||
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
// Load external scripts after the app has been rendered
|
// Load external scripts after the app has been rendered
|
||||||
setupPayments();
|
setupPayments();
|
||||||
@@ -452,7 +455,6 @@ export default {
|
|||||||
this.$root.$off('bv::show::modal');
|
this.$root.$off('bv::show::modal');
|
||||||
this.$root.$off('buyModal::showItem');
|
this.$root.$off('buyModal::showItem');
|
||||||
this.$root.$off('selectMembersModal::showItem');
|
this.$root.$off('selectMembersModal::showItem');
|
||||||
window.removeEventListener('resize', this.setBannerOffset);
|
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
// Remove the index.html loading screen and now show the inapp loading
|
// Remove the index.html loading screen and now show the inapp loading
|
||||||
@@ -611,22 +613,10 @@ export default {
|
|||||||
},
|
},
|
||||||
hideBanner () {
|
hideBanner () {
|
||||||
this.bannerHidden = true;
|
this.bannerHidden = true;
|
||||||
this.setBannerOffset();
|
|
||||||
},
|
},
|
||||||
resumeDamage () {
|
resumeDamage () {
|
||||||
this.$store.dispatch('user:sleep');
|
this.$store.dispatch('user:sleep');
|
||||||
},
|
},
|
||||||
setBannerOffset () {
|
|
||||||
let contentPlacement = 0;
|
|
||||||
if (this.showRestingBanner && this.$refs.restingBanner !== undefined) {
|
|
||||||
contentPlacement = this.$refs.restingBanner.clientHeight;
|
|
||||||
}
|
|
||||||
this.bannerHeight = contentPlacement;
|
|
||||||
let smartBanner = document.getElementsByClassName('smartbanner')[0];
|
|
||||||
if (smartBanner !== undefined) {
|
|
||||||
smartBanner.style.top = `${contentPlacement}px`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,90 +1,78 @@
|
|||||||
.achievement-costumeContest6x {
|
.promo_armoire_backgrounds_201901 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -1136px -148px;
|
background-position: -445px 0px;
|
||||||
width: 144px;
|
|
||||||
height: 156px;
|
|
||||||
}
|
|
||||||
.promo_alligator {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: 0px 0px;
|
|
||||||
width: 480px;
|
|
||||||
height: 360px;
|
|
||||||
}
|
|
||||||
.promo_animal_tails {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: -994px 0px;
|
|
||||||
width: 141px;
|
|
||||||
height: 441px;
|
|
||||||
}
|
|
||||||
.promo_armoire_backgrounds_201811 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: -481px -420px;
|
|
||||||
width: 423px;
|
width: 423px;
|
||||||
height: 147px;
|
height: 147px;
|
||||||
}
|
}
|
||||||
.promo_frost_potions {
|
.promo_bird_buddies_bundle {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -421px -723px;
|
background-position: -421px -337px;
|
||||||
width: 417px;
|
width: 420px;
|
||||||
height: 147px;
|
height: 147px;
|
||||||
}
|
}
|
||||||
.promo_ios {
|
.promo_g1g1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: 0px -361px;
|
background-position: -241px -633px;
|
||||||
width: 375px;
|
width: 237px;
|
||||||
height: 361px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.promo_mystery_201811 {
|
.promo_npc_alex {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -839px -723px;
|
background-position: -566px -485px;
|
||||||
width: 282px;
|
width: 162px;
|
||||||
height: 147px;
|
height: 138px;
|
||||||
}
|
}
|
||||||
.promo_oddballs_bundle {
|
.promo_seasonal_shop {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -481px -568px;
|
background-position: -403px -485px;
|
||||||
|
width: 162px;
|
||||||
|
height: 138px;
|
||||||
|
}
|
||||||
|
.promo_snow_potions {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
|
background-position: -445px -148px;
|
||||||
width: 423px;
|
width: 423px;
|
||||||
height: 147px;
|
height: 147px;
|
||||||
}
|
}
|
||||||
.promo_piyo {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
|
||||||
background-position: -1136px 0px;
|
|
||||||
width: 279px;
|
|
||||||
height: 147px;
|
|
||||||
}
|
|
||||||
.promo_take_this {
|
.promo_take_this {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -1281px -148px;
|
background-position: -729px -485px;
|
||||||
width: 96px;
|
width: 96px;
|
||||||
height: 69px;
|
height: 69px;
|
||||||
}
|
}
|
||||||
.promo_turkey_day_2018 {
|
.promo_winter_wonderland_2019 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: 0px -723px;
|
background-position: 0px -485px;
|
||||||
|
width: 402px;
|
||||||
|
height: 147px;
|
||||||
|
}
|
||||||
|
.promo_wintery_skins {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
|
background-position: 0px -337px;
|
||||||
width: 420px;
|
width: 420px;
|
||||||
height: 147px;
|
height: 147px;
|
||||||
}
|
}
|
||||||
.promo_veteran_pets {
|
.customize-option.promo_wintery_skins {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: 0px -871px;
|
background-position: -25px -352px;
|
||||||
width: 363px;
|
width: 60px;
|
||||||
height: 141px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
.scene_nametag {
|
.scene_hat_guild {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -481px 0px;
|
background-position: 0px 0px;
|
||||||
width: 512px;
|
width: 444px;
|
||||||
height: 208px;
|
height: 336px;
|
||||||
}
|
}
|
||||||
.scene_sleep {
|
.scene_starting_over {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -481px -209px;
|
background-position: -479px -633px;
|
||||||
width: 390px;
|
width: 150px;
|
||||||
height: 210px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.scene_veteran_pets {
|
.scene_todo_list {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||||
background-position: -1136px -305px;
|
background-position: 0px -633px;
|
||||||
width: 242px;
|
width: 240px;
|
||||||
height: 62px;
|
height: 195px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,402 +1,402 @@
|
|||||||
.quest_TEMPLATE_FOR_MISSING_IMAGE {
|
.npc_matt {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -251px -1519px;
|
background-position: -597px -1535px;
|
||||||
width: 221px;
|
width: 195px;
|
||||||
height: 39px;
|
height: 138px;
|
||||||
|
}
|
||||||
|
.background_dysheartener {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: 0px 0px;
|
||||||
|
width: 306px;
|
||||||
|
height: 202px;
|
||||||
|
}
|
||||||
|
.banner_flair_dysheartener {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -1963px -836px;
|
||||||
|
width: 69px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
.phobia_dysheartener {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -1187px -880px;
|
||||||
|
width: 201px;
|
||||||
|
height: 195px;
|
||||||
|
}
|
||||||
|
.quest_alligator {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -1627px -1079px;
|
||||||
|
width: 201px;
|
||||||
|
height: 213px;
|
||||||
|
}
|
||||||
|
.quest_armadillo {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: 0px -435px;
|
||||||
|
width: 219px;
|
||||||
|
height: 219px;
|
||||||
|
}
|
||||||
|
.quest_atom1 {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -1105px -1315px;
|
||||||
|
width: 250px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
.quest_atom2 {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -389px -1535px;
|
||||||
|
width: 207px;
|
||||||
|
height: 138px;
|
||||||
|
}
|
||||||
|
.quest_atom3 {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -747px -440px;
|
||||||
|
width: 216px;
|
||||||
|
height: 180px;
|
||||||
|
}
|
||||||
|
.quest_axolotl {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -747px -220px;
|
||||||
|
width: 219px;
|
||||||
|
height: 219px;
|
||||||
|
}
|
||||||
|
.quest_badger {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: 0px -655px;
|
||||||
|
width: 219px;
|
||||||
|
height: 219px;
|
||||||
|
}
|
||||||
|
.quest_basilist {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -1844px -392px;
|
||||||
|
width: 189px;
|
||||||
|
height: 141px;
|
||||||
|
}
|
||||||
|
.quest_beetle {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -1627px -1293px;
|
||||||
|
width: 204px;
|
||||||
|
height: 201px;
|
||||||
|
}
|
||||||
|
.quest_bunny {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -967px -660px;
|
||||||
|
width: 210px;
|
||||||
|
height: 186px;
|
||||||
|
}
|
||||||
|
.quest_butterfly {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -967px 0px;
|
||||||
|
width: 219px;
|
||||||
|
height: 219px;
|
||||||
|
}
|
||||||
|
.quest_cheetah {
|
||||||
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
|
background-position: -967px -220px;
|
||||||
|
width: 219px;
|
||||||
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_cow {
|
.quest_cow {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1757px -175px;
|
background-position: 0px -1535px;
|
||||||
width: 174px;
|
width: 174px;
|
||||||
height: 213px;
|
height: 213px;
|
||||||
}
|
}
|
||||||
.quest_dilatory {
|
.quest_dilatory {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -440px 0px;
|
background-position: -220px -875px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_dilatoryDistress1 {
|
.quest_dilatoryDistress1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1540px -1085px;
|
background-position: -1627px -868px;
|
||||||
width: 210px;
|
width: 210px;
|
||||||
height: 210px;
|
height: 210px;
|
||||||
}
|
}
|
||||||
.quest_dilatoryDistress2 {
|
.quest_dilatoryDistress2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1757px -959px;
|
background-position: -1844px -534px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.quest_dilatoryDistress3 {
|
.quest_dilatoryDistress3 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -440px -232px;
|
background-position: -880px -875px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_dilatory_derby {
|
.quest_dilatory_derby {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1320px -660px;
|
background-position: 0px -875px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_dustbunnies {
|
.quest_dustbunnies {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -660px 0px;
|
background-position: -1187px 0px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_egg {
|
.quest_egg {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1757px -751px;
|
background-position: -1844px -184px;
|
||||||
width: 165px;
|
width: 165px;
|
||||||
height: 207px;
|
height: 207px;
|
||||||
}
|
}
|
||||||
.quest_evilsanta {
|
.quest_evilsanta {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1562px -1332px;
|
background-position: -1844px -836px;
|
||||||
width: 118px;
|
width: 118px;
|
||||||
height: 131px;
|
height: 131px;
|
||||||
}
|
}
|
||||||
.quest_evilsanta2 {
|
.quest_evilsanta2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -220px -452px;
|
background-position: -1187px -660px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_falcon {
|
.quest_falcon {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -440px -452px;
|
background-position: 0px -1095px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_ferret {
|
.quest_ferret {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -660px -452px;
|
background-position: -307px 0px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_frog {
|
.quest_frog {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -880px -1112px;
|
background-position: -660px -1315px;
|
||||||
width: 221px;
|
width: 221px;
|
||||||
height: 213px;
|
height: 213px;
|
||||||
}
|
}
|
||||||
.quest_ghost_stag {
|
.quest_ghost_stag {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -880px -220px;
|
background-position: -660px -1095px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_goldenknight1 {
|
.quest_goldenknight1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -880px -440px;
|
background-position: -880px -1095px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_goldenknight2 {
|
.quest_goldenknight2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1311px -1332px;
|
background-position: -1356px -1315px;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.quest_goldenknight3 {
|
.quest_goldenknight3 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: 0px 0px;
|
background-position: 0px -203px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 231px;
|
height: 231px;
|
||||||
}
|
}
|
||||||
.quest_gryphon {
|
.quest_gryphon {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1322px -1112px;
|
background-position: -307px -220px;
|
||||||
width: 216px;
|
width: 216px;
|
||||||
height: 177px;
|
height: 177px;
|
||||||
}
|
}
|
||||||
.quest_guineapig {
|
.quest_guineapig {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -660px -672px;
|
background-position: -1407px -440px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_harpy {
|
.quest_harpy {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -880px -672px;
|
background-position: -1407px -660px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_hedgehog {
|
.quest_hedgehog {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: 0px -1332px;
|
background-position: -1407px -1100px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 186px;
|
height: 186px;
|
||||||
}
|
}
|
||||||
.quest_hippo {
|
.quest_hippo {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1100px -220px;
|
background-position: 0px -1315px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_horse {
|
.quest_horse {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1100px -440px;
|
background-position: -220px -1315px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_kangaroo {
|
.quest_kangaroo {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1100px -660px;
|
background-position: -1407px -880px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_kraken {
|
.quest_kraken {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -877px -1332px;
|
background-position: -527px -220px;
|
||||||
width: 216px;
|
width: 216px;
|
||||||
height: 177px;
|
height: 177px;
|
||||||
}
|
}
|
||||||
.quest_lostMasterclasser1 {
|
.quest_lostMasterclasser1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -220px -892px;
|
background-position: -1100px -1095px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_lostMasterclasser2 {
|
.quest_lostMasterclasser2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -440px -892px;
|
background-position: -440px -1095px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_lostMasterclasser3 {
|
.quest_lostMasterclasser3 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -660px -892px;
|
background-position: -1187px -220px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_mayhemMistiflying1 {
|
.quest_mayhemMistiflying1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1757px -1261px;
|
background-position: -1844px -685px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.quest_mayhemMistiflying2 {
|
.quest_mayhemMistiflying2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1100px -892px;
|
background-position: -440px -875px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_mayhemMistiflying3 {
|
.quest_mayhemMistiflying3 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1320px 0px;
|
background-position: -660px -655px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_monkey {
|
.quest_monkey {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1320px -220px;
|
background-position: -440px -655px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_moon1 {
|
.quest_moon1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1540px -217px;
|
background-position: -1627px -651px;
|
||||||
width: 216px;
|
width: 216px;
|
||||||
height: 216px;
|
height: 216px;
|
||||||
}
|
}
|
||||||
.quest_moon2 {
|
.quest_moon2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -220px 0px;
|
background-position: -220px -655px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_moon3 {
|
.quest_moon3 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1320px -880px;
|
background-position: -747px 0px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_moonstone1 {
|
.quest_moonstone1 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: 0px -1112px;
|
background-position: -527px 0px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_moonstone2 {
|
.quest_moonstone2 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -220px -1112px;
|
background-position: -1407px -220px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_moonstone3 {
|
.quest_moonstone3 {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -440px -1112px;
|
background-position: -440px -1315px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_nudibranch {
|
.quest_nudibranch {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1540px -868px;
|
background-position: -1627px -217px;
|
||||||
width: 216px;
|
width: 216px;
|
||||||
height: 216px;
|
height: 216px;
|
||||||
}
|
}
|
||||||
.quest_octopus {
|
.quest_octopus {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -220px -1332px;
|
background-position: -882px -1315px;
|
||||||
width: 222px;
|
width: 222px;
|
||||||
height: 177px;
|
height: 177px;
|
||||||
}
|
}
|
||||||
.quest_owl {
|
.quest_owl {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -660px -1112px;
|
background-position: -1187px -440px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_peacock {
|
.quest_peacock {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1540px 0px;
|
background-position: -1627px 0px;
|
||||||
width: 216px;
|
width: 216px;
|
||||||
height: 216px;
|
height: 216px;
|
||||||
}
|
}
|
||||||
.quest_penguin {
|
.quest_penguin {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1757px -567px;
|
background-position: -1844px 0px;
|
||||||
width: 190px;
|
width: 190px;
|
||||||
height: 183px;
|
height: 183px;
|
||||||
}
|
}
|
||||||
.quest_pterodactyl {
|
.quest_pterodactyl {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1320px -440px;
|
background-position: -660px -875px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_rat {
|
.quest_rat {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -880px -892px;
|
background-position: -967px -440px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_rock {
|
.quest_rock {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1540px -434px;
|
background-position: -1627px -434px;
|
||||||
width: 216px;
|
width: 216px;
|
||||||
height: 216px;
|
height: 216px;
|
||||||
}
|
}
|
||||||
.quest_rooster {
|
.quest_rooster {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1757px 0px;
|
background-position: -175px -1535px;
|
||||||
width: 213px;
|
width: 213px;
|
||||||
height: 174px;
|
height: 174px;
|
||||||
}
|
}
|
||||||
.quest_sabretooth {
|
.quest_sabretooth {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: 0px -892px;
|
background-position: -440px -435px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_seaserpent {
|
.quest_seaserpent {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -1100px 0px;
|
background-position: -220px -435px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_sheep {
|
.quest_sheep {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: -440px -672px;
|
background-position: -220px -1095px;
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
.quest_slime {
|
.quest_slime {
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
||||||
background-position: 0px -672px;
|
background-position: -1407px 0px;
|
||||||
width: 219px;
|
|
||||||
height: 219px;
|
|
||||||
}
|
|
||||||
.quest_sloth {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -880px 0px;
|
|
||||||
width: 219px;
|
|
||||||
height: 219px;
|
|
||||||
}
|
|
||||||
.quest_snail {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -1102px -1112px;
|
|
||||||
width: 219px;
|
|
||||||
height: 213px;
|
|
||||||
}
|
|
||||||
.quest_snake {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -660px -1332px;
|
|
||||||
width: 216px;
|
|
||||||
height: 177px;
|
|
||||||
}
|
|
||||||
.quest_spider {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: 0px -1519px;
|
|
||||||
width: 250px;
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
.quest_squirrel {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: 0px -452px;
|
|
||||||
width: 219px;
|
|
||||||
height: 219px;
|
|
||||||
}
|
|
||||||
.quest_stoikalmCalamity1 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -1757px -1412px;
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
.quest_stoikalmCalamity2 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -660px -220px;
|
|
||||||
width: 219px;
|
|
||||||
height: 219px;
|
|
||||||
}
|
|
||||||
.quest_stoikalmCalamity3 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -220px -232px;
|
|
||||||
width: 219px;
|
|
||||||
height: 219px;
|
|
||||||
}
|
|
||||||
.quest_taskwoodsTerror1 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -1757px -1110px;
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
.quest_taskwoodsTerror2 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -1540px -651px;
|
|
||||||
width: 216px;
|
|
||||||
height: 216px;
|
|
||||||
}
|
|
||||||
.quest_taskwoodsTerror3 {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: 0px -232px;
|
|
||||||
width: 219px;
|
|
||||||
height: 219px;
|
|
||||||
}
|
|
||||||
.quest_treeling {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -1094px -1332px;
|
|
||||||
width: 216px;
|
|
||||||
height: 177px;
|
|
||||||
}
|
|
||||||
.quest_trex {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -1757px -389px;
|
|
||||||
width: 204px;
|
|
||||||
height: 177px;
|
|
||||||
}
|
|
||||||
.quest_trex_undead {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -443px -1332px;
|
|
||||||
width: 216px;
|
|
||||||
height: 177px;
|
|
||||||
}
|
|
||||||
.quest_triceratops {
|
|
||||||
background-image: url('~assets/images/sprites/spritesmith-main-11.png');
|
|
||||||
background-position: -220px -672px;
|
|
||||||
width: 219px;
|
width: 219px;
|
||||||
height: 219px;
|
height: 219px;
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
website/client/assets/images/group@3x.png
Normal file
|
After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 546 KiB After Width: | Height: | Size: 545 KiB |
|
Before Width: | Height: | Size: 500 KiB After Width: | Height: | Size: 532 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 393 KiB |
|
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 66 KiB |