mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-05-10 02:28:44 -05:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38a591bdd1 | |||
| 2736d8acf3 | |||
| 8fe13dbb23 | |||
| 4581bb9315 | |||
| 2999212379 | |||
| d2bd246e6e | |||
| 1482f6c225 | |||
| 1178da3a26 | |||
| 819ed2b355 | |||
| a92999fc11 | |||
| 3489b88752 | |||
| 94bda30385 | |||
| e8bbdc2cb8 | |||
| d465efaf96 | |||
| 3a08de7ab3 | |||
| e6ffd69148 | |||
| 746fcfff49 | |||
| 8aa343d390 | |||
| d80c43c82a | |||
| 80e4b8617a | |||
| b3fac011a9 | |||
| 8f4d871911 | |||
| 19373ce84d | |||
| 55bfca20d9 | |||
| 2ee2b05d1c | |||
| 3d3db1bdd9 | |||
| a5dff99fa1 | |||
| 25435218e1 | |||
| 7746049bb4 | |||
| 036bf43cf3 | |||
| c4e0127a37 | |||
| 7ae059e243 | |||
| 81b9a0b92d | |||
| a6e87452a6 | |||
| c40d384913 | |||
| 65144aef28 | |||
| cc7683a871 | |||
| 31b2781333 | |||
| d37d3bc5ac | |||
| ef3a28791e | |||
| c3c2607bca | |||
| 7a6d64f158 | |||
| 836e63246d |
@@ -21,3 +21,4 @@ services:
|
||||
timeout: 30s
|
||||
start_period: 0s
|
||||
retries: 30
|
||||
|
||||
+1
-1
Submodule habitica-images updated: 980a2ee92e...32a4678c6b
Generated
+169
-281
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "5.46.3",
|
||||
"version": "5.47.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "habitica",
|
||||
"version": "5.46.3",
|
||||
"version": "5.47.8",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -57,7 +57,7 @@
|
||||
"micromustache": "^8.0.3",
|
||||
"moment": "^2.29.4",
|
||||
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
|
||||
"mongoose": "^8.9.5",
|
||||
"mongoose": "^8.23.0",
|
||||
"morgan": "^1.10.1",
|
||||
"nan": "^2.25.0",
|
||||
"nconf": "^0.12.1",
|
||||
@@ -81,7 +81,6 @@
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^9.0.0",
|
||||
"validator": "^13.11.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"winston": "^3.10.0",
|
||||
"winston-loggly-bulk": "^3.3.0",
|
||||
"xml2js": "^0.6.2"
|
||||
@@ -2624,6 +2623,19 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@google-cloud/trace-agent/node_modules/gcp-metadata": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
|
||||
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@google-cloud/trace-agent/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
@@ -3052,9 +3064,9 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz",
|
||||
"integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==",
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz",
|
||||
"integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
@@ -3265,11 +3277,6 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.25",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
|
||||
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ=="
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
@@ -3932,17 +3939,6 @@
|
||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
|
||||
"integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
@@ -6316,6 +6312,7 @@
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "^2.3.5",
|
||||
"safe-buffer": "^5.1.1"
|
||||
@@ -6325,13 +6322,15 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bl/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -6347,6 +6346,7 @@
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -7751,11 +7751,6 @@
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz",
|
||||
"integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw=="
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
@@ -11229,18 +11224,6 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/gcp-metadata": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
|
||||
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -11984,6 +11967,19 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/gcp-metadata": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
|
||||
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
@@ -12516,20 +12512,6 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gzip-size": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
|
||||
"integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
|
||||
"dependencies": {
|
||||
"duplexer": "^0.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/habitica-markdown": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-4.1.0.tgz",
|
||||
@@ -12819,6 +12801,7 @@
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz",
|
||||
"integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
@@ -12871,7 +12854,8 @@
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="
|
||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
@@ -15557,128 +15541,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz",
|
||||
"integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
"denque": "^1.4.1",
|
||||
"optional-require": "^1.1.8",
|
||||
"safe-buffer": "^5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"saslprep": "^1.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"aws4": {
|
||||
"optional": true
|
||||
},
|
||||
"bson-ext": {
|
||||
"optional": true
|
||||
},
|
||||
"kerberos": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb-client-encryption": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb-extjson": {
|
||||
"optional": true
|
||||
},
|
||||
"snappy": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
||||
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
|
||||
"integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/whatwg-url": "^11.0.2",
|
||||
"whatwg-url": "^14.1.0 || ^13.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url/node_modules/tr46": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
|
||||
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz",
|
||||
"integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "^5.0.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb/node_modules/bson": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
|
||||
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.6.19"
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.9.7",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.7.tgz",
|
||||
"integrity": "sha512-mvNXmU0V8qZzMR/qoK2mjT4Ti2ALdtfS0teK+twxhlGkwzOD76V02/zWajTu2MJ7QyEmZe9OWvnJsIY0iAuX3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bson": "^6.10.1",
|
||||
"kareem": "2.6.3",
|
||||
"mongodb": "~6.12.0",
|
||||
"mpath": "0.9.0",
|
||||
"mquery": "5.0.0",
|
||||
"ms": "2.1.3",
|
||||
"sift": "17.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.20.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mongoose"
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose/node_modules/mongodb": {
|
||||
"version": "6.12.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz",
|
||||
"integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.1.9",
|
||||
"bson": "^6.10.1",
|
||||
"mongodb-connection-string-url": "^3.0.0"
|
||||
"@mongodb-js/saslprep": "^1.3.0",
|
||||
"bson": "^6.10.4",
|
||||
"mongodb-connection-string-url": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.20.1"
|
||||
@@ -15689,7 +15559,7 @@
|
||||
"gcp-metadata": "^5.2.0",
|
||||
"kerberos": "^2.0.1",
|
||||
"mongodb-client-encryption": ">=6.0.0 <7",
|
||||
"snappy": "^7.2.2",
|
||||
"snappy": "^7.3.2",
|
||||
"socks": "^2.7.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -15716,6 +15586,72 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
||||
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/whatwg-url": "^11.0.2",
|
||||
"whatwg-url": "^14.1.0 || ^13.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url/node_modules/tr46": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "^5.1.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.23.0.tgz",
|
||||
"integrity": "sha512-Bul4Ha6J8IqzFrb0B1xpVzkC3S0sk43dmLSnhFOn8eJlZiLwL5WO6cRymmjaADdCMjUcCpj2ce8hZI6O4ZFSug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bson": "^6.10.4",
|
||||
"kareem": "2.6.3",
|
||||
"mongodb": "~6.20.0",
|
||||
"mpath": "0.9.0",
|
||||
"mquery": "5.0.0",
|
||||
"ms": "2.1.3",
|
||||
"sift": "17.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.20.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mongoose"
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -15775,6 +15711,56 @@
|
||||
"integrity": "sha512-jSTz73B/+pGTTvhu5Ym8xsG6+QqaWab53UXnXdNNlTijTdLvcHABCLJXudQiJxob5N1Mzr5EOSx5ziwn2sihPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/monk/node_modules/bson": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
|
||||
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.6.19"
|
||||
}
|
||||
},
|
||||
"node_modules/monk/node_modules/mongodb": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz",
|
||||
"integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
"denque": "^1.4.1",
|
||||
"optional-require": "^1.1.8",
|
||||
"safe-buffer": "^5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"saslprep": "^1.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"aws4": {
|
||||
"optional": true
|
||||
},
|
||||
"bson-ext": {
|
||||
"optional": true
|
||||
},
|
||||
"kerberos": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb-client-encryption": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb-extjson": {
|
||||
"optional": true
|
||||
},
|
||||
"snappy": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/morgan": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
|
||||
@@ -15852,14 +15838,6 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
|
||||
"integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
@@ -17128,19 +17106,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"bin": {
|
||||
"opener": "bin/opener-bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/optional-require": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz",
|
||||
"integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==",
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.10.tgz",
|
||||
"integrity": "sha512-0r3OB9EIQsP+a5HVATHq2ExIy2q/Vaffoo4IAikW1spCYswhLxqWQS0i3GwS3AdY/OIP4SWZHLGz8CMU558PGw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"require-at": "^1.0.6"
|
||||
},
|
||||
@@ -18622,6 +18593,7 @@
|
||||
"resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz",
|
||||
"integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -18920,6 +18892,7 @@
|
||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
@@ -19346,19 +19319,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
|
||||
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
|
||||
"dependencies": {
|
||||
"@polka/url": "^1.0.0-next.24",
|
||||
"mrmime": "^2.0.0",
|
||||
"totalist": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
@@ -20849,14 +20809,6 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
|
||||
@@ -21870,50 +21822,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
|
||||
"integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
|
||||
"dependencies": {
|
||||
"@discoveryjs/json-ext": "0.5.7",
|
||||
"acorn": "^8.0.4",
|
||||
"acorn-walk": "^8.0.0",
|
||||
"commander": "^7.2.0",
|
||||
"debounce": "^1.2.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"gzip-size": "^6.0.0",
|
||||
"html-escaper": "^2.0.2",
|
||||
"opener": "^1.5.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"sirv": "^2.0.3",
|
||||
"ws": "^7.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"webpack-bundle-analyzer": "lib/bin/analyzer.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-cli": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
|
||||
@@ -22279,26 +22187,6 @@
|
||||
"typedarray-to-buffer": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml-crypto": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz",
|
||||
|
||||
+2
-3
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "5.46.3",
|
||||
"version": "5.47.8",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -52,7 +52,7 @@
|
||||
"micromustache": "^8.0.3",
|
||||
"moment": "^2.29.4",
|
||||
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
|
||||
"mongoose": "^8.9.5",
|
||||
"mongoose": "^8.23.0",
|
||||
"morgan": "^1.10.1",
|
||||
"nan": "^2.25.0",
|
||||
"nconf": "^0.12.1",
|
||||
@@ -76,7 +76,6 @@
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^9.0.0",
|
||||
"validator": "^13.11.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"winston": "^3.10.0",
|
||||
"winston-loggly-bulk": "^3.3.0",
|
||||
"xml2js": "^0.6.2"
|
||||
|
||||
@@ -1,560 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
import nconf from 'nconf';
|
||||
import Amplitude from 'amplitude';
|
||||
import * as analyticsService from '../../../../website/server/libs/analyticsService';
|
||||
|
||||
describe('analyticsService', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.stub(Amplitude.prototype, 'track').returns(Promise.resolve());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('#getServiceByEnvironment', () => {
|
||||
it('returns mock methods when not in production', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(false);
|
||||
expect(analyticsService.getAnalyticsServiceByEnvironment())
|
||||
.to.equal(analyticsService.mockAnalyticsService);
|
||||
});
|
||||
|
||||
it('returns real methods when in production', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
||||
expect(analyticsService.getAnalyticsServiceByEnvironment().track)
|
||||
.to.equal(analyticsService.track);
|
||||
expect(analyticsService.getAnalyticsServiceByEnvironment().trackPurchase)
|
||||
.to.equal(analyticsService.trackPurchase);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#track', () => {
|
||||
let eventType; let
|
||||
data;
|
||||
|
||||
beforeEach(() => {
|
||||
eventType = 'Cron';
|
||||
data = {
|
||||
category: 'behavior',
|
||||
uuid: 'unique-user-id',
|
||||
resting: true,
|
||||
cronCount: 5,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
},
|
||||
user: {
|
||||
preferences: {
|
||||
analyticsConsent: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
context('Amplitude', () => {
|
||||
it('calls out to amplitude', () => analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
||||
}));
|
||||
|
||||
it('uses a dummy user id if none is provided', () => {
|
||||
delete data.uuid;
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
user_id: 'no-user-id-was-provided',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('platform', () => {
|
||||
it('logs web platform', () => {
|
||||
data.headers = { 'x-client': 'habitica-web' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'Web',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs iOS platform', () => {
|
||||
data.headers = { 'x-client': 'habitica-ios' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'iOS',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs Android platform', () => {
|
||||
data.headers = { 'x-client': 'habitica-android' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'Android',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs 3rd Party platform', () => {
|
||||
data.headers = { 'x-client': 'some-third-party' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: '3rd Party',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs unknown if headers are not passed in', () => {
|
||||
delete data.headers;
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'Unknown',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Operating System', () => {
|
||||
it('sets default', () => {
|
||||
data.headers = {
|
||||
'x-client': 'third-party',
|
||||
'user-agent': 'foo',
|
||||
};
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: 'Other',
|
||||
os_version: '0',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets iOS', () => {
|
||||
data.headers = {
|
||||
'x-client': 'habitica-ios',
|
||||
'user-agent': 'Habitica/148 (iPhone; iOS 9.3; Scale/2.00)',
|
||||
};
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: 'iOS',
|
||||
os_version: '9.3.0',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets Android', () => {
|
||||
data.headers = {
|
||||
'x-client': 'habitica-android',
|
||||
'user-agent': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19',
|
||||
};
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: 'Android',
|
||||
os_version: '4.0.4',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets Unknown if headers are not passed in', () => {
|
||||
delete data.headers;
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: undefined,
|
||||
os_version: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends details about event', () => analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
category: 'behavior',
|
||||
resting: true,
|
||||
cronCount: 5,
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
it('sends english item name for gear if itemKey is provided', () => {
|
||||
data.itemKey = 'headAccessory_special_foxEars';
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
itemKey: data.itemKey,
|
||||
itemName: 'Fox Ears',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends english item name for egg if itemKey is provided', () => {
|
||||
data.itemKey = 'Wolf';
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
itemKey: data.itemKey,
|
||||
itemName: 'Wolf Egg',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends english item name for food if itemKey is provided', () => {
|
||||
data.itemKey = 'Cake_Skeleton';
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
itemKey: data.itemKey,
|
||||
itemName: 'Bare Bones Cake',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends english item name for hatching potion if itemKey is provided', () => {
|
||||
data.itemKey = 'Golden';
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
itemKey: data.itemKey,
|
||||
itemName: 'Golden Hatching Potion',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends english item name for quest if itemKey is provided', () => {
|
||||
data.itemKey = 'atom1';
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
itemKey: data.itemKey,
|
||||
itemName: 'Attack of the Mundane, Part 1: Dish Disaster!',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends english item name for purchased spell if itemKey is provided', () => {
|
||||
data.itemKey = 'seafoam';
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
itemKey: data.itemKey,
|
||||
itemName: 'Seafoam',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends user data if provided', () => {
|
||||
const stats = {
|
||||
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
|
||||
};
|
||||
const user = {
|
||||
stats,
|
||||
contributor: { level: 1 },
|
||||
purchased: { plan: { planId: 'foo-plan' } },
|
||||
flags: { tour: { intro: -2 } },
|
||||
habits: [{ _id: 'habit' }],
|
||||
dailys: [{ _id: 'daily' }],
|
||||
todos: [{ _id: 'todo' }],
|
||||
rewards: [{ _id: 'reward' }],
|
||||
balance: 12,
|
||||
loginIncentives: 1,
|
||||
preferences: {
|
||||
analyticsConsent: true,
|
||||
},
|
||||
};
|
||||
|
||||
data.user = user;
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
user_properties: {
|
||||
Class: 'wizard',
|
||||
Experience: 5,
|
||||
Gold: 23,
|
||||
Health: 10,
|
||||
Level: 4,
|
||||
Mana: 30,
|
||||
tutorialComplete: true,
|
||||
'Number Of Tasks': {
|
||||
habits: 1,
|
||||
dailys: 1,
|
||||
todos: 1,
|
||||
rewards: 1,
|
||||
},
|
||||
contributorLevel: 1,
|
||||
subscription: 'foo-plan',
|
||||
balance: 12,
|
||||
balanceGemAmount: 48,
|
||||
loginIncentives: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trackPurchase', () => {
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
uuid: 'user-id',
|
||||
sku: 'paypal-checkout',
|
||||
paymentMethod: 'PayPal',
|
||||
itemPurchased: 'Gems',
|
||||
purchaseValue: 8,
|
||||
purchaseType: 'checkout',
|
||||
gift: false,
|
||||
quantity: 1,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
},
|
||||
user: {
|
||||
preferences: {
|
||||
analyticsConsent: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
context('Amplitude', () => {
|
||||
it('calls out to amplitude', () => analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
||||
}));
|
||||
|
||||
it('uses a dummy user id if none is provided', () => {
|
||||
delete data.uuid;
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
user_id: 'no-user-id-was-provided',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('platform', () => {
|
||||
it('logs web platform', () => {
|
||||
data.headers = { 'x-client': 'habitica-web' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'Web',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs iOS platform', () => {
|
||||
data.headers = { 'x-client': 'habitica-ios' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'iOS',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs Android platform', () => {
|
||||
data.headers = { 'x-client': 'habitica-android' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'Android',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs 3rd Party platform', () => {
|
||||
data.headers = { 'x-client': 'some-third-party' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: '3rd Party',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs unknown if headers are not passed in', () => {
|
||||
delete data.headers;
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
platform: 'Unknown',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Operating System', () => {
|
||||
it('sets default', () => {
|
||||
data.headers = {
|
||||
'x-client': 'third-party',
|
||||
'user-agent': 'foo',
|
||||
};
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: 'Other',
|
||||
os_version: '0',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets iOS', () => {
|
||||
data.headers = {
|
||||
'x-client': 'habitica-ios',
|
||||
'user-agent': 'Habitica/148 (iPhone; iOS 9.3; Scale/2.00)',
|
||||
};
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: 'iOS',
|
||||
os_version: '9.3.0',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets Android', () => {
|
||||
data.headers = {
|
||||
'x-client': 'habitica-android',
|
||||
'user-agent': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19',
|
||||
};
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: 'Android',
|
||||
os_version: '4.0.4',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets Unknown if headers are not passed in', () => {
|
||||
delete data.headers;
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
os_name: undefined,
|
||||
os_version: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sends details about purchase', () => analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
gift: false,
|
||||
itemPurchased: 'Gems',
|
||||
paymentMethod: 'PayPal',
|
||||
purchaseType: 'checkout',
|
||||
quantity: 1,
|
||||
sku: 'paypal-checkout',
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
it('sends user data if provided', () => {
|
||||
const stats = {
|
||||
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
|
||||
};
|
||||
const user = {
|
||||
stats,
|
||||
contributor: { level: 1 },
|
||||
purchased: { plan: { planId: 'foo-plan' } },
|
||||
flags: { tour: { intro: -2 } },
|
||||
habits: [{ _id: 'habit' }],
|
||||
dailys: [{ _id: 'daily' }],
|
||||
todos: [{ _id: 'todo' }],
|
||||
rewards: [{ _id: 'reward' }],
|
||||
preferences: {
|
||||
analyticsConsent: true,
|
||||
},
|
||||
};
|
||||
|
||||
data.user = user;
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
user_properties: {
|
||||
Class: 'wizard',
|
||||
Experience: 5,
|
||||
Gold: 23,
|
||||
Health: 10,
|
||||
Level: 4,
|
||||
Mana: 30,
|
||||
tutorialComplete: true,
|
||||
'Number Of Tasks': {
|
||||
habits: 1,
|
||||
dailys: 1,
|
||||
todos: 1,
|
||||
rewards: 1,
|
||||
},
|
||||
contributorLevel: 1,
|
||||
subscription: 'foo-plan',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('mockAnalyticsService', () => {
|
||||
it('has stubbed track method', () => {
|
||||
expect(analyticsService.mockAnalyticsService).to.respondTo('track');
|
||||
});
|
||||
|
||||
it('has stubbed trackPurchase method', () => {
|
||||
expect(analyticsService.mockAnalyticsService).to.respondTo('trackPurchase');
|
||||
});
|
||||
});
|
||||
});
|
||||
+116
-136
@@ -13,7 +13,6 @@ import { cron, cronWrapper } from '../../../../website/server/libs/cron';
|
||||
import { model as User } from '../../../../website/server/models/user';
|
||||
import * as Tasks from '../../../../website/server/models/task';
|
||||
import common from '../../../../website/common';
|
||||
import * as analytics from '../../../../website/server/libs/analyticsService';
|
||||
import { model as Group } from '../../../../website/server/models/group';
|
||||
|
||||
const CRON_TIMEOUT_WAIT = new Date(5 * 60 * 1000).getTime();
|
||||
@@ -41,20 +40,17 @@ describe('cron', async () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
sinon.spy(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (clock !== null) clock.restore();
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('updates user.preferences.timezoneOffsetAtLastCron', async () => {
|
||||
const timezoneUtcOffsetFromUserPrefs = -1;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics, timezoneUtcOffsetFromUserPrefs,
|
||||
user, tasksByType, daysMissed, timezoneUtcOffsetFromUserPrefs,
|
||||
});
|
||||
|
||||
expect(user.preferences.timezoneOffsetAtLastCron).to.equal(1);
|
||||
@@ -63,7 +59,7 @@ describe('cron', async () => {
|
||||
it('resets user.items.lastDrop.count', async () => {
|
||||
user.items.lastDrop.count = 4;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
});
|
||||
@@ -71,26 +67,11 @@ describe('cron', async () => {
|
||||
it('increments user cron count', async () => {
|
||||
const cronCountBefore = user.flags.cronCount;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.flags.cronCount).to.be.greaterThan(cronCountBefore);
|
||||
});
|
||||
|
||||
it('calls analytics', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
});
|
||||
expect(analytics.track.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('calls analytics when user is sleeping', async () => {
|
||||
user.preferences.sleep = true;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
});
|
||||
expect(analytics.track.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
describe('end of the month perks', async () => {
|
||||
beforeEach(async () => {
|
||||
user.purchased.plan.customerId = 'subscribedId';
|
||||
@@ -101,7 +82,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.dateUpdated = new Date('2018-12-11');
|
||||
clock = sinon.useFakeTimers(new Date('2019-01-29'));
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.mysteryItems.length).to.eql(2);
|
||||
const filteredNotifications = user.notifications.filter(n => n.type === 'NEW_MYSTERY_ITEMS');
|
||||
@@ -112,7 +93,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.dateUpdated = new Date('2018-11-11');
|
||||
clock = sinon.useFakeTimers(new Date('2019-01-29'));
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.mysteryItems.length).to.eql(4);
|
||||
const filteredNotifications = user.notifications.filter(n => n.type === 'NEW_MYSTERY_ITEMS');
|
||||
@@ -122,7 +103,7 @@ describe('cron', async () => {
|
||||
it('resets plan.gemsBought on a new month', async () => {
|
||||
user.purchased.plan.gemsBought = 10;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.gemsBought).to.equal(0);
|
||||
});
|
||||
@@ -131,7 +112,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.gemsBought = 10;
|
||||
user.purchased.plan.dateUpdated = undefined;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.gemsBought).to.equal(0);
|
||||
});
|
||||
@@ -142,7 +123,7 @@ describe('cron', async () => {
|
||||
|
||||
user.purchased.plan.gemsBought = 10;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.gemsBought).to.equal(10);
|
||||
});
|
||||
@@ -150,7 +131,7 @@ describe('cron', async () => {
|
||||
it('resets plan.dateUpdated on a new month', async () => {
|
||||
const currentMonth = moment().startOf('month');
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(moment(user.purchased.plan.dateUpdated).startOf('month').isSame(currentMonth)).to.eql(true);
|
||||
});
|
||||
@@ -158,7 +139,7 @@ describe('cron', async () => {
|
||||
it('increments plan.consecutive.count', async () => {
|
||||
user.purchased.plan.consecutive.count = 0;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.count).to.equal(1);
|
||||
});
|
||||
@@ -166,7 +147,7 @@ describe('cron', async () => {
|
||||
it('increments plan.cumulativeCount', async () => {
|
||||
user.purchased.plan.cumulativeCount = 0;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.cumulativeCount).to.equal(1);
|
||||
});
|
||||
@@ -175,7 +156,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.dateUpdated = moment().subtract(2, 'months').toDate();
|
||||
user.purchased.plan.consecutive.count = 0;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.count).to.equal(2);
|
||||
});
|
||||
@@ -184,7 +165,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.dateUpdated = moment().subtract(3, 'months').toDate();
|
||||
user.purchased.plan.cumulativeCount = 0;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.cumulativeCount).to.equal(3);
|
||||
});
|
||||
@@ -196,7 +177,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||
@@ -206,7 +187,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.consecutive.gemCapExtra = 26;
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
|
||||
});
|
||||
@@ -214,7 +195,7 @@ describe('cron', async () => {
|
||||
it('does not reset plan stats if we are before the last day of the cancelled month', async () => {
|
||||
user.purchased.plan.dateTerminated = moment(new Date()).add({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.customerId).to.exist;
|
||||
});
|
||||
@@ -225,7 +206,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.purchased.plan.customerId).to.not.exist;
|
||||
@@ -264,7 +245,7 @@ describe('cron', async () => {
|
||||
// Add 2 days so that we're sure we're not affected by any start-of-month effects
|
||||
// e.g., from time zone oddness.
|
||||
await cron({
|
||||
user: user1, tasksByType, daysMissed, analytics,
|
||||
user: user1, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
@@ -276,7 +257,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user1, tasksByType, daysMissed, analytics,
|
||||
user: user1, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user1.purchased.plan.consecutive.count).to.equal(10);
|
||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(11);
|
||||
@@ -311,7 +292,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user3, tasksByType, daysMissed, analytics,
|
||||
user: user3, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
@@ -323,7 +304,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user3, tasksByType, daysMissed, analytics,
|
||||
user: user3, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user3.purchased.plan.consecutive.count).to.equal(10);
|
||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(11);
|
||||
@@ -358,7 +339,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user6, tasksByType, daysMissed, analytics,
|
||||
user: user6, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user6.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
@@ -391,7 +372,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user12, tasksByType, daysMissed, analytics,
|
||||
user: user12, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||
@@ -403,7 +384,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user12, tasksByType, daysMissed, analytics,
|
||||
user: user12, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user12.purchased.plan.consecutive.count).to.equal(10);
|
||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(11);
|
||||
@@ -439,7 +420,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user3g, tasksByType, daysMissed, analytics,
|
||||
user: user3g, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user3g.purchased.plan.consecutive.count).to.equal(1);
|
||||
expect(user3g.purchased.plan.cumulativeCount).to.equal(1);
|
||||
@@ -452,7 +433,7 @@ describe('cron', async () => {
|
||||
.add(2, 'days')
|
||||
.toDate());
|
||||
await cron({
|
||||
user: user3g, tasksByType, daysMissed, analytics,
|
||||
user: user3g, tasksByType, daysMissed,
|
||||
});
|
||||
// subscription has been erased by now
|
||||
expect(user3g.purchased.plan.consecutive.count).to.equal(0);
|
||||
@@ -471,7 +452,7 @@ describe('cron', async () => {
|
||||
it('resets plan.gemsBought on a new month', async () => {
|
||||
user.purchased.plan.gemsBought = 10;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.gemsBought).to.equal(0);
|
||||
});
|
||||
@@ -482,14 +463,14 @@ describe('cron', async () => {
|
||||
|
||||
user.purchased.plan.gemsBought = 10;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.gemsBought).to.equal(10);
|
||||
});
|
||||
|
||||
it('does not reset plan.dateUpdated on a new month', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.dateUpdated).to.be.empty;
|
||||
});
|
||||
@@ -497,7 +478,7 @@ describe('cron', async () => {
|
||||
it('does not increment plan.consecutive.count', async () => {
|
||||
user.purchased.plan.consecutive.count = 0;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.count).to.equal(0);
|
||||
});
|
||||
@@ -505,7 +486,7 @@ describe('cron', async () => {
|
||||
it('does not increment plan.cumulativeCount', async () => {
|
||||
user.purchased.plan.cumulativeCount = 0;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.cumulativeCount).to.equal(0);
|
||||
});
|
||||
@@ -513,7 +494,7 @@ describe('cron', async () => {
|
||||
it('does not increment plan.consecutive.trinkets when user has reached a month that is a multiple of 3', async () => {
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(0);
|
||||
});
|
||||
@@ -521,7 +502,7 @@ describe('cron', async () => {
|
||||
it('does not increment plan.consecutive.gemCapExtra when user has reached a month that is a multiple of 3', async () => {
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(0);
|
||||
});
|
||||
@@ -530,7 +511,7 @@ describe('cron', async () => {
|
||||
user.purchased.plan.consecutive.gemCapExtra = 26;
|
||||
user.purchased.plan.consecutive.count = 5;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
|
||||
});
|
||||
@@ -538,7 +519,7 @@ describe('cron', async () => {
|
||||
it('does nothing to plan stats if we are before the last day of the cancelled month', async () => {
|
||||
user.purchased.plan.dateTerminated = moment(new Date()).add({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.purchased.plan.customerId).to.not.exist;
|
||||
});
|
||||
@@ -564,7 +545,7 @@ describe('cron', async () => {
|
||||
it('should make uncompleted todos redder', async () => {
|
||||
const valueBefore = tasksByType.todos[0].value;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.todos[0].value).to.be.lessThan(valueBefore);
|
||||
});
|
||||
@@ -573,7 +554,7 @@ describe('cron', async () => {
|
||||
tasksByType.todos[0].completed = true;
|
||||
const valueBefore = tasksByType.todos[0].value;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.todos[0].value).to.equal(valueBefore);
|
||||
});
|
||||
@@ -582,7 +563,7 @@ describe('cron', async () => {
|
||||
tasksByType.todos[0].completed = true;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.history.todos).to.be.lengthOf(1);
|
||||
@@ -608,7 +589,7 @@ describe('cron', async () => {
|
||||
expect(user.tasksOrder.todos).to.be.lengthOf(3);
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
// user.tasksOrder.todos should be filtered while tasks by type remains unchanged
|
||||
@@ -635,7 +616,7 @@ describe('cron', async () => {
|
||||
const original = user.tasksOrder.todos; // Preserve the original order
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
let listsAreEqual = true;
|
||||
@@ -675,7 +656,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].everyX = 5;
|
||||
tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate();
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].isDue).to.be.false;
|
||||
});
|
||||
@@ -686,7 +667,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].everyX = 5;
|
||||
tasksByType.dailys[0].startDate = moment().toDate();
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].isDue).to.exist;
|
||||
});
|
||||
@@ -696,14 +677,14 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].everyX = 5;
|
||||
tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate();
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].nextDue.length).to.eql(6);
|
||||
});
|
||||
|
||||
it('should add history', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].history).to.be.lengthOf(1);
|
||||
});
|
||||
@@ -711,7 +692,7 @@ describe('cron', async () => {
|
||||
it('should set tasks completed to false', async () => {
|
||||
tasksByType.dailys[0].completed = true;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].completed).to.be.false;
|
||||
});
|
||||
@@ -720,7 +701,7 @@ describe('cron', async () => {
|
||||
user.preferences.sleep = true;
|
||||
tasksByType.dailys[0].completed = true;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].completed).to.be.false;
|
||||
});
|
||||
@@ -729,7 +710,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
||||
tasksByType.dailys[0].completed = true;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
||||
});
|
||||
@@ -739,7 +720,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
||||
tasksByType.dailys[0].completed = true;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
||||
});
|
||||
@@ -749,7 +730,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
||||
});
|
||||
@@ -759,7 +740,7 @@ describe('cron', async () => {
|
||||
const hpBefore = user.stats.hp;
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.stats.hp).to.be.lessThan(hpBefore);
|
||||
});
|
||||
@@ -770,7 +751,7 @@ describe('cron', async () => {
|
||||
const hpBefore = user.stats.hp;
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.stats.hp).to.equal(hpBefore);
|
||||
});
|
||||
@@ -784,7 +765,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
|
||||
cronOverride({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.hp).to.equal(hpBefore);
|
||||
@@ -797,7 +778,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.hp).to.equal(hpBefore);
|
||||
@@ -808,7 +789,7 @@ describe('cron', async () => {
|
||||
let hpBefore = user.stats.hp;
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
const hpDifferenceOfFullyIncompleteDaily = hpBefore - user.stats.hp;
|
||||
|
||||
@@ -816,7 +797,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: true });
|
||||
tasksByType.dailys[0].checklist.push({ title: 'test2', completed: false });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
const hpDifferenceOfPartiallyIncompleteDaily = hpBefore - user.stats.hp;
|
||||
|
||||
@@ -829,7 +810,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
|
||||
const progress = await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(progress.down).to.equal(-1);
|
||||
@@ -841,7 +822,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||
|
||||
const progress = await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(progress.down).to.equal(0);
|
||||
@@ -862,7 +843,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[1].frequency = 'daily';
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.hp).to.equal(48);
|
||||
@@ -886,7 +867,7 @@ describe('cron', async () => {
|
||||
tasksByType.habits[0].down = false;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].value).to.be.lessThan(1);
|
||||
@@ -897,7 +878,7 @@ describe('cron', async () => {
|
||||
tasksByType.habits[0].up = false;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].value).to.be.lessThan(1);
|
||||
@@ -909,7 +890,7 @@ describe('cron', async () => {
|
||||
tasksByType.habits[0].down = true;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].value).to.equal(1);
|
||||
@@ -928,7 +909,7 @@ describe('cron', async () => {
|
||||
tasksByType.habits[0].counterDown = 1;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -941,7 +922,7 @@ describe('cron', async () => {
|
||||
tasksByType.habits[0].counterDown = 1;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -955,7 +936,7 @@ describe('cron', async () => {
|
||||
|
||||
// should not reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||
@@ -964,7 +945,7 @@ describe('cron', async () => {
|
||||
// should reset
|
||||
daysMissed = 8;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -988,7 +969,7 @@ describe('cron', async () => {
|
||||
|
||||
// should not reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||
@@ -1002,7 +983,7 @@ describe('cron', async () => {
|
||||
|
||||
// should reset after user CDS
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -1026,7 +1007,7 @@ describe('cron', async () => {
|
||||
|
||||
// should not reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||
@@ -1036,7 +1017,7 @@ describe('cron', async () => {
|
||||
// should reset
|
||||
daysMissed = 2;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -1060,7 +1041,7 @@ describe('cron', async () => {
|
||||
|
||||
// should reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -1084,7 +1065,7 @@ describe('cron', async () => {
|
||||
|
||||
// should not reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||
@@ -1098,7 +1079,7 @@ describe('cron', async () => {
|
||||
|
||||
// should not reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||
@@ -1107,7 +1088,7 @@ describe('cron', async () => {
|
||||
// should reset
|
||||
daysMissed = 32;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -1132,7 +1113,7 @@ describe('cron', async () => {
|
||||
|
||||
// should reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -1156,7 +1137,7 @@ describe('cron', async () => {
|
||||
|
||||
// should not reset
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||
@@ -1166,7 +1147,7 @@ describe('cron', async () => {
|
||||
// should reset
|
||||
daysMissed = 2;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||
@@ -1199,7 +1180,7 @@ describe('cron', async () => {
|
||||
user.stats.lvl = 2;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.history.exp).to.have.lengthOf(1);
|
||||
@@ -1212,7 +1193,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].isDue = true;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.achievements.perfect).to.equal(1);
|
||||
@@ -1224,7 +1205,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].isDue = false;
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.achievements.perfect).to.equal(0);
|
||||
@@ -1238,7 +1219,7 @@ describe('cron', async () => {
|
||||
const previousBuffs = user.stats.buffs.toObject();
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||
@@ -1256,7 +1237,7 @@ describe('cron', async () => {
|
||||
const previousBuffs = user.stats.buffs.toObject();
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||
@@ -1280,7 +1261,7 @@ describe('cron', async () => {
|
||||
};
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.equal(0);
|
||||
@@ -1307,7 +1288,7 @@ describe('cron', async () => {
|
||||
};
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.equal(0);
|
||||
@@ -1333,7 +1314,7 @@ describe('cron', async () => {
|
||||
};
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.equal(0);
|
||||
@@ -1360,7 +1341,7 @@ describe('cron', async () => {
|
||||
};
|
||||
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.equal(0);
|
||||
@@ -1381,7 +1362,7 @@ describe('cron', async () => {
|
||||
const previousBuffs = user.stats.buffs.toObject();
|
||||
|
||||
cronOverride({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||
@@ -1401,7 +1382,7 @@ describe('cron', async () => {
|
||||
const previousBuffs = user.stats.buffs.toObject();
|
||||
|
||||
cronOverride({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||
@@ -1420,7 +1401,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].completed = true;
|
||||
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.stats.mp).to.be.greaterThan(mpBefore);
|
||||
|
||||
@@ -1436,7 +1417,7 @@ describe('cron', async () => {
|
||||
tasksByType.dailys[0].completed = true;
|
||||
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.stats.mp).to.equal(mpBefore);
|
||||
|
||||
@@ -1449,7 +1430,7 @@ describe('cron', async () => {
|
||||
user.stats.mp = 120;
|
||||
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.stats.mp).to.equal(common.statsComputed(user).maxMP);
|
||||
|
||||
@@ -1482,7 +1463,7 @@ describe('cron', async () => {
|
||||
|
||||
it('resets user progress', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.party.quest.progress.up).to.equal(0);
|
||||
expect(user.party.quest.progress.down).to.equal(0);
|
||||
@@ -1491,7 +1472,7 @@ describe('cron', async () => {
|
||||
|
||||
it('applies the user progress', async () => {
|
||||
const progress = await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(progress.down).to.equal(-1);
|
||||
});
|
||||
@@ -1529,19 +1510,19 @@ describe('cron', async () => {
|
||||
describe('login incentives', async () => {
|
||||
it('increments incentive counter each cron', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 1 });
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(2);
|
||||
});
|
||||
|
||||
it('pushes a notification of the day\'s incentive each cron', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.notifications.length).to.eql(1);
|
||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||
@@ -1549,13 +1530,13 @@ describe('cron', async () => {
|
||||
|
||||
it('replaces previous notifications', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
|
||||
const filteredNotifications = user.notifications.filter(n => n.type === 'LOGIN_INCENTIVE');
|
||||
@@ -1566,7 +1547,7 @@ describe('cron', async () => {
|
||||
it('increments loginIncentives by 1 even if days are skipped in between', async () => {
|
||||
daysMissed = 3;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
});
|
||||
@@ -1574,14 +1555,14 @@ describe('cron', async () => {
|
||||
it('increments loginIncentives by 1 even if user is sleeping', async () => {
|
||||
user.preferences.sleep = true;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
});
|
||||
|
||||
it('awards user bard robes if login incentive is 1', async () => {
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(1);
|
||||
expect(user.items.gear.owned.armor_special_bardRobes).to.eql(true);
|
||||
@@ -1591,7 +1572,7 @@ describe('cron', async () => {
|
||||
it('awards user incentive backgrounds if login incentive is 2', async () => {
|
||||
user.loginIncentives = 1;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(2);
|
||||
expect(user.purchased.background.blue).to.eql(true);
|
||||
@@ -1605,7 +1586,7 @@ describe('cron', async () => {
|
||||
it('awards user Bard Hat if login incentive is 3', async () => {
|
||||
user.loginIncentives = 2;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(3);
|
||||
expect(user.items.gear.owned.head_special_bardHat).to.eql(true);
|
||||
@@ -1615,7 +1596,7 @@ describe('cron', async () => {
|
||||
it('awards user RoyalPurple Hatching Potion if login incentive is 4', async () => {
|
||||
user.loginIncentives = 3;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(4);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
@@ -1625,7 +1606,7 @@ describe('cron', async () => {
|
||||
it('awards user a Chocolate, Meat and Pink Contton Candy if login incentive is 5', async () => {
|
||||
user.loginIncentives = 4;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(5);
|
||||
|
||||
@@ -1639,7 +1620,7 @@ describe('cron', async () => {
|
||||
it('awards user moon quest if login incentive is 7', async () => {
|
||||
user.loginIncentives = 6;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(7);
|
||||
expect(user.items.quests.moon1).to.eql(1);
|
||||
@@ -1649,7 +1630,7 @@ describe('cron', async () => {
|
||||
it('awards user RoyalPurple Hatching Potion if login incentive is 10', async () => {
|
||||
user.loginIncentives = 9;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(10);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
@@ -1659,7 +1640,7 @@ describe('cron', async () => {
|
||||
it('awards user a Strawberry, Patato and Blue Contton Candy if login incentive is 14', async () => {
|
||||
user.loginIncentives = 13;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(14);
|
||||
|
||||
@@ -1673,7 +1654,7 @@ describe('cron', async () => {
|
||||
it('awards user a bard instrument if login incentive is 18', async () => {
|
||||
user.loginIncentives = 17;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(18);
|
||||
expect(user.items.gear.owned.weapon_special_bardInstrument).to.eql(true);
|
||||
@@ -1683,7 +1664,7 @@ describe('cron', async () => {
|
||||
it('awards user second moon quest if login incentive is 22', async () => {
|
||||
user.loginIncentives = 21;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(22);
|
||||
expect(user.items.quests.moon2).to.eql(1);
|
||||
@@ -1693,7 +1674,7 @@ describe('cron', async () => {
|
||||
it('awards user a RoyalPurple hatching potion if login incentive is 26', async () => {
|
||||
user.loginIncentives = 25;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(26);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
@@ -1703,7 +1684,7 @@ describe('cron', async () => {
|
||||
it('awards user Fish, Milk, Rotten Meat and Honey if login incentive is 30', async () => {
|
||||
user.loginIncentives = 29;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(30);
|
||||
|
||||
@@ -1718,7 +1699,7 @@ describe('cron', async () => {
|
||||
it('awards user a RoyalPurple hatching potion if login incentive is 35', async () => {
|
||||
user.loginIncentives = 34;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(35);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
@@ -1728,7 +1709,7 @@ describe('cron', async () => {
|
||||
it('awards user the third moon quest if login incentive is 40', async () => {
|
||||
user.loginIncentives = 39;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(40);
|
||||
expect(user.items.quests.moon3).to.eql(1);
|
||||
@@ -1738,7 +1719,7 @@ describe('cron', async () => {
|
||||
it('awards user a RoyalPurple hatching potion if login incentive is 45', async () => {
|
||||
user.loginIncentives = 44;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(45);
|
||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||
@@ -1748,7 +1729,7 @@ describe('cron', async () => {
|
||||
it('awards user a saddle if login incentive is 50', async () => {
|
||||
user.loginIncentives = 49;
|
||||
await cron({
|
||||
user, tasksByType, daysMissed, analytics,
|
||||
user, tasksByType, daysMissed,
|
||||
});
|
||||
expect(user.loginIncentives).to.eql(50);
|
||||
expect(user.items.food.Saddle).to.eql(1);
|
||||
@@ -1766,7 +1747,6 @@ describe('cron wrapper', () => {
|
||||
res = generateRes();
|
||||
req = generateReq();
|
||||
user = await res.locals.user.save();
|
||||
res.analytics = analytics;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import nconf from 'nconf';
|
||||
import requireAgain from 'require-again';
|
||||
import { model as User } from '../../../../website/server/models/user';
|
||||
import { RegistrationEventModel } from '../../../../website/server/models/analytics/registrationEvent';
|
||||
import { SubscriptionEventModel } from '../../../../website/server/models/analytics/subscriptionEvent';
|
||||
|
||||
describe('localAnalytics', () => {
|
||||
let user;
|
||||
let localAnalytics;
|
||||
before(() => {
|
||||
const nconfGetStub = sandbox.stub(nconf, 'get');
|
||||
nconfGetStub.withArgs('ANALYTICS_DB').returns('analytics');
|
||||
nconfGetStub.withArgs('DISABLE_LOCAL_ANALYTICS').returns(false);
|
||||
localAnalytics = requireAgain('../../../../website/server/libs/localAnalytics');
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
user = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username',
|
||||
email: 'email@example.com',
|
||||
},
|
||||
},
|
||||
registeredThrough: 'habitica-web',
|
||||
});
|
||||
});
|
||||
|
||||
describe('trackRegistrationEvent', () => {
|
||||
afterEach(async () => {
|
||||
await RegistrationEventModel.deleteMany({});
|
||||
});
|
||||
|
||||
it('creates a registration event when a user registers', async () => {
|
||||
user._id = '00000000-0000-0000-0000-000000000001';
|
||||
await localAnalytics.trackRegistrationEvent({ user, ipAddress: '127.0.0.1' });
|
||||
|
||||
const registrationEvents = await RegistrationEventModel.find({ userId: user._id });
|
||||
expect(registrationEvents).to.have.lengthOf(1);
|
||||
expect(registrationEvents[0]).to.have.property('userId', user._id);
|
||||
expect(registrationEvents[0]).to.have.property('ipAddress', '127.0.0.1');
|
||||
});
|
||||
|
||||
it('saves the correct data to the database', async () => {
|
||||
user._id = '00000000-0000-0000-0000-000000000002';
|
||||
user.auth.google = { id: 'abc', emails: [{ value: 'email@example.com' }] };
|
||||
await localAnalytics.trackRegistrationEvent({ user, ipAddress: '127.0.0.2' });
|
||||
|
||||
const registrationEvent = await RegistrationEventModel.findOne({ userId: user._id });
|
||||
expect(registrationEvent).to.have.property('userId', user._id);
|
||||
expect(registrationEvent).to.have.property('ipAddress', '127.0.0.2');
|
||||
expect(registrationEvent).to.have.property('authenticationMethod', 'google');
|
||||
});
|
||||
});
|
||||
|
||||
describe('trackSubscriptionEvent', () => {
|
||||
afterEach(async () => {
|
||||
await SubscriptionEventModel.deleteMany({});
|
||||
});
|
||||
|
||||
it('creates a subscription event when a user subscribes', async () => {
|
||||
user._id = '00000000-0000-0000-0000-000000000003';
|
||||
await localAnalytics.trackSubscriptionEvent({
|
||||
eventType: 'subscribed',
|
||||
user,
|
||||
paymentMethod: 'stripe',
|
||||
customerId: 'cus_123',
|
||||
planId: 'plan_123',
|
||||
});
|
||||
|
||||
const subscriptionEvents = await SubscriptionEventModel.find({ userId: user._id });
|
||||
expect(subscriptionEvents).to.have.lengthOf(1);
|
||||
expect(subscriptionEvents[0]).to.have.property('userId', user._id);
|
||||
expect(subscriptionEvents[0]).to.have.property('eventType', 'subscribed');
|
||||
expect(subscriptionEvents[0]).to.have.property('paymentMethod', 'stripe');
|
||||
expect(subscriptionEvents[0]).to.have.property('customerId', 'cus_123');
|
||||
expect(subscriptionEvents[0]).to.have.property('planId', 'plan_123');
|
||||
});
|
||||
|
||||
it('creates a subscription event with cancellation reason when a user cancels', async () => {
|
||||
user._id = '00000000-0000-0000-0000-000000000004';
|
||||
await localAnalytics.trackSubscriptionEvent({
|
||||
eventType: 'cancelled',
|
||||
user,
|
||||
paymentMethod: 'stripe',
|
||||
customerId: 'cus_456',
|
||||
planId: 'plan_456',
|
||||
cancellationReason: 'No longer needed',
|
||||
});
|
||||
|
||||
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id });
|
||||
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||
expect(subscriptionEvent).to.have.property('eventType', 'cancelled');
|
||||
expect(subscriptionEvent).to.have.property('paymentMethod', 'stripe');
|
||||
expect(subscriptionEvent).to.have.property('customerId', 'cus_456');
|
||||
expect(subscriptionEvent).to.have.property('planId', 'plan_456');
|
||||
expect(subscriptionEvent).to.have.property('cancellationReason', 'No longer needed');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -66,13 +66,15 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = 'customer-id';
|
||||
group.purchased.plan.planId = subKey;
|
||||
group.purchased.plan.lastBillingDate = new Date();
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
subscriptionBlock = common.content.subscriptionBlocks[subKey];
|
||||
subscriptionLength = subscriptionBlock.months * 30;
|
||||
|
||||
@@ -30,12 +30,14 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = 'customer-id';
|
||||
group.purchased.plan.planId = subKey;
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
amount = common.content.subscriptionBlocks[subKey].price;
|
||||
billingAgreementId = 'billingAgreementId';
|
||||
@@ -246,11 +248,6 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
user.guilds.push(groupId);
|
||||
await user.save();
|
||||
|
||||
// Add existing users
|
||||
user = new User();
|
||||
user.guilds.push(groupId);
|
||||
await user.save();
|
||||
|
||||
// Set expected amount
|
||||
sub.key = 'group_monthly';
|
||||
sub.price = 9;
|
||||
|
||||
@@ -128,11 +128,12 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(publicGroup.purchased.plan.planId).to.not.exist;
|
||||
data.groupId = publicGroup._id;
|
||||
|
||||
// Public Guilds are no longer even findable
|
||||
await expect(api.createSubscription(data))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: i18n.t('onlyPrivateGuildsCanUpgrade'),
|
||||
httpCode: 404,
|
||||
name: 'NotFound',
|
||||
message: i18n.t('groupNotFound'),
|
||||
});
|
||||
|
||||
const updatedGroup = await Group.findById(publicGroup._id).exec();
|
||||
|
||||
@@ -3,7 +3,6 @@ import moment from 'moment';
|
||||
import * as sender from '../../../../../website/server/libs/email';
|
||||
import common from '../../../../../website/common';
|
||||
import api from '../../../../../website/server/libs/payments/payments';
|
||||
import * as analytics from '../../../../../website/server/libs/analyticsService';
|
||||
import * as notifications from '../../../../../website/server/libs/pushNotifications';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { translate as t } from '../../../../helpers/api-integration/v3';
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
import * as worldState from '../../../../../website/server/libs/worldState';
|
||||
import { TransactionModel } from '../../../../../website/server/models/transaction';
|
||||
import { REPEATING_EVENTS } from '../../../../../website/common/script/content/constants/events';
|
||||
import { SubscriptionEventModel } from '../../../../../website/server/models/analytics/subscriptionEvent';
|
||||
|
||||
describe('payments/index', () => {
|
||||
let user;
|
||||
@@ -36,8 +36,6 @@ describe('payments/index', () => {
|
||||
|
||||
sandbox.stub(sender, 'sendTxn');
|
||||
sandbox.stub(user, 'sendMessage');
|
||||
sandbox.stub(analytics.mockAnalyticsService, 'trackPurchase');
|
||||
sandbox.stub(analytics.mockAnalyticsService, 'track');
|
||||
sandbox.stub(notifications, 'sendNotification');
|
||||
|
||||
data = {
|
||||
@@ -97,6 +95,16 @@ describe('payments/index', () => {
|
||||
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||
});
|
||||
|
||||
it('tracks subscription events', async () => {
|
||||
await api.createSubscription(data);
|
||||
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: recipient._id });
|
||||
expect(subscriptionEvent).to.exist;
|
||||
expect(subscriptionEvent).to.have.property('eventType', 'subscribed');
|
||||
expect(subscriptionEvent).to.have.property('userId', recipient._id);
|
||||
expect(subscriptionEvent).to.have.property('planId', 'basic_3mo');
|
||||
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||
});
|
||||
|
||||
it('adds extra months to an existing subscription', async () => {
|
||||
recipient.purchased.plan = plan;
|
||||
|
||||
@@ -298,28 +306,6 @@ describe('payments/index', () => {
|
||||
expect(notifications.sendNotification).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('tracks subscription purchase as gift', async () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledOnce;
|
||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledWith({
|
||||
uuid: user._id,
|
||||
groupId: undefined,
|
||||
itemPurchased: 'Subscription',
|
||||
sku: 'payment method-subscription',
|
||||
purchaseType: 'subscribe',
|
||||
paymentMethod: data.paymentMethod,
|
||||
quantity: 1,
|
||||
gift: true,
|
||||
purchaseValue: 15,
|
||||
firstPurchase: true,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
context('No Active Promotion', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(worldState, 'getCurrentEventList').returns([]);
|
||||
@@ -455,6 +441,16 @@ describe('payments/index', () => {
|
||||
expect(user.purchased.plan.dateCreated).to.exist;
|
||||
});
|
||||
|
||||
it('tracks subscription events', async () => {
|
||||
await api.createSubscription(data);
|
||||
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id });
|
||||
expect(subscriptionEvent).to.exist;
|
||||
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||
expect(subscriptionEvent).to.have.property('ipAddress');
|
||||
expect(subscriptionEvent).to.have.property('planId', 'basic_3mo');
|
||||
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||
});
|
||||
|
||||
it('sets plan.dateCreated if it did not previously exist', async () => {
|
||||
expect(user.purchased.plan.dateCreated).to.not.exist;
|
||||
|
||||
@@ -543,29 +539,24 @@ describe('payments/index', () => {
|
||||
expect(sender.sendTxn).to.be.calledWith(data.user, 'subscription-begins');
|
||||
});
|
||||
|
||||
it('tracks subscription purchase', async () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledOnce;
|
||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledWith({
|
||||
uuid: user._id,
|
||||
groupId: undefined,
|
||||
itemPurchased: 'Subscription',
|
||||
sku: 'payment method-subscription',
|
||||
purchaseType: 'subscribe',
|
||||
paymentMethod: data.paymentMethod,
|
||||
quantity: 1,
|
||||
gift: false,
|
||||
purchaseValue: 15,
|
||||
firstPurchase: true,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
context('Upgrades subscription', () => {
|
||||
it('tracks subscription events', async () => {
|
||||
data.sub.key = 'basic_earned';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
data.sub.key = 'basic_6mo';
|
||||
data.updatedFrom = { key: 'basic_earned' };
|
||||
await api.createSubscription(data);
|
||||
|
||||
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id, planId: 'basic_6mo' });
|
||||
expect(subscriptionEvent).to.exist;
|
||||
expect(subscriptionEvent).to.have.property('eventType', 'upgraded');
|
||||
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||
});
|
||||
|
||||
it('from basic_earned to basic_6mo', async () => {
|
||||
data.sub.key = 'basic_earned';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
@@ -608,6 +599,23 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
context('Downgrades subscription', () => {
|
||||
it('tracks subscription events', async () => {
|
||||
data.sub.key = 'basic_6mo';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
data.sub.key = 'basic_earned';
|
||||
data.updatedFrom = { key: 'basic_6mo' };
|
||||
await api.createSubscription(data);
|
||||
|
||||
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id, planId: 'basic_earned' });
|
||||
expect(subscriptionEvent).to.exist;
|
||||
expect(subscriptionEvent).to.have.property('eventType', 'downgraded');
|
||||
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||
});
|
||||
|
||||
it('from basic_6mo to basic_earned', async () => {
|
||||
data.sub.key = 'basic_6mo';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
@@ -1136,6 +1144,15 @@ describe('payments/index', () => {
|
||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||
});
|
||||
|
||||
it('tracks subscription events', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id });
|
||||
expect(subscriptionEvent).to.exist;
|
||||
expect(subscriptionEvent).to.have.property('eventType', 'cancelled');
|
||||
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||
});
|
||||
|
||||
it('adds extraMonths to dateTerminated value', async () => {
|
||||
user.purchased.plan.extraMonths = 2;
|
||||
|
||||
|
||||
@@ -30,13 +30,15 @@ describe('paypal - subscribeCancel', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = groupCustomerId;
|
||||
group.purchased.plan.planId = subKey;
|
||||
group.purchased.plan.lastBillingDate = new Date();
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
nextBillingDate = new Date();
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('Stripe - Checkout', () => {
|
||||
const group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
const groupId = group._id;
|
||||
@@ -376,11 +376,13 @@ describe('Stripe - Checkout', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
groupId = group._id;
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
});
|
||||
|
||||
it('throws if user is not allowed to change group plan', async () => {
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('Stripe - Subscriptions', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
groupId = group._id;
|
||||
@@ -315,12 +315,14 @@ describe('Stripe - Subscriptions', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = 'customer-id';
|
||||
group.purchased.plan.planId = subKey;
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
groupId = group._id;
|
||||
});
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/* eslint-disable global-require */
|
||||
import nconf from 'nconf';
|
||||
import requireAgain from 'require-again';
|
||||
import {
|
||||
generateRes,
|
||||
generateReq,
|
||||
generateNext,
|
||||
} from '../../../helpers/api-unit.helper';
|
||||
import * as analyticsService from '../../../../website/server/libs/analyticsService';
|
||||
|
||||
describe('analytics middleware', () => {
|
||||
let res; let req; let
|
||||
next;
|
||||
const pathToAnalyticsMiddleware = '../../../../website/server/middlewares/analytics';
|
||||
|
||||
beforeEach(() => {
|
||||
res = generateRes();
|
||||
req = generateReq();
|
||||
next = generateNext();
|
||||
});
|
||||
|
||||
it('attaches analytics object to res', () => {
|
||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
||||
|
||||
attachAnalytics(req, res, next);
|
||||
|
||||
expect(res.analytics).to.exist;
|
||||
});
|
||||
|
||||
it('attaches stubbed methods for non-prod environments', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(false);
|
||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
||||
|
||||
attachAnalytics(req, res, next);
|
||||
|
||||
expect(res.analytics.track).to.eql(analyticsService.mockAnalyticsService.track);
|
||||
expect(res.analytics.trackPurchase).to.eql(analyticsService.mockAnalyticsService.trackPurchase);
|
||||
});
|
||||
|
||||
it('attaches real methods for prod environments', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
||||
|
||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
||||
|
||||
attachAnalytics(req, res, next);
|
||||
|
||||
expect(res.analytics.track).to.eql(analyticsService.track);
|
||||
expect(res.analytics.trackPurchase).to.eql(analyticsService.trackPurchase);
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
SPAM_MESSAGE_LIMIT,
|
||||
SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
|
||||
SPAM_WINDOW_LENGTH,
|
||||
MAX_CHAT_COUNT,
|
||||
MAX_SUBBED_GROUP_CHAT_COUNT,
|
||||
INVITES_LIMIT,
|
||||
model as Group,
|
||||
} from '../../../../website/server/models/group';
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
import * as email from '../../../../website/server/libs/email';
|
||||
import { TAVERN_ID } from '../../../../website/common/script/constants';
|
||||
import shared from '../../../../website/common';
|
||||
import { chatModel as Chat } from '../../../../website/server/models/message';
|
||||
|
||||
describe('Group Model', () => {
|
||||
let party; let questLeader; let participatingMember;
|
||||
@@ -1356,6 +1359,29 @@ describe('Group Model', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getEffectiveChatLimit', () => {
|
||||
it('returns the correct chat limit', () => {
|
||||
const group = new Group();
|
||||
expect(group.getEffectiveChatLimit()).to.eql(MAX_CHAT_COUNT);
|
||||
});
|
||||
|
||||
it('returns the passed limit if it is lower than the max', () => {
|
||||
const group = new Group();
|
||||
expect(group.getEffectiveChatLimit(10)).to.eql(10);
|
||||
});
|
||||
|
||||
it('returns the max if the passed limit is higher', () => {
|
||||
const group = new Group();
|
||||
expect(group.getEffectiveChatLimit(MAX_CHAT_COUNT + 10)).to.eql(MAX_CHAT_COUNT);
|
||||
});
|
||||
|
||||
it('returns the max for group plans', () => {
|
||||
const group = new Group();
|
||||
group.purchased.plan.customerId = '110002222333';
|
||||
expect(group.getEffectiveChatLimit()).to.eql(MAX_SUBBED_GROUP_CHAT_COUNT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#sendChat', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.spy(User, 'updateOne');
|
||||
@@ -1462,6 +1488,34 @@ describe('Group Model', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trimChat', () => {
|
||||
it('Only checks last message when not enough messages to trim', async () => {
|
||||
sandbox.spy(Chat, 'find');
|
||||
sandbox.spy(Chat, 'deleteMany');
|
||||
await Chat.insertOne({ groupId: party._id, timestamp: new Date() });
|
||||
await Chat.insertOne({ groupId: party._id, timestamp: new Date() });
|
||||
await Chat.insertOne({ groupId: party._id, timestamp: new Date() });
|
||||
await party.trimChat();
|
||||
|
||||
expect(Chat.find).to.be.calledOnce;
|
||||
expect(Chat.deleteMany).to.not.be.called;
|
||||
expect(await Chat.countDocuments({ groupId: party._id })).to.eql(3);
|
||||
});
|
||||
it('Deletes messages over the limit', async () => {
|
||||
sandbox.spy(Chat, 'find');
|
||||
sandbox.spy(Chat, 'deleteMany');
|
||||
await Chat.insertOne({ groupId: party._id, timestamp: new Date() });
|
||||
await Chat.insertOne({ groupId: party._id, timestamp: new Date() });
|
||||
await Chat.insertOne({ groupId: party._id, timestamp: new Date() });
|
||||
|
||||
await party.trimChat(1);
|
||||
|
||||
expect(Chat.find).to.be.calledOnce;
|
||||
expect(Chat.deleteMany).to.be.calledOnce;
|
||||
expect(await Chat.countDocuments({ groupId: party._id })).to.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#startQuest', () => {
|
||||
context('Failure Conditions', () => {
|
||||
it('throws an error if group is not a party', async () => {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
requester,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { mockAnalyticsService as analytics } from '../../../../../website/server/libs/analyticsService';
|
||||
|
||||
describe('POST /analytics/track/:eventName', () => {
|
||||
it('calls res.analytics', async () => {
|
||||
const user = await generateUser();
|
||||
sandbox.spy(analytics, 'track');
|
||||
|
||||
const requestWithHeaders = requester(user, { 'x-client': 'habitica-web' });
|
||||
await requestWithHeaders.post('/analytics/track/eventName', { data: 'example' }, { 'x-client': 'habitica-web' });
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
expect(analytics.track).to.be.calledWith('eventName', sandbox.match({ data: 'example' }));
|
||||
|
||||
sandbox.restore();
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
import { TAVERN_ID } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('POST /challenges/:challengeId/join', () => {
|
||||
it('returns error when challengeId is not a valid UUID', async () => {
|
||||
@@ -27,6 +29,37 @@ describe('POST /challenges/:challengeId/join', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('public Guild', () => {
|
||||
let group;
|
||||
let groupLeader;
|
||||
let members;
|
||||
let challenge;
|
||||
before(async () => {
|
||||
({ group, groupLeader, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
upgradeToGroupPlan: true,
|
||||
}));
|
||||
challenge = await generateChallenge(groupLeader, group);
|
||||
// Creation API is shut down, we need to simulate an extant public group
|
||||
await Group.updateOne({ _id: group._id }, { $set: { privacy: 'public' }, $unset: { 'purchased.plan': 1 } }).exec();
|
||||
});
|
||||
|
||||
it('returns error when challengeId is in an old public Guild', async () => {
|
||||
const authorizedUser = members[0]; // eslint-disable-line prefer-destructuring
|
||||
|
||||
await expect(authorizedUser.post(`/challenges/${challenge._id}/join`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('challengeNotFound'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Joining a valid challenge', () => {
|
||||
let groupLeader;
|
||||
let group;
|
||||
@@ -66,6 +99,15 @@ describe('POST /challenges/:challengeId/join', () => {
|
||||
expect(res.name).to.equal(challenge.name);
|
||||
});
|
||||
|
||||
it('succeeds when it\'s a Tavern challenge, even if the user isn\'t a "member" of Tavern', async () => {
|
||||
const tavern = await groupLeader.get(`/groups/${TAVERN_ID}`);
|
||||
const tavernChallenge = await generateChallenge(groupLeader, tavern, { prize: 1 });
|
||||
const generalUser = await generateUser();
|
||||
|
||||
const res = await generalUser.post(`/challenges/${tavernChallenge._id}/join`);
|
||||
expect(res.name).to.equal(tavernChallenge.name);
|
||||
});
|
||||
|
||||
it('returns challenge data', async () => {
|
||||
const res = await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
||||
|
||||
|
||||
@@ -62,9 +62,9 @@ describe('GET /groups/:groupId/chat', () => {
|
||||
|
||||
it('returns error if user attempts to fetch a sunset Guild', async () => {
|
||||
await expect(user.get(`/groups/${group._id}/chat`)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('featureRetired'),
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('groupNotFound'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,9 +121,9 @@ describe('POST /chat/:chatId/like', () => {
|
||||
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat/${message.message.id}/like`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('featureRetired'),
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('groupNotFound'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,6 +91,23 @@ describe('POST /groups/:groupId/quests/accept', () => {
|
||||
expect(partyMembers[0].party.quest.RSVPNeeded).to.be.false;
|
||||
});
|
||||
|
||||
it('heals stuck RSVPNeeded when group already has the user accepted', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
await partyMembers[0].updateOne({ 'party.quest.RSVPNeeded': true });
|
||||
await partyMembers[0].sync();
|
||||
expect(partyMembers[0].party.quest.RSVPNeeded).to.be.true;
|
||||
|
||||
const res = await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
expect(res).to.exist;
|
||||
|
||||
await partyMembers[0].sync();
|
||||
await questingGroup.sync();
|
||||
expect(partyMembers[0].party.quest.RSVPNeeded).to.equal(false);
|
||||
expect(questingGroup.quest.members[partyMembers[0]._id]).to.equal(true);
|
||||
});
|
||||
|
||||
it('does not accept invite for a quest already underway', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
@@ -100,6 +100,23 @@ describe('POST /groups/:groupId/quests/reject', () => {
|
||||
expect(partyMembers[0].party.quest.RSVPNeeded).to.be.false;
|
||||
});
|
||||
|
||||
it('heals stuck RSVPNeeded when group already has the user rejected', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`);
|
||||
|
||||
await partyMembers[0].updateOne({ 'party.quest.RSVPNeeded': true });
|
||||
await partyMembers[0].sync();
|
||||
expect(partyMembers[0].party.quest.RSVPNeeded).to.be.true;
|
||||
|
||||
const res = await partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`);
|
||||
expect(res).to.exist;
|
||||
|
||||
await partyMembers[0].sync();
|
||||
await questingGroup.sync();
|
||||
expect(partyMembers[0].party.quest.RSVPNeeded).to.equal(false);
|
||||
expect(questingGroup.quest.members[partyMembers[0]._id]).to.equal(false);
|
||||
});
|
||||
|
||||
it('return an error when a user rejects an invite already accepted', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { mockAnalyticsService as analytics } from '../../../../../website/server/libs/analyticsService';
|
||||
|
||||
describe('POST /user/sleep', () => {
|
||||
let user;
|
||||
@@ -23,15 +22,4 @@ describe('POST /user/sleep', () => {
|
||||
await user.sync();
|
||||
expect(user.preferences.sleep).to.be.false;
|
||||
});
|
||||
|
||||
it('sends sleep status to analytics service', async () => {
|
||||
sandbox.spy(analytics, 'track');
|
||||
|
||||
await user.post('/user/sleep');
|
||||
await user.sync();
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
expect(analytics.track).to.be.calledWith('sleep', sandbox.match.has('status', user.preferences.sleep));
|
||||
|
||||
sandbox.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { ApiUser } from '../../../../../helpers/api-integration/api-classes';
|
||||
import { encrypt } from '../../../../../../website/server/libs/encryption';
|
||||
import { RegistrationEventModel } from '../../../../../../website/server/models/analytics/registrationEvent';
|
||||
|
||||
function generateRandomUserName () {
|
||||
return (Date.now() + uuid()).substring(0, 20);
|
||||
@@ -41,6 +42,25 @@ describe('POST /user/auth/local/register', () => {
|
||||
expect(user.newUser).to.eql(true);
|
||||
});
|
||||
|
||||
it('tracks a registration event', async () => {
|
||||
const username = generateRandomUserName();
|
||||
const email = `${username}@example.com`;
|
||||
const password = 'password';
|
||||
|
||||
const user = await api.post('/user/auth/local/register', {
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
confirmPassword: password,
|
||||
});
|
||||
|
||||
const registrationEvent = await RegistrationEventModel.findOne({ userId: user._id });
|
||||
expect(registrationEvent).to.exist;
|
||||
expect(registrationEvent).to.have.property('userId', user._id);
|
||||
expect(registrationEvent).to.have.property('ipAddress');
|
||||
expect(registrationEvent).to.have.property('authenticationMethod', 'local');
|
||||
});
|
||||
|
||||
it('registers a new user and sets verifiedUsername to true', async () => {
|
||||
const username = generateRandomUserName();
|
||||
const email = `${username}@example.com`;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getProperty,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import apiErrorMessages from '../../../../../../website/common/script/errors/apiErrorMessages';
|
||||
import { RegistrationEventModel } from '../../../../../../website/server/models/analytics/registrationEvent';
|
||||
|
||||
describe('POST /user/auth/social', () => {
|
||||
let api;
|
||||
@@ -65,6 +66,19 @@ describe('POST /user/auth/social', () => {
|
||||
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
|
||||
});
|
||||
|
||||
it('tracks a registration event', async () => {
|
||||
const socialUser = await api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
network,
|
||||
});
|
||||
|
||||
const registrationEvent = await RegistrationEventModel.findOne({ userId: socialUser.id });
|
||||
expect(registrationEvent).to.exist;
|
||||
expect(registrationEvent).to.have.property('userId', socialUser.id);
|
||||
expect(registrationEvent).to.have.property('ipAddress');
|
||||
expect(registrationEvent).to.have.property('authenticationMethod', 'google');
|
||||
});
|
||||
|
||||
it('includes sanitized version of provided username', async () => {
|
||||
const response = await api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
@@ -231,6 +245,17 @@ describe('POST /user/auth/social', () => {
|
||||
expect(response.newUser).to.be.false;
|
||||
});
|
||||
|
||||
it('does not track a registration event for existing users', async () => {
|
||||
const beforeEvents = await RegistrationEventModel.find({ userId: user._id });
|
||||
await user.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
network,
|
||||
});
|
||||
|
||||
const registrationEvents = await RegistrationEventModel.find({ userId: user._id });
|
||||
expect(registrationEvents).to.have.lengthOf(beforeEvents.length);
|
||||
});
|
||||
|
||||
it('does not log into other account if social auth already exists', async () => {
|
||||
const registerResponse = await api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
|
||||
@@ -13,7 +13,6 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
||||
|
||||
describe('shared.ops.buy', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
@@ -32,12 +31,6 @@ describe('shared.ops.buy', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('returns error when key is not provided', async () => {
|
||||
@@ -51,10 +44,8 @@ describe('shared.ops.buy', () => {
|
||||
|
||||
it('buys health potion', async () => {
|
||||
user.stats.hp = 30;
|
||||
await buy(user, { params: { key: 'potion' } }, analytics);
|
||||
await buy(user, { params: { key: 'potion' } });
|
||||
expect(user.stats.hp).to.eql(45);
|
||||
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('adds equipment to inventory', async () => {
|
||||
|
||||
@@ -29,10 +29,9 @@ describe('shared.ops.buyArmoire', () => {
|
||||
const YIELD_EQUIPMENT = 0.5;
|
||||
const YIELD_FOOD = 0.7;
|
||||
const YIELD_EXP = 0.9;
|
||||
const analytics = { track () {} };
|
||||
|
||||
async function buyArmoire (_user, _req, _analytics) {
|
||||
const buyOp = new BuyArmoireOperation(_user, _req, _analytics);
|
||||
async function buyArmoire (_user, _req) {
|
||||
const buyOp = new BuyArmoireOperation(_user, _req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
@@ -50,12 +49,10 @@ describe('shared.ops.buyArmoire', () => {
|
||||
user.items.food = {};
|
||||
|
||||
sandbox.stub(randomValFns, 'trueRandom');
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
randomValFns.trueRandom.restore();
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
@@ -147,7 +144,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||
|
||||
await buyArmoire(user, {}, analytics);
|
||||
await buyArmoire(user, {});
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(3);
|
||||
|
||||
@@ -155,7 +152,6 @@ describe('shared.ops.buyArmoire', () => {
|
||||
|
||||
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
||||
expect(user.stats.gp).to.eql(100);
|
||||
expect(analytics.track).to.be.calledTwice;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
@@ -11,15 +10,14 @@ import i18n from '../../../../website/common/script/i18n';
|
||||
import { BuyGemOperation } from '../../../../website/common/script/ops/buy/buyGem';
|
||||
import planGemLimits from '../../../../website/common/script/libs/planGemLimits';
|
||||
|
||||
async function buyGem (user, req, analytics) {
|
||||
const buyOp = new BuyGemOperation(user, req, analytics);
|
||||
async function buyGem (user, req) {
|
||||
const buyOp = new BuyGemOperation(user, req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
|
||||
describe('shared.ops.buyGem', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
const goldPoints = 40;
|
||||
const gemsBought = 40;
|
||||
const userGemAmount = 10;
|
||||
@@ -35,23 +33,16 @@ describe('shared.ops.buyGem', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('Gems', () => {
|
||||
it('purchases gems', async () => {
|
||||
const [, message] = await buyGem(user, { params: { type: 'gems', key: 'gem' } }, analytics);
|
||||
const [, message] = await buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
||||
|
||||
expect(message).to.equal(i18n.t('plusGem', { count: 1 }));
|
||||
expect(user.balance).to.equal(userGemAmount + 0.25);
|
||||
expect(user.purchased.plan.gemsBought).to.equal(1);
|
||||
expect(user.stats.gp).to.equal(goldPoints - planGemLimits.convRate);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('purchases gems with a different language than the default', async () => {
|
||||
|
||||
@@ -10,10 +10,9 @@ import i18n from '../../../../website/common/script/i18n';
|
||||
|
||||
describe('shared.ops.buyHealthPotion', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
|
||||
async function buyHealthPotion (_user, _req, _analytics) {
|
||||
const buyOp = new BuyHealthPotionOperation(_user, _req, _analytics);
|
||||
async function buyHealthPotion (_user, _req) {
|
||||
const buyOp = new BuyHealthPotionOperation(_user, _req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
@@ -32,19 +31,13 @@ describe('shared.ops.buyHealthPotion', () => {
|
||||
},
|
||||
stats: { gp: 200 },
|
||||
});
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('Potion', () => {
|
||||
it('recovers 15 hp', async () => {
|
||||
user.stats.hp = 30;
|
||||
await buyHealthPotion(user, {}, analytics);
|
||||
await buyHealthPotion(user, {});
|
||||
expect(user.stats.hp).to.eql(45);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('does not increase hp above 50', async () => {
|
||||
|
||||
@@ -13,15 +13,14 @@ import {
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import { errorMessage } from '../../../../website/common/script/libs/errorMessage';
|
||||
|
||||
async function buyGear (user, req, analytics) {
|
||||
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
||||
async function buyGear (user, req) {
|
||||
const buyOp = new BuyMarketGearOperation(user, req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
|
||||
describe('shared.ops.buyMarketGear', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
let clock;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -47,14 +46,12 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
sinon.stub(shared, 'randomVal');
|
||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||
sinon.stub(shared.fns, 'predictableRandom');
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.randomVal.restore();
|
||||
shared.fns.predictableRandom.restore();
|
||||
shared.onboarding.checkOnboardingStatus.restore();
|
||||
analytics.track.restore();
|
||||
|
||||
if (clock) {
|
||||
clock.restore();
|
||||
@@ -65,7 +62,7 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
it('adds equipment to inventory', async () => {
|
||||
user.stats.gp = 31;
|
||||
|
||||
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
weapon_warrior_0: true,
|
||||
@@ -92,13 +89,12 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
eyewear_special_whiteHalfMoon: true,
|
||||
eyewear_special_yellowHalfMoon: true,
|
||||
});
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('adds the onboarding achievement to the user and checks the onboarding status', async () => {
|
||||
user.stats.gp = 31;
|
||||
|
||||
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||
|
||||
expect(user.addAchievement).to.be.calledOnce;
|
||||
expect(user.addAchievement).to.be.calledWith('purchasedEquipment');
|
||||
@@ -111,7 +107,7 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
user.stats.gp = 31;
|
||||
user.achievements.purchasedEquipment = true;
|
||||
|
||||
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||
|
||||
expect(user.addAchievement).to.not.be.called;
|
||||
});
|
||||
|
||||
@@ -14,7 +14,6 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
||||
|
||||
describe('shared.ops.buyMysterySet', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
let clock;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -27,11 +26,9 @@ describe('shared.ops.buyMysterySet', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
if (clock) {
|
||||
clock.restore();
|
||||
}
|
||||
@@ -93,7 +90,7 @@ describe('shared.ops.buyMysterySet', () => {
|
||||
context('successful purchases', () => {
|
||||
it('buys Steampunk Accessories Set', async () => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
await buyMysterySet(user, { params: { key: '301404' } }, analytics);
|
||||
await buyMysterySet(user, { params: { key: '301404' } });
|
||||
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
||||
@@ -106,7 +103,7 @@ describe('shared.ops.buyMysterySet', () => {
|
||||
it('buys mystery set if it is available', async () => {
|
||||
clock = sinon.useFakeTimers(new Date('2024-01-16'));
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
await buyMysterySet(user, { params: { key: '201601' } }, analytics);
|
||||
await buyMysterySet(user, { params: { key: '201601' } });
|
||||
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||
expect(user.items.gear.owned).to.have.property('shield_mystery_201601', true);
|
||||
|
||||
@@ -12,10 +12,9 @@ describe('shared.ops.buyQuestGems', () => {
|
||||
let user;
|
||||
let clock;
|
||||
const goldPoints = 40;
|
||||
const analytics = { track () {} };
|
||||
|
||||
async function buyQuest (_user, _req, _analytics) {
|
||||
const buyOp = new BuyQuestWithGemOperation(_user, _req, _analytics);
|
||||
async function buyQuest (_user, _req) {
|
||||
const buyOp = new BuyQuestWithGemOperation(_user, _req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
@@ -25,13 +24,11 @@ describe('shared.ops.buyQuestGems', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(analytics, 'track');
|
||||
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
||||
clock = sinon.useFakeTimers(new Date('2024-01-16'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
pinnedGearUtils.removeItemByPath.restore();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
@@ -12,21 +12,15 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
||||
|
||||
describe('shared.ops.buyQuest', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
|
||||
async function buyQuest (_user, _req, _analytics) {
|
||||
const buyOp = new BuyQuestWithGoldOperation(_user, _req, _analytics);
|
||||
async function buyQuest (_user, _req) {
|
||||
const buyOp = new BuyQuestWithGoldOperation(_user, _req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('buys a Quest scroll', async () => {
|
||||
@@ -35,12 +29,11 @@ describe('shared.ops.buyQuest', () => {
|
||||
params: {
|
||||
key: 'dilatoryDistress1',
|
||||
},
|
||||
}, analytics);
|
||||
});
|
||||
expect(user.items.quests).to.eql({
|
||||
dilatoryDistress1: 1,
|
||||
});
|
||||
expect(user.stats.gp).to.equal(5);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', async () => {
|
||||
@@ -49,10 +42,9 @@ describe('shared.ops.buyQuest', () => {
|
||||
user.items.quests[key] = -1;
|
||||
await buyQuest(user, {
|
||||
params: { key },
|
||||
}, analytics);
|
||||
});
|
||||
expect(user.items.quests[key]).to.equal(1);
|
||||
expect(user.stats.gp).to.equal(5);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('buys a Quest scroll with the right quantity if a string is passed for quantity', async () => {
|
||||
@@ -61,13 +53,13 @@ describe('shared.ops.buyQuest', () => {
|
||||
params: {
|
||||
key: 'dilatoryDistress1',
|
||||
},
|
||||
}, analytics);
|
||||
});
|
||||
await buyQuest(user, {
|
||||
params: {
|
||||
key: 'dilatoryDistress1',
|
||||
},
|
||||
quantity: '3',
|
||||
}, analytics);
|
||||
});
|
||||
|
||||
expect(user.items.quests).to.eql({
|
||||
dilatoryDistress1: 4,
|
||||
@@ -82,7 +74,7 @@ describe('shared.ops.buyQuest', () => {
|
||||
key: 'dilatoryDistress1',
|
||||
},
|
||||
quantity: 'a',
|
||||
}, analytics);
|
||||
});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||
@@ -187,12 +179,11 @@ describe('shared.ops.buyQuest', () => {
|
||||
params: {
|
||||
key: 'dilatoryDistress3',
|
||||
},
|
||||
}, analytics);
|
||||
});
|
||||
|
||||
expect(user.items.quests).to.eql({
|
||||
dilatoryDistress3: 1,
|
||||
});
|
||||
expect(user.stats.gp).to.equal(100);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,20 +14,17 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
||||
describe('shared.ops.buySpecialSpell', () => {
|
||||
let user;
|
||||
let clock;
|
||||
const analytics = { track () {} };
|
||||
|
||||
async function buySpecialSpell (_user, _req, _analytics) {
|
||||
const buyOp = new BuySpellOperation(_user, _req, _analytics);
|
||||
async function buySpecialSpell (_user, _req) {
|
||||
const buyOp = new BuySpellOperation(_user, _req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
if (clock) {
|
||||
clock.restore();
|
||||
}
|
||||
@@ -78,7 +75,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
params: {
|
||||
key: 'thankyou',
|
||||
},
|
||||
}, analytics);
|
||||
});
|
||||
|
||||
expect(user.stats.gp).to.equal(1);
|
||||
expect(user.items.special.thankyou).to.equal(1);
|
||||
@@ -89,7 +86,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
expect(message).to.equal(i18n.t('messageBought', {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('buys a limited card when it is available', async () => {
|
||||
@@ -101,7 +97,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
params: {
|
||||
key: 'nye',
|
||||
},
|
||||
}, analytics);
|
||||
});
|
||||
|
||||
expect(user.stats.gp).to.equal(1);
|
||||
expect(user.items.special.nye).to.equal(1);
|
||||
@@ -112,7 +108,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
expect(message).to.equal(i18n.t('messageBought', {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('throws an error if the card is not currently available', async () => {
|
||||
@@ -140,7 +135,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
params: {
|
||||
key: 'seafoam',
|
||||
},
|
||||
}, analytics);
|
||||
});
|
||||
|
||||
expect(user.stats.gp).to.equal(1);
|
||||
expect(user.items.special.seafoam).to.equal(1);
|
||||
@@ -151,7 +146,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
expect(message).to.equal(i18n.t('messageBought', {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('throws an error if the spell is not currently available', async () => {
|
||||
|
||||
@@ -13,21 +13,15 @@ import { BuyHourglassMountOperation } from '../../../../website/common/script/op
|
||||
|
||||
describe('common.ops.hourglassPurchase', () => {
|
||||
let user;
|
||||
const analytics = { track () {} };
|
||||
|
||||
async function buyMount (_user, _req, _analytics) {
|
||||
const buyOp = new BuyHourglassMountOperation(_user, _req, _analytics);
|
||||
async function buyMount (_user, _req) {
|
||||
const buyOp = new BuyHourglassMountOperation(_user, _req);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
@@ -131,12 +125,11 @@ describe('common.ops.hourglassPurchase', () => {
|
||||
it('buys a pet', async () => {
|
||||
user.purchased.plan.consecutive.trinkets = 2;
|
||||
|
||||
const [, message] = await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } }, analytics);
|
||||
const [, message] = await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } });
|
||||
|
||||
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.pets).to.eql({ 'MantisShrimp-Base': 5 });
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('buys a mount', async () => {
|
||||
|
||||
@@ -17,20 +17,17 @@ describe('shared.ops.purchase', () => {
|
||||
let user;
|
||||
let clock;
|
||||
const goldPoints = 40;
|
||||
const analytics = { track () {} };
|
||||
|
||||
before(() => {
|
||||
user = generateUser({ 'stats.class': 'rogue' });
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(analytics, 'track');
|
||||
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
||||
clock = sandbox.useFakeTimers(new Date('2024-01-10'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
pinnedGearUtils.removeItemByPath.restore();
|
||||
clock.restore();
|
||||
});
|
||||
@@ -187,11 +184,10 @@ describe('shared.ops.purchase', () => {
|
||||
const type = 'eggs';
|
||||
const key = 'Wolf';
|
||||
|
||||
await purchase(user, { params: { type, key } }, analytics);
|
||||
await purchase(user, { params: { type, key } });
|
||||
|
||||
expect(user.items[type][key]).to.equal(1);
|
||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('purchases hatchingPotions', async () => {
|
||||
@@ -332,7 +328,7 @@ describe('shared.ops.purchase', () => {
|
||||
const key = 'Wolf';
|
||||
|
||||
try {
|
||||
await purchase(user, { params: { type, key }, quantity: 'jamboree' }, analytics);
|
||||
await purchase(user, { params: { type, key }, quantity: 'jamboree' });
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||
@@ -345,7 +341,7 @@ describe('shared.ops.purchase', () => {
|
||||
user.balance = 10;
|
||||
|
||||
try {
|
||||
await purchase(user, { params: { type, key }, quantity: -2 }, analytics);
|
||||
await purchase(user, { params: { type, key }, quantity: -2 });
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||
@@ -358,7 +354,7 @@ describe('shared.ops.purchase', () => {
|
||||
user.balance = 10;
|
||||
|
||||
try {
|
||||
await purchase(user, { params: { type, key }, quantity: 2.9 }, analytics);
|
||||
await purchase(user, { params: { type, key }, quantity: 2.9 });
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { getMatchingSwap, makeSubstitutionMap } from '../../website/common/script/content/constants/aprilFools';
|
||||
|
||||
describe('April Fools', () => {
|
||||
describe('getMatchingSwap', () => {
|
||||
it('returns Veggie for 2020', () => {
|
||||
const swap = getMatchingSwap(new Date('2020-04-01'));
|
||||
expect(swap).to.equal('Veggie');
|
||||
});
|
||||
it('returns Alien for 2026', () => {
|
||||
const swap = getMatchingSwap(new Date('2026-04-01'));
|
||||
expect(swap).to.equal('Alien');
|
||||
});
|
||||
it('Cycles through swaps correctly', () => {
|
||||
const swap = getMatchingSwap(new Date('2027-04-01'));
|
||||
expect(swap).to.equal('Veggie');
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSubstitutionMap', () => {
|
||||
it('returns correct substitution for Veggie', () => {
|
||||
const substitutions = makeSubstitutionMap('Veggie');
|
||||
expect(substitutions.pets['Pet-Wolf-']).to.equal('Pet-Wolf-Veggie');
|
||||
expect(substitutions.pets['Pet-TigerCub-']).to.equal('Pet-TigerCub-Veggie');
|
||||
expect(substitutions.pets['Pet-Yarn-']).to.equal('Pet-BearCub-Veggie');
|
||||
expect(substitutions.pets.default).to.equal('Pet-Dragon-Veggie');
|
||||
expect(substitutions.pets.noPet).to.equal('Pet-Wolf-Veggie');
|
||||
expect(substitutions.pets.noPetIOS).to.equal('Pet-TigerCub-Veggie');
|
||||
expect(substitutions.pets.noPetAndroid).to.equal('Pet-Cactus-Veggie');
|
||||
});
|
||||
|
||||
it('returns correct substitution for Cryptid', () => {
|
||||
const substitutions = makeSubstitutionMap('Cryptid');
|
||||
expect(substitutions.pets['Pet-Fox-']).to.equal('Pet-Fox-Cryptid');
|
||||
expect(substitutions.pets['Pet-FlyingPig-']).to.equal('Pet-FlyingPig-Cryptid');
|
||||
expect(substitutions.pets['Pet-Yarn-']).to.equal('Pet-BearCub-Cryptid');
|
||||
expect(substitutions.pets.default).to.equal('Pet-Dragon-Cryptid');
|
||||
expect(substitutions.pets.noPet).to.equal('Pet-Wolf-Cryptid');
|
||||
expect(substitutions.pets.noPetAndroid).to.equal('Pet-Cactus-Cryptid');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -54,19 +54,4 @@ describe('armoire', () => {
|
||||
const febuaryItems = armoire.all;
|
||||
expect(febuaryItems.length).to.equal(384);
|
||||
});
|
||||
|
||||
it('sets have at least 2 items', () => {
|
||||
const setMap = {};
|
||||
forEach(armoire.all, item => {
|
||||
// Gotta have one outlier
|
||||
if (!item.set || item.set.startsWith('armoire-')) return;
|
||||
if (setMap[item.set] === undefined) {
|
||||
setMap[item.set] = 0;
|
||||
}
|
||||
setMap[item.set] += 1;
|
||||
});
|
||||
Object.keys(setMap).forEach(set => {
|
||||
expect(setMap[set], set).to.be.at.least(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,7 +40,6 @@ function _requestMaker (user, method, additionalSets = {}) {
|
||||
|| route.indexOf('/paypal') === 0
|
||||
|| route.indexOf('/amazon') === 0
|
||||
|| route.indexOf('/stripe') === 0
|
||||
|| route.indexOf('/analytics') === 0
|
||||
) {
|
||||
url += `${route}`;
|
||||
} else {
|
||||
|
||||
@@ -12,20 +12,12 @@ module.exports = {
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
// TODO find a way to let eslint understand webpack aliases
|
||||
'import/no-unresolved': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/extensions': 'off',
|
||||
'prefer-regex-literals': 'warn',
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/no-mutating-props': 'warn',
|
||||
// this creates issues with the current way we have to push the process.env vars to webpack
|
||||
// https://github.com/eslint/eslint/issues/14918
|
||||
// https://github.com/webpack/webpack/issues/5392
|
||||
// off for now, because any eslint --fix will then still do it anyway
|
||||
// maybe this can be turned on again once we switch to newer vue/vite
|
||||
// Important! process.env.XYZ should not be destructured
|
||||
'prefer-destructuring': 'off',
|
||||
'vue/html-self-closing': ['error', {
|
||||
html: {
|
||||
void: 'never',
|
||||
|
||||
Generated
+20
-589
@@ -41,7 +41,6 @@
|
||||
"vite": "^6.3.6",
|
||||
"vite-plugin-compression2": "^1.3.3",
|
||||
"vue": "^2.7.10",
|
||||
"vue-fragment": "^1.6.0",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-router": "^3.6.5",
|
||||
"vuedraggable": "^2.24.3",
|
||||
@@ -55,9 +54,7 @@
|
||||
"jsdom": "^26.0.0",
|
||||
"mocha": "^11.1.0",
|
||||
"playwright": "^1.50.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"vitest": "^3.0.5",
|
||||
"webpack": "^5.94.0"
|
||||
"vitest": "^3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@amplitude/analytics-connector": {
|
||||
@@ -2111,8 +2108,9 @@
|
||||
"version": "0.3.11",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
|
||||
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
@@ -3634,41 +3632,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint-scope": {
|
||||
"version": "3.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
|
||||
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json5": {
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||
@@ -3679,8 +3648,9 @@
|
||||
"version": "24.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -3876,181 +3846,6 @@
|
||||
"vue-template-compiler": "^2.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
|
||||
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
|
||||
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
|
||||
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
|
||||
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
|
||||
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
|
||||
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ieee754": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
|
||||
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/leb128": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
|
||||
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/utf8": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
|
||||
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
|
||||
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-section": "1.14.1",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1",
|
||||
"@webassemblyjs/wasm-opt": "1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "1.14.1",
|
||||
"@webassemblyjs/wast-printer": "1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-gen": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
|
||||
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/ieee754": "1.13.2",
|
||||
"@webassemblyjs/leb128": "1.13.2",
|
||||
"@webassemblyjs/utf8": "1.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-opt": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
|
||||
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-parser": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
|
||||
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/ieee754": "1.13.2",
|
||||
"@webassemblyjs/leb128": "1.13.2",
|
||||
"@webassemblyjs/utf8": "1.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wast-printer": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
|
||||
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
@@ -4098,48 +3893,6 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/amplitude-js": {
|
||||
"version": "8.21.9",
|
||||
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.21.9.tgz",
|
||||
@@ -4617,8 +4370,9 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
@@ -4783,16 +4537,6 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/chrome-trace-event": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
|
||||
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
@@ -4859,8 +4603,9 @@
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
@@ -5196,20 +4941,6 @@
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enquirer": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
|
||||
@@ -6384,16 +6115,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
|
||||
@@ -6871,13 +6592,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
@@ -6921,13 +6635,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/habitica-markdown": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-4.1.0.tgz",
|
||||
@@ -7726,37 +7433,6 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-worker": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
|
||||
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"merge-stream": "^2.0.0",
|
||||
"supports-color": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-worker/node_modules/supports-color": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||
@@ -7841,13 +7517,6 @@
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
@@ -7911,20 +7580,6 @@
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/loader-runner": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
|
||||
"integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.11.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@@ -8128,13 +7783,6 @@
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
@@ -8458,13 +8106,6 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/neo-async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
@@ -9504,63 +9145,6 @@
|
||||
"node": ">=v12.22.7"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
|
||||
"integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"ajv": "^8.9.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"ajv-keywords": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv-keywords": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/secure-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz",
|
||||
@@ -9838,8 +9422,9 @@
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
@@ -10130,20 +9715,6 @@
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
|
||||
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-mini": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-mini/-/tar-mini-0.2.0.tgz",
|
||||
@@ -10154,8 +9725,9 @@
|
||||
"version": "5.44.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
|
||||
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.15.0",
|
||||
@@ -10169,47 +9741,13 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.14",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz",
|
||||
"integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^4.3.0",
|
||||
"serialize-javascript": "^6.0.2",
|
||||
"terser": "^5.31.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -10585,8 +10123,9 @@
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.4",
|
||||
@@ -10965,15 +10504,6 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-fragment": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-fragment/-/vue-fragment-1.6.0.tgz",
|
||||
"integrity": "sha512-a5T8ZZZK/EQzgVShEl374HbobUJ0a7v12BzOzS6Z/wd/5EE/5SffcyHC+7bf9hP3L7Yc0hhY/GhMdwFQ25O/8A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^2.5.16"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-functional-data-merge": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
|
||||
@@ -11041,20 +10571,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
|
||||
"integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
@@ -11065,91 +10581,6 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.102.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz",
|
||||
"integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-import-phases": "^1.0.3",
|
||||
"browserslist": "^4.26.3",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.17.3",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^4.3.3",
|
||||
"tapable": "^2.3.0",
|
||||
"terser-webpack-plugin": "^5.3.11",
|
||||
"watchpack": "^2.4.4",
|
||||
"webpack-sources": "^3.3.3"
|
||||
},
|
||||
"bin": {
|
||||
"webpack": "bin/webpack.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"webpack-cli": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-sources": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
|
||||
"integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/acorn-import-phases": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
|
||||
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"acorn": "^8.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-encoding": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
"vite": "^6.3.6",
|
||||
"vite-plugin-compression2": "^1.3.3",
|
||||
"vue": "^2.7.10",
|
||||
"vue-fragment": "^1.6.0",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-router": "^3.6.5",
|
||||
"vuedraggable": "^2.24.3",
|
||||
@@ -60,8 +59,6 @@
|
||||
"jsdom": "^26.0.0",
|
||||
"mocha": "^11.1.0",
|
||||
"playwright": "^1.50.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"vitest": "^3.0.5",
|
||||
"webpack": "^5.94.0"
|
||||
"vitest": "^3.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,15 @@
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
.quest_alien {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_alien.gif") no-repeat;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup,
|
||||
.Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi, .Pet_HatchingPotion_Cryptid {
|
||||
.Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi, .Pet_HatchingPotion_Cryptid,
|
||||
.Pet_HatchingPotion_Alien {
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
@@ -52,6 +59,10 @@
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Cryptid.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Alien {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Alien.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Gems {
|
||||
display:inline-block;
|
||||
margin-right:5px;
|
||||
|
||||
@@ -1801,6 +1801,11 @@
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_on_a_strange_planet {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_on_a_strange_planet.png');
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_on_tree_branch {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_on_tree_branch.png');
|
||||
width: 141px;
|
||||
@@ -31485,8 +31490,8 @@
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.slim_armor_armoire_handstandOutfit {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_handstandOutfit .png');
|
||||
.slim_armor_armoire_handstandOutfit {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_handstandOutfit.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -53223,6 +53228,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-BearCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-BearCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -53713,6 +53723,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Cactus-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Cactus-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Amber.png');
|
||||
width: 81px;
|
||||
@@ -54503,6 +54518,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Dragon-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Dragon-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Amber.png');
|
||||
width: 81px;
|
||||
@@ -54998,6 +55018,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-FlyingPig-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-FlyingPig-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Amber.png');
|
||||
width: 81px;
|
||||
@@ -55333,6 +55358,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Fox-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Fox-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Amber.png');
|
||||
width: 81px;
|
||||
@@ -56113,6 +56143,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-LionCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-LionCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -56718,6 +56753,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-PandaCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-PandaCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -58113,6 +58153,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-TigerCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-TigerCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -58758,6 +58803,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Wolf-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Wolf-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Amber.png');
|
||||
width: 81px;
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.d-content {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
* {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@@ -108,15 +108,15 @@ export default {
|
||||
const allEmails = [];
|
||||
if (user.auth.local.email) allEmails.push(user.auth.local.email);
|
||||
if (user.auth.google && user.auth.google.emails) {
|
||||
const emails = user.auth.google.emails;
|
||||
const { emails } = user.auth.google;
|
||||
allEmails.push(...this.findSocialEmails(emails));
|
||||
}
|
||||
if (user.auth.apple && user.auth.apple.emails) {
|
||||
const emails = user.auth.apple.emails;
|
||||
const { emails } = user.auth.apple;
|
||||
allEmails.push(...this.findSocialEmails(emails));
|
||||
}
|
||||
if (user.auth.facebook && user.auth.facebook.emails) {
|
||||
const emails = user.auth.facebook.emails;
|
||||
const { emails } = user.auth.facebook;
|
||||
allEmails.push(...this.findSocialEmails(emails));
|
||||
}
|
||||
return allEmails;
|
||||
|
||||
+1
-1
@@ -609,7 +609,7 @@ import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks
|
||||
import saveHero from '../mixins/saveHero';
|
||||
import LoadingSpinner from '@/components/ui/loadingSpinner';
|
||||
|
||||
const PLAY_CONSOLE_ORDERS_BASE_URL = import.meta.env.PLAY_CONSOLE_ORDERS_BASE_URL;
|
||||
const { PLAY_CONSOLE_ORDERS_BASE_URL } = import.meta.env;
|
||||
|
||||
const humanReadablePaymentDetails = {
|
||||
customerId: {
|
||||
|
||||
@@ -20,6 +20,29 @@
|
||||
class="form mx-auto"
|
||||
@submit.prevent.stop="register()"
|
||||
>
|
||||
<div v-if="needsEmailField">
|
||||
<input
|
||||
id="emailInput"
|
||||
v-model="email"
|
||||
class="form-control dark"
|
||||
type="text"
|
||||
:placeholder="$t('emailAddress')"
|
||||
:class="{
|
||||
'mb-3': !emailError,
|
||||
'input-invalid input-with-error mb-2': emailError,
|
||||
'input-valid': email && emailValid,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="emailError"
|
||||
class="input-error"
|
||||
>
|
||||
{{ emailError }}
|
||||
</div>
|
||||
<p class="purple-600 mb-3">
|
||||
{{ $t('emailRequiredForSupport') }}
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
id="usernameInput"
|
||||
v-model="username"
|
||||
@@ -58,8 +81,9 @@
|
||||
></label>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-info d-block w-100 sign-up mx-auto mb-5"
|
||||
:disabled="!username || usernameInvalid || !privacyAccepted"
|
||||
class="btn btn-info d-flex justify-content-center
|
||||
align-items-center w-100 sign-up mx-auto mb-5"
|
||||
:disabled="!email || emailError || !username || usernameInvalid || !privacyAccepted"
|
||||
type="submit"
|
||||
>
|
||||
{{ $t('getStarted') }}
|
||||
@@ -133,10 +157,12 @@
|
||||
border: 2px solid transparent;
|
||||
box-shadow: 0 1px 3px 0 rgba($black, 0.16), 0 1px 3px 0 rgba($black, 0.24);
|
||||
|
||||
&:focus, &:active {
|
||||
background-color: $blue-50;
|
||||
border: 2px solid $purple-400;
|
||||
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
|
||||
&:not(:disabled):not(.disabled) {
|
||||
&:focus, &:active {
|
||||
background-color: $blue-50;
|
||||
border: 2px solid $purple-400;
|
||||
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,23 +174,19 @@
|
||||
<script>
|
||||
import debounce from 'lodash/debounce';
|
||||
import PrivacyBanner from '@/components/header/banners/privacy';
|
||||
import accountCreation from '@/mixins/accountCreation';
|
||||
import sanitizeRedirect from '@/mixins/sanitizeRedirect';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PrivacyBanner,
|
||||
},
|
||||
mixins: [sanitizeRedirect],
|
||||
mixins: [accountCreation, sanitizeRedirect],
|
||||
data () {
|
||||
return {
|
||||
authData: {},
|
||||
email: '',
|
||||
password: '',
|
||||
passwordConfirm: '',
|
||||
privacyAccepted: false,
|
||||
registrationMethod: null,
|
||||
username: '',
|
||||
usernameIssues: [],
|
||||
needsEmailField: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -183,30 +205,40 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
if (window.sessionStorage.getItem('apple-token')) {
|
||||
this.registrationMethod = 'apple';
|
||||
} else if (!this.$store.state.registrationOptions.registrationMethod) {
|
||||
this.$router.push('/');
|
||||
} else {
|
||||
this.registrationMethod = this.$store.state.registrationOptions.registrationMethod;
|
||||
}
|
||||
this.authData = this.$store.state.registrationOptions.authData;
|
||||
this.email = this.$store.state.registrationOptions.email;
|
||||
this.username = this.$store.state.registrationOptions.username;
|
||||
this.password = this.$store.state.registrationOptions.password;
|
||||
this.passwordConfirm = this.$store.state.registrationOptions.passwordConfirm;
|
||||
|
||||
if (!this.email) {
|
||||
if (window.sessionStorage.getItem('apple-token')) {
|
||||
this.registrationMethod = 'apple';
|
||||
if (!this.email) {
|
||||
this.email = window.sessionStorage.getItem('apple-email');
|
||||
}
|
||||
} else if (!this.$store.state.registrationOptions.registrationMethod) {
|
||||
this.$router.push('/');
|
||||
} else {
|
||||
this.registrationMethod = this.$store.state.registrationOptions.registrationMethod;
|
||||
}
|
||||
|
||||
if (!this.email && this.registrationMethod !== 'apple') {
|
||||
return;
|
||||
}
|
||||
const usernameToCheck = this.email.split('@')[0].replace(/[^a-zA-Z0-9\-_]/g, '');
|
||||
this.$store.dispatch('auth:verifyUsername', {
|
||||
username: usernameToCheck,
|
||||
}).then(res => {
|
||||
if (!res.issues) {
|
||||
this.username = usernameToCheck;
|
||||
}
|
||||
});
|
||||
|
||||
if ((!this.email || this.email === '') && this.registrationMethod === 'apple') {
|
||||
this.needsEmailField = true;
|
||||
}
|
||||
if (this.email) {
|
||||
const usernameToCheck = this.email.split('@')[0].replace(/[^a-zA-Z0-9\-_]/g, '');
|
||||
this.$store.dispatch('auth:verifyUsername', {
|
||||
username: usernameToCheck,
|
||||
}).then(res => {
|
||||
if (!res.issues) {
|
||||
this.username = usernameToCheck;
|
||||
}
|
||||
});
|
||||
}
|
||||
document.getElementById('usernameInput').focus();
|
||||
},
|
||||
methods: {
|
||||
@@ -237,6 +269,7 @@ export default {
|
||||
idToken: window.sessionStorage.getItem('apple-token'),
|
||||
name: window.sessionStorage.getItem('apple-name'),
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
allowRegister: true,
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -321,10 +321,11 @@ export default {
|
||||
return null;
|
||||
},
|
||||
petClass () {
|
||||
const foolEvent = this.currentEventList?.find(event => event.aprilFools && moment()
|
||||
.isBetween(event.start, event.end));
|
||||
if (foolEvent) {
|
||||
return this.foolPet(this.member.items.currentPet, foolEvent.aprilFools);
|
||||
const substitutionEvent = this.currentEventList?.find(event => event.spriteSubstitutions
|
||||
&& moment().isBetween(event.start, event.end));
|
||||
if (substitutionEvent && substitutionEvent.spriteSubstitutions.pets) {
|
||||
return this.foolPet(`Pet-${this.member.items.currentPet}`,
|
||||
substitutionEvent.spriteSubstitutions.pets);
|
||||
}
|
||||
if (this.member?.items.currentPet) return `Pet-${this.member.items.currentPet}`;
|
||||
return '';
|
||||
|
||||
@@ -189,7 +189,7 @@ export default {
|
||||
this.cancel();
|
||||
return [];
|
||||
}
|
||||
this.currentSearch = regexRes[1];
|
||||
this.currentSearch = regexRes[1]; // eslint-disable-line prefer-destructuring
|
||||
|
||||
if (this.currentSearch.length === 0) return [];
|
||||
|
||||
|
||||
@@ -187,7 +187,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="user.purchased.background.birthday_bash"
|
||||
v-if="user.purchased.background.birthday_bash
|
||||
|| user.purchased.background.on_a_strange_planet"
|
||||
>
|
||||
<div
|
||||
class="row justify-content-center title-row mb-3"
|
||||
|
||||
@@ -470,7 +470,7 @@ export default {
|
||||
return this.userGuilds.filter(group => {
|
||||
const leaderId = group.leader?._id || group.leader;
|
||||
if (leaderId !== this.user._id) return false;
|
||||
const purchased = group.purchased;
|
||||
const { purchased } = group;
|
||||
if (!purchased?.wasUpgraded) return false;
|
||||
if (this.activeGroupPlanIds.includes(group._id)) return false;
|
||||
if (!purchased.dateTerminated) return false;
|
||||
@@ -492,7 +492,7 @@ export default {
|
||||
},
|
||||
isPartyPreviouslyUpgraded () {
|
||||
if (!this.userParty) return false;
|
||||
const purchased = this.userParty.purchased;
|
||||
const { purchased } = this.userParty;
|
||||
if (!purchased?.wasUpgraded) return false;
|
||||
if (!purchased.dateTerminated) return false;
|
||||
return new Date(purchased.dateTerminated) < new Date();
|
||||
@@ -533,7 +533,7 @@ export default {
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.upgradeableGuilds.length > 0) {
|
||||
this.selectedOption = this.upgradeableGuilds[0];
|
||||
[this.selectedOption] = this.upgradeableGuilds;
|
||||
} else if (this.upgradeableParty) {
|
||||
this.selectedOption = this.upgradeableParty;
|
||||
} else {
|
||||
|
||||
@@ -198,7 +198,6 @@ import dailyIcon from '@/assets/svg/daily.svg?raw';
|
||||
import todoIcon from '@/assets/svg/todo.svg?raw';
|
||||
import rewardIcon from '@/assets/svg/reward.svg?raw';
|
||||
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
@@ -438,14 +437,6 @@ export default {
|
||||
return false;
|
||||
},
|
||||
changeMirrorPreference (newVal) {
|
||||
Analytics.track({
|
||||
eventName: 'mirror tasks',
|
||||
eventAction: 'mirror tasks',
|
||||
eventCategory: 'behavior',
|
||||
hitType: 'event',
|
||||
mirror: newVal,
|
||||
group: this.group._id,
|
||||
}, { trackOnClient: true });
|
||||
const groupsToMirror = this.user.preferences.tasks.mirrorGroupTasks || [];
|
||||
if (newVal) { // we're turning copy ON for this group
|
||||
groupsToMirror.push(this.group._id);
|
||||
|
||||
@@ -240,7 +240,6 @@
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import closeX from '../ui/closeX';
|
||||
|
||||
@@ -276,11 +275,6 @@ export default {
|
||||
this.$store.state.party.data = party;
|
||||
this.user.party._id = party._id;
|
||||
|
||||
Analytics.updateUser({
|
||||
partyID: party._id,
|
||||
partySize: 1,
|
||||
});
|
||||
|
||||
this.$root.$emit('bv::hide::modal', 'create-party-modal');
|
||||
await this.$router.push('/party');
|
||||
},
|
||||
|
||||
@@ -314,7 +314,6 @@ import extend from 'lodash/extend';
|
||||
import groupUtilities from '@/mixins/groupsUtilities';
|
||||
import styleHelper from '@/mixins/styleHelper';
|
||||
import { mapGetters } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import participantListModal from './participantListModal';
|
||||
import groupFormModal from './groupFormModal';
|
||||
import groupGemsModal from '@/components/groups/groupGemsModal';
|
||||
@@ -560,7 +559,6 @@ export default {
|
||||
|
||||
if (this.isParty) {
|
||||
data.type = 'party';
|
||||
Analytics.updateUser({ partySize: null, partyID: null });
|
||||
this.$store.state.partyMembers = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -334,7 +334,6 @@ import orderBy from 'lodash/orderBy';
|
||||
import * as quests from '@/../../common/script/content/quests';
|
||||
import getItemInfo from '@/../../common/script/libs/getItemInfo';
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
import navigationBack from '@/assets/svg/navigation_back.svg?raw';
|
||||
import questDialogContent from '../shops/quests/questDialogContent';
|
||||
@@ -421,11 +420,6 @@ export default {
|
||||
async questInit () {
|
||||
this.loading = true;
|
||||
|
||||
Analytics.updateUser({
|
||||
partyID: this.group._id,
|
||||
partySize: this.group.memberCount,
|
||||
});
|
||||
|
||||
const groupId = this.group._id || this.user.party._id;
|
||||
|
||||
const key = this.selectedQuest;
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
|
||||
<script>
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { mapGetters, mapActions } from '@/libs/store';
|
||||
import MemberDetails from '../memberDetails';
|
||||
import createPartyModal from '../groups/createPartyModal';
|
||||
@@ -236,22 +235,8 @@ export default {
|
||||
},
|
||||
async createOrInviteParty () {
|
||||
if (this.user.party._id) {
|
||||
await Analytics.track({
|
||||
eventName: 'Header Party CTA',
|
||||
eventAction: 'Header Party CTA',
|
||||
eventCategory: 'behavior',
|
||||
hitType: 'event',
|
||||
state: 'Find Party Members',
|
||||
});
|
||||
this.$router.push('/looking-for-party');
|
||||
} else {
|
||||
await Analytics.track({
|
||||
eventName: 'Header Party CTA',
|
||||
eventAction: 'Header Party CTA',
|
||||
eventCategory: 'behavior',
|
||||
hitType: 'event',
|
||||
state: 'Get Started',
|
||||
});
|
||||
this.$root.$emit('bv::show::modal', 'create-party-modal');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -182,12 +182,10 @@ export default {
|
||||
return 'GreyedOut';
|
||||
},
|
||||
imageName () {
|
||||
const foolEvent = this.currentEventList?.find(event => moment()
|
||||
.isBetween(event.start, event.end) && event.aprilFools);
|
||||
if (this.isOwned() && foolEvent) {
|
||||
if (this.isSpecial()) return `stable_${this.foolPet(this.item.key, foolEvent.aprilFools)}`;
|
||||
const petString = `${this.item.eggKey}-${this.item.key}`;
|
||||
return `stable_${this.foolPet(petString, foolEvent.aprilFools)}`;
|
||||
const substitutionEvent = this.currentEventList?.find(event => moment()
|
||||
.isBetween(event.start, event.end) && event.spriteSubstitutions);
|
||||
if (this.isOwned() && substitutionEvent && substitutionEvent.spriteSubstitutions.pets) {
|
||||
return `stable_${this.foolPet(`Pet-${this.item.key}`, substitutionEvent.spriteSubstitutions.pets)}`;
|
||||
}
|
||||
|
||||
if (this.isOwned() || (this.mountOwned() && this.isHatchable())) {
|
||||
|
||||
@@ -114,7 +114,6 @@ import { mapState } from '@/libs/store';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import guide from '@/mixins/guide';
|
||||
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
import yesterdailyModal from './tasks/yesterdailyModal';
|
||||
import newStuff from './news/modal';
|
||||
@@ -648,15 +647,6 @@ export default {
|
||||
// Reset daily analytics actions
|
||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 0);
|
||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 0);
|
||||
} else {
|
||||
// Note a failed cron event, for our records and investigation
|
||||
Analytics.track({
|
||||
eventName: 'cron failed',
|
||||
eventAction: 'cron failed',
|
||||
eventCategory: 'behavior',
|
||||
hitType: 'event',
|
||||
responseCode: response.status,
|
||||
}, { trackOnClient: true });
|
||||
}
|
||||
|
||||
// Sync
|
||||
|
||||
@@ -433,9 +433,6 @@ import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
|
||||
import notificationsMixin from '@/mixins/notifications';
|
||||
import paymentsMixin from '@/mixins/payments';
|
||||
|
||||
// analytics
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
selectTranslatedArray,
|
||||
@@ -536,16 +533,6 @@ export default {
|
||||
this.close();
|
||||
},
|
||||
submit () {
|
||||
if (this.paymentData.group && !this.paymentData.newGroup) {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventName: 'group plan upgrade',
|
||||
eventAction: 'group plan upgrade',
|
||||
eventCategory: 'behavior',
|
||||
demographics: this.upgradedGroup.demographics,
|
||||
type: this.paymentData.group.type,
|
||||
}, { trackOnClient: true });
|
||||
}
|
||||
this.paymentData = {};
|
||||
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
|
||||
},
|
||||
|
||||
@@ -37,6 +37,9 @@ export default {
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
window.sessionStorage.setItem('apple-token', response.idToken);
|
||||
if (response.email) {
|
||||
window.sessionStorage.setItem('apple-email', response.email);
|
||||
}
|
||||
window.location.href = '/username';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<draggable
|
||||
v-if="taskList.length > 0"
|
||||
v-if="taskList.length > 0 && !rerendering"
|
||||
ref="tasksList"
|
||||
class="sortable-tasks"
|
||||
:disabled="activeFilter.label === 'scheduled' || !canBeDragged()"
|
||||
@@ -432,6 +432,7 @@ export default {
|
||||
|
||||
selectedItemToBuy: {},
|
||||
dragging: false,
|
||||
rerendering: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -548,8 +549,8 @@ export default {
|
||||
if (this.taskListOverride) originTasks = this.taskListOverride;
|
||||
|
||||
// Server
|
||||
const taskIdToReplace = filteredList[data.newIndex];
|
||||
const newIndexOnServer = originTasks.findIndex(taskId => taskId === taskIdToReplace);
|
||||
const taskIdToReplace = filteredList[data.newIndex]._id;
|
||||
const newIndexOnServer = originTasks.findIndex(task => task._id === taskIdToReplace);
|
||||
|
||||
let newOrder;
|
||||
if (taskToMove.group.id && !this.isUser) {
|
||||
@@ -568,6 +569,9 @@ export default {
|
||||
// Client
|
||||
const deleted = originTasks.splice(data.oldIndex, 1);
|
||||
originTasks.splice(data.newIndex, 0, deleted[0]);
|
||||
this.rerendering = true;
|
||||
await this.$nextTick();
|
||||
this.rerendering = false;
|
||||
},
|
||||
async moveTo (task, where) { // where is 'top' or 'bottom'
|
||||
const taskIdToMove = task._id;
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
v-if="addNew || availableToSelect.length > 0"
|
||||
:class="{
|
||||
'item-group': true,
|
||||
'add-new': availableToSelect.length === 0 && search !== '',
|
||||
'add-new': search !== '' && !hasExactMatch,
|
||||
'scroll': availableToSelect.length > 5
|
||||
}"
|
||||
>
|
||||
@@ -86,7 +86,7 @@
|
||||
</b-dropdown-item-button>
|
||||
|
||||
<div
|
||||
v-if="addNew"
|
||||
v-if="addNew && search !== '' && !hasExactMatch"
|
||||
class="hint"
|
||||
>
|
||||
{{ $t('pressEnterToAddTag', { tagName: search }) }}
|
||||
@@ -171,7 +171,8 @@ $itemHeight: 2rem;
|
||||
max-height: #{5*$itemHeight};
|
||||
|
||||
&.add-new {
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
height: auto;
|
||||
|
||||
.hint {
|
||||
display: block;
|
||||
@@ -245,6 +246,7 @@ export default {
|
||||
selected: this.selectedItems,
|
||||
search: '',
|
||||
textbox: null,
|
||||
itemsAdded: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -272,6 +274,16 @@ export default {
|
||||
|
||||
return filteredItems;
|
||||
},
|
||||
hasExactMatch () {
|
||||
const searchTerm = this.search.trim().toLowerCase();
|
||||
if (!searchTerm) return false;
|
||||
if (this.itemsAdded.indexOf(searchTerm) !== -1) return true;
|
||||
if (this.availableToSelect.length === 0) return false;
|
||||
if (this.availableToSelect[0].name.toLowerCase() === searchTerm) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selected () {
|
||||
@@ -310,6 +322,7 @@ export default {
|
||||
this.closeSelectPopup();
|
||||
},
|
||||
selectItem (item) {
|
||||
if (!item) return;
|
||||
this.selectedItems.push(item.id);
|
||||
this.$emit('toggle', item.id);
|
||||
this.preventHide = true;
|
||||
@@ -371,9 +384,16 @@ export default {
|
||||
handleSubmit () {
|
||||
if (!this.addNew) return;
|
||||
const { search } = this;
|
||||
this.$emit('addNew', search);
|
||||
|
||||
this.search = '';
|
||||
// If there is a existing tag
|
||||
if (this.hasExactMatch) {
|
||||
this.selectItem(this.availableToSelect[0]);
|
||||
this.search = '';
|
||||
} else {
|
||||
// Creating a new tag as there is no existing tag present
|
||||
this.$emit('addNew', search);
|
||||
this.itemsAdded.push(search.toLowerCase());
|
||||
this.search = '';
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
}, `type_${task.type}`
|
||||
]"
|
||||
@click="castEnd($event, task)"
|
||||
tabindex="0"
|
||||
@keypress.enter="$emit('editTask', task)"
|
||||
>
|
||||
<div
|
||||
class="d-flex"
|
||||
@@ -98,9 +100,7 @@
|
||||
<div
|
||||
class="task-clickable-area pt-1 pl-75 pb-0"
|
||||
:class="{ 'cursor-auto': !teamManagerAccess }"
|
||||
tabindex="0"
|
||||
@click="edit($event, task)"
|
||||
@keypress.enter="edit($event, task)"
|
||||
>
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3
|
||||
@@ -432,10 +432,6 @@
|
||||
outline: none;
|
||||
transition: none;
|
||||
border: $purple-400 solid 1px;
|
||||
|
||||
:not(task-best-control-inner-habit) { // round icon
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.control-bottom-box {
|
||||
@@ -462,16 +458,13 @@
|
||||
&:hover:not(.task-not-editable.task-not-scoreable),
|
||||
&:focus-within:not(.task-not-editable.task-not-scoreable) {
|
||||
box-shadow: 0 1px 8px 0 rgba($black, 0.12), 0 4px 4px 0 rgba($black, 0.16);
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
|
||||
.task:not(.groupTask) {
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.left-control, .right-control, .task-content {
|
||||
border-color: $purple-400;
|
||||
}
|
||||
&:hover, &:focus {
|
||||
border: none;
|
||||
outline: 1px solid $purple-400;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,11 +515,6 @@
|
||||
&-user {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-radius: 4px;
|
||||
border: $purple-400 solid 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.task-title + .task-dropdown ::v-deep .dropdown-menu {
|
||||
|
||||
@@ -55,11 +55,31 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<lockable-label
|
||||
:class-override="cssClass('headings')"
|
||||
:locked="challengeAccessRequired"
|
||||
:text="`${$t('text')}*`"
|
||||
/>
|
||||
<div class="d-flex align-items-center">
|
||||
<lockable-label
|
||||
class="mr-auto"
|
||||
:class-override="cssClass('headings')"
|
||||
:locked="challengeAccessRequired"
|
||||
:text="`${$t('text')}*`"
|
||||
/>
|
||||
<div
|
||||
id="spi-alert"
|
||||
class="d-flex align-items-center"
|
||||
:class="cssClass('headings')"
|
||||
>
|
||||
<div
|
||||
class="svg svg-icon color icon-16 mr-1"
|
||||
v-html="icons.alert"
|
||||
></div>
|
||||
<small
|
||||
class="my-1"
|
||||
>
|
||||
<a
|
||||
:class="cssClass('headings')"
|
||||
>{{ $t('avoidSPI') }}</a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
ref="inputToFocus"
|
||||
v-model="task.text"
|
||||
@@ -79,10 +99,20 @@
|
||||
@keydown.esc="autoCompleteMixinHandleEscape($event)"
|
||||
>
|
||||
</div>
|
||||
<b-popover
|
||||
:target="'spi-alert'"
|
||||
triggers="hover"
|
||||
placement="bottom"
|
||||
offset="-128"
|
||||
>
|
||||
<div
|
||||
v-html="$t('avoidSPIDetails', spiLinkData)">
|
||||
</div>
|
||||
</b-popover>
|
||||
<div
|
||||
class="form-group mb-0"
|
||||
>
|
||||
<div class="d-flex">
|
||||
<div class="d-flex align-items-center">
|
||||
<lockable-label
|
||||
class="mr-auto"
|
||||
:class-override="cssClass('headings')"
|
||||
@@ -963,6 +993,20 @@
|
||||
box-shadow: 0px 1px 3px 0px rgba(26, 24, 29, 0.12), 0px 1px 2px 0px rgba(26, 24, 29, 0.24);
|
||||
}
|
||||
}
|
||||
|
||||
.b-popover {
|
||||
margin-top: -5px;
|
||||
max-width: 330px;
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
text-align: left;
|
||||
|
||||
a {
|
||||
color: $gray-500;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
@@ -1196,6 +1240,7 @@ import chevronIcon from '@/assets/svg/chevron.svg?raw';
|
||||
import calendarIcon from '@/assets/svg/calendar.svg?raw';
|
||||
import gripIcon from '@/assets/svg/grip.svg?raw';
|
||||
import InformationIcon from '@/components/ui/informationIcon.vue';
|
||||
import alertIcon from '@/assets/svg/for-css/alert-white.svg?raw';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -1231,6 +1276,7 @@ export default {
|
||||
streak: streakIcon,
|
||||
calendar: calendarIcon,
|
||||
grip: gripIcon,
|
||||
alert: alertIcon,
|
||||
}),
|
||||
members: [],
|
||||
membersNameAndId: [],
|
||||
@@ -1251,6 +1297,11 @@ export default {
|
||||
{ key: 'per', label: 'perception', description: 'perTaskText' },
|
||||
],
|
||||
calendarHighlights: { dates: [new Date()] },
|
||||
spiLinkData: {
|
||||
firstLink: '<a href="/static/privacy#section_1" target="_blank">',
|
||||
secondLink: '<a href="/static/privacy" target="_blank">',
|
||||
linkClose: '</a>',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -1399,7 +1450,7 @@ export default {
|
||||
this.task.down = !this.task.down;
|
||||
},
|
||||
weekdaysMin (dayNumber) {
|
||||
return moment.weekdaysMin(dayNumber);
|
||||
return this.$t(`weekdaysMin${dayNumber}`);
|
||||
},
|
||||
formattedDate (date) {
|
||||
return moment(date).format('MM/DD/YYYY');
|
||||
|
||||
@@ -6,7 +6,7 @@ import amplitude from 'amplitude-js';
|
||||
import Vue from 'vue';
|
||||
import getStore from '@/store';
|
||||
|
||||
const AMPLITUDE_KEY = import.meta.env.AMPLITUDE_KEY;
|
||||
const { AMPLITUDE_KEY } = import.meta.env;
|
||||
const REQUIRED_FIELDS = ['eventCategory', 'eventAction'];
|
||||
|
||||
let analyticsLoading = false;
|
||||
|
||||
@@ -1,28 +1,10 @@
|
||||
// Vue plugin to globally expose a '$t' method that calls common/i18n.t.
|
||||
// Can be anywhere inside vue as 'this.$t' or '$t' in templates.
|
||||
|
||||
import moment from 'moment';
|
||||
import i18n from '@/../../common/script/i18n';
|
||||
|
||||
function loadLocale (i18nData) {
|
||||
// Load i18n strings
|
||||
i18n.strings = i18nData.strings;
|
||||
|
||||
// Load Moment.js locale
|
||||
const { language } = i18nData;
|
||||
|
||||
if (language && i18nData.momentLang && language.momentLangCode) {
|
||||
// Make moment available under `window` so that the locale can be set
|
||||
window.moment = moment;
|
||||
|
||||
// Execute the script and set the locale
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.text = i18nData.momentLang;
|
||||
head.appendChild(script);
|
||||
moment.updateLocale(language.momentLangCode);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
NavbarPlugin,
|
||||
CollapsePlugin,
|
||||
} from 'bootstrap-vue';
|
||||
import Fragment from 'vue-fragment';
|
||||
import AppComponent from './app';
|
||||
import { setUpLogging } from '@/libs/logging';
|
||||
import router from './router/index';
|
||||
@@ -44,7 +43,6 @@ Vue.use(FormRadioPlugin);
|
||||
Vue.use(TooltipPlugin);
|
||||
Vue.use(NavbarPlugin);
|
||||
Vue.use(CollapsePlugin);
|
||||
Vue.use(Fragment.Plugin);
|
||||
|
||||
setUpLogging();
|
||||
const store = getStore();
|
||||
|
||||
@@ -1,56 +1,14 @@
|
||||
import includes from 'lodash/includes';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
foolPet (pet, prank) {
|
||||
const SPECIAL_PETS = [
|
||||
'Bear-Veteran',
|
||||
'BearCub-Polar',
|
||||
'Cactus-Veteran',
|
||||
'Dragon-Hydra',
|
||||
'Dragon-Veteran',
|
||||
'Fox-Veteran',
|
||||
'Gryphatrice-Jubilant',
|
||||
'Gryphon-Gryphatrice',
|
||||
'Gryphon-RoyalPurple',
|
||||
'Hippogriff-Hopeful',
|
||||
'Jackalope-RoyalPurple',
|
||||
'JackOLantern-Base',
|
||||
'JackOLantern-Ghost',
|
||||
'JackOLantern-Glow',
|
||||
'JackOLantern-RoyalPurple',
|
||||
'Lion-Veteran',
|
||||
'MagicalBee-Base',
|
||||
'Mammoth-Base',
|
||||
'MantisShrimp-Base',
|
||||
'Orca-Base',
|
||||
'Phoenix-Base',
|
||||
'Tiger-Veteran',
|
||||
'Turkey-Base',
|
||||
'Turkey-Gilded',
|
||||
'Wolf-Cerberus',
|
||||
'Wolf-Veteran',
|
||||
];
|
||||
const BASE_PETS = [
|
||||
'BearCub',
|
||||
'Cactus',
|
||||
'Dragon',
|
||||
'FlyingPig',
|
||||
'Fox',
|
||||
'LionCub',
|
||||
'PandaCub',
|
||||
'TigerCub',
|
||||
'Wolf',
|
||||
];
|
||||
if (!pet) return `Pet-TigerCub-${prank}`;
|
||||
if (SPECIAL_PETS.indexOf(pet) !== -1) {
|
||||
return `Pet-Dragon-${prank}`;
|
||||
foolPet (pet, substitutions) {
|
||||
if (!pet || pet === 'Pet-') return substitutions.noPet;
|
||||
if (substitutions[pet]) return substitutions[pet];
|
||||
for (const key in substitutions) {
|
||||
if (pet.startsWith(key)) {
|
||||
return substitutions[key];
|
||||
}
|
||||
}
|
||||
const species = pet.slice(0, pet.indexOf('-'));
|
||||
if (includes(BASE_PETS, species)) {
|
||||
return `Pet-${species}-${prank}`;
|
||||
}
|
||||
return `Pet-BearCub-${prank}`;
|
||||
return substitutions.default;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,9 +6,8 @@ import { mapState } from '@/libs/store';
|
||||
import encodeParams from '@/libs/encodeParams';
|
||||
import notificationsMixin from '@/mixins/notifications';
|
||||
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
const STRIPE_PUB_KEY = import.meta.env.STRIPE_PUB_KEY;
|
||||
const { STRIPE_PUB_KEY } = import.meta.env;
|
||||
|
||||
let stripeInstance = null;
|
||||
|
||||
@@ -207,16 +206,6 @@ export default {
|
||||
alert(`Error while redirecting to Stripe: ${checkoutSessionResult.error.message}`);
|
||||
throw checkoutSessionResult.error;
|
||||
}
|
||||
if (paymentType === 'groupPlan') {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventName: 'group plan create',
|
||||
eventAction: 'group plan create',
|
||||
eventCategory: 'behavior',
|
||||
demographics: appState.newGroup.demographics,
|
||||
type: appState.newGroup.type,
|
||||
}, { trackOnClient: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error while redirecting to Stripe', err); // eslint-disable-line
|
||||
alert(`Error while redirecting to Stripe: ${err.message}`);
|
||||
|
||||
@@ -3,7 +3,6 @@ import Vue from 'vue';
|
||||
import scoreTask from '@/../../common/script/ops/scoreTask';
|
||||
import notifications from './notifications';
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
||||
|
||||
export default {
|
||||
@@ -58,15 +57,6 @@ export default {
|
||||
|
||||
const tasksScoredCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT);
|
||||
if (!tasksScoredCount || tasksScoredCount < 2) {
|
||||
Analytics.track({
|
||||
eventName: 'task scored',
|
||||
eventAction: 'task scored',
|
||||
eventCategory: 'behavior',
|
||||
hitType: 'event',
|
||||
uuid: user._id,
|
||||
taskType: task.type,
|
||||
direction,
|
||||
}, { trackOnClient: true });
|
||||
if (!tasksScoredCount) {
|
||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 1);
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -90,7 +90,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment v-if="allowedToChangeClass">
|
||||
<div class="d-content" v-if="allowedToChangeClass">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -55,7 +55,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -94,7 +94,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -78,7 +78,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -83,7 +83,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr>
|
||||
<td class="settings-label">
|
||||
{{ $t("showHeader") }}
|
||||
@@ -26,7 +26,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -67,7 +67,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-for="network in SOCIAL_AUTH_NETWORKS"
|
||||
:key="network.key"
|
||||
@@ -39,7 +39,7 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -66,7 +66,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -111,7 +111,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -48,7 +48,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div class="d-content">
|
||||
<tr
|
||||
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
||||
>
|
||||
@@ -76,7 +76,7 @@
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</fragment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
:class="{
|
||||
'casting-spell': castingSpell,
|
||||
}"
|
||||
@dragover.prevent
|
||||
>
|
||||
<!-- <banned-account-modal /> -->
|
||||
<amazon-payments-modal v-if="!isStaticPage" />
|
||||
@@ -130,7 +131,6 @@ import PrivacyBanner from '@/components/header/banners/privacy';
|
||||
import AppFooter from '@/components/appFooter';
|
||||
import notificationsDisplay from '@/components/notifications';
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import BuyModal from '@/components/shops/buyModal.vue';
|
||||
import SelectMembersModal from '@/components/selectMembersModal.vue';
|
||||
import notifications from '@/mixins/notifications';
|
||||
@@ -276,7 +276,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
Analytics.updateUser();
|
||||
return this.loadAllTranslations();
|
||||
}).then(() => {
|
||||
this.$store.state.isUserLoaded = true;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import getStore from '@/store';
|
||||
import handleRedirect from './handleRedirect';
|
||||
|
||||
@@ -11,56 +10,56 @@ import { DEPRECATED_ROUTES } from '@/router/deprecated-routes';
|
||||
|
||||
// NOTE: when adding a page make sure to implement the `common:setTitle` action
|
||||
|
||||
const Logout = () => import(/* webpackChunkName: "auth" */'@/components/auth/logout');
|
||||
const Logout = () => import('@/components/auth/logout');
|
||||
|
||||
// Hall
|
||||
const HallPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/index');
|
||||
const PatronsPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/patrons');
|
||||
const HeroesPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/heroes');
|
||||
const HallPage = () => import('@/components/hall/index');
|
||||
const PatronsPage = () => import('@/components/hall/patrons');
|
||||
const HeroesPage = () => import('@/components/hall/heroes');
|
||||
|
||||
// Admin Pages
|
||||
const AdminContainerPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/container');
|
||||
const AdminPanelPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel');
|
||||
const AdminPanelUserPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel/user-support');
|
||||
const AdminPanelSearchPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel/search');
|
||||
const GroupAdminPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/groups');
|
||||
const GroupAdminGroupPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/groups/group-support');
|
||||
const BlockerPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/blocker');
|
||||
const AdminContainerPage = () => import('@/components/admin/container');
|
||||
const AdminPanelPage = () => import('@/components/admin/admin-panel');
|
||||
const AdminPanelUserPage = () => import('@/components/admin/admin-panel/user-support');
|
||||
const AdminPanelSearchPage = () => import('@/components/admin/admin-panel/search');
|
||||
const GroupAdminPage = () => import('@/components/admin/groups');
|
||||
const GroupAdminGroupPage = () => import('@/components/admin/groups/group-support');
|
||||
const BlockerPage = () => import('@/components/admin/blocker');
|
||||
|
||||
// Tasks
|
||||
const UserTasks = () => import(/* webpackChunkName: "userTasks" */'@/components/tasks/user');
|
||||
const UserTasks = () => import('@/components/tasks/user');
|
||||
|
||||
// Inventory
|
||||
const InventoryContainer = () => import(/* webpackChunkName: "inventory" */'@/components/inventory/index');
|
||||
const ItemsPage = () => import(/* webpackChunkName: "inventory" */'@/components/inventory/items/index');
|
||||
const EquipmentPage = () => import(/* webpackChunkName: "inventory" */'@/components/inventory/equipment/index');
|
||||
const StablePage = () => import(/* webpackChunkName: "inventory" */'@/components/inventory/stable/index');
|
||||
const InventoryContainer = () => import('@/components/inventory/index');
|
||||
const ItemsPage = () => import('@/components/inventory/items/index');
|
||||
const EquipmentPage = () => import('@/components/inventory/equipment/index');
|
||||
const StablePage = () => import('@/components/inventory/stable/index');
|
||||
|
||||
// Guilds & Parties
|
||||
const GroupPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/group');
|
||||
const GroupPlansAppPage = () => import(/* webpackChunkName: "guilds" */ '@/components/static/groupPlans');
|
||||
const LookingForParty = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/lookingForParty');
|
||||
const GroupPage = () => import('@/components/groups/group');
|
||||
const GroupPlansAppPage = () => import('@/components/static/groupPlans');
|
||||
const LookingForParty = () => import('@/components/groups/lookingForParty');
|
||||
|
||||
// Group Plans
|
||||
const GroupPlanIndex = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/index');
|
||||
const GroupPlanTaskInformation = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/taskInformation');
|
||||
const GroupPlanBilling = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/billing');
|
||||
const GroupPlanIndex = () => import('@/components/group-plans/index');
|
||||
const GroupPlanTaskInformation = () => import('@/components/group-plans/taskInformation');
|
||||
const GroupPlanBilling = () => import('@/components/group-plans/billing');
|
||||
|
||||
const MessagesIndex = () => import(/* webpackChunkName: "private-messages" */ '@/pages/private-messages/index.vue');
|
||||
const MessagesIndex = () => import('@/pages/private-messages/index.vue');
|
||||
|
||||
// Challenges
|
||||
const ChallengeIndex = () => import(/* webpackChunkName: "challenges" */ '@/components/challenges/index');
|
||||
const MyChallenges = () => import(/* webpackChunkName: "challenges" */ '@/components/challenges/myChallenges');
|
||||
const FindChallenges = () => import(/* webpackChunkName: "challenges" */ '@/components/challenges/findChallenges');
|
||||
const ChallengeDetail = () => import(/* webpackChunkName: "challenges" */ '@/components/challenges/challengeDetail');
|
||||
const ChallengeIndex = () => import('@/components/challenges/index');
|
||||
const MyChallenges = () => import('@/components/challenges/myChallenges');
|
||||
const FindChallenges = () => import('@/components/challenges/findChallenges');
|
||||
const ChallengeDetail = () => import('@/components/challenges/challengeDetail');
|
||||
|
||||
// Shops
|
||||
const ShopsContainer = () => import(/* webpackChunkName: "shops" */'@/components/shops/index');
|
||||
const MarketPage = () => import(/* webpackChunkName: "shops-market" */'@/components/shops/market/index');
|
||||
const QuestsPage = () => import(/* webpackChunkName: "shops-quest" */'@/components/shops/quests/index');
|
||||
const CustomizationsPage = () => import(/* webpackChunkName: "shops-customizations" */'@/components/shops/customizations/index');
|
||||
const SeasonalPage = () => import(/* webpackChunkName: "shops-seasonal" */'@/components/shops/seasonal/index');
|
||||
const TimeTravelersPage = () => import(/* webpackChunkName: "shops-timetravelers" */'@/components/shops/timeTravelers/index');
|
||||
const ShopsContainer = () => import('@/components/shops/index');
|
||||
const MarketPage = () => import('@/components/shops/market/index');
|
||||
const QuestsPage = () => import('@/components/shops/quests/index');
|
||||
const CustomizationsPage = () => import('@/components/shops/customizations/index');
|
||||
const SeasonalPage = () => import('@/components/shops/seasonal/index');
|
||||
const TimeTravelersPage = () => import('@/components/shops/timeTravelers/index');
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
@@ -85,6 +84,13 @@ const router = new VueRouter({
|
||||
props: true,
|
||||
},
|
||||
{ name: 'profile', path: '/user/profile' },
|
||||
{
|
||||
name: 'avatar',
|
||||
path: '/avatar',
|
||||
children: [
|
||||
{ name: 'backgrounds', path: 'backgrounds' },
|
||||
],
|
||||
},
|
||||
{ name: 'stats', path: '/user/stats' },
|
||||
{ name: 'achievements', path: '/user/achievements' },
|
||||
{
|
||||
@@ -311,15 +317,6 @@ router.beforeEach(async (to, from, next) => {
|
||||
router.app.$root.$emit('update-party');
|
||||
}
|
||||
|
||||
if (to.name === 'lookingForParty') {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventName: 'View Find Members',
|
||||
eventAction: 'View Find Members',
|
||||
eventCategory: 'behavior',
|
||||
}, { trackOnClient: true });
|
||||
}
|
||||
|
||||
// Redirect old guild urls
|
||||
if (to.hash.indexOf('#/options/groups/guilds/') !== -1) {
|
||||
const splits = to.hash.split('/');
|
||||
@@ -410,6 +407,13 @@ router.beforeEach(async (to, from, next) => {
|
||||
router.app.$root.$emit('bv::hide::modal', 'profile');
|
||||
}
|
||||
|
||||
if (to.name === 'backgrounds') {
|
||||
store.state.avatarEditorOptions.editingUser = true;
|
||||
store.state.avatarEditorOptions.startingPage = 'backgrounds';
|
||||
router.app.$root.$emit('bv::show::modal', 'avatar-modal');
|
||||
return null;
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ const NewsPage = () => import('@/components/static/newStuff');
|
||||
const OverviewPage = () => import('@/components/static/overview');
|
||||
const PressKitPage = () => import('@/components/static/pressKit');
|
||||
const PrivacyPage = () => import('@/components/static/privacy');
|
||||
const RegisterLoginReset = () => import(/* webpackChunkName: "auth" */'@/components/auth/registerLoginReset');
|
||||
const RegisterUsername = () => import(/* webpackChunkName: "auth" */'@/components/auth/registerUsername');
|
||||
const RegisterLoginReset = () => import('@/components/auth/registerLoginReset');
|
||||
const RegisterUsername = () => import('@/components/auth/registerUsername');
|
||||
const SubscriptionBenefitsFaq = () => import('@/components/static/subscriptionBenefitsFaq');
|
||||
const TermsPage = () => import('@/components/static/terms');
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ export async function appleAuth (store, params) {
|
||||
id_token: params.idToken,
|
||||
name: params.name,
|
||||
username: params.username,
|
||||
email: params.email,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -109,7 +110,10 @@ export async function appleAuth (store, params) {
|
||||
}
|
||||
|
||||
if (result.data.message && result.data.id_token) {
|
||||
return { idToken: result.data.id_token };
|
||||
return {
|
||||
idToken: result.data.id_token,
|
||||
email: result.data.email,
|
||||
};
|
||||
}
|
||||
|
||||
const user = result.data.data;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import Vue from 'vue';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
export async function getChat (store, payload) {
|
||||
const response = await axios.get(`/api/v4/groups/${payload.groupId}/chat?limit=400`);
|
||||
@@ -17,13 +16,6 @@ export async function postChat (store, payload) {
|
||||
url += `?previousMsg=${payload.previousMsg}`;
|
||||
}
|
||||
|
||||
if (group.type === 'party') {
|
||||
Analytics.updateUser({
|
||||
partyID: group.id,
|
||||
partySize: group.memberCount,
|
||||
});
|
||||
}
|
||||
|
||||
const response = await axios.post(url, {
|
||||
message: payload.message,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import omit from 'lodash/omit';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { loadAsyncResource } from '@/libs/asyncResource';
|
||||
|
||||
export async function getPublicGuilds (store, payload) {
|
||||
@@ -74,7 +73,6 @@ export async function join (store, payload) {
|
||||
if (invitationI !== -1) invitations.parties.splice(invitationI, 1);
|
||||
|
||||
user.party._id = groupId;
|
||||
Analytics.updateUser({ partyID: groupId });
|
||||
// load the party members so that they get shown in the header
|
||||
store.dispatch('party:getMembers');
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import * as shops from './shops';
|
||||
import * as snackbars from './snackbars';
|
||||
import * as worldState from './worldState';
|
||||
import * as news from './news';
|
||||
import * as analytics from './analytics';
|
||||
import * as faq from './faq';
|
||||
import * as blockers from './blockers';
|
||||
|
||||
@@ -44,7 +43,6 @@ const actions = flattenAndNamespace({
|
||||
snackbars,
|
||||
worldState,
|
||||
news,
|
||||
analytics,
|
||||
faq,
|
||||
blockers,
|
||||
});
|
||||
|
||||
@@ -1,26 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
// export async function initQuest (store) {
|
||||
// }
|
||||
|
||||
export async function sendAction (store, payload) { // eslint-disable-line import/prefer-default-export, max-len
|
||||
// @TODO: Maybe move this to server
|
||||
let partyData = {};
|
||||
if (store.state.party && store.state.party.data) {
|
||||
partyData = {
|
||||
partyID: store.state.party.data._id,
|
||||
partySize: store.state.party.data.memberCount,
|
||||
};
|
||||
} else {
|
||||
partyData = {
|
||||
partyID: store.state.user.data.party._id,
|
||||
partySize: store.state.partyMembers.data.length,
|
||||
};
|
||||
}
|
||||
|
||||
Analytics.updateUser(partyData);
|
||||
|
||||
const response = await axios.post(`/api/v4/groups/${payload.groupId}/${payload.action}`);
|
||||
|
||||
// @TODO: Update user?
|
||||
|
||||
@@ -3,7 +3,6 @@ import Vue from 'vue';
|
||||
import compact from 'lodash/compact';
|
||||
import omit from 'lodash/omit';
|
||||
import { loadAsyncResource } from '@/libs/asyncResource';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
||||
|
||||
export function fetchUserTasks (store, options = {}) {
|
||||
@@ -112,15 +111,6 @@ export async function create (store, createdTask) {
|
||||
}
|
||||
const tasksCreatedCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT);
|
||||
if (!tasksCreatedCount || tasksCreatedCount < 2) {
|
||||
const uuid = store.state.user.data._id;
|
||||
Analytics.track({
|
||||
eventName: 'task created',
|
||||
eventAction: 'task created',
|
||||
eventCategory: 'behavior',
|
||||
hitType: 'event',
|
||||
uuid,
|
||||
taskType: taskRes.type,
|
||||
}, { trackOnClient: true });
|
||||
if (!tasksCreatedCount) {
|
||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 1);
|
||||
} else {
|
||||
@@ -168,11 +158,15 @@ export async function collapseChecklist (store, task) {
|
||||
}
|
||||
|
||||
export async function destroy (store, task) {
|
||||
const list = store.state.tasks.data[`${task.type}s`];
|
||||
const taskIndex = list.findIndex(t => t._id === task._id);
|
||||
const type = `${task.type}s`;
|
||||
const listIndex = store.state.tasks.data[type].findIndex(t => t._id === task._id);
|
||||
const orderIndex = store.state.user.data.tasksOrder[type].indexOf(task._id);
|
||||
|
||||
if (taskIndex > -1) {
|
||||
list.splice(taskIndex, 1);
|
||||
if (listIndex > -1) {
|
||||
store.state.tasks.data[type].splice(listIndex, 1);
|
||||
}
|
||||
if (orderIndex > -1) {
|
||||
store.state.user.data.tasksOrder[type].splice(orderIndex, 1);
|
||||
}
|
||||
|
||||
await axios.delete(`/api/v4/tasks/${task._id}`);
|
||||
|
||||
@@ -159,10 +159,6 @@ export default defineConfig({
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'^/analytics': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -162,5 +162,8 @@
|
||||
"achievementCatsModalText": "Nasbíral jsi všechny kočičí mazlíčky!",
|
||||
"achievementRoughRiderModalText": "Nasbíral jsi všechny základní barvy nepohodlných mazlíčků a mountů!",
|
||||
"achievementRodentRulerModalText": "Nasbíral jsi všechny hlodavce!",
|
||||
"achievementCatsText": "Vylíhly se všechny standardní barvy kočičích mazlíčků: gepard, lev, šavlozubý tygr a tygr!"
|
||||
"achievementCatsText": "Vylíhly se všechny standardní barvy kočičích mazlíčků: gepard, lev, šavlozubý tygr a tygr!",
|
||||
"achievementRodentRuler": "Vládce hlodavců",
|
||||
"achievementCats": "Pasák koček",
|
||||
"achievementDomesticated": "Hejá"
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
"backgroundTavernNotes": "Navštiv krčmu města Habitica.",
|
||||
"backgrounds102015": "Sada 17: zveřejněna v říjnu 2015",
|
||||
"backgroundHarvestMoonText": "Měsíc při sklizni",
|
||||
"backgroundHarvestMoonNotes": "Kdákání pod měsícem při sklizni.",
|
||||
"backgroundHarvestMoonNotes": "Chechtej se pod sklizňovým měsícem.",
|
||||
"backgroundSlimySwampText": "Slizká bažina",
|
||||
"backgroundSlimySwampNotes": "Přebroď se slizkou bažinou.",
|
||||
"backgroundSwarmingDarknessText": "Valící se temnota",
|
||||
@@ -213,7 +213,7 @@
|
||||
"backgroundStormyRooftopsNotes": "Propliž se přes bouřlivé střechy.",
|
||||
"backgroundWindyAutumnText": "Větrný podzim",
|
||||
"backgroundWindyAutumnNotes": "Hoň se za listy během větrného podzimu.",
|
||||
"incentiveBackgrounds": "Prosté pozadí",
|
||||
"incentiveBackgrounds": "Standardní pozadí",
|
||||
"backgroundVioletText": "Fialová",
|
||||
"backgroundVioletNotes": "Živá fialová tapeta.",
|
||||
"backgroundBlueText": "Modrá",
|
||||
@@ -736,7 +736,64 @@
|
||||
"backgroundMaskMakersWorkshopNotes": "Vyzkoušej novou tvář v maskářově dílně.",
|
||||
"backgroundCemeteryGateText": "Hřbitovní brána",
|
||||
"backgroundCemeteryGateNotes": "Straš u hřbitovní brány.",
|
||||
"backgroundAutumnBridgeText": "Podzimní most",
|
||||
"backgroundAutumnBridgeNotes": "Obdivuj krásu podzimního mostu.",
|
||||
"backgroundInsideACrystalText": "Uvnitř krystalu."
|
||||
"backgroundAutumnBridgeText": "Most na podzim",
|
||||
"backgroundAutumnBridgeNotes": "Obdivuj krásu mostu na podzim.",
|
||||
"backgroundInsideACrystalText": "Uvnitř krystalu",
|
||||
"backgrounds032023": "Sada 106: Zveřejněna v březnu 2023",
|
||||
"backgroundOldTimeyBasketballCourtText": "Retro basketbalové hřiště",
|
||||
"backgroundOldTimeyBasketballCourtNotes": "Zaházej si na koš na retro basketbalovém hřišti.",
|
||||
"backgroundJungleWateringHoleText": "Napajedlo v džungli",
|
||||
"backgroundJungleWateringHoleNotes": "Zastav se na doušek u džunglového napajedla.",
|
||||
"backgroundMangroveForestText": "Mangrovový les",
|
||||
"backgroundMangroveForestNotes": "Prozkoumej okraj mangrovového lesa.",
|
||||
"backgrounds052023": "Sada 108: Zveřejněna v květnu 2023",
|
||||
"backgroundInAPaintingText": "V obraze",
|
||||
"backgroundFlyingOverHedgeMazeText": "Let nad labyrintem ze živého plotu",
|
||||
"backgroundFlyingOverHedgeMazeNotes": "Žasněte při letu nad labyrintem ze živého plotu.",
|
||||
"backgroundCretaceousForestText": "Křídový les",
|
||||
"backgroundCretaceousForestNotes": "Vychutnejte si pradávnou zeleň křídového lesa.",
|
||||
"backgroundLeafyTreeTunnelNotes": "Procházejte se tunelem z listnatých stromů.",
|
||||
"backgroundSpringtimeShowerText": "Jarní přeháňka",
|
||||
"backgroundSpringtimeShowerNotes": "Podívejte se na květnatou jarní přeháňku.",
|
||||
"backgroundUnderWisteriaText": "Pod vistérií",
|
||||
"backgrounds022023": "SADA 105: Vydáno v únoru 2023",
|
||||
"backgroundInFrontOfFountainText": "Před Fontánou",
|
||||
"backgroundInFrontOfFountainNotes": "Procházej se před Fontánou.",
|
||||
"backgroundGoldenBirdcageText": "Zlatá klec",
|
||||
"backgroundGoldenBirdcageNotes": "Schovej se v zlaté kleci.",
|
||||
"backgroundFancyBedroomText": "Luxusní ložnice",
|
||||
"backgroundFancyBedroomNotes": "Dopřej si luxus v luxusní ložnici.",
|
||||
"backgrounds042023": "Sada 107: Zveřejněna v dubnu 2023",
|
||||
"backgroundLeafyTreeTunnelText": "Tunel z listnatých stromů",
|
||||
"backgroundUnderWisteriaNotes": "Odpočiňte si pod vistérií.",
|
||||
"backgroundInAPaintingNotes": "Užijte si kreativní činnosti uvnitř obrazu.",
|
||||
"backgrounds012023": "SADA 104: Vydáno v lednu 2023",
|
||||
"backgroundRimeIceText": "Jinovatka",
|
||||
"backgroundRimeIceNotes": "Pokochej se třpytivou jinovatkou.",
|
||||
"backgroundSnowyTempleText": "Zasněžený chrám",
|
||||
"backgroundSnowyTempleNotes": "Pokochej se klidným zasněženým chrámem.",
|
||||
"backgroundWinterLakeWithSwansText": "Zimní jezero s labutěmi",
|
||||
"backgroundWinterLakeWithSwansNotes": "Užij si přírodu u zimního jezera s labutěmi.",
|
||||
"backgrounds122022": "SADA 103: Vydáno v prosinci 2022",
|
||||
"backgroundBranchesOfAHolidayTreeText": "Větve svátečního stromku",
|
||||
"backgroundBranchesOfAHolidayTreeNotes": "Dováděj na větvích svátečního stromku.",
|
||||
"backgroundInsideACrystalNotes": "Vyhlédni z nitra krystalu.",
|
||||
"backgroundSnowyVillageText": "Zasněžená vesnice",
|
||||
"backgroundSnowyVillageNotes": "Pokochej se zasněženou vesnicí.",
|
||||
"backgrounds062023": "Sada 109: Zveřejněna v červnu 2023",
|
||||
"backgroundInAnAquariumText": "V akváriu",
|
||||
"backgroundInAnAquariumNotes": "Zaplavejte si poklidně s rybkami v akváriu.",
|
||||
"backgroundInsideAdventurersHideoutText": "V úkrytu dobrodruhů",
|
||||
"backgroundInsideAdventurersHideoutNotes": "Naplánujte cestu v úkrytu dobrodruhů.",
|
||||
"backgroundCraterLakeText": "Kráterové jezero",
|
||||
"backgroundCraterLakeNotes": "Obdivujte nádherné kráterové jezero.",
|
||||
"backgrounds072023": "Sada 110: Zveřejněna v červenci 2023",
|
||||
"backgroundOnAPaddlewheelBoatText": "Na loďce s lopatkovým kolem",
|
||||
"backgroundOnAPaddlewheelBoatNotes": "Projet se na loďce s lopatkovým kolem.",
|
||||
"backgroundColorfulCoralText": "Barevný korál",
|
||||
"backgroundColorfulCoralNotes": "Potopte se mezi barevné korály.",
|
||||
"backgrounds082023": "Sada 111: zveřejněaa v srpnu 2023",
|
||||
"backgroundBonsaiCollectionText": "Sbírka bonsají",
|
||||
"backgroundBoardwalkIntoSunsetNotes": "Vydejte se po Stezce do západu slunce.",
|
||||
"backgroundBoardwalkIntoSunsetText": "Stezka do západu slunce"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user